Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add support for ...

Now for function calls and array initializers, you can use ... to match 0 or
more elements of the list.
  • Loading branch information...
commit 2cf1d47cd5599ba53c488c1d1f8ddd24e33631fd 1 parent 4c6b655
Ryan Patterson authored
Showing with 8,501 additions and 5 deletions.
  1. +11 −2 README.mdown
  2. +51 −3 jsgrep
  3. +8,437 −0 tests/connect.js
  4. +2 −0  tests/simple.js
View
13 README.mdown
@@ -25,6 +25,15 @@ cases, the second parameter to setTimeout was a literal value of 0.
When the pattern references the same metavariable multiple times, jsgrep ensures
that the value of the metavariable is the same throughout the match.
+**Find the names of all invoked events**
+
+ $ ./jsgrep -p B "A.fire(B, ...)" tests/*.j
+ tests/connect.js: 'auth.logout'
+ tests/connect.js: 'auth.login'
+
+The `...` operator matches 0 or more expressions in function calls and array
+initializers.
+
**Find classes that have a 'path' property**
$ jsgrep -p C "JX.install(C, { properties: { path: X } })" tests/*.js
@@ -37,8 +46,8 @@ the `-p` flag to print only a particular matched variable.
## TODO
-* Add support for ... to the parser
-* Consider support for --where/--eval
+* Consider support for --where/--eval and/or not-patterns
+* Support for -l -L -n -h -H
* Support for most statements in patterns
## Contributors
View
54 jsgrep
@@ -257,9 +257,7 @@ function astIsEqual(node, pattern, variables) {
case tokens.HOOK:
case tokens.INDEX:
// Special
- case tokens.ARRAY_INIT: // TODO: handle ...
- case tokens.LIST: // TODO: handle ...
- case tokens.OBJECT_INIT: // TODO: handle ...
+ case tokens.OBJECT_INIT:
case tokens.PROPERTY_INIT:
//case tokens.SCRIPT:
if (node.children.length == pattern.children.length) {
@@ -272,6 +270,11 @@ function astIsEqual(node, pattern, variables) {
}
break;
+ case tokens.ARRAY_INIT:
+ case tokens.LIST:
+ return astMatchEllipsis(node.children, pattern.children, variables);
+ break;
+
case tokens.LET:
case tokens.VAR:
// All of var's children are IDENTIFIERs with name/initializer values
@@ -413,6 +416,51 @@ function astIsEqual(node, pattern, variables) {
}
}
+function astMatchEllipsis(nodes, patterns, variables) {
+ // XXX(rpatterson): this needs testing!
+ const tokens = Narcissus.definitions.tokenIds;
+ var permitVar = true, clonedVars = null;
+ function go(i, j) {
+ if (i == nodes.length && j == patterns.length) {
+ return true;
+ }
+
+ if (j == patterns.length) {
+ return false;
+ }
+
+ if (patterns[j].type == tokens.ELLIPSIS && j == patterns.length - 1) {
+ return true;
+ }
+
+ if(i == nodes.length) {
+ return false;
+ }
+
+ if (patterns[j].type == tokens.ELLIPSIS) {
+ permitVar = false;
+ clonedVars = _.clone(variables);
+ return go(i, j + 1) || go(i + 1, j);
+ }
+
+ return astIsEqual(nodes[i], patterns[j],
+ permitVar ? variables : clonedVars) &&
+ go(i + 1, j + 1);
+ }
+ var result = go(0, 0);
+ if (!permitVar && _.keys(clonedVars).length != _.keys(variables).length) {
+ // ..., A, ... is ambiguous, and if A was matched later in the pattern, we
+ // would have to backtrack to the ellipsis to try a different match. That's
+ // annoying.
+ //
+ // XXX(rpatterson): This incorrectly bails for (..., A)
+ console.error("jsgrep: Matching metavariables inside partially-matched " +
+ "lists is unsupported. Sorry!");
+ process.exit(1);
+ }
+ return result;
+}
+
function forEachNode(node, callback) {
const tokens = Narcissus.definitions.tokenIds;
View
8,437 tests/connect.js
8,437 additions, 0 deletions not shown
View
2  tests/simple.js
@@ -21,6 +21,8 @@ function hats(c) {
a = (b = 2) || c + 1;
hats(b);
+foo(0, 'haru', 'mamburu');
+
try {
q = REFERENCEERROR;
} catch(e) {

0 comments on commit 2cf1d47

Please sign in to comment.
Something went wrong with that request. Please try again.