Mathias Panzenböck avatar Mathias Panzenböck committed edcd078

patterns++, builder++

Comments (0)

Files changed (5)

 
 var esprima = require("esprima");
 
-exports.visitors = {};
-exports.visitors.Visitor = require("./lib/visitor.js").Visitor;
-exports.visitors.CloneVisitor = require("./lib/clonevisitor.js").CloneVisitor;
-exports.visitors.OptimizingVisitor = require("./lib/optimizingvisitor.js").OptimizingVisitor;
-exports.visitors.MinVisitor = require("./lib/minvisitor.js").MinVisitor;
-exports.visitors.EnrichingVisitor = require("./lib/enrichingvisitor.js").EnrichingVisitor;
-exports.visitors.TransformingVisitor = require("./lib/transformingvisitor.js").TransformingVisitor;
+exports.visitors = {
+	Visitor:             require("./lib/visitor.js").Visitor,
+	CloneVisitor:        require("./lib/clonevisitor.js").CloneVisitor,
+	OptimizingVisitor:   require("./lib/optimizingvisitor.js").OptimizingVisitor,
+	MinVisitor:          require("./lib/minvisitor.js").MinVisitor,
+	EnrichingVisitor:    require("./lib/enrichingvisitor.js").EnrichingVisitor,
+	TransformingVisitor: require("./lib/transformingvisitor.js").TransformingVisitor
+};
 exports.nodes = require("./lib/nodes.js");
 exports.transform = require("./lib/transform.js");
+exports.i18n = require("./lib/i18n.js");
 
 function optimize (code) {
 	if (typeof(code) === "string") code = esprima.parse(code);
+"use strict";
+
+var transform = require("./transform.js");
+var match = require("./patterns.js").PatternHelper;
+var make = require("./nodes.js").NodeHelper;
+
+function getstrings (code, options) {
+	if (!options) options = {};
+	var strings = {};
+	var has = Object.prototype.hasOwnProperty;
+	new transform.Transformer().on(
+		match.global('_').call(match.string(), match.many()),
+		options.loc ? function (node) {
+			var s = node.arguments[0].value;
+			if (has.call(strings, s)) {
+				strings[s].push(node.loc);
+			}
+			else {
+				strings[s] = [node.loc];
+			}
+		} : function (node) {
+			var s = node.arguments[0].value;
+			if (has.call(strings, s)) {
+				strings[s] += 1;
+			}
+			else {
+				strings[s] = 1;
+			}
+		}).transform(code, options);
+	return strings;
+}
+
+function compile (code, options) {
+	var strings = {};
+	var nextid = 0;
+	var has = Object.prototype.hasOwnProperty;
+	var node = new transform.Transformer().replace(
+		match.global('_').call(match.string(), match.many()),
+		function (node) {
+			var s = node.arguments[0].value;
+			var id;
+			if (has.call(strings, s)) {
+				id = strings[s];
+			}
+			else {
+				id = '_'+(nextid++);
+			}
+			return make.id('_').get(id);
+		}).transform(code, options);
+	var inverse = {};
+	for (var s in strings) {
+		inverse[strings[s]] = s;
+	}
+	return {
+		strings: inverse,
+		code: node
+	};
+}
+
+exports.getstrings = getstrings;
+exports.compile = compile;
 	}
 }
 
