pegjs-util
Advanced tools
Comparing version 0.9.5 to 0.9.6
{ | ||
"name": "pegjs-util", | ||
"version": "0.9.5", | ||
"version": "0.9.6", | ||
"description": "Utility Class for PEG.js", | ||
@@ -5,0 +5,0 @@ "main": "PEGUtil.js", |
{ | ||
"name": "pegjs-util", | ||
"version": "0.9.5", | ||
"version": "0.9.6", | ||
"description": "Utility Class for PEG.js", | ||
@@ -28,4 +28,7 @@ "keywords": [ "pegjs", "parser", "AST", "unroll" ], | ||
"grunt-eslint": "~2.1.0", | ||
"pegjs": "~0.8.0" | ||
"pegjs": "~0.8.0", | ||
"asty": "~0.9.0" | ||
}, | ||
"dependencies": { | ||
} | ||
} |
224
PEGUtil.js
@@ -45,210 +45,13 @@ /* | ||
/* Abstract Syntax Tree (AST) */ | ||
PEGUtil.AST = function () { | ||
if (!(this instanceof PEGUtil.AST)) { | ||
var self = new PEGUtil.AST(""); | ||
return self.init.apply(self, arguments); | ||
} | ||
return this.init.apply(this, arguments); | ||
/* helper function for generating a function to generate an AST node */ | ||
PEGUtil.makeAST = function makeAST (line, column, offset) { | ||
if (makeAST._cb === null) | ||
throw new Error("makeAST: no callback set: you have to provide it via PEGUtil.makeAST.cb() first"); | ||
return function () { | ||
return makeAST._cb.call(PEGUtil, line(), column(), offset(), arguments); | ||
}; | ||
}; | ||
PEGUtil.AST.prototype = { | ||
/* constructor helper: AST node initialization */ | ||
init: function (T) { | ||
if (typeof T === "undefined") | ||
throw new Error("init: invalid argument"); | ||
this.T = T; | ||
this.A = {}; | ||
this.C = []; | ||
this.P = { L: 0, C: 0, O: 0 }; | ||
return this; | ||
}, | ||
PEGUtil.makeAST.cb = function (cb) { this._cb = cb; }; | ||
PEGUtil.makeAST._cb = null; | ||
/* merge attributes and childs of an AST node */ | ||
merge: function (node, takePos, attrMap) { | ||
if (typeof node !== "object") | ||
throw new Error("merge: invalid AST node argument"); | ||
if (typeof takePos === "undefined") | ||
takePos = false; | ||
if (typeof attrMap === "undefined") | ||
attrMap = {}; | ||
var self = this; | ||
if (takePos) { | ||
var pos = node.pos(); | ||
self.pos(pos.L, pos.C, pos.O); | ||
} | ||
node.attrs().forEach(function (attrSource) { | ||
var attrTarget = (typeof attrMap[attrSource] !== "undefined" ? | ||
attrMap[attrSource] : attrSource); | ||
if (attrTarget !== null) | ||
self.set(attrTarget, node.get(attrSource)); | ||
}); | ||
node.childs().forEach(function (child) { | ||
self.add(child); | ||
}); | ||
return this; | ||
}, | ||
/* check the type of an AST node */ | ||
type: function (T) { | ||
if (arguments.length === 0) | ||
return this.T; | ||
else if (arguments.length === 1) { | ||
this.T = T; | ||
return this; | ||
} | ||
else | ||
throw new Error("type: invalid number of arguments"); | ||
}, | ||
/* set the parsing position */ | ||
pos: function (L, C, O) { | ||
if (arguments.length === 0) | ||
return this.P; | ||
else if (arguments.length <= 3) { | ||
this.P.L = L || 0; | ||
this.P.C = C || 0; | ||
this.P.O = O || 0; | ||
return this; | ||
} | ||
else | ||
throw new Error("pos: invalid number of arguments"); | ||
}, | ||
/* set AST node attributes */ | ||
set: function () { | ||
if (arguments.length === 1 && typeof arguments[0] === "object") { | ||
var self = this; | ||
var args = arguments; | ||
Object.keys(args[0]).forEach(function (key) { self.A[key] = args[0][key]; }); | ||
} | ||
else if (arguments.length === 2) | ||
this.A[arguments[0]] = arguments[1]; | ||
else | ||
throw new Error("set: invalid arguments"); | ||
return this; | ||
}, | ||
/* get AST node attributes */ | ||
get: function (key) { | ||
if (arguments.length !== 1) | ||
throw new Error("get: invalid number of arguments"); | ||
if (typeof key !== "string") | ||
throw new Error("get: invalid argument"); | ||
return this.A[key]; | ||
}, | ||
/* get names of all AST node attributes */ | ||
attrs: function () { | ||
return Object.keys(this.A); | ||
}, | ||
/* add child AST node(s) */ | ||
add: function () { | ||
if (arguments.length === 0) | ||
throw new Error("add: missing argument(s)"); | ||
var _add = function (C, node) { | ||
if (!((typeof node === "object") && | ||
(typeof node.T === "string") && | ||
(typeof node.P === "object") && | ||
(typeof node.A === "object") && | ||
(typeof node.C === "object" && node.C instanceof Array))) | ||
throw new Error("add: invalid AST node: " + JSON.stringify(node)); | ||
C.push(node); | ||
}; | ||
var self = this; | ||
Array.prototype.slice.call(arguments, 0).forEach(function (arg) { | ||
if (typeof arg === "object" && arg instanceof Array) | ||
arg.forEach(function (child) { _add(self.C, child); }); | ||
else if (arg !== null) | ||
_add(self.C, arg); | ||
}); | ||
return this; | ||
}, | ||
/* delete child AST node(s) */ | ||
del: function () { | ||
if (arguments.length === 0) | ||
throw new Error("del: invalid argument"); | ||
var self = this; | ||
Array.prototype.slice.call(arguments, 0).forEach(function (arg) { | ||
var found = false; | ||
for (var j = 0; j < self.C.length; j++) { | ||
if (self.C[j] === arg) { | ||
self.C.splice(j, 1); | ||
found = true; | ||
break; | ||
} | ||
} | ||
if (!found) | ||
throw new Error("del: child not found"); | ||
}); | ||
return this; | ||
}, | ||
/* get child AST nodes */ | ||
childs: function () { | ||
return this.C; | ||
}, | ||
/* walk the AST recursively */ | ||
walk: function (cb, when) { | ||
if (typeof when === "undefined") | ||
when = "before"; | ||
var _walk = function (node, depth) { | ||
if (when === "before" || when === "both") | ||
cb.call(null, node, depth, "before"); | ||
node.C.forEach(function (child) { _walk(child, depth + 1); }); | ||
if (when === "after" || when === "both") | ||
cb.call(null, node, depth, "after"); | ||
}; | ||
_walk(this, 0); | ||
return this; | ||
}, | ||
/* dump the AST recursively */ | ||
dump: function () { | ||
var out = ""; | ||
this.walk(function (node, depth /*, when */) { | ||
for (var i = 0; i < depth; i++) | ||
out += " "; | ||
out += node.T + " "; | ||
var keys = Object.keys(node.A); | ||
if (keys.length > 0) { | ||
out += "("; | ||
var first = true; | ||
keys.forEach(function (key) { | ||
if (!first) | ||
out += ", "; | ||
else | ||
first = false; | ||
out += key + ": "; | ||
var value = node.A[key]; | ||
switch (typeof value) { | ||
case "string": | ||
out += "\"" + value.replace(/\n/, "\\n").replace(/"/, "\\\"") + "\""; | ||
break; | ||
case "object": | ||
if (value instanceof RegExp) | ||
out += "/" + | ||
value.toString() | ||
.replace(/^\//, "") | ||
.replace(/\/$/, "") | ||
.replace(/\//g, "\\/") + | ||
"/"; | ||
else | ||
out += JSON.stringify(value); | ||
break; | ||
default: | ||
out += JSON.stringify(value); | ||
break; | ||
} | ||
}); | ||
out += ") "; | ||
} | ||
out += "[" + node.P.L + "/" + node.P.C + "]\n"; | ||
}, "before"); | ||
return out; | ||
} | ||
}; | ||
/* helper function for generating a function to unroll the parse stack */ | ||
@@ -259,3 +62,3 @@ PEGUtil.makeUnroll = function (line, column, offset, SyntaxError) { | ||
|| !(list instanceof Array)) | ||
throw new SyntaxError("invalid list argument for unrolling", | ||
throw new SyntaxError("unroll: invalid list argument for unrolling", | ||
(typeof list), "Array", offset(), line(), column()); | ||
@@ -282,9 +85,2 @@ if (typeof take !== "undefined") { | ||
/* helper function for generating a function to generate an AST node */ | ||
PEGUtil.makeAST = function (line, column, offset) { | ||
return function (T, A, C) { | ||
return new PEGUtil.AST(T, A, C).pos(line(), column(), offset()); | ||
}; | ||
}; | ||
/* utility function: create a source excerpt */ | ||
@@ -291,0 +87,0 @@ var excerpt = function (txt, o) { |
@@ -75,5 +75,9 @@ | ||
var fs = require("fs") | ||
var ASTY = require("asty") | ||
var PEG = require("pegjs") | ||
var PEGUtil = require("pegjs-util") | ||
PEGUtil.makeAST.cb(function (line, column, offset, args) { | ||
return ASTY.apply(null, args).pos(line, column, offset) | ||
}) | ||
var parser = PEG.buildParser(fs.readFileSync("sample.pegjs", "utf8")) | ||
@@ -108,4 +112,4 @@ var result = PEGUtil.parse(parser, fs.readFileSync(process.argv[2], "utf8"), "start") | ||
ERROR: Parsing Failure: | ||
ERROR: line 2 (col 16): */\nfoo, bar, quux baz\n | ||
ERROR: --------------------------------------^ | ||
ERROR: line 2 (column 16): */\nfoo, bar, quux baz\n | ||
ERROR: -----------------------------------------^ | ||
ERROR: Expected "," or end of input but "b" found. | ||
@@ -166,3 +170,3 @@ ``` | ||
be the generation of an Abstract Syntax Tree (AST) node. | ||
For this PEGUtil provides you a simple AST implementation. | ||
For this libraries like e.g. [ASTy](http://github.com/rse/asty) can be used. | ||
@@ -188,62 +192,22 @@ ``` | ||
The `options.util` above again points to the PEGUtil API and is made available | ||
automatically by using `PEGUtil.parse` instead of PEG.js's standard | ||
parser method `parse`. | ||
Additionally, before performing the parsing step, your | ||
application has to tell PEGUtil how to map this call | ||
onto the underlying AST implementation. For [ASTy](http://github.com/rse/asty) you | ||
can use: | ||
The `ast` function has the following signature: | ||
```js | ||
PEGUtil.makeAST.cb(function (line, column, offset, args) { | ||
return ASTY.apply(null, args).pos(line, column, offset) | ||
}) | ||
``` | ||
ast(type: String): Node | ||
``` | ||
Each AST Node has the following methods: | ||
The `args` argument is an array containing all arguments | ||
you supply to the generated `ast()` function. For | ||
[ASTy](http://github.com/rse/asty) this would be | ||
at least the type of the AST node. | ||
- `Node#merge(node: Node, takePos?: Boolean, attrMap?: ([from: String]: [to: (String|null)])): Node`:<br/> | ||
Merge attributes, childs and optionally the position of a node. | ||
The attributes can be renamed or skipped (if mapped onto `null`). | ||
The `options.util` above again points to the PEGUtil API and is made available | ||
automatically by using `PEGUtil.parse` instead of PEG.js's standard | ||
parser method `parse`. | ||
- `Node#type(type: String): Boolean`:<br/> | ||
`Node#type(): String`:<br/> | ||
Set or get type of node. | ||
- `Node#pos(line: Number, column: Number, offset: Number): Node`:<br/> | ||
`Node#pos(): Object`:<br/> | ||
Set or get the position for the node. This is done automatically | ||
in the function which is generated by `PEGUtil.makeAST`. | ||
- `Node#set(name: String, value: Object): Node`:<br/> | ||
Set a single attribute `name` to `value`. | ||
- `Node#set({ [name: String]: [value: Object] }): Node`:<br/> | ||
Set multiple attributes, each consisting of name and value pairs. | ||
- `Node#get(name: String): Object`:<br/> | ||
Get value of attribute `name`. | ||
- `Node#attrs(): String[]:<br/> | ||
Get names of all node attributes. | ||
- `Node#add(childs: Node[]): Node`:<br/> | ||
Add one or more childs to a node. The array `childs` | ||
can either contain `Node` objects or even arrays | ||
of `Node` objects. | ||
- `Node#del(childs: Node[]): Node`:<br/> | ||
Delete one or more childs from a node. | ||
- `Node#childs(): Node[]`:<br/> | ||
Get a nodes list of childs. | ||
- `Node#walk(callback: (node: Node, depth: Number, whenNow: String) => Void, when?: String): Node`:<br/> | ||
Recursively walk the AST starting at this node (at depth 0). For each | ||
visited node the `callback` function is called with the current node | ||
and the tree depth. By default (and if `when` is either `before` or | ||
`both`), the callback is called before(!) all child nodes are visited | ||
and with `whenNow` set to `before`. If `when` is set to `after` or | ||
`both`, the callback is called after(!) all child nodes are visited | ||
ande with `whenNow` set to `after`. | ||
- `Node#dump(): String`:<br/> | ||
Returns a textual dump of the AST starting at the current node. | ||
### Cooked Error Reporting | ||
@@ -250,0 +214,0 @@ |
var fs = require("fs") | ||
var ASTY = require("asty") | ||
var PEG = require("pegjs") | ||
var PEGUtil = require("./PEGUtil") | ||
PEGUtil.makeAST.cb(function (line, column, offset, args) { | ||
return ASTY.apply(null, args).pos(line, column, offset) | ||
}) | ||
var parser = PEG.buildParser(fs.readFileSync("sample.pegjs", "utf8")) | ||
@@ -6,0 +10,0 @@ var result = PEGUtil.parse(parser, fs.readFileSync(process.argv[2], "utf8"), "start") |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25871
7
415
266