Permalink
Browse files

Improved patch file processing

Now works on the token level after matching on the AST level. Needs testing!
  • Loading branch information...
Ryan Patterson
Ryan Patterson committed Jan 12, 2012
1 parent 759c7c1 commit 25774f575dd2cbaee76038671ae5ba218a9a5de2
Showing with 274 additions and 117 deletions.
  1. +47 −3 README.mdown
  2. +33 −64 jspatch
  3. +182 −50 lib/jsgrep.js
  4. +12 −0 tests/transform.spatch
View
@@ -44,7 +44,6 @@ matched variable.
Jsgrep allows you to search object initializations partially, which enables
easily drilling into the structure of JavaScript classes.
-
## jspatch
Jsgrep allows you to identify source code that resembles a certain pattern.
@@ -53,7 +52,7 @@ original source code.
**Replace setTimeout with window.setTimeout**
- $ ./jspatch -e 's/setTimeout( A, B )/window.setTimeout( A, B )/' tests/jquery.js
+ $ jspatch -e 's/setTimeout( A, B )/window.setTimeout( A, B )/' tests/jquery.js
--- tests/jquery.js 2012-01-10 14:51:36.000000000 -0800
+++ /tmp/jspatch.jquery.js 2012-01-11 17:13:56.000000000 -0800
@@ -420,7 +420,7 @@
@@ -77,16 +76,61 @@ original source code.
Sed mode allows you to quickly perform simple substitutions on the source code.
+**Refactor properties/members into just properties**
+
+ $ cat tests/transform.spatch
+ JX.install(N, {
+ ...,
+ - properties : {
+ + members : {
+ ...
+ + ,
+ - },
+ - members : {
+ ...
+ },
+ ...
+ })
+
+ $ jspatch -f tests/transform.spatch tests/javelin.js
+ --- tests/javelin.js 2012-01-08 10:55:04.000000000 -0800
+ +++ /tmp/jspatch.javelin.js 2012-01-12 03:06:45.000000000 -0800
+ @@ -3615,16 +3615,14 @@
+ }
+ },
+
+ - properties : {
+ + members : {
+ protocol: undefined,
+ port: undefined,
+ path: undefined,
+ queryParams: undefined,
+ fragment: undefined,
+ - querySerializer: undefined
+ - },
+ -
+ - members : {
+ + querySerializer: undefined,
+ +
+ _domain: undefined,
+
+ /**
+
+This complex spatch file leverages the fact that properties and members are
+always adjacent to one another in the source. This could be verified using
+jsgrep to ensure that no cases were missed.
+
## TODO
* Consider a metavar that matches but doesn't save, to prevent ambiguities.
+* Rewrite forEachNode to be iterative rather than recursive (to fix ...)
* Consider support for --where/--eval and/or not-patterns
* Support for most statements in patterns
* Formally testing the pattern matchers
jspatch:
-* Make the substitution work at the tokenizer level rather than the AST level.
+* Testing
## Contributors
View
97 jspatch
@@ -75,77 +75,48 @@ function usage(hasError) {
var paths = config.paths;
var matchFn;
-function processPatch(patchSource) {
- var lines = patchSource.split('\n');
-
- var i = 0;
- var chunks = [];
-
- function processChunk() {
- var chunk = { top: [], find: [], replace: [] };
- var inReplacement = false;
+function processPatch(patchSource, patchFilename) {
+ var lineNumber = 1;
+ var chunks = _.map(patchSource.split('\n---\n'), function(chunkSource) {
+ var lines = chunkSource.split('\n');
+ var chunk = {
+ find: [],
+ patch: chunkSource,
+ filename: patchFilename || "",
+ lineNumber: lineNumber
+ };
- for(; i < lines.length; i++) {
- if (lines[i] == '---') {
- break;
- } else if (lines[i][0] == '-') {
- if (!inReplacement) {
- if (chunk.find.length > 0) {
- // XXX: due to the way I compile the patch, each replacement would be
- // independent of one another, which would be confusing.
- console.error('jspatch: Only 1 replacement is permitted per chunk.');
- process.exit(1);
- }
- inReplacement = true;
- chunk.top.push('A_jspatchAutovar');
- }
+ for(var i = 0; i < lines.length; i++) {
+ if (lines[i][0] == '-') {
chunk.find.push(lines[i].substr(1));
} else if (lines[i][0] == '+') {
- if (!inReplacement) {
- console.error(
- 'jspatch: Insertions are not yet supported, only replacements.');
- process.exit(1);
- }
- chunk.replace.push(lines[i].substr(1));
+ // So line numbers match up
+ chunk.find.push('\n');
} else {
- if (inReplacement) {
- if (chunk.replace.length == 0) {
- console.error(
- 'jspatch: Removals are not yet supported, only replacements.');
- process.exit(1);
- }
- inReplacement = false;
- }
- chunk.top.push(lines[i]);
+ chunk.find.push(lines[i]);
}
}
- chunk.top = chunk.top.join('\n');
- chunk.find = chunk.find.join('\n');
- chunk.replace = chunk.replace.join('\n');
-
- chunks.push(chunk);
- }
-
- while (i < lines.length) {
- processChunk();
- }
+ chunk.find = chunk.find.join('\n').trim();
- function generateMatchFn(chunks) {
- return function(jsgrep) {
- _.each(chunks, function(chunk) {
- jsgrep.find(chunk.top, function(vars) {
- vars.A_jspatchAutovar.match(chunk.find, function(vars) {
- vars.node.replaceWith(chunk.replace);
- });
- });
- });
- };
- }
+ lineNumber += lines.length + 1;
+ return chunk;
+ });
- return generateMatchFn(chunks);
+ return function(ast) {
+ _.each(chunks, function(chunk) {
+ var matchOptions = {
+ strictMatches: true,
+ filename: chunk.filename,
+ lineNumber: chunk.lineNumber
+ };
+ var matches = ast.find(chunk.find, function(v) {
+ v.node.applyPatch(chunk.patch, patchFilename, chunk.lineNumber);
+ }, matchOptions);
+ });
+ };
}
function generateDiff(oldFilename, newFile, callback) {
@@ -273,14 +244,12 @@ if (config.sedMode) {
'"s/search/replace/".');
process.exit(1);
}
- matchFn = function(jsgrep) {
- jsgrep.replaceAll(sedMode[1], sedMode[2]);
- };
+ matchFn = processPatch('-' + sedMode[1] + '\n+' + sedMode[2], 'sed mode');
} else if (config.asJavascript) {
matchFn = config.patchFile;
} else {
var patchSource = fs.readFileSync(config.patchFile).toString();
- matchFn = processPatch(patchSource);
+ matchFn = processPatch(patchSource, config.patchFile);
}
jspatch_loop();
Oops, something went wrong.

0 comments on commit 25774f5

Please sign in to comment.