AST: Store rules in an array instead of an object

This simplifies the code a bit and makes the AST more regular (each node
type has a fixed set of properties). The latter may get useful later
when generalizing visitors.
redux
David Majda 12 years ago
parent bc5abfef5c
commit 4d5b1d58aa

@ -281,7 +281,9 @@ PEG.compiler.emitter = function(ast) {
' */', ' */',
' parse: function(input, startRule) {', ' parse: function(input, startRule) {',
' var parseFunctions = {', ' var parseFunctions = {',
' #block parseFunctionTableItems.join(",\\n")', ' #for rule in node.rules',
' #{string(rule.name) + ": parse_" + rule.name},',
' #end',
' };', ' };',
' ', ' ',
' if (startRule !== undefined) {', ' if (startRule !== undefined) {',
@ -340,8 +342,8 @@ PEG.compiler.emitter = function(ast) {
' rightmostFailuresExpected.push(failure);', ' rightmostFailuresExpected.push(failure);',
' }', ' }',
' ', ' ',
' #for definition in parseFunctionDefinitions', ' #for rule in node.rules',
' #block definition', ' #block emit(rule)',
' ', ' ',
' #end', ' #end',
' ', ' ',
@ -686,26 +688,7 @@ PEG.compiler.emitter = function(ast) {
} }
var emit = buildNodeVisitor({ var emit = buildNodeVisitor({
grammar: function(node) { grammar: emitSimple("grammar"),
var name;
var parseFunctionTableItems = [];
for (name in node.rules) {
parseFunctionTableItems.push(quote(name) + ": parse_" + name);
}
parseFunctionTableItems.sort();
var parseFunctionDefinitions = [];
for (name in node.rules) {
parseFunctionDefinitions.push(emit(node.rules[name]));
}
return fill("grammar", {
node: node,
parseFunctionTableItems: parseFunctionTableItems,
parseFunctionDefinitions: parseFunctionDefinitions
});
},
initializer: function(node) { return node.code; }, initializer: function(node) { return node.code; },

@ -33,62 +33,62 @@ PEG.parser = (function(){
*/ */
parse: function(input, startRule) { parse: function(input, startRule) {
var parseFunctions = { var parseFunctions = {
"__": parse___,
"action": parse_action,
"and": parse_and,
"braced": parse_braced,
"bracketDelimitedCharacter": parse_bracketDelimitedCharacter,
"choice": parse_choice,
"class": parse_class,
"classCharacter": parse_classCharacter,
"classCharacterRange": parse_classCharacterRange,
"colon": parse_colon,
"comment": parse_comment,
"digit": parse_digit,
"dot": parse_dot,
"doubleQuotedCharacter": parse_doubleQuotedCharacter,
"doubleQuotedString": parse_doubleQuotedString,
"eol": parse_eol,
"eolChar": parse_eolChar,
"eolEscapeSequence": parse_eolEscapeSequence,
"equals": parse_equals,
"grammar": parse_grammar, "grammar": parse_grammar,
"hexDigit": parse_hexDigit,
"hexEscapeSequence": parse_hexEscapeSequence,
"identifier": parse_identifier,
"initializer": parse_initializer, "initializer": parse_initializer,
"rule": parse_rule,
"choice": parse_choice,
"sequence": parse_sequence,
"labeled": parse_labeled, "labeled": parse_labeled,
"letter": parse_letter,
"literal": parse_literal,
"lowerCaseLetter": parse_lowerCaseLetter,
"lparen": parse_lparen,
"multiLineComment": parse_multiLineComment,
"nonBraceCharacter": parse_nonBraceCharacter,
"nonBraceCharacters": parse_nonBraceCharacters,
"not": parse_not,
"plus": parse_plus,
"prefixed": parse_prefixed, "prefixed": parse_prefixed,
"suffixed": parse_suffixed,
"primary": parse_primary, "primary": parse_primary,
"question": parse_question, "action": parse_action,
"rparen": parse_rparen, "braced": parse_braced,
"rule": parse_rule, "nonBraceCharacters": parse_nonBraceCharacters,
"nonBraceCharacter": parse_nonBraceCharacter,
"equals": parse_equals,
"colon": parse_colon,
"semicolon": parse_semicolon, "semicolon": parse_semicolon,
"sequence": parse_sequence,
"simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter,
"simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter,
"simpleEscapeSequence": parse_simpleEscapeSequence,
"simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter,
"singleLineComment": parse_singleLineComment,
"singleQuotedCharacter": parse_singleQuotedCharacter,
"singleQuotedString": parse_singleQuotedString,
"slash": parse_slash, "slash": parse_slash,
"and": parse_and,
"not": parse_not,
"question": parse_question,
"star": parse_star, "star": parse_star,
"plus": parse_plus,
"lparen": parse_lparen,
"rparen": parse_rparen,
"dot": parse_dot,
"identifier": parse_identifier,
"literal": parse_literal,
"string": parse_string, "string": parse_string,
"suffixed": parse_suffixed, "doubleQuotedString": parse_doubleQuotedString,
"doubleQuotedCharacter": parse_doubleQuotedCharacter,
"simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter,
"singleQuotedString": parse_singleQuotedString,
"singleQuotedCharacter": parse_singleQuotedCharacter,
"simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter,
"class": parse_class,
"classCharacterRange": parse_classCharacterRange,
"classCharacter": parse_classCharacter,
"bracketDelimitedCharacter": parse_bracketDelimitedCharacter,
"simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter,
"simpleEscapeSequence": parse_simpleEscapeSequence,
"zeroEscapeSequence": parse_zeroEscapeSequence,
"hexEscapeSequence": parse_hexEscapeSequence,
"unicodeEscapeSequence": parse_unicodeEscapeSequence, "unicodeEscapeSequence": parse_unicodeEscapeSequence,
"eolEscapeSequence": parse_eolEscapeSequence,
"digit": parse_digit,
"hexDigit": parse_hexDigit,
"letter": parse_letter,
"lowerCaseLetter": parse_lowerCaseLetter,
"upperCaseLetter": parse_upperCaseLetter, "upperCaseLetter": parse_upperCaseLetter,
"__": parse___,
"comment": parse_comment,
"singleLineComment": parse_singleLineComment,
"multiLineComment": parse_multiLineComment,
"eol": parse_eol,
"eolChar": parse_eolChar,
"whitespace": parse_whitespace, "whitespace": parse_whitespace,
"zeroEscapeSequence": parse_zeroEscapeSequence
}; };
if (startRule !== undefined) { if (startRule !== undefined) {
@ -189,13 +189,10 @@ PEG.parser = (function(){
} }
if (result0 !== null) { if (result0 !== null) {
result0 = (function(initializer, rules) { result0 = (function(initializer, rules) {
var rulesConverted = {};
each(rules, function(rule) { rulesConverted[rule.name] = rule; });
return { return {
type: "grammar", type: "grammar",
initializer: initializer !== "" ? initializer : null, initializer: initializer !== "" ? initializer : null,
rules: rulesConverted, rules: rules,
startRule: rules[0].name startRule: rules[0].name
}; };
})(result0[1], result0[2]); })(result0[1], result0[2]);

@ -1,12 +1,9 @@
grammar grammar
= __ initializer:initializer? rules:rule+ { = __ initializer:initializer? rules:rule+ {
var rulesConverted = {};
each(rules, function(rule) { rulesConverted[rule.name] = rule; });
return { return {
type: "grammar", type: "grammar",
initializer: initializer !== "" ? initializer : null, initializer: initializer !== "" ? initializer : null,
rules: rulesConverted, rules: rules,
startRule: rules[0].name startRule: rules[0].name
}; };
} }

@ -17,13 +17,7 @@ PEG.compiler.passes = {
} }
var check = buildNodeVisitor({ var check = buildNodeVisitor({
grammar: grammar: checkSubnodes("rules"),
function(node) {
for (var name in node.rules) {
check(node.rules[name]);
}
},
rule: checkExpression, rule: checkExpression,
choice: checkSubnodes("alternatives"), choice: checkSubnodes("alternatives"),
sequence: checkSubnodes("elements"), sequence: checkSubnodes("elements"),
@ -39,7 +33,7 @@ PEG.compiler.passes = {
rule_ref: rule_ref:
function(node) { function(node) {
if (ast.rules[node.name] === undefined) { if (!findRuleByName(ast, node.name)) {
throw new PEG.GrammarError( throw new PEG.GrammarError(
"Referenced rule \"" + node.name + "\" does not exist." "Referenced rule \"" + node.name + "\" does not exist."
); );
@ -62,25 +56,23 @@ PEG.compiler.passes = {
check(node.expression, appliedRules); check(node.expression, appliedRules);
} }
function checkSubnodes(propertyName) {
return function(node, appliedRules) {
each(node[propertyName], function(subnode) {
check(subnode, appliedRules);
});
};
}
var check = buildNodeVisitor({ var check = buildNodeVisitor({
grammar: grammar: checkSubnodes("rules"),
function(node, appliedRules) {
for (var name in node.rules) {
check(node.rules[name], appliedRules);
}
},
rule: rule:
function(node, appliedRules) { function(node, appliedRules) {
check(node.expression, appliedRules.concat(node.name)); check(node.expression, appliedRules.concat(node.name));
}, },
choice: choice: checkSubnodes("alternatives"),
function(node, appliedRules) {
each(node.alternatives, function(alternative) {
check(alternative, appliedRules);
});
},
sequence: sequence:
function(node, appliedRules) { function(node, appliedRules) {
@ -106,7 +98,7 @@ PEG.compiler.passes = {
"Left recursion detected for rule \"" + node.name + "\"." "Left recursion detected for rule \"" + node.name + "\"."
); );
} }
check(ast.rules[node.name], appliedRules); check(findRuleByName(ast, node.name), appliedRules);
}, },
literal: nop, literal: nop,
@ -141,13 +133,7 @@ PEG.compiler.passes = {
} }
var replace = buildNodeVisitor({ var replace = buildNodeVisitor({
grammar: grammar: replaceInSubnodes("rules"),
function(node, from, to) {
for (var name in node.rules) {
replace(node.rules[name], from, to);
}
},
rule: replaceInExpression, rule: replaceInExpression,
choice: replaceInSubnodes("alternatives"), choice: replaceInSubnodes("alternatives"),
sequence: replaceInSubnodes("elements"), sequence: replaceInSubnodes("elements"),
@ -176,15 +162,23 @@ PEG.compiler.passes = {
replace(ast, from, to); replace(ast, from, to);
} }
for (var name in ast.rules) { var indices = [];
if (isProxyRule(ast.rules[name])) {
replaceRuleRefs(ast, ast.rules[name].name, ast.rules[name].expression.name); each(ast.rules, function(rule, i) {
if (name === ast.startRule) { if (isProxyRule(rule)) {
ast.startRule = ast.rules[name].expression.name; replaceRuleRefs(ast, rule.name, rule.expression.name);
if (rule.name === ast.startRule) {
ast.startRule = rule.expression.name;
} }
delete ast.rules[name]; indices.push(i);
} }
} });
indices.reverse();
each(indices, function(index) {
ast.rules.splice(index, 1);
});
}, },
/* /*
@ -240,11 +234,9 @@ PEG.compiler.passes = {
var compute = buildNodeVisitor({ var compute = buildNodeVisitor({
grammar: grammar:
function(node, index) { function(node, index) {
var name; each(node.rules, function(node) {
compute(node, index);
for (name in node.rules) { });
compute(node.rules[name], index);
}
}, },
rule: rule:
@ -356,11 +348,7 @@ PEG.compiler.passes = {
var compute = buildNodeVisitor({ var compute = buildNodeVisitor({
grammar: grammar:
function(node) { function(node) {
var name; each(node.rules, compute);
for (name in node.rules) {
compute(node.rules[name]);
}
}, },
rule: computeForScopedExpression, rule: computeForScopedExpression,

@ -12,6 +12,15 @@ function range(start, stop) {
return result; return result;
} }
function find(array, callback) {
var length = array.length;
for (var i = 0; i < length; i++) {
if (callback(array[i])) {
return array[i];
}
}
}
function contains(array, value) { function contains(array, value) {
/* /*
* Stupid IE does not have Array.prototype.indexOf, otherwise this function * Stupid IE does not have Array.prototype.indexOf, otherwise this function
@ -168,3 +177,7 @@ function buildNodeVisitor(functions) {
return functions[node.type].apply(null, arguments); return functions[node.type].apply(null, arguments);
}; };
} }
function findRuleByName(ast, name) {
return find(ast.rules, function(r) { return r.name === name; });
}

@ -125,7 +125,7 @@ function oneRuleGrammar(expression) {
return { return {
type: "grammar", type: "grammar",
initializer: null, initializer: null,
rules: { start: rule("start", null, expression) }, rules: [rule("start", null, expression)],
startRule: "start" startRule: "start"
}; };
} }
@ -155,16 +155,14 @@ function actionGrammar(action) {
var initializerGrammar = { var initializerGrammar = {
type: "grammar", type: "grammar",
initializer: initializer(" code "), initializer: initializer(" code "),
rules: { rules: [rule("a", null, literalAbcd)],
a: rule("a", null, literalAbcd)
},
startRule: "a" startRule: "a"
}; };
var namedRuleGrammar = { var namedRuleGrammar = {
type: "grammar", type: "grammar",
initializer: null, initializer: null,
rules: { start: rule("start", "abcd", literalAbcd) }, rules: [rule("start", "abcd", literalAbcd)],
startRule: "start" startRule: "start"
}; };
@ -175,7 +173,7 @@ test("parses grammar", function() {
{ {
type: "grammar", type: "grammar",
initializer: null, initializer: null,
rules: { a: rule("a", null, literalAbcd) }, rules: [rule("a", null, literalAbcd)],
startRule: "a" startRule: "a"
} }
); );
@ -185,11 +183,11 @@ test("parses grammar", function() {
{ {
type: "grammar", type: "grammar",
initializer: null, initializer: null,
rules: { rules: [
a: rule("a", null, literalAbcd), rule("a", null, literalAbcd),
b: rule("b", null, literalEfgh), rule("b", null, literalEfgh),
c: rule("c", null, literalIjkl) rule("c", null, literalIjkl)
}, ],
startRule: "a" startRule: "a"
} }
); );
@ -212,7 +210,7 @@ test("parses rule", function() {
{ {
type: "grammar", type: "grammar",
initializer: null, initializer: null,
rules: { start: rule("start", "start rule", choiceLiterals) }, rules: [rule("start", "start rule", choiceLiterals)],
startRule: "start" startRule: "start"
} }
); );

@ -93,15 +93,15 @@ test("removes proxy rules", function() {
function simpleGrammarWithStartAndProxied(startRuleExpression) { function simpleGrammarWithStartAndProxied(startRuleExpression) {
return simpleGrammar( return simpleGrammar(
{ [
start: { {
type: "rule", type: "rule",
name: "start", name: "start",
displayName: null, displayName: null,
expression: startRuleExpression expression: startRuleExpression
}, },
proxied: proxiedRule proxiedRule
}, ],
"start" "start"
); );
} }
@ -109,7 +109,7 @@ test("removes proxy rules", function() {
var cases = [ var cases = [
{ {
grammar: 'start = proxy; proxy = proxied; proxied = "a"', grammar: 'start = proxy; proxy = proxied; proxied = "a"',
ast: simpleGrammar({ proxied: proxiedRule }, "proxied") ast: simpleGrammar([proxiedRule], "proxied")
}, },
{ {
grammar: 'start = proxy / "a" / "b"; proxy = proxied; proxied = "a"', grammar: 'start = proxy / "a" / "b"; proxy = proxied; proxied = "a"',
@ -436,9 +436,9 @@ test("computes variable names", function() {
var ast = PEG.parser.parse(cases[i].grammar); var ast = PEG.parser.parse(cases[i].grammar);
PEG.compiler.passes.computeVarNames(ast); PEG.compiler.passes.computeVarNames(ast);
deepEqual(ast.rules["start"].resultVars, cases[i].resultVars); deepEqual(ast.rules[0].resultVars, cases[i].resultVars);
deepEqual(ast.rules["start"].posVars, cases[i].posVars); deepEqual(ast.rules[0].posVars, cases[i].posVars);
checkDetails(ast.rules["start"].expression, cases[i].details); checkDetails(ast.rules[0].expression, cases[i].details);
} }
}); });
@ -588,7 +588,7 @@ test("computes params", function() {
PEG.compiler.passes.computeParams(ast); PEG.compiler.passes.computeParams(ast);
deepEqual( deepEqual(
cases[i].extractor(ast.rules["start"].expression).params, cases[i].extractor(ast.rules[0].expression).params,
cases[i].params cases[i].params
); );
} }

Loading…
Cancel
Save