Browse Source

Moved AST and visitor classes

master
Futago-za Ryuu 3 years ago
parent
commit
5476eca59f
  1. 2
      gulpfile.js
  2. 18
      lib/ast/Grammar.js
  3. 14
      lib/ast/Node.js
  4. 5
      lib/ast/index.js
  5. 32
      lib/ast/visitor.js
  6. 5
      lib/compiler/index.js
  7. 2
      lib/compiler/passes/calc-report-failures.js
  8. 2
      lib/compiler/passes/generate-bytecode.js
  9. 2
      lib/compiler/passes/inference-match-result.js
  10. 2
      lib/compiler/passes/remove-proxy-rules.js
  11. 2
      lib/compiler/passes/report-duplicate-labels.js
  12. 2
      lib/compiler/passes/report-duplicate-rules.js
  13. 2
      lib/compiler/passes/report-infinite-recursion.js
  14. 2
      lib/compiler/passes/report-infinite-repetition.js
  15. 2
      lib/compiler/passes/report-undefined-rules.js
  16. 2
      lib/parser.js
  17. 9
      lib/parser/index.js
  18. 2
      lib/peg.js
  19. 344
      lib/typings/api.d.ts
  20. 38
      lib/typings/modules.d.ts
  21. 0
      src/parser.pegjs
  22. 2
      src/pegjs.config.js
  23. 4
      test/spec/api/plugin-api.spec.js
  24. 13
      test/spec/unit/parser.spec.js

2
gulpfile.js