-function blockwrap (body) {
+function stmtwrap (body) {
 	if (body instanceof Expression) {
 		return new ExpressionStatement(body);
 	}
 	else if (body instanceof Node) {
 		return body;
 	}
-	return new BlockStatement(body.map(blockwrap));
+	else if (body === null || body === undefined) {
+		return new EmptyStatement();
+	}
+	return new BlockStatement(body.map(stmtwrap));
 }
 
 var NodeHelper = {
 			this.block(body));
 	},
 	block: function (body) {
-		return new BlockStatement((body||[]).map(blockwrap));
+		return new BlockStatement((body||[]).map(stmtwrap));
 	},
 	program: function (body) {
-		return new Program((body||[]).map(blockwrap));
+		return new Program((body||[]).map(stmtwrap));
 	},
 	this: function () {
 		return new ThisExpression();
 	},
 	dec: function (expr) {
 		return wrap(expr).predec();
+	},
+	new: function (callee, args) {
+		callee = wrap(callee);
+		return callee.new.apply(callee,args||[]);
+	},
+	iif: function (test, consequent, alternate) {
+		return new ConditionalExpression(
+			wrap(test),
+			wrap(consequent),
+			wrap(alternate));
+	},
+	if: function (test, consequent, alternate) {
+		return new IfStatement(
+			wrap(test),
+			wrap(consequent),
+			alternate ? wrap(alternate) : null);
+	},
+	while: function (test, body) {
+		return new WhileStatement(
+			wrap(test),
+			stmtwrap(body));
+	},
+	do_while: function (body, test) {
+		return new DoWhileStatement(
+			stmtwrap(body),
+			wrap(test));
+	},
+	switch: function (discriminant, cases) {
+		if (arguments.length !== 2 || !Array.isArray(cases)) {
+			cases = Array.prototype.slice.call(arguments,1);
+		}
+		return new SwitchStatement(wrap(discriminant), cases);
+	},
+	case: function (test, consequent) {
+		return new SwitchCase(
+			test ? wrap(test) : null,
+			stmtwrap(consequent));
+	},
+	default: function (consequent) {
+		return this.case(null, consequent);
+	},
+	try: function (block, handlers, finalizer) {
+		if (handlers instanceof CatchClause) {
+			handlers = [handlers];
+		}
+		else if (!handlers) {
+			handlers = [];
+		}
+		return new TryStatement(
+			stmtwrap(block||[]),
+			handlers,
+			finalizer ? stmtwrap(finalizer) : null);
+	},
+	catch: function (param, body) {
+		return new CatchClause(idwrap(param), stmtwrap(body));
+	},
+	return: function (argument) {
+		return new ReturnStatement(
+			argument ? wrap(argument) : null);
+	},
+	throw: function (argument) {
+		return new ThrowStatement(wrap(argument));
+	},
+	with: function (object, body) {
+		return new WithStatement(
+			wrap(object),
+			stmtwrap(body));
+	},
+	debugger: function () {
+		return new DebuggerStatement();
+	},
+	for: function (init, test, update, body) {
+		return new ForStatement(
+			init ? wrap(init) : null,
+			test ? wrap(test) : null,
+			update ? wrap(update) : null,
+			stmtwrap(body));
+	},
+	for_in: function (iterator, object, body) {
+		return new ForInStatement(
+			wrap(iterator),
+			wrap(object),
+			stmtwrap(body));
+	},
+	break: function (label) {
+		return new BreakStatement(label ? idwrap(label) : null);
+	},
+	continue: function (label) {
+		return new ContinueStatement(label ? idwrap(label) : null);
+	},
+	label: function (label, stmt) {
+		return this.stmt(stmt).lable(label);
+	},
+	sequence: function () {
+		return new SequenceExpression(Array.prototype.map.call(arguments,wrap));
 	}
 };
 
 	}
 };
 
-Statement.prototype = new Node();
+Statement.prototype = extend(new Node(), {
+	label: function (label) {
+		return new LabeledStatement(idwrap(label), this);
+	},
+	if: function (test) {
+		return new IfStatement(wrap(test), this, null);
+	}
+});
+
 Expression.prototype = extend(new Node(), {
 	hasLeftPrecedence: function (node) {
 		if (this.precedence.order < node.precedence.order) {
 	},
 	stmt: function () {
 		return new ExpressionStatement(this);
+	},
+	comma: function (value) {
+		if (value instanceof SequenceExpression) {
+			return new SequenceExpression([this].concat(value.expressions));
+		}
+		else {
+			return new SequenceExpression([this,wrap(value)]);
+		}
 	}
 });
 Expression.prototype.hasPrecedence = Expression.prototype.hasRightPrecedence;
 EmptyStatement.prototype = new Statement();
 BlockStatement.prototype = new Statement();
 ExpressionStatement.prototype = new Statement();
-IfStatement.prototype = new Statement();
+IfStatement.prototype = extend(new Statement(), {
+	else: function (alternate) {
+		return new IfStatement(this.test, this.consequent, stmtwrap(alternate));
+	},
+	else_if: function (test, consequent, alternate) {
+		return new IfStatement(
+			this.test,
+			this.consequent,
+			NodeHelper.if(test, consequent, alternate));
+	}
+});
 LabeledStatement.prototype = new Statement();
 BreakStatement.prototype = new Statement();
 ContinueStatement.prototype = new Statement();
 ArrayExpression.prototype = new Expression();
 ObjectExpression.prototype = new Expression();
 FunctionExpression.prototype = new Expression();
-SequenceExpression.prototype = new Expression();
+SequenceExpression.prototype = extend(new Expression(), {
+	comma: function (value) {
+		if (value instanceof SequenceExpression) {
+			return new SequenceExpression(this.expressions.concat(value.expressions));
+		}
+		else {
+			return new SequenceExpression(this.expressions.concat([value]));
+		}
+	}
+});
 UnaryExpression.prototype = new Expression();
 BinaryExpression.prototype = new Expression();
 AssignmentExpression.prototype = new Expression();
 CatchClause.prototype = new Node();
 Identifier.prototype = new Expression();
 Literal.prototype = new Expression();
-Property.prototype = new Expression();
+Property.prototype = new Node();
 
 exports.NodeHelper = NodeHelper;
 exports.Node = Node;
 	else if (value instanceof RegExp) {
 		return new RegExpPattern(value);
 	}
+	else if (Array.isArray(value)) {
+		return new ArrayPattern(value.map(wrap));
+	}
+	else if (typeof value === "object" && value !== null) {
+		var props = [];
+		for (var name in value) {
+			props.push(new PropertyPattern(null, wrap(value[name])));
+		}
+		return new ObjectPattern(props,false);
+	}
 	else {
 		return new LiteralPattern(value);
 	}
 }
 
+function idwrap (name) {
+	if (name instanceof Pattern) {
+		return name;
+	}
+	else if (name === "this") {
+		return new ThisPattern();
+	}
+	else {
+		return new IdentifierPattern(name);
+	}
+}
+
 function argwrap (value) {
 	if (value === undefined || value === null) {
 		return new Pattern();
 		return IgnorableElements;
 	}
 	else {
-		return new LiteralPattern(value);
+		return new IdentifierPattern(value);
 	}
 }
 
+function stmtwrap (body) {
+	if (body instanceof ExpressionPattern) {
+		return new ExpressionStatementPattern(body);
+	}
+	else if (body instanceof Pattern) {
+		return body;
+	}
+	else if (body === null || body === undefined) {
+		return new EmptyStatementPattern();
+	}
+	return new BlockStatementPattern(body.map(stmtwrap));
+}
+
 var PatternHelper = {
 	val: wrap,
 	any: function () {
 		}
 	},
 	array: function (value) {
-		// TODO
+		return new ArrayPattern(value ? value.map(wrap) : null);
 	},
