forked from pegjs/pegjs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue pegjs#38 (import): Add pass for include (at AST level) one gram…
…mar to another.
- Loading branch information
Showing
5 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
var GrammarError = require("../../grammar-error"), | ||
objects = require("../../utils/objects"), | ||
arrays = require("../../utils/arrays"), | ||
visitor = require("../visitor"); | ||
Resolver = require("../resolver"); | ||
|
||
/* Include one grammar to another recursively. Cyclic includes not supported. */ | ||
function includeGrammars(ast, options) { | ||
options = objects.clone(options); | ||
options.output = "ast"; | ||
|
||
var resolver = options.resolver || new Resolver(); | ||
|
||
function doInclude(ast, aliases) { | ||
// Mapping from alias to default rule of imported grammar | ||
var defaultRules = {}; | ||
// Arrays with initiaizers and rules from all imported grammars in order of import | ||
var initializers = []; | ||
var rules = []; | ||
|
||
function appendFrom(ast) { | ||
if (ast.initializers) { | ||
Array.prototype.push.apply(initializers, ast.initializers); | ||
} | ||
Array.prototype.push.apply(rules, ast.rules); | ||
} | ||
|
||
function fixCode(node, prefix) { | ||
if (node.namespace) { | ||
prefix = prefix.concat(node.namespace); | ||
} | ||
node.namespace = prefix.join('.'); | ||
if (node.expression) { | ||
addPrefix(node.expression, prefix); | ||
} | ||
} | ||
var addPrefix = visitor.build({ | ||
initializer: fixCode, | ||
action: fixCode, | ||
semantic_and: fixCode, | ||
semantic_not: fixCode, | ||
|
||
rule: function(node, prefix) { | ||
node.name = prefix.concat(node.name).join('$'); | ||
addPrefix(node.expression, prefix); | ||
}, | ||
rule_ref: function(node, prefix) { | ||
var name = node.name; | ||
// If referred rule not from this grammar. | ||
if (node.namespace) { | ||
// If |name| not set, use default rule of grammar | ||
name = name || defaultRules[node.namespace]; | ||
prefix = prefix.concat(node.namespace); | ||
|
||
// Make rule be part of this grammar -- for preventing of repeated processing. | ||
node.namespace = null; | ||
} | ||
if (!name) { | ||
throw new GrammarError(node); | ||
} | ||
node.name = prefix.concat(name).join('$'); | ||
} | ||
}); | ||
var include = visitor.build({ | ||
grammar: function(node) { | ||
if (node.imports) { | ||
// Fill |rules| and |initializers| arrays | ||
arrays.each(node.imports, include); | ||
} | ||
// Mark that all imports resolved. | ||
delete node.imports; | ||
addPrefix(node, aliases); | ||
|
||
// Append included rules and initializers first -- this need for generating action functions | ||
// and initializers call in correct order. | ||
appendFrom(node); | ||
|
||
|
||
// Update AST | ||
node.initializers = initializers; | ||
node.rules = rules; | ||
}, | ||
"import": function(node) { | ||
var importedAst = resolver.resolve(node.path, node.alias, options); | ||
|
||
// Resolve default names before they changed in |doInclude| | ||
defaultRules[node.alias] = importedAst.rules[0].name; | ||
|
||
doInclude(importedAst, aliases.concat(node.alias)); | ||
|
||
appendFrom(importedAst); | ||
if (resolver.done) { | ||
resolver.done(node.path, node.alias, options); | ||
} | ||
} | ||
}); | ||
|
||
include(ast); | ||
} | ||
doInclude(ast, []); | ||
} | ||
|
||
module.exports = includeGrammars; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
var GrammarError = require("../grammar-error"), | ||
parser = require("../parser"), | ||
arrays = require("../utils/arrays"); | ||
|
||
var imported = []; | ||
|
||
function Resolver() { | ||
if (arguments.length > 0) { | ||
var read = arguments[0]; | ||
if (typeof read !== 'function') { | ||
throw new GrammarError('Expected function for Resolver argument, but got '+(typeof read)); | ||
} | ||
this.read = read; | ||
} | ||
} | ||
|
||
Resolver.prototype = { | ||
read: function(path) { | ||
var fullPath = require("path").resolve(path); | ||
return { | ||
path: fullPath, | ||
data: require("fs").readFileSync(fullPath, 'utf-8') | ||
}; | ||
}, | ||
resolve: function(path, alias, options) { | ||
var data = this.read(path, alias, options); | ||
var has = arrays.contains(imported, function(e) { return e.path === data.path; }); | ||
imported.push({ path: data.path, alias: alias }); | ||
if (has) { | ||
var message = arrays.map(imported, function(e) { return e.path + ' as "' + e.alias + '"'; }); | ||
throw new GrammarError("Cyclic include dependence for included grammars!:\n" + message.join('\n')); | ||
} | ||
return parser.parse(data.data, options); | ||
}, | ||
done: function() { | ||
imported.pop(); | ||
} | ||
}; | ||
|
||
module.exports = Resolver; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters