Comparing version 0.0.2 to 0.0.3
@@ -51,2 +51,5 @@ "use strict"; | ||
}, | ||
literate: { | ||
"README.md": "lib/jsstana.js", | ||
}, | ||
}); | ||
@@ -59,50 +62,6 @@ | ||
grunt.loadNpmTasks("grunt-mocha-cov"); | ||
grunt.loadNpmTasks("grunt-literate"); | ||
// Default task. | ||
grunt.registerTask("default", ["jshint", "simplemocha"]); | ||
// use esprima to generate README.md from source | ||
grunt.registerTask("readme", "Generate README.md", function () { | ||
var src = "./lib/jsstana.js"; | ||
var dest = "README.md"; | ||
var esprima = require("esprima"); | ||
var _ = require("underscore"); | ||
var content = grunt.file.read(src); | ||
var syntax = esprima.parse(content, { comment: true }); | ||
var comments = syntax.comments; | ||
function isWhitespace(str) { | ||
return (/^\s*$/).test(str); | ||
} | ||
var mdContent = _.reduce(comments, function (acc, comment) { | ||
if (comment.type === "Block" && comment.value[0] === "*") { | ||
// block comment starting with /** | ||
var value = comment.value.slice(1); | ||
var lines = value.split(/\n/); | ||
var first = _.find(lines, function (line) { return !isWhitespace(line); } ); | ||
var indent = first ? /^(\s*)/.exec(first)[1] : ""; | ||
// unindent lines | ||
lines = _.map(lines, function (line) { | ||
if (line.indexOf(indent) === 0) { | ||
return line.replace(indent, ""); | ||
} else if (isWhitespace(line)) { | ||
return ""; | ||
} else { | ||
return line; | ||
} | ||
}); | ||
return acc + lines.join("\n"); | ||
} else { | ||
// do nothing with rest | ||
return acc; | ||
} | ||
}, ""); | ||
grunt.file.write(dest, mdContent); | ||
}); | ||
}; |
@@ -65,3 +65,3 @@ /** | ||
p.seq(lexemeP("("), p.repeat(function () { return sexprP; }), lexemeP(")")).onMatch(function (arr) { return arr[1]; }), | ||
lexemeP(p.regex(/[a-zA-Z\?\.\-][a-zA-Z0-9_\?\.\-]*/)).onMatch(_.first), | ||
lexemeP(p.regex(/[a-zA-Z\?\.\-\/*+<>=!][a-zA-Z0-9_\?\.\-\/*+<>+!]*/)).onMatch(_.first), | ||
lexemeP(p.regex(/[0-9]+/)).onMatch(function (m) { return parseInt(_.first(m), 10); } ) | ||
@@ -135,2 +135,3 @@ ); | ||
"call": callMatcher, | ||
"expr": expressionMatcher, | ||
"member": memberMatcher.bind(undefined, undefined), | ||
@@ -141,3 +142,14 @@ "property": memberMatcher.bind(undefined, false), | ||
"ternary": ternaryMatcher, | ||
"undefined": undefinedMatcher, | ||
"literal": literalMatcher.bind(undefined, "any"), | ||
"literal-string": literalMatcher.bind(undefined, "string"), | ||
"literal-number": literalMatcher.bind(undefined, "number"), | ||
"literal-bool": literalMatcher.bind(undefined, "bool"), | ||
"literal-regexp": literalMatcher.bind(undefined, "regexp"), | ||
"null": literalBuiltinMatcher.bind(undefined, "null"), | ||
"true": literalBuiltinMatcher.bind(undefined, "true"), | ||
"false": literalBuiltinMatcher.bind(undefined, "false"), | ||
"infinity": literalBuiltinMatcher.bind(undefined, "infinity"), | ||
"nan": literalBuiltinMatcher.bind(undefined, "nan"), | ||
"undefined": literalBuiltinMatcher.bind(undefined, "undefined"), | ||
"null-node": nullNodeMatcher, | ||
}; | ||
@@ -162,10 +174,10 @@ | ||
/** | ||
#### (undefined) | ||
#### (null-node) | ||
matches `undefined` node | ||
Matches `undefined` node. | ||
*/ | ||
function undefinedMatcher() { | ||
assertArguments("undefined", 0, arguments); | ||
function nullNodeMatcher() { | ||
assertArguments("null-node", 0, arguments); | ||
return function (node) { | ||
return node === undefined ? {} : undefined; | ||
return node === null ? {} : undefined; | ||
}; | ||
@@ -195,5 +207,126 @@ } | ||
/** | ||
#### (literal value) | ||
Matches `Literal`. | ||
There are some additional version: | ||
- `(literal-string value)` - string values | ||
- `(literal-number value)` - number values | ||
- `(literal-bool value)` - boolean values | ||
- `(literal-regexp value)` - regular expressions | ||
- `(true)` - matches `true` | ||
- `(false)` - matches `false` | ||
- `(null)` - matches `null` | ||
- `(infinity)` - matches `Infinity` | ||
- `(nan)` - matches `NaN` | ||
- `(undefined)` - matches `undefined` | ||
*/ | ||
function literalBuiltinMatcher(type) { | ||
assertArguments("true/false/null/infinity/nan/undefined", 0, arguments, 1); | ||
// Constants | ||
if (type === "true") { | ||
return function (node) { | ||
return node.type === "Literal" && node.value === true ? {} : undefined; | ||
}; | ||
} else if (type === "false") { | ||
return function (node) { | ||
return node.type === "Literal" && node.value === false ? {} : undefined; | ||
}; | ||
} else if (type === "null") { | ||
return function (node) { | ||
return node.type === "Literal" && node.value === null ? {} : undefined; | ||
}; | ||
} else if (type === "infinity") { | ||
return function (node) { | ||
return node.type === "Identifier" && node.name === "Infinity" ? {} : undefined; | ||
}; | ||
} else if (type === "nan") { | ||
return function (node) { | ||
return node.type === "Identifier" && node.name === "NaN" ? {} : undefined; | ||
}; | ||
} else if (type === "undefined") { | ||
return function (node) { | ||
return node.type === "Identifier" && node.name === "undefined" ? {} : undefined; | ||
}; | ||
} | ||
} | ||
function literalMatcher(type, value) { | ||
assertArguments("literal", 1, arguments, 1); | ||
value = value || "?"; | ||
if (value[0] === "?") { | ||
var valueCheck = { | ||
any: function () { return true; }, | ||
string: _.isString, | ||
number: _.isNumber, | ||
regexp: _.isRegExp, | ||
bool: _.isBoolean, | ||
}[type]; | ||
var valueCapture; | ||
if (value === "?") { | ||
valueCapture = function() { return {}; }; | ||
} else { | ||
value = value.substr(1); | ||
valueCapture = function(v) { | ||
var res = {}; | ||
res[value] = v; | ||
return res; | ||
}; | ||
} | ||
return function (node) { | ||
if (node.type !== "Literal") { return undefined; } | ||
if (!valueCheck(node.value)) { return undefined; } | ||
return valueCapture(node.value); | ||
}; | ||
} else { | ||
if (type === "regexp") { | ||
return function (node) { | ||
if (node.type !== "Literal") { return undefined; } | ||
if (!_.isRegExp(node.value)) { return undefined; } | ||
return node.value.toString() === value ? {} : undefined; | ||
}; | ||
} else { | ||
value = { | ||
any: _.identity, | ||
string: _.identity, | ||
number: function (v) { | ||
v = parseFloat(v); | ||
if (isNaN(v)) { | ||
// TODO: improve check, regexp? | ||
throw new Error("invalid number value"); | ||
} else { | ||
return v; | ||
} | ||
}, | ||
bool: function (v) { | ||
if (v === "true") { | ||
return true; | ||
} else if (v === "false") { | ||
return false; | ||
} else { | ||
throw new Error("bool values are true and false"); | ||
} | ||
} | ||
}[type](value); | ||
return function (node) { | ||
if (node.type !== "Literal") { return undefined; } | ||
return node.value === value ? {} : undefined; | ||
}; | ||
} | ||
} | ||
} | ||
/** | ||
#### (var name init) | ||
Matches `VariableDeclarator | ||
Matches `VariableDeclarator`. | ||
*/ | ||
@@ -224,2 +357,6 @@ function varMatcher(id, init) { | ||
Matches `CallExpression`. | ||
`(call fun arg1 arg2)` matches exact amount of arguments, | ||
for arbitrary arguments use | ||
`(call fun . ?)` or similar dotted list syntax. | ||
*/ | ||
@@ -230,4 +367,28 @@ function callMatcher(callee) { | ||
var calleeMatcher = matcher(callee); | ||
var argumentMatchers = _.toArray(arguments).slice(1).map(matcher); | ||
var args = _.toArray(arguments).slice(1); | ||
var dotted = false; | ||
var dottedM; | ||
if (args.length > 1 && args[args.length - 2] === ".") { | ||
dotted = args[args.length - 1]; | ||
args = args.slice(0, -2); | ||
if (dotted === "?") { | ||
dottedM = function () { | ||
return {}; | ||
}; | ||
} else if (dotted[0] === "?") { | ||
dotted = dotted.substr(1); | ||
dottedM = function(v) { | ||
var res = {}; | ||
res[dotted] = v; | ||
return res; | ||
}; | ||
} else { | ||
throw new Error("call should have pattern variable after dot"); | ||
} | ||
} | ||
var argumentMatchers = args.map(matcher); | ||
return function (node) { | ||
@@ -238,2 +399,3 @@ if (node.type !== "CallExpression") { return undefined; } | ||
if (calleeM === undefined) { return undefined; } | ||
if (!dotted && argumentMatchers.length !== node.arguments.length) { return undefined; } | ||
@@ -247,2 +409,6 @@ for (var i = 0; i < argumentMatchers.length; i++) { | ||
if (dottedM) { | ||
calleeM = _.extend(calleeM, dottedM(node.arguments.slice(argumentMatchers.length))); | ||
} | ||
return calleeM; | ||
@@ -253,2 +419,20 @@ }; | ||
/** | ||
#### (expression expr) | ||
Matches expression statement, `ExpressionStatement`. | ||
*/ | ||
function expressionMatcher(expr) { | ||
assertArguments("expr", 1, arguments); | ||
expr = expr || "?"; | ||
var exprMatcher = matcher(expr); | ||
return function (node) { | ||
if (node.type !== "ExpressionStatement") { return undefined; } | ||
return exprMatcher(node.expression); | ||
}; | ||
} | ||
/** | ||
#### (member object property) | ||
@@ -378,2 +562,7 @@ | ||
- 0.0.3 More rands | ||
- call dotted syntax | ||
- literals | ||
- expr - expression statement | ||
- use grunt-literate to generate README.md | ||
- 0.0.2 Dev setup | ||
@@ -380,0 +569,0 @@ - 0.0.1 Preview release |
{ | ||
"name": "jsstana", | ||
"description": "s-expression match patterns for Mozilla Parser AST", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"homepage": "https://github.com/phadej/jsstana", | ||
@@ -41,2 +41,3 @@ "author": { | ||
"grunt-mocha-cov": "0.0.7", | ||
"grunt-literate": "~0.1.1", | ||
"grunt": "~0.4.1", | ||
@@ -43,0 +44,0 @@ "esprima": "~1.0.4", |
@@ -1,2 +0,1 @@ | ||
# jsstana [![Build Status](https://secure.travis-ci.org/phadej/jsstana.png?branch=master)](http://travis-ci.org/phadej/jsstana) | ||
@@ -28,5 +27,5 @@ | ||
#### (undefined) | ||
#### (null-node) | ||
matches `undefined` node | ||
Matches `undefined` node. | ||
@@ -37,5 +36,21 @@ #### (return value) | ||
#### (literal value) | ||
Matches `Literal`. | ||
There are some additional version: | ||
- `(literal-string value)` - string values | ||
- `(literal-number value)` - number values | ||
- `(literal-bool value)` - boolean values | ||
- `(literal-regexp value)` - regular expressions | ||
- `(true)` - matches `true` | ||
- `(false)` - matches `false` | ||
- `(null)` - matches `null` | ||
- `(infinity)` - matches `Infinity` | ||
- `(nan)` - matches `NaN` | ||
- `(undefined)` - matches `undefined` | ||
#### (var name init) | ||
Matches `VariableDeclarator | ||
Matches `VariableDeclarator`. | ||
@@ -46,2 +61,10 @@ #### (call callee arg0...argn) | ||
`(call fun arg1 arg2)` matches exact amount of arguments, | ||
for arbitrary arguments use | ||
`(call fun . ?)` or similar dotted list syntax. | ||
#### (expression expr) | ||
Matches expression statement, `ExpressionStatement`. | ||
#### (member object property) | ||
@@ -78,3 +101,2 @@ | ||
## Contributing | ||
@@ -88,2 +110,7 @@ | ||
- 0.0.3 More rands | ||
- call dotted syntax | ||
- literals | ||
- expr - expression statement | ||
- use grunt-literate to generate README.md | ||
- 0.0.2 Dev setup | ||
@@ -96,1 +123,2 @@ - 0.0.1 Preview release | ||
Licensed under the BSD3 license. | ||
@@ -57,3 +57,3 @@ /* global describe:true, it:true */ | ||
it("consecutive arguments match arguments, more arguments than specified", function () { | ||
it("matches exact amout of argument", function () { | ||
var syntax = esprima.parse("module.fun(foo, bar, baz)"); | ||
@@ -65,10 +65,10 @@ var node = syntax.body[0].expression; | ||
assert.deepEqual(matcher(syntax), undefined); | ||
assert.deepEqual(matcher(node), {}); | ||
assert.deepEqual(matcher(node), undefined); | ||
}); | ||
it("can use (undefined)", function () { | ||
var syntax = esprima.parse("module.fun(foo, bar)"); | ||
it("can use (call fun . ?) to match rest arguments", function () { | ||
var syntax = esprima.parse("module.fun(foo, bar, baz, quux)"); | ||
var node = syntax.body[0].expression; | ||
var matcher = jsstana.match("(call (lookup module.fun) foo bar (undefined))"); | ||
var matcher = jsstana.match("(call (lookup module.fun) . ?)"); | ||
@@ -79,11 +79,17 @@ assert.deepEqual(matcher(syntax), undefined); | ||
it("can use (undefined)", function () { | ||
var syntax = esprima.parse("module.fun(foo, bar, baz)"); | ||
it("can use (call . ?dotted-syntax) to capture rest arguments", function () { | ||
var syntax = esprima.parse("module.fun(foo, bar, baz, quux)"); | ||
var node = syntax.body[0].expression; | ||
var matcher = jsstana.match("(call (lookup module.fun) foo bar (undefined))"); | ||
var matcher = jsstana.match("(call (lookup module.fun) foo ? . ?rest)"); | ||
assert.deepEqual(matcher(syntax), undefined); | ||
assert.deepEqual(matcher(node), undefined); | ||
assert.deepEqual(matcher(node).rest.length, 2); | ||
}); | ||
it("should have pattern variable after dot", function () { | ||
assert.throws(function () { | ||
jsstana.match("(call foo . foo)"); | ||
}); | ||
}); | ||
}); |
@@ -73,3 +73,12 @@ /* global describe:true, it:true */ | ||
}); | ||
it("can use (null-node) to match uninitialized declrations", function () { | ||
var syntax = esprima.parse("var foo;"); | ||
var node = syntax.body[0].declarations[0]; | ||
var matcher = jsstana.match("(var foo (null-node))"); | ||
assert.deepEqual(matcher(syntax), undefined); | ||
assert.deepEqual(matcher(node), {}); | ||
}); | ||
}); | ||
}); |
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
53729
23
1368
119
9