Mercurial > repos > tabletprog
view interp.js @ 277:2b58eafa360b
Add SDL bindings for creating a renderer, clearing it, presenting it and destroying it
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 20 Jul 2014 17:30:46 -0700 |
parents | 0dc7322590da |
children | 23b52d2d05a0 |
line wrap: on
line source
Number.prototype['tpmeth_+'] = function(other) { return this + other; }; Number.prototype['tpmeth_-'] = function(other) { return this - other; }; Number.prototype['tpmeth_*'] = function(other) { return this * other; }; Number.prototype['tpmeth_/'] = function(other) { return this / other; }; Number.prototype['tpmeth_='] = function(other) { return this == other ? tptrue : tpfalse; }; Number.prototype['tpmeth_>'] = function(other) { return this > other ? tptrue : tpfalse; }; Number.prototype['tpmeth_<'] = function(other) { return this < other ? tptrue : tpfalse; }; Number.prototype['tpmeth_>='] = function(other) { return this >= other ? tptrue : tpfalse; }; Number.prototype['tpmeth_<='] = function(other) { return this <= other ? tptrue : tpfalse; }; Number.prototype.tpmeth_asStringChar = function() { return String.fromCharCode(this); }; Number.prototype['tpmeth_isString?'] = function() { return false; }; Number.prototype.tpmeth_string = function() { return '' + this; }; Number.prototype.tpmeth_print = function() { print(this); }; String.prototype['tpmeth_.'] = function(other) { return this + other; }; String.prototype['tpmeth_='] = function(other) { return this == other ? tptrue : tpfalse; }; String.prototype.tpmeth_length = function() { return this.length; }; String.prototype.tpmeth_byte_length = function() { return this.length; }; String.prototype.tpmeth_byte = function(index) { return this.charCodeAt(index); }; String.prototype['tpmeth_from:withLength'] = function(pos, length) { return this.substr(pos, length); }; String.prototype['tpmeth_isString?'] = function() { return true; }; String.prototype.tpmeth_print = function() { print(this); }; Function.prototype['tpmeth_while:do'] = function(body) { var ret = null; for (;;) { var res = this.call(null); if (res != tptrue) { break; } ret = body.call(null); } return ret; }; var tptrue = null; var tpfalse = null; var tplist = null; function topenv(moduledirs) { this.names = {}; this.modules = {}; for (var dirnum in moduledirs) { var results = os.system("ls", [moduledirs[dirnum]]).split('\n'); for (var i in results) { var tpidx = results[i].indexOf('.tp') if (tpidx > 0 && tpidx == results[i].length - 3) { this.names[results[i].substr(0, tpidx)] = moduledirs[dirnum] + "/" + results[i]; } } } if (!tptrue) { tptrue = this.find('true'); tptrue.valueOf = function() { return true; }; } if (!tpfalse) { tpfalse = this.find('false'); tpfalse.valueOf = function() { return false; }; } if (!tplist) { tplist = this.find('list'); if (tplist instanceof Function) { tplist = tplist.call(null); } } } topenv.prototype = { find: function(name) { if (name in this.modules) { return this.modules[name]; } if (name in this.names) { var parsed = parseFile(this.names[name]); var ret = parsed.macroexpand(this).eval(this); if (typeof ret == 'function') { ret = ret(); } this.modules[name] = ret; return ret; } return null; }, findNoTop: function(name) { return null; }, findSetPresent: function(name, val) { return false; }, findMacro: function(name) { return null; }, findQuoteTrans: function(name) { return null; } } function environment(parent) { this.parent = parent; this.syms = {}; this.macros = {}; this.quotetrans = {}; } environment.prototype = { find: function(name) { if (name in this.syms) { return this.syms[name]; } if (this.parent) { return this.parent.find(name); } return null; }, findNoTop: function(name) { if (name in this.syms) { return this.syms[name]; } if (this.parent) { return this.parent.findNoTop(name); } return null; }, findSet: function(name, val) { if (name in this.syms || !this.parent || !this.parent.findSetPresent(name, val)) { this.syms[name] = val; } }, findSetPresent: function(name, val) { if (name in this.syms) { this.syms[name] = val; return true; } if (this.parent) { return this.parent.findSetPresent(name, val); } return false; }, findMacro: function(name) { if (name in this.syms) { if (name in this.macros) { return this.syms[name]; } return null; } if (this.parent) { return this.parent.findMacro(name); } return null; }, findQuoteTrans: function(name) { if (name in this.quotetrans) { return this.quotetrans[name]; } if (this.parent) { return this.parent.findQuoteTrans(name); } return null; }, defMacro: function(name, def) { this.syms[name] = def; this.macros[name] = true; } }; function makeASTNode(val) { if (typeof val == 'number') { return new intlit(val); } if (typeof val == 'string') { return new strlit(val); } if (val instanceof Array) { return new arraylit(val); } if (val == tptrue) { return new symbol('true'); } if (val == tpfalse) { return new symbol('false'); } if ('makeHygienic' in val) { return val; } return null; } op.prototype.eval = function(env) { var l = this.left.eval(env); var name = this.op; if (name == '&&' || name == '||') { var r = (new lambda([], [this.right])).eval(env); } else { var r = this.right.eval(env); } var fun = env.findNoTop(name); var ret; if (fun) { ret = fun(l,r) } else { if (name == '&&') { name = 'if' } else if (name == '||') { name = 'ifnot' } else if (name == '|') { return r['tpmeth_|'](l); } ret = l['tpmeth_'+name](r); } return ret; }; op.prototype.macroexpand = function(env) { this.left = this.left.macroexpand(env); this.right = this.right.macroexpand(env); return this; }; op.prototype.quote = function(env) { var left = this.left.quote(env); var right = this.right.quote(env); return new op(left, this.op, right); }; op.prototype.makeHygienic = function(env) { var left = this.left.makeHygienic(env); var right = this.right.makeHygienic(env); return new op(left, this.op, right); }; op.prototype.tpmeth_nodeType = function() { return "op"; }; op.prototype.tpmeth_left = function() { return this.left; }; op.prototype.tpmeth_right = function() { return this.right; }; op.prototype.tpmeth_opName = function() { return this.op; }; var quote_prefix = 0; symbol.prototype.eval = function(env) { var res = env.find(this.name); if (res === null) { throw new Error('Symbol ' + this.name + ' is not bound'); } return res; }; symbol.prototype.quote = function(env) { var val = env.find(this.name); if (val !== null) { var newnode = makeASTNode(val); if (!newnode) { throw new Error(this.name + ' contains a value ' + val + ' that could not be converted to an AST node'); } return newnode; } else { this.dirty = true; return this; } }; symbol.prototype.makeHygienic = function(env) { if (this.dirty && this.cleanName() != 'self') { var hygenic = env.findQuoteTrans(this.cleanName()); if (hygenic) { return new symbol(hygenic, this.symbols); } else { return this; } } else { return this; } }; symbol.prototype.tpmeth_nodeType = function() { return "symbol"; }; symbol.prototype.tpmeth_name = function() { return this.cleanName(); }; symbol.prototype['tpmeth_name!'] = function(val) { this.name = val; return this; }; intlit.prototype.isconstant = floatlit.prototype.isconstant = strlit.prototype.isconstant = lambda.prototype.isconstant = function() { return true; }; object.prototype.isconstant = symbol.prototype.isconstant = funcall.prototype.isconstant = op.prototype.isconstant = assignment.prototype.isconstant = function() { return false; } arraylit.prototype.isconstant = listlit.prototype.isconstant = function() { for (var idx = 0; idx < this.val.length; ++idx) { if (!this.val[idx].isconstant()) { return false; } } return true; }; intlit.prototype.eval = floatlit.prototype.eval = strlit.prototype.eval = arraylit.prototype.eval = function(env) { return this.val; }; listlit.prototype.eval = function(env) { var cur = tplist.tpmeth_empty(); for (var idx = this.val.length - 1; idx >= 0; --idx) { cur = tplist['tpmeth_node:withTail'](this.val[idx].eval(env), cur); } return cur; }; symbol.prototype.macroexpand = intlit.prototype.macroexpand = floatlit.prototype.macroexpand = strlit.prototype.macroexpand = function(env) { return this; }; arraylit.prototype.macroexpand = listlit.prototype.macroexpand = function(env) { for (var i = 0; i < this.val.length; i++) { this.val[i] = this.val[i].macroexpand(env); } return this; }; intlit.prototype.quote = floatlit.prototype.quote = strlit.prototype.quote = arraylit.prototype.quote = listlit.prototype.quote = function(env) { return this; }; intlit.prototype.makeHygienic = floatlit.prototype.makeHygienic = strlit.prototype.makeHygienic = arraylit.prototype.makeHygienic = listlit.prototype.makeHygienic = function(env) { return this; }; intlit.prototype.tpmeth_nodeType = function() { return "intlit"; }; floatlit.prototype.tpmeth_nodeType = function() { return "floatlit"; }; strlit.prototype.tpmeth_nodeType = function() { return "strlit"; }; arraylit.prototype.tpmeth_nodeType = function() { return "arraylit"; }; listlit.prototype.tpmeth_nodeType = function() { return "strlit"; }; intlit.prototype.tpmeth_value = floatlit.prototype.tpmeth_value = strlit.prototype.tpmeth_value = arraylit.prototype.tpmeth_value = function() { return this.val; }; listlit.prototype.tpmeth_value = function() { var cur = tplist.tpmeth_empty(); for (var idx = this.val.length - 1; idx >= 0; --idx) { cur = tplist['tpmeth_node:withTail'](this.val[idx], cur); } return cur; }; funcall.prototype.eval = function(env) { var args = []; var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } if (name == 'quote') { if (this.receiver) { return this.receiver.quote(env).macroexpand(env); } if (this.args.length) { var cur = env; return this.args[0].quote(env).macroexpand(env); } throw new Error('quote takes an argument'); } if (name == 'macro') { return null; } if (this.receiver) { args.push(this.receiver.eval(env)); } for (var i = 0; i < this.args.length; i++) { args.push(this.args[i].eval(env)); } if (name == 'eval:else') { try { var res = args[0].eval(env); } catch(e) { return args[2].call(null); } return args[1].call(null, res); } var fun = env.findNoTop(name); if (fun && (fun.numargs === undefined || fun.numargs == args.length)) { return fun.apply(null, args); } else { //if (typeof args[0]'tpmeth_'+name in args[0]) { try { return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); } catch(e) { var msg = 'Error, \n\t' + e.message.split('\n').join('\n\t') + '\ninvoking method ' + name + ' on object ' + args[0]; if (typeof args[0] == 'object') { msg += ' with keys ' + JSON.stringify(Object.keys(args[0]) + ' and constructor ' + args[0].constructor.name); } throw new Error(msg); } /*} else {JSON.stringify throw new Error('No method named ' + name + ' on object ' + JSON.stringify(args[0])); }*/ } }; funcall.prototype.macroexpand = function(env) { var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } if (name == 'quote') { return this; } var macro = env.findMacro(name); if (this.receiver) { this.receiver = this.receiver.macroexpand(env); } for (var i = 0; i < this.args.length; i++) { this.args[i] = this.args[i].macroexpand(env); } if (!macro) { return this; } var args = []; if (this.receiver) { args.push(this.receiver); } for (var i = 0; i < this.args.length; i++) { args.push(this.args[i]); } var ret = makeASTNode(macro.apply(null, args)); ret = ret.makeHygienic(env); quote_prefix++; return ret; }; funcall.prototype.quote = function(env) { var receiver = this.receiver ? this.receiver.quote(env) : null; var args = []; for (var i = 0; i < this.args.length; i++) { args.push(this.args[i].quote(env)); } var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } var fun = env.find(name); if (fun) { fun = makeASTNode(fun); if (fun instanceof symbol) { name = fun.cleanName(); } else if (fun instanceof lambda) { throw new Error('FIXME'); } } else { this.dirty = true; } var ret = new funcall(name, args); ret.receiver = receiver; return ret; }; funcall.prototype.makeHygienic = function(env) { var receiver = this.receiver ? this.receiver.makeHygienic(env) : null; var args = []; for (var i = 0; i < this.args.length; i++) { args.push(this.args[i].makeHygienic(env)); } var name = this.name; if (name[name.length-1] == ":") { name = name.substr(0, name.length-1); } var hygienic = env.findQuoteTrans(name); if (hygienic) { name = hygienic; } var ret = new funcall(name, args); ret.receiver = receiver; return ret; }; funcall.prototype.tpmeth_nodeType = function() { return "funcall"; }; funcall.prototype['tpmeth_args!'] = function(arglist) { this.args = []; //TODO: Finish this }; object.prototype.eval = function(parentenv) { var env = new environment(parentenv); var obj = {env: env}; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { if (msg.expression instanceof lambda) { obj['tpmeth_' + msg.symbol.name] = msg.expression.eval(env); (function(name) { env.syms[name] = function() { var ret = obj['tpmeth_' + name].apply(obj, arguments); return ret; }; env.syms[name].numargs = msg.expression.numArgs(); })(msg.symbol.name); } else { var makeProp = function(obj, name) { obj['tprop_' + name] = msg.expression.eval(env); obj['tpmeth_' + name] = function() { return this['tprop_'+name]; }; var setname = name+'!'; obj['tpmeth_' + setname] = function(val) { this['tprop_'+name] = val; return this; }; }; makeProp(obj, msg.symbol.name); } } else { throw new Error('pseudo function calls in object definitions not implemented yet'); } } return obj; }; object.prototype.macroexpand = function(parentenv) { var env = new environment(parentenv); var obj = {env: env}; var outmessages = []; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i].macroexpand(env); if (msg instanceof assignment) { if (msg.expression instanceof lambda) { (function(name, expr) { env.syms[name] = function() { if (!obj['tpmeth_' + name]) { obj['tpmeth_' + name] = expr.eval(env); } return obj['tpmeth_' + name].apply(obj, arguments); }; env.syms[name].numargs = expr.numArgs(); })(msg.symbol.name, msg.expression); outmessages.push(msg); } else if (msg.expression instanceof funcall && msg.expression.name == 'macro:') { env.defMacro(msg.symbol.name, msg.expression.args[0].eval(env)); } else { outmessages.push(msg); /* var makeProp = function(obj, name) { obj['tprop_' + name] = msg.expression.eval(env); name = 'tpmeth_' + name; obj[name] = function() { return this[name]; }; var setname = name+'!'; obj[setname] = function(val) { this[setname] = val; return this; }; }; makeProp(obj, msg.symbol.name);*/ } } else { outmessages.push(msg); //throw new Error('pseudo function calls in object definitions not implemented yet'); } } this.messages = outmessages; return this; }; object.prototype.quote = function(parentenv) { var env = new environment(parentenv); var outmessages = []; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { env.syms[msg.symbol.name] = null; if (!(msg.expression instanceof lambda)) { env.syms[msg.symbol.name + '!'] = null; } } } for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { outmessages.push(new assignment(msg.symbol, msg.expression.quote(env))); } else { outmessages.push(msg.quote(env)); } } return new object(outmessages); }; object.prototype.makeHygienic = function(parentenv) { var env = new environment(parentenv); var outmessages = []; for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { env.syms[msg.symbol.name] = null; //make sure method names don't get translated for hygiene env.quotetrans[msg.symbol.name] = null; if (!(msg.expression instanceof lambda)) { env.syms[msg.symbol.name + '!'] = null; env.quotetrans[msg.symbol.name + '!'] = null; } } } for (var i = 0; i < this.messages.length; i++) { var msg = this.messages[i]; if (msg instanceof assignment) { outmessages.push(new assignment(msg.symbol, msg.expression.makeHygienic(env))); } else { outmessages.push(msg.makeHygienic(env)); } } return new object(outmessages); }; object.prototype.tpmeth_nodeType = function() { return "object"; }; object.prototype.tpmeth_addMessage = function(message) { this.messages.push(makeASTNode(message)); return this; }; lambda.prototype.eval = function(parentenv) { var args = this.args; var exprs = this.expressions; return function() { var env = new environment(parentenv); for (var i = 0, j = 0; i < arguments.length && j < args.length; i++, j++) { while (j < args.length && args[j].cleanName() == 'self') { j++; } env.syms[args[j].cleanName()] = arguments[i]; } if (this != null) { env.syms['self'] = this; } var res = null; for (var i = 0; i < exprs.length; i++) { res = exprs[i].eval(env); } return res; }; }; lambda.prototype.macroexpand = function(parentenv) { var env = new environment(parentenv); for (var i = 0; i < this.args.length; i++) { env.syms[this.args[i].cleanName()] = {}; } var outexprs = [] for (var i = 0; i < this.expressions.length; i++) { var expr = this.expressions[i]; if (expr instanceof assignment) { if (expr.expression instanceof funcall && expr.expression.name == 'macro:') { env.defMacro(expr.symbol.name, expr.expression.args[0].eval(env)); } else { env.syms[expr.symbol.cleanName()] = {}; outexprs.push(expr.macroexpand(env)); try { if (this.expressions[i].expression.isconstant()) { env.syms[expr.symbol.cleanName()] = this.expressions[i].expression.eval(env); } else { env.syms[expr.symbol.cleanName()] = null; } } catch(e) { var msg = 'Error, \n\t' + e.message.split('\n').join('\n\t') + '\nevaling node ' + this.expressions[i].expression if (typeof this.expressions[i].expression == 'object') { msg += ' with keys ' + JSON.stringify(Object.keys(this.expressions[i].expression) + ' and constructor ' + this.expressions[i].expression.constructor.name); } throw new Error(msg); } } } else { outexprs.push(expr.macroexpand(env)); } } this.expressions = outexprs; return this; }; lambda.prototype.quote = function(parentenv) { var args = []; var expressions = []; var env = new environment(parentenv); for (var i = 0; i < this.args.length; i++) { env.syms[this.args[i].cleanName()] = null; var sym = new symbol(this.args[i].name, this.args[i].symbols); sym.dirty = true; args.push(sym); } for (var i = 0; i < this.expressions.length; i++) { expressions.push(this.expressions[i].quote(env)); } return new lambda(args, expressions); }; lambda.prototype.makeHygienic = function(parentenv) { var args = []; var expressions = []; var env = new environment(parentenv); for (var i = 0; i < this.args.length; i++) { env.syms[this.args[i].cleanName()] = null; if (this.args[i].dirty && this.args[i].cleanName() != 'self') { var hygienic = '' + quote_prefix + this.args[i].cleanName(); env.quotetrans[this.args[i].cleanName()] = hygienic; args.push(new symbol(hygienic, this.args[i].symbols)); } else { args.push(this.args[i]); } } for (var i = 0; i < this.expressions.length; i++) { expressions.push(this.expressions[i].makeHygienic(env)); } return new lambda(args, expressions); }; lambda.prototype.numArgs = function() { var num = this.args.length; if (num && (this.args[0].cleanName() == 'self')) { --num; } return num; }; lambda.prototype.tpmeth_nodeType = function() { return "lambda"; }; lambda.prototype.tpmeth_args = function() { var cur = tplist.tpmeth_empty(); for (var idx = this.args.length - 1; idx >= 0; --idx) { cur = tplist['tpmeth_node:withTail'](this.args[idx], cur); } return cur; }; lambda.prototype.tpmeth_expressions = function() { var cur = tplist.tpmeth_empty(); for (var idx = this.expressions.length - 1; idx >= 0; --idx) { cur = tplist['tpmeth_node:withTail'](this.expressions[idx], cur); } return cur; }; lambda.prototype['tpmeth_expressions!'] = function(exprs) { var expressions = []; while (!exprs['tpmeth_empty?']().valueOf()) { expressions.push(makeASTNode(exprs.tpmeth_value())); exprs = exprs.tpmeth_tail(); } this.expressions = expressions; return this; }; lambda.prototype.tpmeth_addExpression = function(expr) { this.expressions.push(makeASTNode(expr)); return this; }; assignment.prototype.eval = function(env) { var val = this.expression.eval(env); env.findSet(this.symbol.name, val); return val; }; assignment.prototype.macroexpand = function(env) { this.expression = this.expression.macroexpand(env); return this; }; assignment.prototype.quote = function(env) { var name = this.symbol.cleanName(); var val = env.find(name); var dirty = true; if (val) { var node = makeASTNode(val); if (!(node instanceof symbol)) { throw new Error('Left hand side of assignment expression must be a symbol not a '+ node.constructor.name + ' '+(typeof node) + ', old name was ' + name); } name = node.cleanName(); dirty = node.dirty; } env.syms[name] = null; var sym = new symbol(name, this.symbol.symbols); sym.dirty = dirty; return new assignment(sym, this.expression.quote(env)); }; assignment.prototype.makeHygienic = function(env) { var name = this.symbol.cleanName(); env.syms[name] = null; if (this.symbol.dirty) { name = env.quotetrans[name] = '' + quote_prefix + name; } return new assignment(new symbol(name, this.symbol.symbols), this.expression.makeHygienic(env)); }; assignment.prototype.tpmeth_nodeType = function() { return "assignment"; }; assignment.prototype.tpmeth_symbol = function() { return this.symbol; }; assignment.prototype['tpmeth_symbol!'] = function(val) { this.symbol = makeASTNode(val); return this; }; assignment.prototype.tpmeth_expression = function() { return this.expression; }; assignment.prototype['tpmeth_expression!'] = function(val) { this.expression = makeASTNode(val); return this; };