-	object: function (value) {
-		// TODO
+	object: function (value, slice) {
+		if (!value) {
+			return new ObjectPattern(null);
+		}
+		var props;
+		if (Array.isArray(value)) {
+			props = value;
+		}
+		else {
+			for (var name in value) {
+				props.push(new PropertyPattern(null, wrap(value[name])));
+			}
+		}
+		return new ObjectPattern(props, !!slice);
+	},
+	prop: function (kind, key, value) {
+		if (arguments.length < 3) {
+			key = kind;
+			value = key;
+			kind = null;
+		}
+		return new PropertyPattern(kind, idwrap(key), wrap(value));
 	},
 	pos: function (expr) {
 		return wrap(expr).pos();
 	},
 	dec: function (expr) {
 		return wrap(expr).predec();
+	},
+	sequence: function () {
+		return new SequencePattern(Array.prototype.map.call(arguments,wrap));
+	},
+	program: function (body) {
+		return new ProgramPattern(body ? body.map(stmtwrap) : null);
+	},
+	block: function (body) {
+		return new BlockStatementPattern(body ? body.map(stmtwrap) : null);
+	},
+	// TODO: label, if, while, do while, for, for in, with, return, break,
+	//       continue, try, throw, catch, switch, case
+	label: function (label, stmt) {
+		return stmtwrap(stmt).label(label);
+	},
+	if: function (test, consequent, alternate) {
+	},
+	while: function (test, body) {
+	},
+	do_while: function (body, test) {
+	},
+	for: function (init, test, update, body) {
+	},
+	for_in: function (iterator, object, body) {
+	},
+	with: function (object, body) {
+	},
+	return: function (argument) {
+	},
+	break: function (label) {
+	},
+	continue: function (label) {
+	},
+	try: function (block, handlers, finalizer) {
+	},
+	throw: function (argument) {
+	},
+	catch: function (param, body) {
+	},
+	switch: function (discriminant, cases) {
+	},
+	case: function (test, consequent) {
+	},
+	default: function (consequent) {
 	}
-	// TODO: label, if, while, do while, for, for in, with, return, break,
-	//       continue, try, throw, catch, switch, case, block, program
 };
 
 function Pattern () {}
 
 Pattern.prototype = {
-	// TODO
 	test: function (node) {
 		return true;
 	},
 StatementPattern.prototype = extend(new Pattern(), {
 	test: function (node) {
 		return node instanceof nodes.Statement;
+	},
+	label: function (label) {
+		// TODO
+	},
+	if: function (test) {
+		// TODO
 	}
 });
 
 		return this.logical('||', value);
 	},
 	new: function () {
-		return new NewPattern(this, Array.prototype.map.call(arguments,argwrap));
+		return new NewPattern(this, Array.prototype.map.call(arguments,wrap));
 	},
 	call: function () {
-		return new CallPattern(this, Array.prototype.map.call(arguments,argwrap));
+		return new CallPattern(this, Array.prototype.map.call(arguments,wrap));
 	},
 	stmt: function () {
 		return new ExpressionStatementPattern(this);
+	},
+	comma: function (pattern) {
+		if (pattern instanceof SequenceExpressionPattern) {
+			if (pattern.expressions === null) {
+				return new SequencePattern([this, IgnorableElements]);
+			}
+			else {
+				return new SequencePattern([this].concat(pattern.expressions));
+			}
+		}
+		else {
+			return new SequencePattern([this,wrap(pattern)]);
+		}
 	}
 });
 
-function CallPattern (callee, args) {
-	this.callee = callee;
-	this.arguments = args;
+function EmptyStatementPattern () {}
+
+EmptyStatementPattern.prototype = extend(new StatementPattern(), {
+	test: function (node) {
+		return node instanceof nodes.EmptyStatement;
+	}
+});
+
+function ExpressionStatementPattern (expression) {
+	this.expression = expression;
 }
 
+ExpressionStatementPattern.prototype = extend(new StatementPattern(), {
+	test: function (node) {
+		if (!(node instanceof ExpressionStatement)) return false;
+		return this.expression.test(node.expression);
+	}
+});
+
 function OptionalElement (pattern) {
 	this.pattern = pattern;
 }
 	return pati === patterns.length && goti === seq.length;
 }
 
+function CallPattern (callee, args) {
+	this.callee = callee;
+	this.arguments = args;
+}
+
 CallPattern.prototype = extend(new ExpressionPattern(), {
 	test: function (node) {
 		if (!(node instanceof nodes.CallExpression)) return false;
 	}
 });
 
