Parser calls AST node creator now
Before this commit, the PEG.js parser always created the AST using a plain JavaSctript object, but allthough simple and effective for the job, this method sacrificies performance slightly. From now on the parser shall call a Node creator. This should help with performance, as well as in the future move some AST helpers into the new AST functions.
This commit is contained in:
parent
7cdfc03e9f
commit
93cc6c5b26
|
@ -35,7 +35,7 @@ task( "lint", () => gulp
|
||||||
.src( [
|
.src( [
|
||||||
"**/.*rc.js",
|
"**/.*rc.js",
|
||||||
"lib/**/*.js",
|
"lib/**/*.js",
|
||||||
"!lib/parser.js",
|
"!lib/parser/index.js",
|
||||||
"test/benchmark/**/*.js",
|
"test/benchmark/**/*.js",
|
||||||
"test/benchmark/run",
|
"test/benchmark/run",
|
||||||
"test/impact",
|
"test/impact",
|
||||||
|
@ -65,7 +65,7 @@ task( "benchmark", cb => {
|
||||||
// Generate the grammar parser.
|
// Generate the grammar parser.
|
||||||
task( "build:parser", cb => {
|
task( "build:parser", cb => {
|
||||||
|
|
||||||
node( "bin/peg src/parser.pegjs -o lib/parser.js -c src/config.json", cb );
|
node( "bin/peg src/parser.pegjs -o lib/parser/index.js -c src/config.json", cb );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
28
lib/parser/ast.js
Normal file
28
lib/parser/ast.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
|
||||||
|
constructor( type, location ) {
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
this.location = location;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
exports.Node = Node;
|
||||||
|
|
||||||
|
class Grammar extends Node {
|
||||||
|
|
||||||
|
// Creates a new AST
|
||||||
|
constructor( initializer, rules, location ) {
|
||||||
|
|
||||||
|
super( "grammar", location );
|
||||||
|
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.rules = rules;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
exports.Grammar = Grammar;
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var ast = require("./ast");
|
||||||
|
var util = require("../util");
|
||||||
|
|
||||||
function peg$subclass(child, parent) {
|
function peg$subclass(child, parent) {
|
||||||
function C() { this.constructor = child; }
|
function C() { this.constructor = child; }
|
||||||
C.prototype = parent.prototype;
|
C.prototype = parent.prototype;
|
||||||
|
@ -138,61 +141,50 @@ function peg$parse(input, options) {
|
||||||
var peg$startRuleFunction = peg$parseGrammar;
|
var peg$startRuleFunction = peg$parseGrammar;
|
||||||
|
|
||||||
var peg$c0 = function(initializer, rules) {
|
var peg$c0 = function(initializer, rules) {
|
||||||
return {
|
return new ast.Grammar(
|
||||||
type: "grammar",
|
extractOptional(initializer, 0),
|
||||||
initializer: extractOptional(initializer, 0),
|
extractList(rules, 0),
|
||||||
rules: extractList(rules, 0),
|
location(),
|
||||||
location: location()
|
);
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c1 = function(code) {
|
var peg$c1 = function(code) {
|
||||||
return { type: "initializer", code: code, location: location() };
|
return createNode( "initializer", { code: code } );
|
||||||
};
|
};
|
||||||
var peg$c2 = "=";
|
var peg$c2 = "=";
|
||||||
var peg$c3 = peg$literalExpectation("=", false);
|
var peg$c3 = peg$literalExpectation("=", false);
|
||||||
var peg$c4 = function(name, displayName, expression) {
|
var peg$c4 = function(name, displayName, expression) {
|
||||||
return {
|
return createNode( "rule", {
|
||||||
type: "rule",
|
|
||||||
name: name,
|
name: name,
|
||||||
expression: displayName !== null
|
expression: displayName !== null
|
||||||
? {
|
? createNode( "named", {
|
||||||
type: "named",
|
|
||||||
name: displayName[0],
|
name: displayName[0],
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: expression,
|
: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c5 = "/";
|
var peg$c5 = "/";
|
||||||
var peg$c6 = peg$literalExpectation("/", false);
|
var peg$c6 = peg$literalExpectation("/", false);
|
||||||
var peg$c7 = function(head, tail) {
|
var peg$c7 = function(head, tail) {
|
||||||
return tail.length > 0
|
return tail.length > 0
|
||||||
? {
|
? createNode( "choice", {
|
||||||
type: "choice",
|
|
||||||
alternatives: buildList(head, tail, 3),
|
alternatives: buildList(head, tail, 3),
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: head;
|
: head;
|
||||||
};
|
};
|
||||||
var peg$c8 = function(expression, code) {
|
var peg$c8 = function(expression, code) {
|
||||||
return code !== null
|
return code !== null
|
||||||
? {
|
? createNode( "action", {
|
||||||
type: "action",
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
code: code[1],
|
code: code[1],
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: expression;
|
: expression;
|
||||||
};
|
};
|
||||||
var peg$c9 = function(head, tail) {
|
var peg$c9 = function(head, tail) {
|
||||||
return tail.length > 0
|
return tail.length > 0
|
||||||
? {
|
? createNode( "sequence", {
|
||||||
type: "sequence",
|
|
||||||
elements: buildList(head, tail, 1),
|
elements: buildList(head, tail, 1),
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: head;
|
: head;
|
||||||
};
|
};
|
||||||
var peg$c10 = ":";
|
var peg$c10 = ":";
|
||||||
|
@ -202,19 +194,15 @@ function peg$parse(input, options) {
|
||||||
error(`Label can't be a reserved word "${label[0]}".`, label[1]);
|
error(`Label can't be a reserved word "${label[0]}".`, label[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return createNode( "labeled", {
|
||||||
type: "labeled",
|
|
||||||
label: label[0],
|
label: label[0],
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c13 = function(operator, expression) {
|
var peg$c13 = function(operator, expression) {
|
||||||
return {
|
return createNode( OPS_TO_PREFIXED_TYPES[operator], {
|
||||||
type: OPS_TO_PREFIXED_TYPES[operator],
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c14 = "$";
|
var peg$c14 = "$";
|
||||||
var peg$c15 = peg$literalExpectation("$", false);
|
var peg$c15 = peg$literalExpectation("$", false);
|
||||||
|
@ -223,11 +211,9 @@ function peg$parse(input, options) {
|
||||||
var peg$c18 = "!";
|
var peg$c18 = "!";
|
||||||
var peg$c19 = peg$literalExpectation("!", false);
|
var peg$c19 = peg$literalExpectation("!", false);
|
||||||
var peg$c20 = function(expression, operator) {
|
var peg$c20 = function(expression, operator) {
|
||||||
return {
|
return createNode( OPS_TO_SUFFIXED_TYPES[operator], {
|
||||||
type: OPS_TO_SUFFIXED_TYPES[operator],
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c21 = "?";
|
var peg$c21 = "?";
|
||||||
var peg$c22 = peg$literalExpectation("?", false);
|
var peg$c22 = peg$literalExpectation("?", false);
|
||||||
|
@ -245,18 +231,14 @@ function peg$parse(input, options) {
|
||||||
// nodes that already isolate label scope themselves. This leaves us with
|
// nodes that already isolate label scope themselves. This leaves us with
|
||||||
// "labeled" and "sequence".
|
// "labeled" and "sequence".
|
||||||
return expression.type === "labeled" || expression.type === "sequence"
|
return expression.type === "labeled" || expression.type === "sequence"
|
||||||
? { type: "group", expression: expression, location: location() }
|
? createNode( "group", { expression: expression } )
|
||||||
: expression;
|
: expression;
|
||||||
};
|
};
|
||||||
var peg$c32 = function(name) {
|
var peg$c32 = function(name) {
|
||||||
return { type: "rule_ref", name: name, location: location() };
|
return createNode( "rule_ref", { name: name } );
|
||||||
};
|
};
|
||||||
var peg$c33 = function(operator, code) {
|
var peg$c33 = function(operator, code) {
|
||||||
return {
|
return createNode( OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], { code: code } );
|
||||||
type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator],
|
|
||||||
code: code,
|
|
||||||
location: location()
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c34 = peg$anyExpectation();
|
var peg$c34 = peg$anyExpectation();
|
||||||
var peg$c35 = peg$otherExpectation("whitespace");
|
var peg$c35 = peg$otherExpectation("whitespace");
|
||||||
|
@ -292,12 +274,10 @@ function peg$parse(input, options) {
|
||||||
var peg$c65 = peg$otherExpectation("literal");
|
var peg$c65 = peg$otherExpectation("literal");
|
||||||
var peg$c66 = "i";
|
var peg$c66 = "i";
|
||||||
var peg$c67 = function(value, ignoreCase) {
|
var peg$c67 = function(value, ignoreCase) {
|
||||||
return {
|
return createNode( "literal", {
|
||||||
type: "literal",
|
|
||||||
value: value,
|
value: value,
|
||||||
ignoreCase: ignoreCase !== null,
|
ignoreCase: ignoreCase !== null,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c68 = peg$otherExpectation("string");
|
var peg$c68 = peg$otherExpectation("string");
|
||||||
var peg$c69 = "\"";
|
var peg$c69 = "\"";
|
||||||
|
@ -309,13 +289,11 @@ function peg$parse(input, options) {
|
||||||
var peg$c75 = "^";
|
var peg$c75 = "^";
|
||||||
var peg$c76 = "]";
|
var peg$c76 = "]";
|
||||||
var peg$c77 = function(inverted, parts, ignoreCase) {
|
var peg$c77 = function(inverted, parts, ignoreCase) {
|
||||||
return {
|
return createNode( "class", {
|
||||||
type: "class",
|
|
||||||
parts: parts.filter(part => part !== ""),
|
parts: parts.filter(part => part !== ""),
|
||||||
inverted: inverted !== null,
|
inverted: inverted !== null,
|
||||||
ignoreCase: ignoreCase !== null,
|
ignoreCase: ignoreCase !== null,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
};
|
};
|
||||||
var peg$c78 = "-";
|
var peg$c78 = "-";
|
||||||
var peg$c79 = function(begin, end) {
|
var peg$c79 = function(begin, end) {
|
||||||
|
@ -351,7 +329,7 @@ function peg$parse(input, options) {
|
||||||
var peg$c99 = /^[0-9a-f]/i;
|
var peg$c99 = /^[0-9a-f]/i;
|
||||||
var peg$c100 = ".";
|
var peg$c100 = ".";
|
||||||
var peg$c101 = peg$literalExpectation(".", false);
|
var peg$c101 = peg$literalExpectation(".", false);
|
||||||
var peg$c102 = function() { return { type: "any", location: location() }; };
|
var peg$c102 = function() { return createNode( "any", {} ); };
|
||||||
var peg$c103 = peg$otherExpectation("code block");
|
var peg$c103 = peg$otherExpectation("code block");
|
||||||
var peg$c104 = "{";
|
var peg$c104 = "{";
|
||||||
var peg$c105 = "}";
|
var peg$c105 = "}";
|
||||||
|
@ -3121,6 +3099,12 @@ function peg$parse(input, options) {
|
||||||
return [head].concat(extractList(tail, index));
|
return [head].concat(extractList(tail, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNode( type, details ) {
|
||||||
|
const node = new ast.Node( type, location() );
|
||||||
|
util.extend( node, details );
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
peg$begin();
|
peg$begin();
|
||||||
peg$result = peg$startRuleFunction();
|
peg$result = peg$startRuleFunction();
|
|
@ -1,3 +1,7 @@
|
||||||
{
|
{
|
||||||
"header": "/* eslint-disable */"
|
"header": "/* eslint-disable */",
|
||||||
|
"dependencies": {
|
||||||
|
"ast": "./ast",
|
||||||
|
"util": "../util"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,23 +89,28 @@
|
||||||
function buildList(head, tail, index) {
|
function buildList(head, tail, index) {
|
||||||
return [head].concat(extractList(tail, index));
|
return [head].concat(extractList(tail, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNode( type, details ) {
|
||||||
|
const node = new ast.Node( type, location() );
|
||||||
|
util.extend( node, details );
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Syntactic Grammar -----
|
// ---- Syntactic Grammar -----
|
||||||
|
|
||||||
Grammar
|
Grammar
|
||||||
= __ initializer:(Initializer __)? rules:(Rule __)+ {
|
= __ initializer:(Initializer __)? rules:(Rule __)+ {
|
||||||
return {
|
return new ast.Grammar(
|
||||||
type: "grammar",
|
extractOptional(initializer, 0),
|
||||||
initializer: extractOptional(initializer, 0),
|
extractList(rules, 0),
|
||||||
rules: extractList(rules, 0),
|
location(),
|
||||||
location: location()
|
);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Initializer
|
Initializer
|
||||||
= code:CodeBlock EOS {
|
= code:CodeBlock EOS {
|
||||||
return { type: "initializer", code: code, location: location() };
|
return createNode( "initializer", { code: code } );
|
||||||
}
|
}
|
||||||
|
|
||||||
Rule
|
Rule
|
||||||
|
@ -114,19 +119,15 @@ Rule
|
||||||
"=" __
|
"=" __
|
||||||
expression:Expression EOS
|
expression:Expression EOS
|
||||||
{
|
{
|
||||||
return {
|
return createNode( "rule", {
|
||||||
type: "rule",
|
|
||||||
name: name,
|
name: name,
|
||||||
expression: displayName !== null
|
expression: displayName !== null
|
||||||
? {
|
? createNode( "named", {
|
||||||
type: "named",
|
|
||||||
name: displayName[0],
|
name: displayName[0],
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: expression,
|
: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression
|
Expression
|
||||||
|
@ -135,34 +136,28 @@ Expression
|
||||||
ChoiceExpression
|
ChoiceExpression
|
||||||
= head:ActionExpression tail:(__ "/" __ ActionExpression)* {
|
= head:ActionExpression tail:(__ "/" __ ActionExpression)* {
|
||||||
return tail.length > 0
|
return tail.length > 0
|
||||||
? {
|
? createNode( "choice", {
|
||||||
type: "choice",
|
|
||||||
alternatives: buildList(head, tail, 3),
|
alternatives: buildList(head, tail, 3),
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: head;
|
: head;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionExpression
|
ActionExpression
|
||||||
= expression:SequenceExpression code:(__ CodeBlock)? {
|
= expression:SequenceExpression code:(__ CodeBlock)? {
|
||||||
return code !== null
|
return code !== null
|
||||||
? {
|
? createNode( "action", {
|
||||||
type: "action",
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
code: code[1],
|
code: code[1],
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: expression;
|
: expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceExpression
|
SequenceExpression
|
||||||
= head:LabeledExpression tail:(__ LabeledExpression)* {
|
= head:LabeledExpression tail:(__ LabeledExpression)* {
|
||||||
return tail.length > 0
|
return tail.length > 0
|
||||||
? {
|
? createNode( "sequence", {
|
||||||
type: "sequence",
|
|
||||||
elements: buildList(head, tail, 1),
|
elements: buildList(head, tail, 1),
|
||||||
location: location()
|
} )
|
||||||
}
|
|
||||||
: head;
|
: head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,22 +167,18 @@ LabeledExpression
|
||||||
error(`Label can't be a reserved word "${label[0]}".`, label[1]);
|
error(`Label can't be a reserved word "${label[0]}".`, label[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return createNode( "labeled", {
|
||||||
type: "labeled",
|
|
||||||
label: label[0],
|
label: label[0],
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
/ PrefixedExpression
|
/ PrefixedExpression
|
||||||
|
|
||||||
PrefixedExpression
|
PrefixedExpression
|
||||||
= operator:PrefixedOperator __ expression:SuffixedExpression {
|
= operator:PrefixedOperator __ expression:SuffixedExpression {
|
||||||
return {
|
return createNode( OPS_TO_PREFIXED_TYPES[operator], {
|
||||||
type: OPS_TO_PREFIXED_TYPES[operator],
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
/ SuffixedExpression
|
/ SuffixedExpression
|
||||||
|
|
||||||
|
@ -198,11 +189,9 @@ PrefixedOperator
|
||||||
|
|
||||||
SuffixedExpression
|
SuffixedExpression
|
||||||
= expression:PrimaryExpression __ operator:SuffixedOperator {
|
= expression:PrimaryExpression __ operator:SuffixedOperator {
|
||||||
return {
|
return createNode( OPS_TO_SUFFIXED_TYPES[operator], {
|
||||||
type: OPS_TO_SUFFIXED_TYPES[operator],
|
|
||||||
expression: expression,
|
expression: expression,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
/ PrimaryExpression
|
/ PrimaryExpression
|
||||||
|
|
||||||
|
@ -223,22 +212,18 @@ PrimaryExpression
|
||||||
// nodes that already isolate label scope themselves. This leaves us with
|
// nodes that already isolate label scope themselves. This leaves us with
|
||||||
// "labeled" and "sequence".
|
// "labeled" and "sequence".
|
||||||
return expression.type === "labeled" || expression.type === "sequence"
|
return expression.type === "labeled" || expression.type === "sequence"
|
||||||
? { type: "group", expression: expression, location: location() }
|
? createNode( "group", { expression: expression } )
|
||||||
: expression;
|
: expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuleReferenceExpression
|
RuleReferenceExpression
|
||||||
= name:IdentifierName !(__ (StringLiteral __)? "=") {
|
= name:IdentifierName !(__ (StringLiteral __)? "=") {
|
||||||
return { type: "rule_ref", name: name, location: location() };
|
return createNode( "rule_ref", { name: name } );
|
||||||
}
|
}
|
||||||
|
|
||||||
SemanticPredicateExpression
|
SemanticPredicateExpression
|
||||||
= operator:SemanticPredicateOperator __ code:CodeBlock {
|
= operator:SemanticPredicateOperator __ code:CodeBlock {
|
||||||
return {
|
return createNode( OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], { code: code } );
|
||||||
type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator],
|
|
||||||
code: code,
|
|
||||||
location: location()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SemanticPredicateOperator
|
SemanticPredicateOperator
|
||||||
|
@ -322,12 +307,10 @@ UnicodeConnectorPunctuation
|
||||||
|
|
||||||
LiteralMatcher "literal"
|
LiteralMatcher "literal"
|
||||||
= value:StringLiteral ignoreCase:"i"? {
|
= value:StringLiteral ignoreCase:"i"? {
|
||||||
return {
|
return createNode( "literal", {
|
||||||
type: "literal",
|
|
||||||
value: value,
|
value: value,
|
||||||
ignoreCase: ignoreCase !== null,
|
ignoreCase: ignoreCase !== null,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringLiteral "string"
|
StringLiteral "string"
|
||||||
|
@ -351,13 +334,11 @@ CharacterClassMatcher "character class"
|
||||||
"]"
|
"]"
|
||||||
ignoreCase:"i"?
|
ignoreCase:"i"?
|
||||||
{
|
{
|
||||||
return {
|
return createNode( "class", {
|
||||||
type: "class",
|
|
||||||
parts: parts.filter(part => part !== ""),
|
parts: parts.filter(part => part !== ""),
|
||||||
inverted: inverted !== null,
|
inverted: inverted !== null,
|
||||||
ignoreCase: ignoreCase !== null,
|
ignoreCase: ignoreCase !== null,
|
||||||
location: location()
|
} );
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassCharacterRange
|
ClassCharacterRange
|
||||||
|
@ -426,7 +407,7 @@ HexDigit
|
||||||
= [0-9a-f]i
|
= [0-9a-f]i
|
||||||
|
|
||||||
AnyMatcher
|
AnyMatcher
|
||||||
= "." { return { type: "any", location: location() }; }
|
= "." { return createNode( "any", {} ); }
|
||||||
|
|
||||||
CodeBlock "code block"
|
CodeBlock "code block"
|
||||||
= "{" code:Code "}" { return code; }
|
= "{" code:Code "}" { return code; }
|
||||||
|
|
Loading…
Reference in a new issue