# HG changeset patch # User Mike Pavone # Date 1385177845 28800 # Node ID a1b4a2bc8d7215e24bbdce90cdec2a9e97743ccd # Parent 60eff5f81d9ad768e0b2b0e315db7047390c2021 Initial work on pattern match macrosfor the new parser diff -r 60eff5f81d9a -r a1b4a2bc8d72 interp.js --- a/interp.js Tue Nov 19 22:02:11 2013 -0800 +++ b/interp.js Fri Nov 22 19:37:25 2013 -0800 @@ -25,6 +25,9 @@ Number.prototype['tpmeth_<='] = function(other) { return this <= other ? tptrue : tpfalse; }; +Number.prototype.tpmeth_asStringChar = function() { + return String.fromCharCode(this); +}; Number.prototype.tpmeth_string = function() { return '' + this; }; @@ -38,9 +41,33 @@ 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_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; @@ -93,6 +120,9 @@ findMacro: function(name) { return null; }, + findQuoteTrans: function(name) { + return null; + } } function environment(parent) @@ -100,6 +130,7 @@ this.parent = parent; this.syms = {}; this.macros = {}; + this.quotetrans = {}; } environment.prototype = { @@ -150,6 +181,15 @@ } 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; @@ -162,7 +202,7 @@ return new intlit(val); } if (typeof val == 'string') { - return new stringlit(val); + return new strlit(val); } if (val instanceof Array) { return new arraylit(val); @@ -196,8 +236,30 @@ 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); +}; + +var quote_prefix = 0; + symbol.prototype.eval = function(env) { - return env.find(this.name); + 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) { + return makeASTNode(val); + } else { + var hygenic = env.findQuoteTrans(this.name); + return hygenic ? new symbol(hygenic, this.symbols) : this; + } }; intlit.prototype.eval = @@ -216,6 +278,14 @@ return this; }; +intlit.prototype.quote = + floatlit.prototype.quote = + strlit.prototype.quote = + arraylit.prototype.quote = + listlit.prototype.quote = function(env) { + return this; +}; + funcall.prototype.eval = function(env) { var args = []; var name = this.name; @@ -224,10 +294,10 @@ } if (name == 'quote') { if (this.receiver) { - return this.receiver; + return this.receiver.quote(env); } if (this.args.length) { - return this.args[0]; + return this.args[0].quote(env); } throw new Error('quote takes an argument'); } @@ -253,11 +323,11 @@ return fun.apply(null, args); } else { //if (typeof args[0]'tpmeth_'+name in args[0]) { - //try { + try { return args[0]['tpmeth_'+name].apply(args[0], args.slice(1)); - /*} catch(e) { - throw new Error('Error, \n\t' + e.message.split('\n').join('\n\t') + '\ninvoking method ' + name + ' on object ' + args[0] + ' ' + JSON.stringify(Object.keys(args[0]))); - }*/ + } catch(e) { + throw new Error('Error, \n\t' + e.message.split('\n').join('\n\t') + '\ninvoking method ' + name + ' on object ' + args[0]); + } /*} else {JSON.stringify throw new Error('No method named ' + name + ' on object ' + JSON.stringify(args[0])); }*/ @@ -289,6 +359,21 @@ return makeASTNode(macro.apply(null, args)); }; +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 = env.findQuoteTrans(this.name); + if (!name) { + name = this.name; + } + var ret = new funcall(name, args); + ret.receiver = receiver; + return ret; +}; + object.prototype.eval = function(parentenv) { var env = new environment(parentenv); var obj = {env: env}; @@ -369,6 +454,32 @@ 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) { + //Make sure method names don't get renamed for hygene + env.syms[msg.symbol.name] = null; + env.quotetrans[msg.symbol.name] = msg.symbol.name; + if (msg.expression instanceof lambda) { + env.syms[msg.symbol.name + '!'] = null; + env.quotetrans[msg.symbol.name + '!'] = msg.symbol.name + '!'; + } + } + } + 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); +}; + lambda.prototype.eval = function(parentenv) { var args = this.args; var exprs = this.expressions; @@ -380,7 +491,7 @@ } env.syms[args[j].cleanName()] = arguments[i]; } - if (this != null && (args.length == 0 || args[0].cleanName() != 'self')) { + if (this != null && !(args.length == 0 || args[0].cleanName() != 'self')) { env.syms['self'] = this; } var res = null; @@ -412,6 +523,22 @@ 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 hygenic = '' + quote_prefix + this.args[i].cleanName(); + env.quotetrans[this.args[i].cleanName()] = hygenic; + args.push(new symbol(hygenic, this.args[i].symbols)); + } + for (var i = 0; i < this.expressions.length; i++) { + expressions.push(this.expressions[i].quote(env)); + } + return new lambda(args, expressions); +}; + assignment.prototype.eval = function(env) { var val = this.expression.eval(env); env.findSet(this.symbol.name, val); @@ -422,3 +549,11 @@ this.expression = this.expression.macroexpand(env); return this; }; + +assignment.prototype.quote = function(env) { + var name = this.symbol.cleanName(); + env.syms[name] = null; + var hygenic = '' + quote_prefix + name; + env.quotetrans[name] = hygenic; + return new assignment(new symbol(hygenic, this.symbol.symbols), this.expression.quote(env)); +}; diff -r 60eff5f81d9a -r a1b4a2bc8d72 modules/parser.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/parser.tp Fri Nov 22 19:37:25 2013 -0800 @@ -0,0 +1,109 @@ +#{ + expandClass <- :chars { + if: (chars length) > 0 { + pos <- 0 + inverted <- false + if: (chars byte: 0) = ("^" byte: 0) { + pos <- 1 + inverted <- true + } + state_begin <- 0 + state_normal <- 1 + state_rangeend <- 2 + state <- state_begin + out <- "" + while: { pos < (chars byte_length)} do: { + if: state = state_begin { + out <- out . (chars from: pos withLength: 1) + state <- state_normal + } else: { + if: state = state_normal { + if: (chars byte: pos) = ("-" byte: 0) { + state <- state_rangeend + } else: { + out <- out . (chars from: pos withLength: 1) + } + } else: { + rangestart <- out byte: ((out byte_length) - 1) + rangeend <- chars byte: pos + if: rangeend < rangestart { + tmp <- rangeend + rangeend <- rangestart + rangestart <- tmp + } + out <- out from: 0 withLength: ((out length) - 1) + while: { rangestart <= rangeend } do: { + out <- out . (rangestart asStringChar) + rangestart <- rangestart + 1 + } + state <- state_begin + } + } + pos <- pos + 1 + } + if: inverted { + old <- out + out <- "" + cur <- 0 + while: { cur < 256 } do: { + out <- out . (cur asStringChar) + cur <- cur + 1 + } + } + out + } else: { + "" + } + } + charClass <- macro: :rawchars { + eval: rawchars :chars { + chars <- expandClass: chars + //TODO: Use a more sophisticated approach for large classes + quote: :tomatch { + if: (tomatch isString?) { + check <- 0 + + nomatch <- true + while: { nomatch && check < (chars byte_length) } do: { + if: (tomatch byte: 0) = (chars byte: check) { + nomatch <- false + } + check <- check + 1 + } + if: nomatch { + #{ + matched? <- { false } + } + } else: { + #{ + matched? <- { true } + matchlen <- { 1 } + } + } + } else: { + #{ + matched? <- { false } + } + } + } + } else: { + print: "uh oh" + } + } + alpha <- charClass: "a-zA-Z" + + main <- { + cmatch <- alpha: "c0123" + zeromatch <- alpha: "01234" + if: (cmatch matched?) { + print: "c0123 matched with length " . (cmatch matchlen) . "\n" + } else: { + print: "c0123 didn't match\n" + } + if: (zeromatch matched?) { + print: "0123 matched with length " . (zeromatch matchlen) . "\n" + } else: { + print: "0123 didn't match\n" + } + } +}