@ -66,7 +66,7 @@ task( "benchmark", cb => {
// Generate the grammar parser.
task( "build:parser", cb => {
node( "bin/peg src/grammar.pegjs -o lib/parser/grammar.js -c src/pegjs.config.js", cb );
node( "bin/peg src/parser.pegjs -o lib/parser.js -c src/pegjs.config.js", cb );
} );

18
lib/parser/ast.js → lib/ast/Grammar.js

@ -1,20 +1,9 @@
"use strict";
const visitor = require( "../compiler/visitor" );
const Node = require( "./Node" );
const visitor = require( "./visitor" );
const util = require( "../util" );
class Node {
constructor( type, location ) {
this.type = type;
this.location = location;
}
}
exports.Node = Node;
class Grammar extends Node {
// Creates a new AST
@ -57,7 +46,8 @@ class Grammar extends Node {
}
}
exports.Grammar = Grammar;
module.exports = Grammar;
/* ***************************** @private ***************************** */

14
lib/ast/Node.js

@ -0,0 +1,14 @@
"use strict";
class Node {
constructor( type, location ) {
this.type = type;
this.location = location;
}
}
module.exports = Node;

5
lib/ast/index.js

@ -0,0 +1,5 @@
"use strict";
exports.visitor = require( "./visitor" );
exports.Node = require( "./Node" );
exports.Grammar = require( "./Grammar" );

32
lib/compiler/visitor.js → lib/ast/visitor.js

@ -10,32 +10,34 @@ class ASTVisitor {
visit( node ) {
// istanbul ignore next
if ( ! node ) throw new Error( "Visitor function called with no or `null` node" );
if ( ! node ) throw new Error( "Visitor function called with no arguments or a `falsy` node" );
const func = this[ node.type ];
const fn = this[ node.type ];
// istanbul ignore next
if ( ! func ) throw new Error( "Visitor function for node type '" + node.type + " not defined" );
if ( ! fn ) throw new Error( `Visitor function for node type "${ node.type }" not defined` );
const args = __slice.call( arguments, 0 );
return func.apply( this, args );
return fn.apply( this, arguments );
}
}
module.exports = {
ASTVisitor,
// Simple AST node visitor builder for PEG.js
static build( functions ) {
build( functions ) {
let visitor = new ASTVisitor();
util.extend( visitor, functions );
visitor = util.enforceFastProperties( visitor );
return visitor.visit.bind( visitor );
}
},
}
ASTVisitor.ASTVisitor = ASTVisitor;
module.exports = ASTVisitor;
};
// Helper's to create visitor's for use with the ASTVisitor class
const on = ASTVisitor.for = {
@ -99,17 +101,19 @@ const DEFAULT_FUNCTIONS = {
grammar( node ) {
const extraArgs = __slice.call( arguments, 1 );
const args = [ void 0 ].concat( __slice.call( arguments, 1 ) );
if ( node.initializer ) {
this.visit.apply( this, [ node.initializer ].concat( extraArgs ) );
args[ 0 ] = node.initializer;
this.visit.apply( this, args );
}
node.rules.forEach( rule => {
this.visit.apply( this, [ rule ].concat( extraArgs ) );
args[ 0 ] = rule;
this.visit.apply( this, args );
} );

5
lib/compiler/index.js

@ -10,7 +10,6 @@ const reportInfiniteRecursion = require( "./passes/report-infinite-recursion" );
const reportInfiniteRepetition = require( "./passes/report-infinite-repetition" );
const reportUndefinedRules = require( "./passes/report-undefined-rules" );
const inferenceMatchResult = require( "./passes/inference-match-result" );
const visitor = require( "./visitor" );
const util = require( "../util" );
function processOptions( options, defaults ) {
@ -25,9 +24,6 @@ function processOptions( options, defaults ) {
}
const compiler = {
// AST node visitor builder. Useful mainly for plugins which manipulate the
// AST.
visitor: visitor,
// Compiler passes.
//
@ -96,6 +92,7 @@ const compiler = {
}
}
};
module.exports = compiler;

2
lib/compiler/passes/calc-report-failures.js

@ -1,6 +1,6 @@
"use strict";
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
// Determines if rule always used in disabled report failure context,
// that means, that any failures, reported within it, are never will be

2
lib/compiler/passes/generate-bytecode.js

@ -1,7 +1,7 @@
"use strict";
const op = require( "../opcodes" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
const util = require( "../../util" );
// Generates bytecode.

2
lib/compiler/passes/inference-match-result.js

@ -1,6 +1,6 @@
"use strict";
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
const GrammarError = require( "../../grammar-error" );
// Inference match result of the rule. Can be:

2
lib/compiler/passes/remove-proxy-rules.js

@ -1,6 +1,6 @@
"use strict";
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
// Removes proxy rules -- that is, rules that only delegate to other rule.
function removeProxyRules( ast, options ) {

2
lib/compiler/passes/report-duplicate-labels.js

@ -1,7 +1,7 @@
"use strict";
const GrammarError = require( "../../grammar-error" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
const util = require( "../../util" );
const __hasOwnProperty = Object.prototype.hasOwnProperty;

2
lib/compiler/passes/report-duplicate-rules.js

@ -1,7 +1,7 @@
"use strict";
const GrammarError = require( "../../grammar-error" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
const __hasOwnProperty = Object.prototype.hasOwnProperty;
// Checks that each rule is defined only once.

2
lib/compiler/passes/report-infinite-recursion.js

@ -1,7 +1,7 @@
"use strict";
const GrammarError = require( "../../grammar-error" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
// Reports left recursion in the grammar, which prevents infinite recursion in
// the generated parser.

2
lib/compiler/passes/report-infinite-repetition.js

@ -1,7 +1,7 @@
"use strict";
const GrammarError = require( "../../grammar-error" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
// Reports expressions that don't consume any input inside |*| or |+| in the
// grammar, which prevents infinite loops in the generated parser.

2
lib/compiler/passes/report-undefined-rules.js

@ -1,7 +1,7 @@
"use strict";
const GrammarError = require( "../../grammar-error" );
const visitor = require( "../visitor" );
const visitor = require( "../../ast" ).visitor;
// Checks that all referenced rules exist.
function reportUndefinedRules( ast, options ) {

2
lib/parser/grammar.js → lib/parser.js

@ -5,7 +5,7 @@
"use strict";
var ast = require("./ast");
var util = require("../util");
var util = require("./util");
function peg$subclass(child, parent) {
function C() { this.constructor = child; }

9
lib/parser/index.js

@ -1,9 +0,0 @@
"use strict";
const ast = require( "./ast" );
const parser = require( "./grammar" );
parser.Grammar = ast.Grammar;
parser.Node = ast.Node;
module.exports = parser;

2
lib/peg.js

@ -1,6 +1,7 @@
"use strict";
const GrammarError = require( "./grammar-error" );
const ast = require( "./ast" );
const compiler = require( "./compiler" );
const parser = require( "./parser" );
const util = require( "./util" );
@ -10,6 +11,7 @@ const peg = {
VERSION: "0.11.0-dev",
GrammarError: GrammarError,
ast: ast,
parser: parser,
compiler: compiler,
util: util,

344
lib/typings/api.d.ts

@ -5,7 +5,7 @@ export as namespace peg;
declare namespace peg {
type AST = parser.Grammar;
type Grammar = ast.Grammar;
type GeneratedParser<T = any> = gp.API<T>;
type SyntaxError = gp.SyntaxErrorConstructor;
type SourceLocation = gp.SourceLocation;
@ -28,13 +28,10 @@ declare namespace peg {
}
/**
* A generated PEG.js parser to parse PEG.js grammar source's.
*/
namespace parser {
namespace ast {
/**
* PEG.js node constructor, used internally by the PEG.js to create nodes.
* PEG.js node constructor, used internally by the PEG.js parser to create nodes.
*/
class Node {
@ -54,20 +51,20 @@ declare namespace peg {
private readonly _alwaysConsumesOnSuccess: any;
type: "grammar";
comments?: CommentMao;
initializer?: ast.Initializer;
rules: ast.Rule[];
comments?: Comment;
initializer?: Initializer;
rules: Rule[];
constructor(
initializer: void | ast.Initializer,
rules: ast.Rule[],
comments: void | CommentMao,
initializer: void | Initializer,
rules: Rule[],
comments: void | Comment,
location: SourceLocation,
);
findRule( name: string ): ast.Rule | void;
findRule( name: string ): Rule | void;
indexOfRule( name: string ): number;
alwaysConsumesOnSuccess( node: ast.Node ): boolean;
alwaysConsumesOnSuccess( node: Object ): boolean;
// Added by Bytecode generator
@ -82,7 +79,7 @@ declare namespace peg {
}
interface CommentMao {
interface Comment {
[ offset: number ]: {
@ -94,189 +91,234 @@ declare namespace peg {
}
interface INode extends peg.ast.Node { }
/**
* Interface's that describe the abstact sytax tree used by PEG.js
* This type represent's all PEG.js AST node's.
*/
namespace ast {
type Object
= Grammar
| Initializer
| Rule
| Named
| Expression;
interface INode extends parser.Node { }
interface Initializer extends INode {
/**
* Unlike `parser.Node` this interface represent's all PEG.js node's.
*/
type Node
= parser.Grammar
| Initializer
| Rule
| Named
| Expression;
type: "initializer";
code: string;
interface Initializer extends INode {
}
type: "initializer";
code: string;
interface Rule extends INode {
}
// Default properties
interface Rule extends INode {
type: "rule",
name: string;
expression: Named | Expression;
// Default properties
// Added by calc-report-failures pass
type: "rule",
name: string;
expression: Named | Expression;
reportFailures?: boolean;
// Added by calc-report-failures pass
// Added by inference-match-result pass
reportFailures?: boolean;
match?: number;
// Added by inference-match-result pass
// Added by generate-bytecode pass
match?: number;
bytecode?: number[];
// Added by generate-bytecode pass
}
bytecode?: number[];
interface Named extends INode {
}
type: "named";
name: string;
expression: Expression;
interface Named extends INode {
}
type: "named";
name: string;
expression: Expression;
type Expression
= ChoiceExpression
| ActionExpression
| SequenceExpression
| LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression;
}
interface ChoiceExpression extends INode {
type Expression
= ChoiceExpression
| ActionExpression
type: "choice";
alternatives: (
ActionExpression
| SequenceExpression
| LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression;
| PrimaryExpression
)[];
interface ChoiceExpression extends INode {
}
type: "choice";
alternatives: (
ActionExpression
| SequenceExpression
| LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression
)[];
interface ActionExpression extends INode {
}
type: "action";
expression: (
SequenceExpression
| LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression
);
code: string;
interface ActionExpression extends INode {
}
type: "action";
expression: (
SequenceExpression
| LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression
);
code: string;
interface SequenceExpression extends INode {
}
type: "sequence",
elements: (
LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression
)[];
interface SequenceExpression extends INode {
}
type: "sequence",
elements: (
LabeledExpression
| PrefixedExpression
| SuffixedExpression
| PrimaryExpression
)[];
interface LabeledExpression extends INode {
}
type: "labeled";
label: string;
expression: (
PrefixedExpression
| SuffixedExpression
| PrimaryExpression
);
interface LabeledExpression extends INode {
}
type: "labeled";
label: string;
expression: (
PrefixedExpression
| SuffixedExpression
| PrimaryExpression
);
interface PrefixedExpression extends INode {
}
type: "text" | "simple_and" | "simple_not";
expression: SuffixedExpression | PrimaryExpression;
}
interface PrefixedExpression extends INode {
interface SuffixedExpression extends INode {
type: "text" | "simple_and" | "simple_not";
expression: SuffixedExpression | PrimaryExpression;
type: "optional" | "zero_or_more" | "one_or_more";
expression: PrimaryExpression;
}
}
interface SuffixedExpression extends INode {
type PrimaryExpression
= LiteralMatcher
| CharacterClassMatcher
| AnyMatcher
| RuleReferenceExpression
| SemanticPredicateExpression
| GroupExpression;
type: "optional" | "zero_or_more" | "one_or_more";
expression: PrimaryExpression;
interface LiteralMatcher extends INode {
}
type: "literal";
value: string;
ignoreCase: boolean;
type PrimaryExpression
= LiteralMatcher
| CharacterClassMatcher
| AnyMatcher
| RuleReferenceExpression
| SemanticPredicateExpression
| GroupExpression;
}
interface LiteralMatcher extends INode {
interface CharacterClassMatcher extends INode {
type: "literal";
value: string;
ignoreCase: boolean;
type: "class";
parts: ( string[] | string )[];
inverted: boolean;
ignoreCase: boolean;
}
}
interface CharacterClassMatcher extends INode {
interface AnyMatcher extends INode {
type: "class";
parts: ( string[] | string )[];
inverted: boolean;
ignoreCase: boolean;
type: "any";
}
}
interface AnyMatcher extends INode {
interface RuleReferenceExpression extends INode {
type: "any";
type: "rule_ref";
name: string;
}
}
interface SemanticPredicateExpression extends INode {
type: "semantic_and" | "semantic_not";
code: string;
}
interface RuleReferenceExpression extends INode {
interface GroupExpression extends INode {
type: "rule_ref";
name: string;
type: "group";
expression: LabeledExpression | SequenceExpression;
}
namespace visitor {
interface IVisitorMap<U = void> {
[ key: string ]: any;
grammar?<R = U>( node: Grammar, ...args ): R;
initializer?<R = U>( node: Initializer, ...args ): R;
rule?<R = U>( node: Rule, ...args ): R;
named?<R = U>( node: Named, ...args ): R;
choice?<R = U>( node: ChoiceExpression, ...args ): R;
action?<R = U>( node: ActionExpression, ...args ): R;
sequence?<R = U>( node: SequenceExpression, ...args ): R;
labeled?<R = U>( node: LabeledExpression, ...args ): R;
text?<R = U>( node: PrefixedExpression, ...args ): R;
simple_and?<R = U>( node: PrefixedExpression, ...args ): R;
simple_not?<R = U>( node: PrefixedExpression, ...args ): R;
optional?<R = U>( node: SuffixedExpression, ...args ): R;
zero_or_more?<R = U>( node: SuffixedExpression, ...args ): R;
one_or_more?<R = U>( node: SuffixedExpression, ...args ): R;
literal?<R = U>( node: LiteralMatcher, ...args ): R;
class?<R = U>( node: CharacterClassMatcher, ...args ): R;
any?<R = U>( node: AnyMatcher, ...args ): R;
rule_ref?<R = U>( node: RuleReferenceExpression, ...args ): R;
semantic_and?<R = U>( node: SemanticPredicateExpression, ...args ): R;
semantic_not?<R = U>( node: SemanticPredicateExpression, ...args ): R;
group?<R = U>( node: GroupExpression, ...args ): R;
}
interface SemanticPredicateExpression extends INode {
class ASTVisitor implements IVisitorMap {
type: "semantic_and" | "semantic_not";
code: string;
visit<R = any>( node: Object, ...args ): R;
}
interface GroupExpression extends INode {
interface IVisitor<R = any> {
type: "group";
expression: LabeledExpression | SequenceExpression;
( node: Object, ...args ): R;
}
function build<T = void, R = any>( functions: IVisitorMap<T> ): IVisitor<R>;
}
}
/**
* A generated PEG.js parser to parse PEG.js grammar source's.
*/
namespace parser {
const SyntaxError: SyntaxError;
function parse( input: string, options?: gp.IOptions ): Grammar;
@ -330,48 +372,6 @@ declare namespace peg {
}
interface IVisitor<R = any> {
( node: parser.ast.Node, ...args ): R;
}
interface IVisitorMap<U = void> {
[ key: string ]: any;
grammar?<R = U>( node: Grammar, ...args ): R;
initializer?<R = U>( node: parser.ast.Initializer, ...args ): R;
rule?<R = U>( node: parser.ast.Rule, ...args ): R;
named?<R = U>( node: parser.ast.Named, ...args ): R;
choice?<R = U>( node: parser.ast.ChoiceExpression, ...args ): R;
action?<R = U>( node: parser.ast.ActionExpression, ...args ): R;
sequence?<R = U>( node: parser.ast.SequenceExpression, ...args ): R;
labeled?<R = U>( node: parser.ast.LabeledExpression, ...args ): R;
text?<R = U>( node: parser.ast.PrefixedExpression, ...args ): R;
simple_and?<R = U>( node: parser.ast.PrefixedExpression, ...args ): R;
simple_not?<R = U>( node: parser.ast.PrefixedExpression, ...args ): R;
optional?<R = U>( node: parser.ast.SuffixedExpression, ...args ): R;
zero_or_more?<R = U>( node: parser.ast.SuffixedExpression, ...args ): R;
one_or_more?<R = U>( node: parser.ast.SuffixedExpression, ...args ): R;
literal?<R = U>( node: parser.ast.LiteralMatcher, ...args ): R;
class?<R = U>( node: parser.ast.CharacterClassMatcher, ...args ): R;
any?<R = U>( node: parser.ast.AnyMatcher, ...args ): R;
rule_ref?<R = U>( node: parser.ast.RuleReferenceExpression, ...args ): R;
semantic_and?<R = U>( node: parser.ast.SemanticPredicateExpression, ...args ): R;
semantic_not?<R = U>( node: parser.ast.SemanticPredicateExpression, ...args ): R;
group?<R = U>( node: parser.ast.GroupExpression, ...args ): R;
}
class visitor implements IVisitorMap {
visit: IVisitor;
static build<T = void, R = any>( functions: IVisitorMap<T> ): IVisitor<R>;
static ASTVisitor: visitor;
}
namespace passes {
namespace check {

38
lib/typings/modules.d.ts

@ -18,22 +18,40 @@ declare module "pegjs/lib/parser" {
}
declare module "pegjs/lib/parser/ast" {
declare module "pegjs/lib/peg" {
export const Node: peg.parser.Node;
export const Grammar: peg.parser.Grammar;
export default peg;
}
declare module "pegjs/lib/parser/index" {
declare module "pegjs/lib/ast" {
export default peg.parser;
export const Node: peg.ast.Node;
export const Grammar: peg.ast.Grammar;
export const visitor: {
ASTVisitor: peg.ast.visitor.ASTVisitor;
build<T = void, R = any>( functions: peg.ast.visitor.IVisitorMap<T> ): peg.ast.visitor.IVisitor<R>;
};
}
declare module "pegjs/lib/peg" {
declare module "pegjs/lib/ast/Grammar" {
export default peg;
export default peg.ast.Grammar;
}
declare module "pegjs/lib/ast/Node" {
export default peg.ast.Node;
}
declare module "pegjs/lib/ast/visitor" {
export default peg.ast.visitor;
}
@ -68,12 +86,6 @@ declare module "pegjs/lib/compiler/opcodes" {
}
declare module "pegjs/lib/compiler/visitor" {
export default peg.compiler.visitor;
}
declare module "pegjs/lib/compiler/passes/calc-report-failures" {
export default peg.compiler.passes.generate.calcReportFailures;

0
src/grammar.pegjs → src/parser.pegjs

2
src/pegjs.config.js

@ -7,7 +7,7 @@ module.exports = {
dependencies: {
ast: "./ast",
util: "../util"
util: "./util"
},

4
test/spec/api/plugin-api.spec.js

@ -103,11 +103,11 @@ describe( "plugin API", function () {
config.parser = peg.generate( `
{
const pp = require( process.cwd() + "/lib/peg" ).parser;
const ast = require( process.cwd() + "/lib/ast" );
}
start = .* {
return new pp.Grammar( void 0, [{
return new ast.Grammar( void 0, [{
type: "rule",
name: "start",
expression: {

13
test/spec/unit/parser.spec.js

@ -3,6 +3,7 @@
const chai = require( "chai" );
const parser = require( "pegjs-dev" ).parser;
const util = require( "pegjs-dev" ).util;
const visitor = require( "pegjs-dev" ).ast.visitor;
const expect = chai.expect;
@ -167,16 +168,6 @@ describe( "PEG.js grammar parser", function () {
let strip;
function buildVisitor( functions ) {
return function ( node ) {
return functions[ node.type ].apply( null, arguments );
};
}
function stripLeaf( node ) {
delete node.location;
@ -203,7 +194,7 @@ describe( "PEG.js grammar parser", function () {
}
strip = buildVisitor( {
strip = visitor.build( {
grammar( node ) {
delete node.location;

Loading…
Cancel
Save