+function ArrayPattern (elements) {
+	this.elements = elements;
+}
+
+ArrayPattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.ArrayExpression)) return false;
+		return !this.elements || testSeq(0, 0, this.elements, node.elements);
+	}
+});
+
+function ObjectPattern (properties, slice) {
+	this.properties = properties;
+	this.slice = slice;
+}
+
+ObjectPattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.ObjectExpression)) return false;
+		if (!this.properties) return true;
+
+		// This has a O(n**2) performance.
+		// For the case when all PropertyPatterns have a key defined it could be implemented as O(n).
+		if (this.slice) {
+			for (var i = 0; i < this.properties.length; ++ i) {
+				var found = false;
+				for (var j = 0; j < node.properties.length; ++ j) {
+					if (this.properties[i].test(node.properties[j])) {
+						found = true;
+						break;
+					}
+				}
+				if (!found) return false;
+			}
+			return true;
+		}
+		else {
+			var matched = new Array(this.properties.length);
+			var matchCount = 0;
+			for (var i = 0; i < node.properties.length; ++ i) {
+				for (var j = 0; j < this.properties.length; ++ j) {
+					if (this.properties[j].test(node.properties)) {
+						if (!matched[j]) {
+							matched[j] = true;
+							++ matchCount;
+						}
+					}
+				}
+			}
+			return matchCount === this.properties.length;
+		}
+	}
+});
+
+function PropertyPattern (kind, key, value) {
+	this.kind = kind;
+	this.key = key;
+	this.value = value;
+}
+
+PropertyPattern.prototype = extend(new Pattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.Property)) return false;
+		if (this.kind !== null && this.kind !== node.kind) return false;
+		return this.key.test(node.key) && this.value.test(node.value);
+	}
+});
+
+function SequencePattern (expressions) {
+	this.expressions = expressions;
+}
+
+SequencePattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.SequenceExpression)) return false;
+		return testSeq(0, 0, this.expressions, node.expressions);
+	},
+	comma: function (pattern) {
+		var expressions = this.expressions;
+		if (!expressions) {
+			expressions = [IgnorableElements];
+		}
+		if (pattern instanceof SequenceExpressionPattern) {
+			if (pattern.expressions === null) {
+				return new SequencePattern(expressions.concat([IgnorableElements]));
+			}
+			else {
+				return new SequencePattern(expressions.concat(pattern.expressions));
+			}
+		}
+		else {
+			return new SequencePattern(expressions.concat([wrap(pattern)]));
+		}
+	}
+});
+
 function IdentifierPattern (name) {
 	this.name = name;
 }
 	}
 });
 
-// TODO: ObjectPattern, ArrayPattern
-
 function BinaryPattern (operator, left, right) {
 	this.operator = operator;
 	this.left = left;
 	}
 });
 
-// TODO: arg, unary, update, ConditionalExpressionPattern, NewPattern
-// function decl/expr, array, object, empty, statements...
+function UnaryPattern (operator, argument) {
+	this.operator = operator;
+	this.argument = argument;
+}
+
+UnaryPattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.UnaryExpression)) return false;
+		if (this.operator !== null && this.operator !== node.operator) return false;
+		return this.argument.test(node.argument);
+	}
+});
+
+function UpdatePattern (operator, argument, prefix) {
+	this.operator = operator;
+	this.argument = argument;
+	this.prefix = prefix;
+}
+
+UpdatePattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.UpdateExpression)) return false;
+		if (this.operator !== null && this.operator !== node.operator) return false;
+		if (this.prefix !== null && this.prefix !== node.prefix) return false;
+		return this.argument.test(node.argument);
+	}
+});
 
 function MemberPattern (object, property, computed) {
 	this.object = object;
 	}
 });
 
