2011-03-03 13:31:56 -05:00
|
|
|
var PaperScript = new function() {
|
2011-03-03 17:55:18 -05:00
|
|
|
//TODO: Make sure there are all the correct copyrights for the inlined
|
|
|
|
//parse-js:
|
|
|
|
//#include "../../lib/parse-js-min.js"
|
|
|
|
|
2011-03-03 17:19:12 -05:00
|
|
|
// 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]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-03 13:31:56 -05:00
|
|
|
function compile(code) {
|
2011-03-03 17:19:12 -05:00
|
|
|
// 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);
|
2011-03-03 13:31:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function run(code) {
|
2011-03-03 13:35:37 -05:00
|
|
|
// Use paper.extend() to create a paper scope within which the code is
|
|
|
|
// evaluated.
|
|
|
|
with (paper.extend()) {
|
2011-03-03 13:31:56 -05:00
|
|
|
return eval(compile(code));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//#ifdef BROWSER
|
|
|
|
// Code borrowed from Coffee Script:
|
|
|
|
function load(url) {
|
|
|
|
var xhr = new (window.ActiveXObject || XMLHttpRequest)('Microsoft.XMLHTTP');
|
|
|
|
xhr.open('GET', url, true);
|
|
|
|
if ('overrideMimeType' in xhr) {
|
|
|
|
xhr.overrideMimeType('text/plain');
|
|
|
|
}
|
|
|
|
xhr.onreadystatechange = function() {
|
|
|
|
if (xhr.readyState === 4) {
|
|
|
|
return run(xhr.responseText);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return xhr.send(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
function runScripts() {
|
|
|
|
var scripts = document.getElementsByTagName('script');
|
|
|
|
for (var i = 0, l = scripts.length; i < l; i++) {
|
|
|
|
var script = scripts[i];
|
|
|
|
if (script.type === 'text/paperscript') {
|
|
|
|
if (script.src) {
|
|
|
|
load(script.src);
|
|
|
|
} else {
|
|
|
|
run(script.innerHTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.addEventListener) {
|
2011-03-03 13:53:06 -05:00
|
|
|
addEventListener('load', runScripts, false);
|
2011-03-03 13:31:56 -05:00
|
|
|
} else {
|
|
|
|
attachEvent('onload', runScripts);
|
|
|
|
}
|
|
|
|
//#endif // BROWSER
|
|
|
|
|
|
|
|
return {
|
|
|
|
compile: compile,
|
|
|
|
run: run
|
|
|
|
};
|
|
|
|
};
|