Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

jspatch custom patch format

Lets you write pseudo-patch-files, similar to those that spatch accepts.
  • Loading branch information...
commit 0379cbe97758f9c8f145abae87d3332dd2c54aca 1 parent 05b5585
Ryan Patterson authored
Showing with 85 additions and 22 deletions.
  1. +73 −12 jspatch
  2. +12 −10 lib/jsgrep.js
View
85 jspatch
@@ -12,6 +12,7 @@ var config = {
paths: [ ],
recursive: false,
patchFile: false,
+ asJavascript: false
};
function usage(hasError) {
@@ -25,6 +26,7 @@ function usage(hasError) {
console.log("");
console.log("Options:");
console.log(" -f, --patch-script=FILE Path to patch script.");
+ console.log(" -J, --javascript Interpret PATCHFILE as a match script.");
console.log(" -r, --recursive Scan directories for JavaScript files.");
process.exit(0);
}
@@ -32,7 +34,7 @@ function usage(hasError) {
(function parseArgs() {
var getopt = require('node-getopt');
var parser = new getopt.BasicParser(
- 'f:(match-script)r(recursive)_(help)',
+ 'f:(match-script)J(javascript)r(recursive)_(help)',
process.argv);
while ((option = parser.getopt()) !== undefined) {
@@ -40,6 +42,9 @@ function usage(hasError) {
case 'f':
config.patchFile = option.optarg;
break;
+ case 'J':
+ config.asJavascript = true;
+ break;
case 'r':
config.recursive = true;
break;
@@ -62,16 +67,67 @@ function usage(hasError) {
}
})();
-var patchSource = fs.readFileSync(config.patchFile).toString();
-// Validate patch file before evaling
-try {
- var patchAst =
- Narcissus.parser.parse(patchSource, config.patchFile, 1);
-} catch(e) {
- console.error("jspatch: " + e.name + ": " + e.message);
- process.exit(1);
+var paths = config.paths;
+var matchFn;
+
+function processPatch(patchSource) {
+ var lines = patchSource.split('\n');
+
+ var inReplacement = false;
+ var topPattern = [];
+ var find = [];
+ var replace = [];
+
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i][0] == '-') {
+ if (!inReplacement) {
+ if (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;
+ topPattern.push('A_jspatchAutovar');
+ } else {
+ topPattern.push('');
+ }
+ 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);
+ }
+ topPattern.push('');
+ replace.push(lines[i].substr(1));
+
+ } else {
+ if (inReplacement) {
+ if (replace.length == 0) {
+ console.error(
+ 'jspatch: Removals are not yet supported, only replacements.');
+ process.exit(1);
+ }
+ inReplacement = false;
+ }
+ topPattern.push(lines[i]);
+ }
+ }
+
+ topPattern = topPattern.join('\n');
+ find = find.join('\n');
+ replace = replace.join('\n');
+
+ return function(jsgrep) {
+ jsgrep.find(topPattern, function(vars) {
+ vars.A_jspatchAutovar.match(find, function(vars) {
+ vars.node.replaceWith(replace);
+ });
+ });
+ };
}
-var matchFn = eval("(function custom_matcher(jsgrep) {" + patchSource + "})");
function generateDiff(oldFilename, newFile, callback) {
var tempFilename, tries = 1;
@@ -160,8 +216,6 @@ function doFile(filename, callback) {
generateDiff(filename, modifiedSource, callback);
}
-var paths = config.paths;
-
function queueDirectory(directory) {
if (!config.recursive) {
return;
@@ -192,6 +246,13 @@ function jspatch_loop() {
}
}
+if (config.asJavascript) {
+ matchFn = config.patchFile;
+} else {
+ var patchSource = fs.readFileSync(config.patchFile).toString();
+ matchFn = processPatch(patchSource);
+}
+
jspatch_loop();
// vim: ft=javascript
View
22 lib/jsgrep.js
@@ -118,17 +118,19 @@ Matcher.prototype._replace = function(target, replacement) {
// Cannot cache because we modify its source in-place.
var replAst = Matcher.compilePatternNoCache(replacement);
- for (var i in this.options.boundVars) {
- if (Matcher.identifierIsMetavar(i)) {
- var self = this;
- forEachNode(replAst, function(node) {
- if (node.type == tokens.IDENTIFIER && node.value == i) {
- self._replaceWithString(node,
- Matcher.getSourceForNode(self.options.boundVars[i].ast));
- }
- });
+ var self = this;
+ forEachNode(replAst, function(node) {
+ if (node.type == tokens.IDENTIFIER &&
+ Matcher.identifierIsMetavar(node.value)) {
+ if (!(node.value in self.options.boundVars)) {
+ throw {
+ message: node.value + ' is not a bound metavariable'
+ };
+ }
+ self._replaceWithString(node,
+ Matcher.getSourceForNode(self.options.boundVars[node.value].ast));
}
- }
+ });
this._replaceWithString(target, replAst.tokenizer.source);
};
Please sign in to comment.
Something went wrong with that request. Please try again.