+function ConditionalExpressionPattern (test, consequent, alternate) {
+	this.test = test;
+	this.consequent = consequent;
+	this.alternate = alternate;
+}
+
+ConditionalExpressionPattern.prototype = extend(new ExpressionPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.ConditionalExpression)) return false;
+		return this.test.test(node.test) &&
+			this.consequent.test(node.consequent) &&
+			this.alternate.test(node.alternate);
+	}
+});
+
+function ProgramPattern (body) {
+	this.body = body;
+}
+
+ProgramPattern.prototype = extend(new Pattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.Program)) return false;
+		if (this.body === null) return true;
+		return testSeq(0, 0, this.body, node.body);
+	}
+});
+
+function BlockStatementPattern (body) {
+	this.body = body;
+}
+
+BlockStatementPattern.prototype = extend(new StatementPattern(), {
+	test: function (node) {
+		if (!(node instanceof nodes.BlockStatement)) return false;
+		if (this.body === null) return true;
+		return testSeq(0, 0, this.body, node.body);
+	}
+});
 
 exports.PatternHelper = PatternHelper;
 exports.AlternativesPattern = AlternativesPattern;
-//exports.ArrayPattern = ArrayPattern;
+exports.ArrayPattern = ArrayPattern;
+exports.ObjectPattern = ObjectPattern;
 exports.AssignmentPattern = AssignmentPattern;
 exports.BinaryPattern = BinaryPattern;
 exports.CallPattern = CallPattern;
-//exports.ConditionalExpressionPattern = ConditionalExpressionPattern;
+exports.ConditionalExpressionPattern = ConditionalExpressionPattern;
 exports.ConjunctionsPattern = ConjunctionsPattern;
 exports.DeclarationPattern = DeclarationPattern;
-//exports.EmptyStatementPattern = EmptyStatementPattern;
+exports.EmptyStatementPattern = EmptyStatementPattern;
 exports.ExpressionPattern = ExpressionPattern;
-//exports.ExpressionStatementPattern = ExpressionStatementPattern;
+exports.ExpressionStatementPattern = ExpressionStatementPattern;
 exports.FunctionDeclarationPattern = FunctionDeclarationPattern;
 exports.FunctionExpressionPattern = FunctionExpressionPattern;
 exports.GlobalVarPattern = GlobalVarPattern;
 exports.RegExpPattern = RegExpPattern;
 exports.StatementPattern = StatementPattern;
 exports.ThisPattern = ThisPattern;
-//exports.UnaryPattern = UnaryPattern;
-//exports.UpdatePattern = UpdatePattern;
+exports.UnaryPattern = UnaryPattern;
+exports.UpdatePattern = UpdatePattern;
 exports.VariableDeclaratorPattern = VariableDeclaratorPattern;
 exports.IgnorableElements = IgnorableElements;
 exports.OptionalElement = OptionalElement;
+exports.ProgramPattern = ProgramPattern;
+exports.BlockStatementPattern = BlockStatementPattern;
+exports.PropertyPattern = PropertyPattern;
 var OptimizingVisitor = require("./optimizingvisitor.js").OptimizingVisitor;
 var EnrichingVisitor = require("./enrichingvisitor.js").EnrichingVisitor;
 var TransformingVisitor = require("./transformingvisitor.js").TransformingVisitor;
-var patterns = require("./patterns.js");
-
 
 function Transformer () {
 	this.handlers = [];
 };
 
 
-function test (code) {
-var match = patterns.PatternHelper;
-var make = nodes.NodeHelper;
-if (!code) code = "a._('foo'); _('bar'); _(x); _(12); _(); function f () { var _; _('baz'); } _('bar'); function g () { _('egg'+' '+'bacon', a, b); }";
-var MinVisitor = require("./minvisitor.js").MinVisitor;
-var tbl = {};
-var nextid = 0;
-var node = new Transformer().replace(match.global('_').call(match.string(), '...'), function (node) {
-	console.log("found:", new MinVisitor().visit(node));
-	var s = node.arguments[0].value;
-	var id;
-	if (Object.prototype.hasOwnProperty.call(tbl,s)) {
-		id = tbl[s];
-	}
-	else {
-		id = '_'+(nextid++);
-		tbl[s] = id;
-	}
-	return make.id('_').get(id);
-}).transform(code,{optimizeBefore:true});
-console.log("result:", new MinVisitor().visit(node));
-console.log("tbl:", tbl);
-}
-exports.test = test;
-
 exports.Transformer = Transformer;
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.