avg-storyscript
Version:
Build-in Story Script System for AVG.js.
1,529 lines (1,333 loc) • 317 kB
JavaScript
/*!
* Copyright 2016 Icemic Jia <bingfeng.web@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/*!
* Copyright 2016 Icemic Jia <bingfeng.web@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var parser = __webpack_require__(1);
var variable = __webpack_require__(73);
var _require = __webpack_require__(74),
IfBlock = _require.IfBlock,
WhileBlock = _require.WhileBlock,
ForeachBlock = _require.ForeachBlock,
InsertedBlock = _require.InsertedBlock;
var StoryScript = function () {
function StoryScript(onGlobalChanged) {
_classCallCheck(this, StoryScript);
this.BLOCKSTACK = [];
this.CURRENTBLOCK = null;
this.onGlobalChanged = onGlobalChanged;
}
_createClass(StoryScript, [{
key: 'load',
value: function load(string) {
var result = parser.parse(string);
var system = new IfBlock(result);
this.CURRENTBLOCK = system;
this.BLOCKSTACK = [];
// variable.reset();
}
}, {
key: 'getBlockData',
value: function getBlockData() {
var blocks = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = [].concat(_toConsumableArray(this.BLOCKSTACK), [this.CURRENTBLOCK]).reverse().entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _slicedToArray(_step.value, 2),
node = _step$value[0],
block = _step$value[1];
var blockData = block.getData();
blockData.scope = variable.getScope(node);
blocks.push(blockData);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return blocks.reverse();
}
}, {
key: 'getGlobalScope',
value: function getGlobalScope() {
return variable.getGlobalScope();
}
}, {
key: 'getSaveScope',
value: function getSaveScope() {
return variable.getSaveScope();
}
// @deprecated
}, {
key: 'getData',
value: function getData() {
console.warn('[Storyscript] getData() has been deprecated!');
return {
blocks: this.getBlockData(),
globalScope: this.getGlobalScope(),
saveScope: this.getSaveScope()
};
}
}, {
key: 'setGlobalScope',
value: function setGlobalScope(scope) {
variable.setGlobalScope(scope);
}
}, {
key: 'setSaveScope',
value: function setSaveScope(scope) {
variable.setSaveScope(scope);
}
}, {
key: 'setBlockData',
value: function setBlockData(blocks) {
var scopes = [blocks[0].scope];
variable.setScopes(scopes);
this.CURRENTBLOCK.setCurrentLine(blocks[0].currentLine);
if (blocks.length === 1) {
return true;
}
for (var i = 0; i < blocks.length - 1; i++) {
var block = blocks[i];
var nextBlock = blocks[i + 1];
var lastLine = block.currentLine - 1;
var line = this.CURRENTBLOCK.getLine(lastLine);
if (line.name === nextBlock.type) {
switch (line.name) {
case 'if':
var ifBlock = new IfBlock(line.blocks[nextBlock.blockIndex], nextBlock.blockIndex);
ifBlock.setCurrentLine(nextBlock.currentLine);
variable.pushScope(nextBlock.scope);
// variable.popScope();
this.BLOCKSTACK.push(this.CURRENTBLOCK);
this.CURRENTBLOCK = ifBlock;
break;
case 'while':
var whileBlock = new WhileBlock(line.block, line.condition);
whileBlock.setCurrentLine(nextBlock.currentLine);
variable.pushScope(nextBlock.scope);
// variable.popScope();
this.BLOCKSTACK.push(this.CURRENTBLOCK);
this.CURRENTBLOCK = whileBlock;
break;
case 'foreach':
var foreachBlock = new ForeachBlock(line.block, line.child, line.children);
foreachBlock.setCurrentLine(nextBlock.currentLine);
variable.pushScope(nextBlock.scope);
// variable.popScope();
this.BLOCKSTACK.push(this.CURRENTBLOCK);
this.CURRENTBLOCK = foreachBlock;
break;
default:
throw 'Bad savedata';
}
} else if (nextBlock.type === 'inserted') {
var insertedBlock = new InsertedBlock(nextBlock.data);
insertedBlock.setCurrentLine(nextBlock.currentLine);
variable.pushScope(nextBlock.scope);
// variable.popScope();
this.BLOCKSTACK.push(this.CURRENTBLOCK);
this.CURRENTBLOCK = insertedBlock;
} else {
throw 'Bad savedata';
}
}
}
// @deprecated
}, {
key: 'setData',
value: function setData(object) {
console.warn('[Storyscript] setData() has been deprecated!');
this.setGlobalScope(object.globalScope);
this.setSaveScope(object.saveScope);
this.setBlockData(object.blocks);
}
}, {
key: Symbol.iterator,
value: function value() {
return this;
}
}, {
key: 'next',
value: function next() {
var _CURRENTBLOCK$next = this.CURRENTBLOCK.next(),
value = _CURRENTBLOCK$next.value,
done = _CURRENTBLOCK$next.done;
if (done) {
var CURRENTBLOCK = this.BLOCKSTACK.pop();
if (CURRENTBLOCK) {
this.CURRENTBLOCK = CURRENTBLOCK;
variable.popScope();
return this.next();
} else {
return { done: true };
}
} else {
var retValue = this.handleScript(value);
if (retValue) {
return { value: retValue, done: false };
} else {
// handleLogic will return undefined, so should exec next line
return this.next();
}
}
}
}, {
key: 'handleScript',
value: function handleScript(argLine) {
// deep copy
var line = Object.assign({}, argLine);
if (line.type === 'content') {
return this.handleContent(line);
} else if (line.type === 'logic') {
return this.handleLogic(line);
} else if (line.type === 'comment') {
return null;
} else {
throw 'Unrecognized type ' + line.type;
}
}
}, {
key: 'handleContent',
value: function handleContent(line) {
var params = line.params;
var keys = Object.keys(params);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = keys[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var key = _step2.value;
params[key] = params[key].value;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return line;
}
}, {
key: 'handleLogic',
value: function handleLogic(line) {
switch (line.name) {
case 'if':
return this.handleLogic_IF(line);break;
case 'while':
return this.handleLogic_WHILE(line);break;
case 'foreach':
return this.handleLogic_FOREACH(line);break;
case 'let':
return this.handleLogic_LET(line);break;
default:
throw 'Unrecognized name ' + line.name;
}
}
}, {
key: 'handleLogic_IF',
value: function handleLogic_IF(line) {
var blockIndex = 0;
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = line.conditions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var condition = _step3.value;
if (variable.calc(condition)) {
break;
} else {
blockIndex++;
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
this.BLOCKSTACK.push(this.CURRENTBLOCK);
var blockData = line.blocks[blockIndex];
var block = new IfBlock(blockData, blockIndex);
this.CURRENTBLOCK = block;
// variable.pushScope();
}
}, {
key: 'handleLogic_WHILE',
value: function handleLogic_WHILE(line) {
var result = variable.calc(line.condition);
if (result) {
this.BLOCKSTACK.push(this.CURRENTBLOCK);
var blockData = line.block;
var block = new WhileBlock(blockData, line.condition);
this.CURRENTBLOCK = block;
}
// variable.pushScope();
}
}, {
key: 'handleLogic_FOREACH',
value: function handleLogic_FOREACH(line) {
var children = variable.calc(line.children);
if (children instanceof Array) {
this.BLOCKSTACK.push(this.CURRENTBLOCK);
var blockData = line.block;
var block = new ForeachBlock(blockData, line.child, line.children);
this.CURRENTBLOCK = block;
} else {
throw '[Foreach] Children must be a array';
}
// variable.pushScope();
}
}, {
key: 'handleLogic_LET',
value: function handleLogic_LET(line) {
if (line.left.prefix === '$') {
this.onGlobalChanged && this.onGlobalChanged();
}
variable.assign(line.left.value, line.left.prefix, line.right, line.explicit);
}
}]);
return StoryScript;
}();
// module.exports = StoryScript;
exports.default = StoryScript;
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
/**
* Copyright 2016 Icemic Jia <bingfeng.web@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var ohm = __webpack_require__(2);
// var fs = require('fs');
var actions = __webpack_require__(63);
var contents = __webpack_require__(72);
var myGrammar = ohm.grammar(contents);
var mySemantics = myGrammar.createSemantics();
mySemantics.addOperation('parse', actions);
exports.parse = function (string) {
var m = myGrammar.match(string);
if (m.succeeded()) {
return mySemantics(m).parse();
} else {
throw m.message;
}
};
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
/* global document, XMLHttpRequest */
'use strict';
// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------
var Builder = __webpack_require__(3);
var Grammar = __webpack_require__(5);
var Namespace = __webpack_require__(14);
var common = __webpack_require__(8);
var errors = __webpack_require__(13);
var pexprs = __webpack_require__(38);
var util = __webpack_require__(11);
var isBuffer = __webpack_require__(57);
// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------
// The metagrammar, i.e. the grammar for Ohm grammars. Initialized at the
// bottom of this file because loading the grammar requires Ohm itself.
var ohmGrammar;
// An object which makes it possible to stub out the document API for testing.
var documentInterface = {
querySelector: function(sel) { return document.querySelector(sel); },
querySelectorAll: function(sel) { return document.querySelectorAll(sel); }
};
// Check if `obj` is a DOM element.
function isElement(obj) {
return !!(obj && obj.nodeType === 1);
}
function isUndefined(obj) {
return obj === void 0; // eslint-disable-line no-void
}
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
function isArrayLike(obj) {
if (obj == null) {
return false;
}
var length = obj.length;
return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
}
// TODO: just use the jQuery thing
function load(url) {
var req = new XMLHttpRequest();
req.open('GET', url, false);
try {
req.send();
if (req.status === 0 || req.status === 200) {
return req.responseText;
}
} catch (e) {}
throw new Error('unable to load url ' + url);
}
// Returns a Grammar instance (i.e., an object with a `match` method) for
// `tree`, which is the concrete syntax tree of a user-written grammar.
// The grammar will be assigned into `namespace` under the name of the grammar
// as specified in the source.
function buildGrammar(match, namespace, optOhmGrammarForTesting) {
var builder = new Builder();
var decl;
var currentRuleName;
var currentRuleFormals;
var overriding = false;
var metaGrammar = optOhmGrammarForTesting || ohmGrammar;
// A visitor that produces a Grammar instance from the CST.
var helpers = metaGrammar.createSemantics().addOperation('visit', {
Grammar: function(n, s, open, rs, close) {
var grammarName = n.visit();
decl = builder.newGrammar(grammarName, namespace);
s.visit();
rs.visit();
var g = decl.build();
g.source = this.source.trimmed();
if (grammarName in namespace) {
throw errors.duplicateGrammarDeclaration(g, namespace);
}
namespace[grammarName] = g;
return g;
},
SuperGrammar: function(_, n) {
var superGrammarName = n.visit();
if (superGrammarName === 'null') {
decl.withSuperGrammar(null);
} else {
if (!namespace || !(superGrammarName in namespace)) {
throw errors.undeclaredGrammar(superGrammarName, namespace, n.source);
}
decl.withSuperGrammar(namespace[superGrammarName]);
}
},
Rule_define: function(n, fs, d, _, b) {
currentRuleName = n.visit();
currentRuleFormals = fs.visit()[0] || [];
// If there is no default start rule yet, set it now. This must be done before visiting
// the body, because it might contain an inline rule definition.
if (!decl.defaultStartRule && decl.ensureSuperGrammar() !== Grammar.ProtoBuiltInRules) {
decl.withDefaultStartRule(currentRuleName);
}
var body = b.visit();
var description = d.visit()[0];
var source = this.source.trimmed();
return decl.define(currentRuleName, currentRuleFormals, body, description, source);
},
Rule_override: function(n, fs, _, b) {
currentRuleName = n.visit();
currentRuleFormals = fs.visit()[0] || [];
overriding = true;
var body = b.visit();
var source = this.source.trimmed();
var ans = decl.override(currentRuleName, currentRuleFormals, body, null, source);
overriding = false;
return ans;
},
Rule_extend: function(n, fs, _, b) {
currentRuleName = n.visit();
currentRuleFormals = fs.visit()[0] || [];
var body = b.visit();
var source = this.source.trimmed();
var ans = decl.extend(currentRuleName, currentRuleFormals, body, null, source);
return ans;
},
RuleBody: function(_, terms) {
var args = terms.visit();
return builder.alt.apply(builder, args).withSource(this.source);
},
Formals: function(opointy, fs, cpointy) {
return fs.visit();
},
Params: function(opointy, ps, cpointy) {
return ps.visit();
},
Alt: function(seqs) {
var args = seqs.visit();
return builder.alt.apply(builder, args).withSource(this.source);
},
TopLevelTerm_inline: function(b, n) {
var inlineRuleName = currentRuleName + '_' + n.visit();
var body = b.visit();
var source = this.source.trimmed();
var isNewRuleDeclaration =
!(decl.superGrammar && decl.superGrammar.rules[inlineRuleName]);
if (overriding && !isNewRuleDeclaration) {
decl.override(inlineRuleName, currentRuleFormals, body, null, source);
} else {
decl.define(inlineRuleName, currentRuleFormals, body, null, source);
}
var params = currentRuleFormals.map(function(formal) { return builder.app(formal); });
return builder.app(inlineRuleName, params).withSource(body.source);
},
Seq: function(expr) {
return builder.seq.apply(builder, expr.visit()).withSource(this.source);
},
Iter_star: function(x, _) {
return builder.star(x.visit()).withSource(this.source);
},
Iter_plus: function(x, _) {
return builder.plus(x.visit()).withSource(this.source);
},
Iter_opt: function(x, _) {
return builder.opt(x.visit()).withSource(this.source);
},
Pred_not: function(_, x) {
return builder.not(x.visit()).withSource(this.source);
},
Pred_lookahead: function(_, x) {
return builder.lookahead(x.visit()).withSource(this.source);
},
Lex_lex: function(_, x) {
return builder.lex(x.visit()).withSource(this.source);
},
Base_application: function(rule, ps) {
return builder.app(rule.visit(), ps.visit()[0] || []).withSource(this.source);
},
Base_range: function(from, _, to) {
return builder.range(from.visit(), to.visit()).withSource(this.source);
},
Base_terminal: function(expr) {
return builder.terminal(expr.visit()).withSource(this.source);
},
Base_paren: function(open, x, close) {
return x.visit();
},
ruleDescr: function(open, t, close) {
return t.visit();
},
ruleDescrText: function(_) {
return this.sourceString.trim();
},
caseName: function(_, space1, n, space2, end) {
return n.visit();
},
name: function(first, rest) {
return this.sourceString;
},
nameFirst: function(expr) {},
nameRest: function(expr) {},
terminal: function(open, cs, close) {
return cs.visit().map(function(c) { return common.unescapeChar(c); }).join('');
},
terminalChar: function(_) {
return this.sourceString;
},
escapeChar: function(_) {
return this.sourceString;
},
NonemptyListOf: function(x, _, xs) {
return [x.visit()].concat(xs.visit());
},
EmptyListOf: function() {
return [];
},
_terminal: function() {
return this.primitiveValue;
}
});
return helpers(match).visit();
}
function compileAndLoad(source, namespace) {
var m = ohmGrammar.match(source, 'Grammars');
if (m.failed()) {
throw errors.grammarSyntaxError(m);
}
return buildGrammar(m, namespace);
}
// Return the contents of a script element, fetching it via XHR if necessary.
function getScriptElementContents(el) {
if (!isElement(el)) {
throw new TypeError('Expected a DOM Node, got ' + common.unexpectedObjToString(el));
}
if (el.type !== 'text/ohm-js') {
throw new Error('Expected a script tag with type="text/ohm-js", got ' + el);
}
return el.getAttribute('src') ? load(el.getAttribute('src')) : el.innerHTML;
}
function grammar(source, optNamespace) {
var ns = grammars(source, optNamespace);
// Ensure that the source contained no more than one grammar definition.
var grammarNames = Object.keys(ns);
if (grammarNames.length === 0) {
throw new Error('Missing grammar definition');
} else if (grammarNames.length > 1) {
var secondGrammar = ns[grammarNames[1]];
var interval = secondGrammar.source;
throw new Error(
util.getLineAndColumnMessage(interval.inputStream.source, interval.startIdx) +
'Found more than one grammar definition -- use ohm.grammars() instead.');
}
return ns[grammarNames[0]]; // Return the one and only grammar.
}
function grammars(source, optNamespace) {
var ns = Namespace.extend(Namespace.asNamespace(optNamespace));
if (typeof source !== 'string') {
// For convenience, detect Node.js Buffer objects and automatically call toString().
if (isBuffer(source)) {
source = source.toString();
} else {
throw new TypeError(
'Expected string as first argument, got ' + common.unexpectedObjToString(source));
}
}
compileAndLoad(source, ns);
return ns;
}
function grammarFromScriptElement(optNode) {
var node = optNode;
if (isUndefined(node)) {
var nodeList = documentInterface.querySelectorAll('script[type="text/ohm-js"]');
if (nodeList.length !== 1) {
throw new Error(
'Expected exactly one script tag with type="text/ohm-js", found ' + nodeList.length);
}
node = nodeList[0];
}
return grammar(getScriptElementContents(node));
}
function grammarsFromScriptElements(optNodeOrNodeList) {
// Simple case: the argument is a DOM node.
if (isElement(optNodeOrNodeList)) {
return grammars(optNodeOrNodeList);
}
// Otherwise, it must be either undefined or a NodeList.
var nodeList = optNodeOrNodeList;
if (isUndefined(nodeList)) {
// Find all script elements with type="text/ohm-js".
nodeList = documentInterface.querySelectorAll('script[type="text/ohm-js"]');
} else if (typeof nodeList === 'string' || (!isElement(nodeList) && !isArrayLike(nodeList))) {
throw new TypeError('Expected a Node, NodeList, or Array, but got ' + nodeList);
}
var ns = Namespace.createNamespace();
for (var i = 0; i < nodeList.length; ++i) {
// Copy the new grammars into `ns` to keep the namespace flat.
common.extend(ns, grammars(getScriptElementContents(nodeList[i]), ns));
}
return ns;
}
function makeRecipe(recipe) {
if (typeof recipe === 'function') {
return recipe.call(new Builder());
} else {
if (typeof recipe === 'string') {
// stringified JSON recipe
recipe = JSON.parse(recipe);
}
return (new Builder()).fromRecipe(recipe);
}
}
// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------
// Stuff that users should know about
module.exports = {
createNamespace: Namespace.createNamespace,
grammar: grammar,
grammars: grammars,
grammarFromScriptElement: grammarFromScriptElement,
grammarsFromScriptElements: grammarsFromScriptElements,
makeRecipe: makeRecipe,
ohmGrammar: null, // Initialized below, after Grammar.BuiltInRules.
pexprs: pexprs,
util: util,
extras: __webpack_require__(58)
};
// Stuff for testing, etc.
module.exports._buildGrammar = buildGrammar;
module.exports._setDocumentInterfaceForTesting = function(doc) { documentInterface = doc; };
// Late initialization for stuff that is bootstrapped.
Grammar.BuiltInRules = __webpack_require__(60);
var Semantics = __webpack_require__(15);
var operationsAndAttributesGrammar = __webpack_require__(61);
Semantics.initBuiltInSemantics(Grammar.BuiltInRules);
Semantics.initPrototypeParser(operationsAndAttributesGrammar); // requires BuiltInSemantics
module.exports.ohmGrammar = ohmGrammar = __webpack_require__(62);
Grammar.initApplicationParser(ohmGrammar, buildGrammar);
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------
var GrammarDecl = __webpack_require__(4);
var pexprs = __webpack_require__(38);
// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------
function Builder() {}
Builder.prototype = {
currentDecl: null,
newGrammar: function(name) {
return new GrammarDecl(name);
},
grammar: function(metaInfo, name, superGrammar, defaultStartRule, rules) {
var gDecl = new GrammarDecl(name);
if (superGrammar) {
gDecl.withSuperGrammar(this.fromRecipe(superGrammar));
}
if (defaultStartRule) {
gDecl.withDefaultStartRule(defaultStartRule);
}
if (metaInfo && metaInfo.source) {
gDecl.withSource(metaInfo.source);
}
var self = this;
this.currentDecl = gDecl;
Object.keys(rules).forEach(function(ruleName) {
var ruleRecipe = rules[ruleName];
var action = ruleRecipe[0]; // define/extend/override
var metaInfo = ruleRecipe[1];
var description = ruleRecipe[2];
var formals = ruleRecipe[3];
var body = self.fromRecipe(ruleRecipe[4]);
var source;
if (gDecl.source && metaInfo && metaInfo.sourceInterval) {
var inputStream = gDecl.source.inputStream;
source = inputStream.interval.apply(inputStream, metaInfo.sourceInterval);
}
gDecl[action](ruleName, formals, body, description, source);
});
this.currentDecl = null;
return gDecl.build();
},
terminal: function(x) {
return new pexprs.Terminal(x);
},
range: function(from, to) {
return new pexprs.Range(from, to);
},
param: function(index) {
return new pexprs.Param(index);
},
alt: function(/* term1, term1, ... */) {
var terms = [];
for (var idx = 0; idx < arguments.length; idx++) {
var arg = arguments[idx];
if (!(arg instanceof pexprs.PExpr)) {
arg = this.fromRecipe(arg);
}
if (arg instanceof pexprs.Alt) {
terms = terms.concat(arg.terms);
} else {
terms.push(arg);
}
}
return terms.length === 1 ? terms[0] : new pexprs.Alt(terms);
},
seq: function(/* factor1, factor2, ... */) {
var factors = [];
for (var idx = 0; idx < arguments.length; idx++) {
var arg = arguments[idx];
if (!(arg instanceof pexprs.PExpr)) {
arg = this.fromRecipe(arg);
}
if (arg instanceof pexprs.Seq) {
factors = factors.concat(arg.factors);
} else {
factors.push(arg);
}
}
return factors.length === 1 ? factors[0] : new pexprs.Seq(factors);
},
star: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Star(expr);
},
plus: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Plus(expr);
},
opt: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Opt(expr);
},
not: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Not(expr);
},
la: function(expr) {
// TODO: temporary to still be able to read old recipes
return this.lookahead(expr);
},
lookahead: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Lookahead(expr);
},
lex: function(expr) {
if (!(expr instanceof pexprs.PExpr)) {
expr = this.fromRecipe(expr);
}
return new pexprs.Lex(expr);
},
app: function(ruleName, optParams) {
if (optParams && optParams.length > 0) {
optParams = optParams.map(function(param) {
return param instanceof pexprs.PExpr ? param :
this.fromRecipe(param);
}, this);
}
return new pexprs.Apply(ruleName, optParams);
},
fromRecipe: function(recipe) {
// the meta-info of 'grammar' is proccessed in Builder.grammar
var result = this[recipe[0]].apply(this,
recipe[0] === 'grammar' ? recipe.slice(1) : recipe.slice(2));
var metaInfo = recipe[1];
if (metaInfo) {
if (metaInfo.sourceInterval && this.currentDecl) {
result.withSource(
this.currentDecl.sourceInterval.apply(this.currentDecl, metaInfo.sourceInterval)
);
}
}
return result;
}
};
// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------
module.exports = Builder;
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------
var Grammar = __webpack_require__(5);
var InputStream = __webpack_require__(35);
var common = __webpack_require__(8);
var errors = __webpack_require__(13);
var pexprs = __webpack_require__(38);
// --------------------------------------------------------------------
// Private Stuff
// --------------------------------------------------------------------
// Constructors
function GrammarDecl(name) {
this.name = name;
}
// Helpers
GrammarDecl.prototype.sourceInterval = function(startIdx, endIdx) {
var inputStream = this.source.inputStream;
return inputStream.interval(startIdx, endIdx);
};
GrammarDecl.prototype.ensureSuperGrammar = function() {
if (!this.superGrammar) {
this.withSuperGrammar(
// TODO: The conditional expression below is an ugly hack. It's kind of ok because
// I doubt anyone will ever try to declare a grammar called `BuiltInRules`. Still,
// we should try to find a better way to do this.
this.name === 'BuiltInRules' ?
Grammar.ProtoBuiltInRules :
Grammar.BuiltInRules);
}
return this.superGrammar;
};
GrammarDecl.prototype.installOverriddenOrExtendedRule = function(name, formals, body, source) {
var duplicateParameterNames = common.getDuplicates(formals);
if (duplicateParameterNames.length > 0) {
throw errors.duplicateParameterNames(name, duplicateParameterNames, source);
}
var ruleInfo = this.ensureSuperGrammar().rules[name];
var expectedFormals = ruleInfo.formals;
var expectedNumFormals = expectedFormals ? expectedFormals.length : 0;
if (formals.length !== expectedNumFormals) {
throw errors.wrongNumberOfParameters(name, expectedNumFormals, formals.length, source);
}
return this.install(name, formals, body, ruleInfo.description, source);
};
GrammarDecl.prototype.install = function(name, formals, body, description, source) {
this.rules[name] = {
body: body.introduceParams(formals),
formals: formals,
description: description,
source: source
};
return this;
};
// Stuff that you should only do once
GrammarDecl.prototype.withSuperGrammar = function(superGrammar) {
if (this.superGrammar) {
throw new Error('the super grammar of a GrammarDecl cannot be set more than once');
}
this.superGrammar = superGrammar;
this.rules = Object.create(superGrammar.rules);
// Grammars with an explicit supergrammar inherit a default start rule.
if (!superGrammar.isBuiltIn()) {
this.defaultStartRule = superGrammar.defaultStartRule;
}
return this;
};
GrammarDecl.prototype.withDefaultStartRule = function(ruleName) {
this.defaultStartRule = ruleName;
return this;
};
GrammarDecl.prototype.withSource = function(source) {
this.source = new InputStream(source).interval(0, source.length);
return this;
};
// Creates a Grammar instance, and if it passes the sanity checks, returns it.
GrammarDecl.prototype.build = function() {
var grammar = new Grammar(
this.name,
this.ensureSuperGrammar(),
this.rules,
this.defaultStartRule);
// TODO: change the pexpr.prototype.assert... methods to make them add
// exceptions to an array that's provided as an arg. Then we'll be able to
// show more than one error of the same type at a time.
// TODO: include the offending pexpr in the errors, that way we can show
// the part of the source that caused it.
var grammarErrors = [];
var grammarHasInvalidApplications = false;
Object.keys(grammar.rules).forEach(function(ruleName) {
var body = grammar.rules[ruleName].body;
try {
body.assertChoicesHaveUniformArity(ruleName);
} catch (e) {
grammarErrors.push(e);
}
try {
body.assertAllApplicationsAreValid(ruleName, grammar);
} catch (e) {
grammarErrors.push(e);
grammarHasInvalidApplications = true;
}
});
if (!grammarHasInvalidApplications) {
// The following check can only be done if the grammar has no invalid applications.
Object.keys(grammar.rules).forEach(function(ruleName) {
var body = grammar.rules[ruleName].body;
try {
body.assertIteratedExprsAreNotNullable(grammar, ruleName);
} catch (e) {
grammarErrors.push(e);
}
});
}
if (grammarErrors.length > 0) {
errors.throwErrors(grammarErrors);
}
if (this.source) {
grammar.source = this.source;
}
return grammar;
};
// Rule declarations
GrammarDecl.prototype.define = function(name, formals, body, description, source) {
this.ensureSuperGrammar();
if (this.superGrammar.rules[name]) {
throw errors.duplicateRuleDeclaration(name, this.name, this.superGrammar.name, source);
} else if (this.rules[name]) {
throw errors.duplicateRuleDeclaration(name, this.name, this.name, source);
}
var duplicateParameterNames = common.getDuplicates(formals);
if (duplicateParameterNames.length > 0) {
throw errors.duplicateParameterNames(name, duplicateParameterNames, source);
}
return this.install(name, formals, body, description, source);
};
GrammarDecl.prototype.override = function(name, formals, body, descIgnored, source) {
var ruleInfo = this.ensureSuperGrammar().rules[name];
if (!ruleInfo) {
throw errors.cannotOverrideUndeclaredRule(name, this.superGrammar.name, source);
}
this.installOverriddenOrExtendedRule(name, formals, body, source);
return this;
};
GrammarDecl.prototype.extend = function(name, formals, fragment, descIgnored, source) {
var ruleInfo = this.ensureSuperGrammar().rules[name];
if (!ruleInfo) {
throw errors.cannotExtendUndeclaredRule(name, this.superGrammar.name, source);
}
var body = new pexprs.Extend(this.superGrammar, name, fragment);
body.source = fragment.source;
this.installOverriddenOrExtendedRule(name, formals, body, source);
return this;
};
// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------
module.exports = GrammarDecl;
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------
var MatchResult = __webpack_require__(6);
var Semantics = __webpack_require__(15);
var State = __webpack_require__(34);
var common = __webpack_require__(8);
var errors = __webpack_require__(13);
var pexprs = __webpack_require__(38);
// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------
function getSortedRuleValues(grammar) {
return Object.keys(grammar.rules).sort().map(function(name) { return grammar.rules[name]; });
}
function Grammar(
name,
superGrammar,
rules,
optDefaultStartRule) {
this.name = name;
this.superGrammar = superGrammar;
this.rules = rules;
if (optDefaultStartRule) {
if (!(optDefaultStartRule in rules)) {
throw new Error("Invalid start rule: '" + optDefaultStartRule +
"' is not a rule in grammar '" + name + "'");
}
this.defaultStartRule = optDefaultStartRule;
}
}
var ohmGrammar;
var buildGrammar;
// This method is called from main.js once Ohm has loaded.
Grammar.initApplicationParser = function(grammar, builderFn) {
ohmGrammar = grammar;
buildGrammar = builderFn;
};
Grammar.prototype = {
// Return true if the grammar is a built-in grammar, otherwise false.
// NOTE: This might give an unexpected result if called before BuiltInRules is defined!
isBuiltIn: function() {
return this === Grammar.ProtoBuiltInRules || this === Grammar.BuiltInRules;
},
equals: function(g) {
if (this === g) {
return true;
}
// Do the cheapest comparisons first.
if (g == null ||
this.name !== g.name ||
this.defaultStartRule !== g.defaultStartRule ||
!(this.superGrammar === g.superGrammar || this.superGrammar.equals(g.superGrammar))) {
return false;
}
var myRules = getSortedRuleValues(this);
var otherRules = getSortedRuleValues(g);
return myRules.length === otherRules.length && myRules.every(function(rule, i) {
return rule.description === otherRules[i].description &&
rule.formals.join(',') === otherRules[i].formals.join(',') &&
rule.body.toString() === otherRules[i].body.toString();
});
},
_match: function(input, opts) {
var state = new State(this, input, opts);
state.evalFromStart();
return state;
},
match: function(input, optStartApplication) {
var state = this._match(input, {startApplication: optStartApplication});
return MatchResult.newFor(state);
},
trace: function(input, optStartApplication) {
var state = this._match(input, {startApplication: optStartApplication, trace: true});
// The trace node for the start rule is always the last entry. If it is a syntactic rule,
// the first entry is for an application of 'spaces'.
// TODO(pdubroy): Clean this up by introducing a special `Match<startAppl>` rule, which will
// ensure that there is always a single root trace node.
var rootTrace = state.trace[state.trace.length - 1];
rootTrace.state = state;
rootTrace.result = MatchResult.newFor(state);
return rootTrace;
},
semantics: function() {
// TODO: Remove this eventually! Deprecated in v0.12.
throw new Error('semantics() is deprecated -- use createSemantics() instead.');
},
createSemantics: function() {
return Semantics.createSemantics(this);
},
extendSemantics: function(superSemantics) {
return Semantics.createSemantics(this, superSemantics._getSemantics());
},
// Check that every key in `actionDict` corresponds to a semantic action, and that it maps to
// a function of the correct arity. If not, throw an exception.
_checkTopDownActionDict: function(what, name, actionDict) {
function isSpecialAction(a) {
return a === '_iter' || a === '_terminal' || a === '_nonterminal' || a === '_default';
}
var problems = [];
for (var k in actionDict) {
var v = actionDict[k];
if (!isSpecialAction(k) && !(k in this.rules)) {
problems.push("'" + k + "' is not a valid semantic action for '" + this.name + "'");
} else if (typeof v !== 'function') {
problems.push(
"'" + k + "' must be a function in an action dictionary for '" + this.name + "'");
} else {
var actual = v.length;
var expected = this._topDownActionArity(k);
if (actual !== expected) {
problems.push(
"Semantic action '" + k + "' has the wrong arity: " +
'expected ' + expected + ', got ' + actual);
}
}
}
if (problems.length > 0) {
var prettyProblems = problems.map(function(problem) { return '- ' + problem; });
var error = new Error(
"Found errors in the action dictionary of the '" + name + "' " + what + ':\n' +
prettyProblems.join('\n'));
error.problems = problems;
throw error;
}
},
// Return the expected arity for a semantic action named `actionName`, which
// is either a rule name or a special action name like '_nonterminal'.
_topDownActionArity: function(actionName) {
if (actionName === '_iter' || actionName === '_nonterminal' || actionName === '_default') {
return 1;
} else if (actionName === '_terminal') {
return 0;
}
return this.rules[actionName].body.getArity();
},
_inheritsFrom: function(grammar) {
var g = this.superGrammar;
while (g) {
if (g.equals(grammar, true)) {
return true;
}
g = g.superGrammar;
}
return false;
},
toRecipe: function(optVarName) {
var metaInfo = {};
// Include the grammar source if it is available.
if (this.source) {
metaInfo.source = this.source.contents;
}
var superGrammar = null;
if (this.superGrammar && !this.superGrammar.isBuiltIn()) {
superGrammar = JSON.parse(this.superGrammar.toRecipe());
}
var startRule = null;
if (this.defaultStartRule) {
startRule = this.defaultStartRule;
}
var rules = {};
var self = this;
Object.keys(this.rules).forEach(function(ruleName) {
var ruleInfo = self.rules[ruleName];
var body = ruleInfo.body;
var isDefinition = !self.superGrammar || !self.superGrammar.rules[ruleName];
var operation;
if (isDefinition) {
operation = 'define';
} else {
operation = body instanceof pexprs.Extend ? 'extend' : 'override';
}
var metaInfo = {};
if (ruleInfo.source && self.source) {
var adjusted = ruleInfo.source.relativeTo(self.source);
metaInfo.sourceInterval = [adjusted.startIdx, adjusted.endIdx];
}
var description = isDefinition ? ruleInfo.description : null;
var bodyRecipe = body.outputRecipe(ruleInfo.formals, self.source);
rules[ruleName] = [
operation, // "define"/"extend"/"override"
metaInfo,
description,
ruleInfo.formals,
bodyRecipe
];
});
return JSON.stringify([
'grammar',
metaInfo,
this.name,
superGrammar,
startRule,
rules
]);
},
// TODO: Come up with better names for these methods.
// TODO: Write the analog of these methods for inherited attributes.
toOperationActionDictionaryTemplate: function() {
return this._toOperationOrAttributeActionDictionaryTemplate();
},
toAttributeActionDictionaryTemplate: function() {
return this._toOperationOrAttributeActionDictionaryTemplate();
},
_toOperationOrAttributeActionDictionaryTemplate: function() {
// TODO: add the super-grammar's templates at the right place, e.g., a case for AddExpr_plus
// should appear next to other cases of AddExpr.
var sb = new common.StringBuffer();
sb.append('{');
var first = true;
for (var ruleName in this.rules) {
var body = this.rules[ruleName].body;
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append('\n');
sb.append(' ');
this.addSemanticActionTemplate(ruleName, body, sb);
}
sb.append('\n}');
return sb.contents();
},
addSemanticActionTemplate: function(ruleName, body, sb) {
sb.append(ruleName);
sb.append(': function(');
var arity = this._topDownActionArity(ruleName);
sb.append(common.repeat('_', arity).join(', '));
sb.append(') {\n');
sb.append(' }');
},
// Parse a string which expresses a rule application in this grammar, and return the
// resulting Apply node.
parseApplication: function(str) {
var app;
if (str.indexOf('<') === -1) {
// simple application
app = new pexprs.Apply(str);
} else {
// parameterized application
var cst = ohmGrammar.match(str, 'Base_application');
app = buildGrammar(cst, {});
}
// Ensure that the application is valid.
if (!(app.ruleName in this.rules)) {
throw errors.undeclar