diff --git a/gulpfile.js b/gulpfile.js index 61bb7a4..745a00c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -35,7 +35,7 @@ task( "lint", () => gulp .src( [ "**/.*rc.js", "lib/**/*.js", - "!lib/parser.js", + "!lib/parser/index.js", "test/benchmark/**/*.js", "test/benchmark/run", "test/impact", @@ -65,7 +65,7 @@ task( "benchmark", cb => { // Generate the grammar parser. 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 ); } ); diff --git a/lib/parser/ast.js b/lib/parser/ast.js new file mode 100644 index 0000000..9b09769 --- /dev/null +++ b/lib/parser/ast.js @@ -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; diff --git a/lib/parser.js b/lib/parser/index.js similarity index 98% rename from lib/parser.js rename to lib/parser/index.js index 87b25ff..898d76f 100644 --- a/lib/parser.js +++ b/lib/parser/index.js @@ -4,6 +4,9 @@ "use strict"; +var ast = require("./ast"); +var util = require("../util"); + function peg$subclass(child, parent) { function C() { this.constructor = child; } C.prototype = parent.prototype; @@ -138,61 +141,50 @@ function peg$parse(input, options) { var peg$startRuleFunction = peg$parseGrammar; var peg$c0 = function(initializer, rules) { - return { - type: "grammar", - initializer: extractOptional(initializer, 0), - rules: extractList(rules, 0), - location: location() - }; + return new ast.Grammar( + extractOptional(initializer, 0), + extractList(rules, 0), + location(), + ); }; var peg$c1 = function(code) { - return { type: "initializer", code: code, location: location() }; + return createNode( "initializer", { code: code } ); }; var peg$c2 = "="; var peg$c3 = peg$literalExpectation("=", false); var peg$c4 = function(name, displayName, expression) { - return { - type: "rule", + return createNode( "rule", { name: name, expression: displayName !== null - ? { - type: "named", + ? createNode( "named", { name: displayName[0], expression: expression, - location: location() - } + } ) : expression, - location: location() - }; + } ); }; var peg$c5 = "/"; var peg$c6 = peg$literalExpectation("/", false); var peg$c7 = function(head, tail) { return tail.length > 0 - ? { - type: "choice", + ? createNode( "choice", { alternatives: buildList(head, tail, 3), - location: location() - } + } ) : head; }; var peg$c8 = function(expression, code) { return code !== null - ? { - type: "action", + ? createNode( "action", { expression: expression, code: code[1], - location: location() - } + } ) : expression; }; var peg$c9 = function(head, tail) { return tail.length > 0 - ? { - type: "sequence", + ? createNode( "sequence", { elements: buildList(head, tail, 1), - location: location() - } + } ) : head; }; var peg$c10 = ":"; @@ -202,19 +194,15 @@ function peg$parse(input, options) { error(`Label can't be a reserved word "${label[0]}".`, label[1]); } - return { - type: "labeled", + return createNode( "labeled", { label: label[0], expression: expression, - location: location() - }; + } ); }; var peg$c13 = function(operator, expression) { - return { - type: OPS_TO_PREFIXED_TYPES[operator], + return createNode( OPS_TO_PREFIXED_TYPES[operator], { expression: expression, - location: location() - }; + } ); }; var peg$c14 = "$"; var peg$c15 = peg$literalExpectation("$", false); @@ -223,11 +211,9 @@ function peg$parse(input, options) { var peg$c18 = "!"; var peg$c19 = peg$literalExpectation("!", false); var peg$c20 = function(expression, operator) { - return { - type: OPS_TO_SUFFIXED_TYPES[operator], + return createNode( OPS_TO_SUFFIXED_TYPES[operator], { expression: expression, - location: location() - }; + } ); }; var peg$c21 = "?"; 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 // "labeled" and "sequence". return expression.type === "labeled" || expression.type === "sequence" - ? { type: "group", expression: expression, location: location() } + ? createNode( "group", { expression: expression } ) : expression; }; 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) { - return { - type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], - code: code, - location: location() - }; + return createNode( OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], { code: code } ); }; var peg$c34 = peg$anyExpectation(); var peg$c35 = peg$otherExpectation("whitespace"); @@ -292,12 +274,10 @@ function peg$parse(input, options) { var peg$c65 = peg$otherExpectation("literal"); var peg$c66 = "i"; var peg$c67 = function(value, ignoreCase) { - return { - type: "literal", + return createNode( "literal", { value: value, ignoreCase: ignoreCase !== null, - location: location() - }; + } ); }; var peg$c68 = peg$otherExpectation("string"); var peg$c69 = "\""; @@ -309,13 +289,11 @@ function peg$parse(input, options) { var peg$c75 = "^"; var peg$c76 = "]"; var peg$c77 = function(inverted, parts, ignoreCase) { - return { - type: "class", + return createNode( "class", { parts: parts.filter(part => part !== ""), inverted: inverted !== null, ignoreCase: ignoreCase !== null, - location: location() - }; + } ); }; var peg$c78 = "-"; var peg$c79 = function(begin, end) { @@ -351,7 +329,7 @@ function peg$parse(input, options) { var peg$c99 = /^[0-9a-f]/i; var peg$c100 = "."; 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$c104 = "{"; var peg$c105 = "}"; @@ -3121,6 +3099,12 @@ function peg$parse(input, options) { 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$result = peg$startRuleFunction(); diff --git a/src/config.json b/src/config.json index 99165cc..6f3be12 100644 --- a/src/config.json +++ b/src/config.json @@ -1,3 +1,7 @@ { - "header": "/* eslint-disable */" + "header": "/* eslint-disable */", + "dependencies": { + "ast": "./ast", + "util": "../util" + } } diff --git a/src/parser.pegjs b/src/parser.pegjs index 04aaebb..3d9957c 100644 --- a/src/parser.pegjs +++ b/src/parser.pegjs @@ -89,23 +89,28 @@ function buildList(head, 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 ----- Grammar = __ initializer:(Initializer __)? rules:(Rule __)+ { - return { - type: "grammar", - initializer: extractOptional(initializer, 0), - rules: extractList(rules, 0), - location: location() - }; + return new ast.Grammar( + extractOptional(initializer, 0), + extractList(rules, 0), + location(), + ); } Initializer = code:CodeBlock EOS { - return { type: "initializer", code: code, location: location() }; + return createNode( "initializer", { code: code } ); } Rule @@ -114,19 +119,15 @@ Rule "=" __ expression:Expression EOS { - return { - type: "rule", + return createNode( "rule", { name: name, expression: displayName !== null - ? { - type: "named", + ? createNode( "named", { name: displayName[0], expression: expression, - location: location() - } + } ) : expression, - location: location() - }; + } ); } Expression @@ -135,34 +136,28 @@ Expression ChoiceExpression = head:ActionExpression tail:(__ "/" __ ActionExpression)* { return tail.length > 0 - ? { - type: "choice", + ? createNode( "choice", { alternatives: buildList(head, tail, 3), - location: location() - } + } ) : head; } ActionExpression = expression:SequenceExpression code:(__ CodeBlock)? { return code !== null - ? { - type: "action", + ? createNode( "action", { expression: expression, code: code[1], - location: location() - } + } ) : expression; } SequenceExpression = head:LabeledExpression tail:(__ LabeledExpression)* { return tail.length > 0 - ? { - type: "sequence", + ? createNode( "sequence", { elements: buildList(head, tail, 1), - location: location() - } + } ) : head; } @@ -172,22 +167,18 @@ LabeledExpression error(`Label can't be a reserved word "${label[0]}".`, label[1]); } - return { - type: "labeled", + return createNode( "labeled", { label: label[0], expression: expression, - location: location() - }; + } ); } / PrefixedExpression PrefixedExpression = operator:PrefixedOperator __ expression:SuffixedExpression { - return { - type: OPS_TO_PREFIXED_TYPES[operator], + return createNode( OPS_TO_PREFIXED_TYPES[operator], { expression: expression, - location: location() - }; + } ); } / SuffixedExpression @@ -198,11 +189,9 @@ PrefixedOperator SuffixedExpression = expression:PrimaryExpression __ operator:SuffixedOperator { - return { - type: OPS_TO_SUFFIXED_TYPES[operator], + return createNode( OPS_TO_SUFFIXED_TYPES[operator], { expression: expression, - location: location() - }; + } ); } / PrimaryExpression @@ -223,22 +212,18 @@ PrimaryExpression // nodes that already isolate label scope themselves. This leaves us with // "labeled" and "sequence". return expression.type === "labeled" || expression.type === "sequence" - ? { type: "group", expression: expression, location: location() } + ? createNode( "group", { expression: expression } ) : expression; } RuleReferenceExpression = name:IdentifierName !(__ (StringLiteral __)? "=") { - return { type: "rule_ref", name: name, location: location() }; + return createNode( "rule_ref", { name: name } ); } SemanticPredicateExpression = operator:SemanticPredicateOperator __ code:CodeBlock { - return { - type: OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], - code: code, - location: location() - }; + return createNode( OPS_TO_SEMANTIC_PREDICATE_TYPES[operator], { code: code } ); } SemanticPredicateOperator @@ -322,12 +307,10 @@ UnicodeConnectorPunctuation LiteralMatcher "literal" = value:StringLiteral ignoreCase:"i"? { - return { - type: "literal", + return createNode( "literal", { value: value, ignoreCase: ignoreCase !== null, - location: location() - }; + } ); } StringLiteral "string" @@ -351,13 +334,11 @@ CharacterClassMatcher "character class" "]" ignoreCase:"i"? { - return { - type: "class", + return createNode( "class", { parts: parts.filter(part => part !== ""), inverted: inverted !== null, ignoreCase: ignoreCase !== null, - location: location() - }; + } ); } ClassCharacterRange @@ -426,7 +407,7 @@ HexDigit = [0-9a-f]i AnyMatcher - = "." { return { type: "any", location: location() }; } + = "." { return createNode( "any", {} ); } CodeBlock "code block" = "{" code:Code "}" { return code; }