Add operator overloading through JS AST parsing and mangling to PaperScript.

This commit is contained in:
Jürg Lehni 2011-03-03 22:19:12 +00:00
parent 111fd5ea14
commit 15a7b7e245

View file

@ -1,8 +1,115 @@
var PaperScript = new function() {
// Handle Math Operators
var operators = {
'+': 'add',
'-': 'subtract',
'*': 'multiply',
'/': 'divide',
'%': 'modulo',
'==': 'equals',
'!=': 'equals'
};
paper.handleOperator = function(operator, left, right) {
var handler = operators[operator];
if (left && left[handler]) {
var res = left[handler](right);
return operator == '!=' ? !res : res;
}
switch (operator) {
case '+': return left + right;
case '-': return left - right;
case '*': return left * right;
case '/': return left / right;
case '%': return left % right;
case '==': return left == right;
case '!=': return left != right;
default:
throw new Error('Implement Operator: ' + operator);
}
};
// Handle Sign Operators
var signOperator = {
'-': 'negate'
};
paper.handleSignOperator = function(operator, exp) {
var handler = signOperator[operator];
if (exp && exp[handler]) {
return exp[handler]();
}
switch (operator) {
case '+': return +exp;
case '-': return -exp;
default:
throw new Error('Implement Sign Operator: ' + operator);
}
}
// AST Helpers
function isDynamic(exp) {
var type = exp[0];
return type != 'num' && type != 'string';
}
function handleOperator(operator, left, right) {
// Only replace operators with calls to paper.handleOperator if
// the left hand side is potentially an object.
if (operators[operator] && isDynamic(left)) {
// Replace with paper.handleOperator(operator, left, right):
return ['call', ['dot', ['name', 'paper'], 'handleOperator'],
[['string', operator], left, right]];
}
}
function compile(code) {
// TODO: Parse code and replace math operators with calls to methods
// that handle overloading.
return code;
// Use parse-js to translate the code into a AST structure which is then
// walked and parsed for operators to overload. The resulting AST is
// translated back to code and evaluated.
var ast = parse_js.parse(code),
walker = parse_js.walker(),
walk = walker.walk;
ast = walker.with_walkers({
'binary': function(operator, left, right) {
// Handle simple mathematical operators here:
return handleOperator(operator, left = walk(left),
right = walk(right))
// Always return something since we're walking left and
// right for the handleOperator() call already.
|| [this[0], operator, left, right];
},
'assign': function(operator, left, right) {
// Handle assignments like +=, -=, etc:
// Check if the assignment operator needs to be handled by paper
// if so, convert the assignment to a simple = and use result of
// of handleOperator on the right hand side.
var res = handleOperator(operator, left = walk(left),
right = walk(right));
if (res)
return [this[0], true, left, res];
// Always return something for the same reason as in binary
return [this[0], operator, left, right];
},
'unary-prefix': function(operator, exp) {
if (signOperator[operator] && isDynamic(exp)) {
return ['call', ['dot', ['name', 'paper'],
'handleSignOperator'],
[['string', operator], walk(exp)]];
}
}
}, function() {
return walk(ast);
});
return parse_js.stringify(ast);
}
function run(code) {