diff --git a/src/util/PaperScript.js b/src/util/PaperScript.js index 1726d4f5..f5f239d5 100644 --- a/src/util/PaperScript.js +++ b/src/util/PaperScript.js @@ -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) {