Browse Source

Add location information to AST nodes

This will allow to add location information to |GrammarError| exceptions
thrown in various passes.
redux
David Majda 7 years ago
parent
commit
89146915ce
  1. 71
      lib/parser.js
  2. 72
      spec/unit/parser.spec.js
  3. 71
      src/parser.pegjs

71
lib/parser.js

@ -35,10 +35,13 @@ module.exports = (function() {
return {
type: "grammar",
initializer: extractOptional(initializer, 0),
rules: extractList(rules, 0)
rules: extractList(rules, 0),
location: location()
};
},
peg$c1 = function(code) { return { type: "initializer", code: code }; },
peg$c1 = function(code) {
return { type: "initializer", code: code, location: location() };
},
peg$c2 = "=",
peg$c3 = { type: "literal", value: "=", description: "\"=\"" },
peg$c4 = function(name, displayName, expression) {
@ -49,35 +52,59 @@ module.exports = (function() {
? {
type: "named",
name: displayName[0],
expression: expression
expression: expression,
location: location()
}
: expression
: expression,
location: location()
};
},
peg$c5 = "/",
peg$c6 = { type: "literal", value: "/", description: "\"/\"" },
peg$c7 = function(first, rest) {
return rest.length > 0
? { type: "choice", alternatives: buildList(first, rest, 3) }
? {
type: "choice",
alternatives: buildList(first, rest, 3),
location: location()
}
: first;
},
peg$c8 = function(expression, code) {
return code !== null
? { type: "action", expression: expression, code: code[1] }
? {
type: "action",
expression: expression,
code: code[1],
location: location()
}
: expression;
},
peg$c9 = function(first, rest) {
return rest.length > 0
? { type: "sequence", elements: buildList(first, rest, 1) }
? {
type: "sequence",
elements: buildList(first, rest, 1),
location: location()
}
: first;
},
peg$c10 = ":",
peg$c11 = { type: "literal", value: ":", description: "\":\"" },
peg$c12 = function(label, expression) {
return { type: "labeled", label: label, expression: expression };
return {
type: "labeled",
label: label,
expression: expression,
location: location()
};
},
peg$c13 = function(operator, expression) {
return { type: OPS_TO_PREFIXED_TYPES[operator], expression: expression };
return {
type: OPS_TO_PREFIXED_TYPES[operator],
expression: expression,
location: location()
};
},
peg$c14 = "$",
peg$c15 = { type: "literal", value: "$", description: "\"$\"" },
@ -86,7 +113,11 @@ module.exports = (function() {
peg$c18 = "!",
peg$c19 = { type: "literal", value: "!", description: "\"!\"" },
peg$c20 = function(expression, operator) {
return { type: OPS_TO_SUFFIXED_TYPES[operator], expression: expression };
return {
type: OPS_TO_SUFFIXED_TYPES[operator],
expression: expression,
location: location()
};
},
peg$c21 = "?",
peg$c22 = { type: "literal", value: "?", description: "\"?\"" },
@ -100,10 +131,14 @@ module.exports = (function() {
peg$c30 = { type: "literal", value: ")", description: "\")\"" },
peg$c31 = function(expression) { return expression; },
peg$c32 = function(name) {
return { type: "rule_ref", name: name };
return { type: "rule_ref", name: name, location: location() };
},
peg$c33 = function(operator, code) {
return { type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], code: code };
return {
type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator],
code: code,
location: location()
};
},
peg$c34 = { type: "any", description: "any character" },
peg$c35 = { type: "other", description: "whitespace" },
@ -155,7 +190,12 @@ module.exports = (function() {
peg$c81 = "i",
peg$c82 = { type: "literal", value: "i", description: "\"i\"" },
peg$c83 = function(value, ignoreCase) {
return { type: "literal", value: value, ignoreCase: ignoreCase !== null };
return {
type: "literal",
value: value,
ignoreCase: ignoreCase !== null,
location: location()
};
},
peg$c84 = { type: "other", description: "string" },
peg$c85 = "\"",
@ -177,7 +217,8 @@ module.exports = (function() {
parts: filterEmptyStrings(parts),
inverted: inverted !== null,
ignoreCase: ignoreCase !== null,
rawText: text()
rawText: text(),
location: location()
};
},
peg$c99 = "-",
@ -226,7 +267,7 @@ module.exports = (function() {
peg$c132 = { type: "class", value: "[0-9a-f]i", description: "[0-9a-f]i" },
peg$c133 = ".",
peg$c134 = { type: "literal", value: ".", description: "\".\"" },
peg$c135 = function() { return { type: "any" }; },
peg$c135 = function() { return { type: "any", location: location() }; },
peg$c136 = { type: "other", description: "code block" },
peg$c137 = "{",
peg$c138 = { type: "literal", value: "{", description: "\"{\"" },

72
spec/unit/parser.spec.js

@ -98,6 +98,74 @@ describe("PEG.js grammar parser", function() {
rules: [ruleA, ruleB]
};
var stripLocation = (function() {
function buildVisitor(functions) {
return function(node) {
return functions[node.type].apply(null, arguments);
};
}
function stripLeaf(node) {
delete node.location;
}
function stripExpression(node) {
delete node.location;
strip(node.expression);
}
function stripChildren(property) {
return function(node) {
var i;
delete node.location;
for (i = 0; i < node[property].length; i++) {
strip(node[property][i]);
}
};
}
var strip = buildVisitor({
grammar: function(node) {
var i;
delete node.location;
if (node.initializer) {
strip(node.initializer);
}
for (i = 0; i < node.rules.length; i++) {
strip(node.rules[i]);
}
},
initializer: stripLeaf,
rule: stripExpression,
named: stripExpression,
choice: stripChildren("alternatives"),
action: stripExpression,
sequence: stripChildren("elements"),
labeled: stripExpression,
text: stripExpression,
simple_and: stripExpression,
simple_not: stripExpression,
optional: stripExpression,
zero_or_more: stripExpression,
one_or_more: stripExpression,
semantic_and: stripLeaf,
semantic_not: stripLeaf,
rule_ref: stripLeaf,
literal: stripLeaf,
"class": stripLeaf,
any: stripLeaf
});
return strip;
})();
beforeEach(function() {
this.addMatchers({
toParseAs: function(expected) {
@ -106,6 +174,8 @@ describe("PEG.js grammar parser", function() {
try {
result = PEG.parser.parse(this.actual);
stripLocation(result);
this.message = function() {
return "Expected " + jasmine.pp(this.actual) + " "
+ (this.isNot ? "not " : "")
@ -132,6 +202,8 @@ describe("PEG.js grammar parser", function() {
try {
result = PEG.parser.parse(this.actual);
stripLocation(result);
this.message = function() {
return "Expected " + jasmine.pp(this.actual) + " to fail to parse"
+ (details ? " with details " + jasmine.pp(details) : "") + ", "

71
src/parser.pegjs

@ -79,12 +79,15 @@ Grammar
return {
type: "grammar",
initializer: extractOptional(initializer, 0),
rules: extractList(rules, 0)
rules: extractList(rules, 0),
location: location()
};
}
Initializer
= code:CodeBlock EOS { return { type: "initializer", code: code }; }
= code:CodeBlock EOS {
return { type: "initializer", code: code, location: location() };
}
Rule
= name:IdentifierName __
@ -99,9 +102,11 @@ Rule
? {
type: "named",
name: displayName[0],
expression: expression
expression: expression,
location: location()
}
: expression
: expression,
location: location()
};
}
@ -111,33 +116,55 @@ Expression
ChoiceExpression
= first:ActionExpression rest:(__ "/" __ ActionExpression)* {
return rest.length > 0
? { type: "choice", alternatives: buildList(first, rest, 3) }
? {
type: "choice",
alternatives: buildList(first, rest, 3),
location: location()
}
: first;
}
ActionExpression
= expression:SequenceExpression code:(__ CodeBlock)? {
return code !== null
? { type: "action", expression: expression, code: code[1] }
? {
type: "action",
expression: expression,
code: code[1],
location: location()
}
: expression;
}
SequenceExpression
= first:LabeledExpression rest:(__ LabeledExpression)* {
return rest.length > 0
? { type: "sequence", elements: buildList(first, rest, 1) }
? {
type: "sequence",
elements: buildList(first, rest, 1),
location: location()
}
: first;
}
LabeledExpression
= label:Identifier __ ":" __ expression:PrefixedExpression {
return { type: "labeled", label: label, expression: expression };
return {
type: "labeled",
label: label,
expression: expression,
location: location()
};
}
/ PrefixedExpression
PrefixedExpression
= operator:PrefixedOperator __ expression:SuffixedExpression {
return { type: OPS_TO_PREFIXED_TYPES[operator], expression: expression };
return {
type: OPS_TO_PREFIXED_TYPES[operator],
expression: expression,
location: location()
};
}
/ SuffixedExpression
@ -148,7 +175,11 @@ PrefixedOperator
SuffixedExpression
= expression:PrimaryExpression __ operator:SuffixedOperator {
return { type: OPS_TO_SUFFIXED_TYPES[operator], expression: expression };
return {
type: OPS_TO_SUFFIXED_TYPES[operator],
expression: expression,
location: location()
};
}
/ PrimaryExpression
@ -167,12 +198,16 @@ PrimaryExpression
RuleReferenceExpression
= name:IdentifierName !(__ (StringLiteral __)? "=") {
return { type: "rule_ref", name: name };
return { type: "rule_ref", name: name, location: location() };
}
SemanticPredicateExpression
= operator:SemanticPredicateOperator __ code:CodeBlock {
return { type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], code: code };
return {
type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator],
code: code,
location: location()
};
}
SemanticPredicateOperator
@ -306,7 +341,12 @@ BooleanLiteral
LiteralMatcher "literal"
= value:StringLiteral ignoreCase:"i"? {
return { type: "literal", value: value, ignoreCase: ignoreCase !== null };
return {
type: "literal",
value: value,
ignoreCase: ignoreCase !== null,
location: location()
};
}
StringLiteral "string"
@ -335,7 +375,8 @@ CharacterClassMatcher "character class"
parts: filterEmptyStrings(parts),
inverted: inverted !== null,
ignoreCase: ignoreCase !== null,
rawText: text()
rawText: text(),
location: location()
};
}
@ -405,7 +446,7 @@ HexDigit
= [0-9a-f]i
AnyMatcher
= "." { return { type: "any" }; }
= "." { return { type: "any", location: location() }; }
CodeBlock "code block"
= "{" code:Code "}" { return code; }

Loading…
Cancel
Save