# HG changeset patch # User Mike Pavone # Date 1375771037 25200 # Node ID a2d2d8e0929124689fb33e734943d203a2143b2a # Parent 6f8d868e8da00de44c05e5f1c0c515b558ded0af# Parent 1157639353e773de034f5624d39e0dd21450262b Merge diff -r 6f8d868e8da0 -r a2d2d8e09291 TASKS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TASKS Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,16 @@ +Improve string library +Add basic UTF-8 support +Implement import: in C backend +Implement route:via +Implement a hash dict based on hash set implementation +Rejigger how built-in type/modules are handled slightly so we can add array new: which will return an array pre-allocated to a certain size +Implement immutable objects +Implement lists +Add support for actors +Check for breakage in Javascript backend and fix it +Port all library stuff from C backend to Javascript backend +Fix block comments in grammar +Add dict literals to grammar and compiler +Re-write compiler in TP +Finish type system design +Implement type system \ No newline at end of file diff -r 6f8d868e8da0 -r a2d2d8e09291 cbackend.js --- a/cbackend.js Mon Aug 05 23:36:18 2013 -0700 +++ b/cbackend.js Mon Aug 05 23:37:17 2013 -0700 @@ -7,43 +7,33 @@ { if (!(methodName in methodIds)) { methodIds[methodName] = nextmethodId++; - + } return methodIds[methodName]; } -function importSym(obj, src, key) -{ - if(!(key in src)) { - throw new Error(key +' not found in source object for import'); - } - if(key in obj) { - throw new Error(key +' already exists in target object for import') - } - obj[key] = src[key]; -} - -function doImport(obj, src, symlist) +function getOpMethodName(opname) { - if (symlist === undefined) { - each(src, function(key,val) { - if (key != 'parent') { - importSym(obj, src, key); - } - }); + var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_', '&&':'if', '||':'ifnot'}; + if (opname in optoMeth) { + return optoMeth[opname]; } else { - for (var i = 0; i < symlist.length; ++i) { - importSym(obj, src, symlist[i]); - } + return opname; } - return obj; } op.prototype.toC = function(isReceiver) { - var optoMeth = {'+': 'ADD_', '-': 'SUB_', '*': 'MUL_', '/': 'DIV_', '%': 'MOD_', '=': 'EQ_', '!=': 'NEQ_', '<': 'LT_', '>': 'GT_', '>=': 'GEQ_', '<=': 'LEQ_', '.': 'CAT_'}; - var method = optoMeth[this.op]; - return 'mcall(' + getMethodId(method) + '/* ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; + var method = getOpMethodName(this.op); + return 'mcall(' + getMethodId(method) + '/* operator ' + method + ' */, 2, (object *)' + this.left.toC() + ', ' + this.right.toC() + ')\n'; }; +op.prototype.toCLLExpr = function(vars) { + var opmap = {'=': '==', 'xor': '^'}; + return this.left.toCLLExpr(vars) + (this.op in opmap ? opmap[this.op] : this.op) + this.right.toCLLExpr(vars); +}; +op.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';']; +}; + function escapeCName(name) { @@ -60,7 +50,7 @@ var pre = ''; switch(info.type) { case 'self': - + pre = (new symbol('self', symbols)).toC() + '->'; break; case 'parent': @@ -108,6 +98,40 @@ } return getSymbolPrefix(info, this.symbols) + escapeCName(name); } +symbol.prototype.toCTypeName = function() { + return this.cleanName(); +}; +symbol.prototype.toCLLExpr = function(vars) { + var name = this.cleanName(); + if (name in vars) { + return name; + } + if (name == 'self') { + return 'self'; + } + var info = this.symbols.find(name, false, true); + var symbols = this.symbols; + while (info && info.type == 'local') { + symbols = symbols.parent; + info = symbols.find(name, false, true); + } + if (!info) { + return name; + } + if (info.type == 'toplevel') { + return toplevel.moduleVar(name); + } else if (info.type == 'self') { + if (info.isll || !(info.def instanceof lambda)) { + return 'self->' + name; + } else { + return 'mcall(' + getMethodId(name) + '/* ' + name + ' */, 1, self)'; + } + } + throw new Error('Unsupported reference type ' + info.type + ' for variable ' + name); +}; +symbol.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; var declaredInts = {}; @@ -119,10 +143,22 @@ } return '((object *)&int32_' + str + ')'; } +intlit.prototype.toCLLExpr = function(vars) { + return this.val.toString(); +}; +intlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; floatlit.prototype.toC = function() { return 'make_float(' + this.val.toString() + ')'; } +floatlit.prototype.toCLLExpr = function(vars) { + return this.val.toString(); +}; +floatlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) + ';' ]; +}; var declaredStrings = {}; var nextStringId = 0; @@ -134,7 +170,13 @@ declaredStrings[this.val] = nextStringId++; } return '((object *)&str_' + declaredStrings[this.val] + ')'; -} +}; +strlit.prototype.toCLLExpr = function(vars) { + return '"' + this.val.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r') + '"'; +}; +strlit.prototype.toCLines = function(vars, needsreturn) { + return [ (needsreturn ? 'return (object *)' : '' ) + this.toCLLExpr(vars) +';' ]; +}; listlit.prototype.toC = function() { var ret = 'make_list(' + this.val.length; @@ -162,6 +204,8 @@ } else { throw new Error("Unexpected AST type for foreign:"); } + } else if(name == 'llProperty:withType' || name == 'llProperty:withVars:andCode') { + return null; } var args = this.args.slice(0, this.args.length); if (this.receiver) { @@ -170,7 +214,7 @@ var method = false; var funinfo = this.symbols.find(name); var start = 0; - if (!funinfo || funinfo.def instanceof setter) { + if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { method = true; } else { switch(funinfo.type) @@ -202,12 +246,122 @@ } var callpart; if (method) { - callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; + if (funinfo && funinfo.type == 'self' && funinfo.def.name) { + callpart = funinfo.def.name + '(' + (funinfo.def.symbols.parent.needsenv ? (new symbol('self', this.symbols)).toC() + '->env' : 'NULL' ); + } else { + callpart = 'mcall(' + getMethodId(name) + '/* ' + name + ' */'; + } } else { callpart = 'ccall(' + (new symbol(name, this.symbols)).toC(); } return callpart + ', ' + args.length + args.join('') + ')'; -} +}; +funcall.prototype.toCTypeName = function() { + switch(this.name) + { + case 'ptr:': + case 'ptr': + var receiver = this.receiver ? this.receiver : this.args[0]; + return receiver.toCTypeName() + ' *'; + break; + default: + throw new Error('invalid use of funcall expression where a C type name is expected'); + } +}; +funcall.prototype.toCLines = function(vars, needsreturn) { + var lines = []; + var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; + var args = this.args.slice(0, this.args.length); + if (this.receiver) { + args.splice(0, 0, [this.receiver]); + } + switch(name) + { + case 'if': + lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(vars, needsreturn); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + if (needsreturn) { + lines.push('} else {'); + lines.push('\t return module_false;'); + lines.push('}'); + } else { + lines.push('}'); + } + break; + case 'if:else': + lines.push('if (' + this.args[0].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(vars, needsreturn); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + lines.push('} else {'); + blines = this.args[2].toCLines(vars, needsreturn); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + lines.push('}'); + break; + case 'while:do': + if (needsreturn) { + throw new Error("while:do can't be last statement in llMessage code block"); + } + lines.push('while (' + this.args[0].toCLLExpr(vars) + ') {'); + var blines = this.args[1].toCLines(vars); + for (var i in blines) { + lines.push('\t' + blines[i]); + } + lines.push('}'); + break; + default: + lines.push( (needsreturn ? 'return (object *)' : '') + this.toCLLExpr(vars) + ';'); + } + return lines; +}; + +funcall.prototype.toCLLExpr = function(vars) { + var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; + var args = this.args.slice(0, this.args.length); + if (this.receiver) { + if(this.args.length == 0) { + return this.receiver.toCLLExpr(vars) + '->' + this.name; + } else if (this.args.length == 1 && name[name.length-1] == '!') { + return this.receiver.toCLLExpr(vars) + '->' + this.name.substr(0, name.length-1) + ' = ' + args[0].toCLLExpr(vars); + } else { + args.splice(0, 0, this.receiver); + } + } + switch(name) + { + case 'if': + return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : 0)'; + case 'if:else': + return '((' + args[0].toCLLExpr(vars) + ') ? (' + args[1].toCLLExpr(vars) + ') : (' + args[2].toCLLExpr(vars) + '))'; + case 'while:do': + throw new Error('while:do not allowed in expression context in llMessage block'); + case 'addr_of': + return '&(' + args[0].toCLLExpr(vars) + ')'; + case 'sizeof': + return 'sizeof(' + args[0].toCTypeName() + ')'; + case 'get': + return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + ']'; + case 'set': + return args[0].toCLLExpr(vars) + '[' + args[1].toCLLExpr(vars) + '] = ' + args[2].toCLLExpr(vars); + case 'not': + return '!(' + args[0].toCLLExpr(vars) + ')'; + case 'mcall': + if (args[0] instanceof symbol) { + args[0] = new intlit(getMethodId(args[0].name)); + } + default: + for (var i in args) { + args[i] = args[i].toCLLExpr(vars); + } + return name + '(' + args.join(', ') + ')'; + } +}; function cObject(name) { this.name = name; @@ -275,7 +429,7 @@ this.addMessage('_init', { vars: {}, lines: init - }); + }); this.initmsgadded = true; } } @@ -316,7 +470,7 @@ } if (this.slots[i].length == 1) { slotdefs += '\tif (method_id == ' + this.slots[i][0][0] + ') { /* ' + this.slots[i][0][2] + '*/\n' + - '\t\t' + this.slots[i][0][1] + '\n' + + '\t\t' + this.slots[i][0][1] + '\n' + '\t}\n' + '\treturn no_impl(method_id, num_params, (object *)self, args);\n}\n'; } else { @@ -327,7 +481,7 @@ } slotdefs += '\t\tdefault:\n' + '\t\t\treturn no_impl(method_id, num_params, (object *)self, args);\n\t}\n}\n'; - + } metadef += this.name + '_slot_' + i; } else { @@ -355,11 +509,15 @@ var nextobject = 0; -object.prototype.toC = function() { + +object.prototype.toCObject = function() { var messages = this.messages; var values = []; - var imports = [] - var me = new cObject('object_' + nextobject++); + var imports = []; + if (!this.name) { + this.name = 'object_' + nextobject++; + } + var me = new cObject(this.name); this.symbols.typename = me.name; if (this.symbols.needsenv) { me.addProperty('env', this.symbols.envVar(), 'struct ' + this.symbols.getEnvType() + ' * '); @@ -381,16 +539,34 @@ importsyms.push(new strlit(el.name)); }); imports.push({symbols: new listlit(importsyms), src: messages[i].args[1]}); + } else if(messages[i].name == 'llProperty:withType:' && messages[i].args.length == 2) { + me.addProperty(messages[i].args[0].name, null, messages[i].args[1].toCTypeName()) + } else if(messages[i].name == 'llMessage:withVars:andCode:' && messages[i].args.length == 3) { + var msgname = messages[i].args[0].name + var rawvars = messages[i].args[1].expressions; + var vars = {}; + for(var v in rawvars) { + vars[rawvars[v].symbol.name] = rawvars[v].expression.toCTypeName(); + } + me.addMessage(msgname, { + vars: vars, + lines: messages[i].args[2].toCLines(vars, true) + }); } else { - throw new Error('Only import and import:from calls allowed in object context'); + + throw new Error('Only import and import:from calls allowed in object context. ' + messages[i].name + 'with ' + messages[i].args.length + ' arguments found instead.'); } } else { messages[i].toCObject(me); } } - return me.toC(); -} + return me; +}; + +object.prototype.toC = function() { + return this.toCObject().toC(); +}; var toplevelcode; var forwarddec; @@ -444,10 +620,16 @@ 'str = (string *)make_object(&string_meta, NULL, 0);', 'str->data = GC_MALLOC(12);', 'sprintf(str->data, "%d", self->num);', - 'str->length = str->bytes = strlen(str->data);', + 'str->len = str->bytes = strlen(str->data);', 'return &(str->header);' ] }); + int32.addMessage('isInteger?', { + vars: {}, + lines: [ + 'return ' + toplevel.moduleVar('true') + ';' + ] + }); int32.addMessage('hash', { vars: {}, lines: [ @@ -459,169 +641,20 @@ function makeArray() { - var array = new cObject('array'); - array.addProperty('size', null, 'uint32_t'); - array.addProperty('storage', null, 'uint32_t'); - array.addProperty('data', null, 'object **'); - array.addMessage('get', { - vars: {index: 'obj_int32 *'}, - lines: [ - 'index = va_arg(args, obj_int32 *);', - 'if (index->num >= 0 && index->num < self->size) {', - ' return self->data[index->num];', - '}', - 'return ' + toplevel.moduleVar('false') + ';' - ] - }); - array.addMessage('set', { - vars: {index: 'obj_int32 *'}, - lines: [ - 'index = va_arg(args, obj_int32 *);', - 'if (index->num >= 0 && index->num < self->size) {', - ' self->data[index->num] = va_arg(args, object *);', - '}', - 'return &(self->header);' - ] - }); - array.addMessage('foreach', { - vars: {index: 'obj_int32 *', i: 'int32_t', clos: 'lambda *'}, - lines: [ - 'clos = va_arg(args, lambda *);', - 'for (i = 0; i < self->size; i++) {', - ' index = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - ' index->num = i;', - ' ccall(clos, 2, index, self->data[i]);', - '}', - 'return &(self->header);' - ] - }); - array.addMessage('append', { - vars: {tmp: 'object **'}, - lines: [ - 'if (self->storage == self->size) {', - ' self->storage *= 2;', - ' tmp = GC_REALLOC(self->data, self->storage * sizeof(object *));', - ' if (!tmp) {', - ' fputs("Failed to increase array size\\n", stderr);', - ' exit(1);', - ' }', - ' self->data = tmp;', - '}', - 'self->data[self->size++] = va_arg(args, object *);', - 'return &(self->header);' - ] - }); - array.addMessage('length', { - vars: {intret: 'obj_int32 *'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = self->size;', - 'return &(intret->header);' - ] - }); - return array; + var arrayfile = toplevel.names['array']; + var ast = parseFile(arrayfile.path + '/' + arrayfile.file); + ast.name = 'array'; + ast.populateSymbols(toplevel); + return ast.toCObject(); } function makeString() { - var string = new cObject('string'); - string.addProperty('length', null, 'uint32_t'); - string.addProperty('bytes', null, 'uint32_t'); - string.addProperty('data', null, 'char *'); - string.addMessage('length', { - vars: {intret: 'obj_int32 *'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = self->length;', - 'return &(intret->header);' - ] - }); - string.addMessage('byte_length', { - vars: {intret: 'obj_int32 *'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = self->bytes;', - 'return &(intret->header);' - ] - }); - string.addMessage('EQ_', { - vars: {argb: 'string *'}, - lines: [ - 'argb = va_arg(args, string *);', - 'if (self->length == argb->length && self->bytes == argb->bytes && !memcmp(self->data, argb->data, self->bytes)) {', - ' return ' + toplevel.moduleVar('true') + ';', - '}', - 'return ' + toplevel.moduleVar('false') + ';', - ] - }); - string.addMessage('NEQ_', { - vars: {argb: 'string *'}, - lines: [ - 'argb = va_arg(args, string *);', - 'if (self->length != argb->length || self->bytes != argb->bytes || memcmp(self->data, argb->data, self->bytes)) {', - ' return ' + toplevel.moduleVar('true') + ';', - '}', - 'return ' + toplevel.moduleVar('false') + ';', - ] - }); - string.addMessage('print', { - vars: {}, - lines: [ - 'fwrite(self->data, 1, self->bytes, stdout);', - 'return &(self->header);' - ] - }); - string.addMessage('string', { - vars: {}, - lines: [ 'return &(self->header);' ] - }); - string.addMessage('CAT_', { - vars: {argbo: 'object *', argb: 'string *', out: 'string *'}, - lines: [ - 'argbo = va_arg(args, object *);', - 'argb = (string *)mcall(' + getMethodId('string') + ', 1, argbo);', - 'out = (string *)make_object(&string_meta, NULL, 0);', - 'out->bytes = self->bytes + argb->bytes;', - 'out->length = self->length + argb->length;', - 'out->data = GC_MALLOC_ATOMIC(out->bytes+1);', - 'memcpy(out->data, self->data, self->bytes);', - 'memcpy(out->data + self->bytes, argb->data, argb->bytes + 1);', - 'return &(out->header);' - ] - }); - string.addMessage('byte', { - vars: {index: 'obj_int32 *', intret: 'obj_int32 *'}, - lines: [ - 'index = va_arg(args, obj_int32 *);', - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = index->num < self->bytes ? self->data[index->num] : 0;', - 'return &(intret->header);' - ] - }); - string.addMessage('int32', { - vars: {intret: 'obj_int32 *'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = atoi(self->data);', - 'return &(intret->header);' - ] - }); - string.addMessage('hash', { - vars: {intret: 'obj_int32 *', i: 'uint32_t'}, - lines: [ - 'intret = (obj_int32 *)make_object(&obj_int32_meta, NULL, 0);', - 'intret->num = 0;', - 'if (self->bytes) {', - ' intret->num = self->data[0] << 7;', - ' for (i = 0; i < self->bytes; i++) {', - ' intret->num = (1000003 * intret->num) ^ self->data[i];', - ' }', - ' intret->num = intret->num ^ self->bytes;', - '}', - 'return &(intret->header);' - ] - }); - return string; + var arrayfile = toplevel.names['string']; + var ast = parseFile(arrayfile.path + '/' + arrayfile.file); + ast.name = 'string'; + ast.populateSymbols(toplevel); + return ast.toCObject(); } function makelambda() @@ -753,31 +786,21 @@ toplevel.names['os'] = os; } -modulefile.prototype.populateSymbols = function (toplevel) { - if (!this.ast) { - this.ast = parseFile(this.path + '/' + this.file); - this.ast.populateSymbols(toplevel); - } -}; - modulefile.prototype.toC = function(){ - this.populateSymbols(toplevel); return this.ast.toCModuleInstance(); }; function processUsedToplevel(toplevel) -{ +{ var alwaysused = ['true', 'false']; var ret = ''; var modulenum = 0; - var newused = Object.keys(toplevel.used); - var allused = newused; var visited = {}; for (var i in alwaysused) { - forwarddec += 'object * ' + toplevel.moduleVar(alwaysused[i]) + ';\n'; - toplevel.names[alwaysused[i]].populateSymbols(toplevel); - visited[alwaysused[i]] = true; + toplevel.used[alwaysused[i]] = true; } + var newused = Object.keys(toplevel.used); + var allused = newused; while (newused.length) { for (var i in newused) { debugprint('//---module', newused[i], '--- populate symbols'); @@ -794,13 +817,10 @@ } } } - for (var i in alwaysused) { - allused.push(alwaysused[i]); - } - + for (var i = allused.length-1; i >= 0; i--) { var symbol = allused[i]; - debugprint('//---module', symbol, '--- compile'); + debugprint('//---module', symbol, '(' + i +')--- compile'); ret += '\t' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toC() + ';\n'; } return ret; @@ -808,8 +828,8 @@ function makeCProg(obj) { + forwarddec = toplevelcode = ''; var builtins = builtinTypes(); - forwarddec = toplevelcode = ''; for (var i in builtins) { forwarddec += builtins[i].toEarlyCDef(); toplevelcode += builtins[i].toCDef(); @@ -833,15 +853,13 @@ return this.toC(); } -var lambdanum = 0; - lambda.prototype.toC = function() { var args = this.args ? this.args.slice(0, this.args.length) : []; var exprs = this.expressions; - var mynum = lambdanum++; - debugprint('//lambda', mynum); + debugprint('//', this.name); if (Object.keys(this.symbols.closedover).length) { - this.symbols.envtype = 'lambda_' + mynum + '_env'; + this.symbols.envtype = this.name + '_env'; + forwarddec += 'typedef struct ' + this.symbols.envtype + ' ' + this.symbols.envtype + ';\n' } if (this.selftype) { this.symbols.defineVar('self', this.selftype); @@ -854,7 +872,7 @@ } for (var i = 0; i < args.length; ++i) { var argname = args[i].toC(); - + args[i] = (argname.indexOf('->') < 0 ? '\tobject * ' : '\t') + argname + ' = va_arg(args, object *);\n'; } var compiled = [] @@ -868,9 +886,9 @@ if (exprs.length) { exprs[exprs.length-1] = 'return (object *)(' + exprs[exprs.length-1] + ');'; } - + if (Object.keys(this.symbols.closedover).length) { - forwarddec += 'typedef struct lambda_' + mynum + '_env {\n'; + forwarddec += 'struct ' + this.name + '_env {\n'; if (this.symbols.needsParentEnv) { forwarddec += '\tstruct ' + this.symbols.parentEnvType() + ' * parent;\n'; } @@ -881,18 +899,19 @@ forwarddec += '\tobject * ' + escapeCName(varname) + ';\n'; } } - forwarddec += '} lambda_' + mynum + '_env;\n' - - var myenvinit = '\tlambda_' + mynum + '_env * myenv = GC_MALLOC(sizeof(lambda_' + mynum + '_env));\n'; + forwarddec += '};\n' + + var myenvinit = '\t' + this.name + '_env * myenv = GC_MALLOC(sizeof(' + this.name + '_env));\n'; if (this.symbols.needsParentEnv) { myenvinit += '\tmyenv->parent = env;\n'; } - this.symbols.envtype = 'lambda_' + mynum + '_env'; + this.symbols.envtype = this.name + '_env'; } else { var myenvinit = ''; } - - toplevelcode += 'object * lambda_' + mynum + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; + forwarddec += 'object *' + this.name + ' (' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...);\n'; + + toplevelcode += 'object * ' + this.name + ' ( ' + this.symbols.parentEnvType() + ' * env, uint32_t num_args, ...) {\n\tva_list args;\n' + myenvinit + '\tva_start(args, num_args);\n'; if (this.selftype) { var selfvar = (new symbol('self', this.symbols)).toC(); if (selfvar == 'self') { @@ -900,11 +919,10 @@ } else { toplevelcode += '\t' + selfvar + ' = va_arg(args, ' + this.selftype + ' *);\n'; } - + } toplevelcode += args.join('') + '\tva_end(args);\n' + exprs.join(';\n\t') + '\n}\n'; - this.name = 'lambda_' + mynum; - + if (this.selftype) { return this.name; } else { @@ -914,11 +932,11 @@ } else { var envvar = 'myenv'; } - debugprint('//lambda_' + mynum, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); + debugprint('//' + this.name, 'has envvar:', envvar, 'num vars closed over:', Object.keys(this.symbols.closedover).length); return 'make_lambda(' + envvar + ', (closure_func)' + this.name + ')'; - } else { - toplevelcode += 'lambda lambda_obj_' + mynum + ' = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n'; - return '((object *)&lambda_obj_' + mynum + ')'; + } else { + toplevelcode += 'lambda ' + this.name + '_obj = {{&lambda_meta, NULL}, NULL, lambda_' + mynum + '};\n'; + return '((object *)&' + this.name + '_obj)'; } } }; @@ -932,6 +950,31 @@ }; lambda.prototype.toCModule = function() { return makeCProg(this); +}; +lambda.prototype.toCLines = function(vars, needsreturn) { + var lines = []; + for (var i in this.args) { + var name = this.args[i].name; + if (name[0] == ':') { + name = name.substr(1); + } + if(name != 'self') { + lines.push(name + ' = va_arg(args, ' + vars[name] + ');'); + } + } + for (var i in this.expressions) { + var exprlines = this.expressions[i].toCLines(vars, needsreturn && i == this.expressions.length - 1); + for (var j in exprlines) { + lines.push('\t' + exprlines[j]); + } + } + return lines; +} +lambda.prototype.toCLLExpr = function(vars) { + if (this.expressions.length != 1) { + throw new Error('lambda in expression context must have a single statement in llMessage block'); + } + return this.expressions[0].toCLLExpr(vars); } assignment.prototype.toC = function() { @@ -971,7 +1014,7 @@ paramget += escaped + ' = va_arg(args, object *); '; } } - cobj.addMessage(this.symbol.name, { + cobj.addMessage(getOpMethodName(this.symbol.name), { vars: messagevars, lines: [paramget + 'return ' + val + '(' + (cobj.hasenv ? 'self->env' : 'NULL') + ', ' + params.length + (params.length ? ', ' : '') + params.join(', ') + ');'] }); @@ -982,3 +1025,6 @@ } } }; +assignment.prototype.toCLines = function(vars, needsreturn) { + return [(needsreturn ? 'return ' : '') + this.symbol.toCLLExpr(vars) + ' = ' + this.expression.toCLLExpr(vars) + ';'] +}; diff -r 6f8d868e8da0 -r a2d2d8e09291 compiler.js --- a/compiler.js Mon Aug 05 23:36:18 2013 -0700 +++ b/compiler.js Mon Aug 05 23:37:17 2013 -0700 @@ -11,23 +11,57 @@ this.file = file; } +modulefile.prototype.populateSymbols = function (toplevel) { + if (!this.ast) { + this.ast = parseFile(this.path + '/' + this.file); + this.ast.populateSymbols(toplevel); + } +}; + +modulefile.prototype.popuplateSymbolsAsync = function(toplevel, whenDone) { + if (!this.ast) { + var self = this; + get(this.path + '/' + this.file, function(data) { + self.ast = parser.parse(data.responseText); + self.ast.populateSymbols(toplevel); + whenDone(); + }); + } else { + whenDone(); + } +}; + +function getfileshtml(path, data, names) +{ + var fakeEl = newEl("div", { + innerHTML: data.response + }); + each(qall('a', fakeEl), function(idx, a) { + var tpidx = a.textContent.indexOf('.tp'); + var modname = a.textContent.substr(0, tpidx); + if (tpidx > -1) { + names[modname] = new modulefile(path, modname + '.tp'); + } + }); +} + var toplevel = new topsymbols([]); function topsymbols(moduledirs) { this.names = null; this.used = {}; this.nextmodulenum = 0; + this.onready = null; var self = this; if (typeof window === "object") { - get('/src/', function(data) { - self.names = {}; - var fakeEl = newEl("div", { - innerHTML: data.response - }); - each(qall('a', fakeEl), function(idx, a) { - var tpidx = a.textContent.indexOf('.tp'); - if (tpidx > -1) { - self.names[a.textContent.substr(0, tpidx)] = true; + get('/modules/', function(data) { + var names = {} + getfileshtml('/modules', data, names); + get('/src/', function(data) { + getfileshtml('/src', data, names); + self.names = names; + if (self.onready) { + self.onready(); } }); }); @@ -57,10 +91,10 @@ }; } return null; -} +}; topsymbols.prototype.getEnvType = function() { return 'void'; -} +}; topsymbols.prototype.moduleVar = function(name) { if (!(name in this.names)) { throw new Error('symbol ' + name + ' not found at toplevel'); @@ -72,17 +106,33 @@ this.names[name].modulevar = 'module_' + this.nextmodulenum++ } return this.names[name].modulevar; -} +}; +topsymbols.prototype.onReady = function(fun) { + if (this.names) { + fun(); + return; + } + if (!this.onready) { + this.onready = fun; + } else { + var oldready = this.onready; + this.onready = function() { + oldready(); + fun(); + }; + } +}; function osymbols(parent) { this.parent = parent; this.names = {}; + this.llnames = {}; this.needsenv = false; this.typename = null; this.needsparent = false; } -osymbols.prototype.find = function(name, nestedcall) { +osymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//osymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { @@ -93,11 +143,18 @@ } var ret = { type: 'self', + isll: false, def: this.names[name], selftype: this.typename }; + } else if(allowll && name in this.llnames) { + return { + type: 'self', + isll: true, + selftype: this.typename + }; } else if(this.parent) { - var ret = this.parent.find(name, nestedcall); + var ret = this.parent.find(name, nestedcall, allowll); if (ret) { if(ret.type == 'self') { ret.type = 'parent'; @@ -119,6 +176,9 @@ osymbols.prototype.defineMsg = function(name, def) { this.names[name] = def; } +osymbols.prototype.defineLLProperty = function(name) { + this.llnames[name] = true; +} osymbols.prototype.parentObject = function() { if (!this.parent) { return 'null'; @@ -160,7 +220,7 @@ this.envtype = 'void'; this.needsParentEnv = false; } -lsymbols.prototype.find = function(name, nestedcall) { +lsymbols.prototype.find = function(name, nestedcall, allowll) { debugprint('//lsymbols.find', name + ', exists?:', name in this.names, ', nested?:', nestedcall); if (name in this.names) { if (this.names[name] instanceof funcall && this.names[name].name == 'foreign:') { @@ -187,7 +247,7 @@ } } } else if(this.parent) { - var ret = this.parent.find(name, true); + var ret = this.parent.find(name, true, allowll); if (ret) { if (ret.type == 'closedover') { ret.type = 'upvar'; @@ -262,22 +322,17 @@ function toobj(val) { - switch(typeof val) - { - case 'boolean': - if(val) { - return mainModule.strue; - } else { - return mainModule.sfalse; - } - case 'number': - return mainModule.snumber(val); - } - throw new Error("can't make val into object"); + return (typeof val == "boolean") ? (val ? module_true : module_false) : val; } op.prototype.populateSymbols = function(symbols, isReceiver) { this.left.populateSymbols(symbols); + if (this.op == '&&' || this.op == '||') { + //&& and || are syntactic sugar for if and ifnot with + //the second argument transformed into a lambda to + //achieve short-circuit evalutation + this.right = new lambda([], [this.right]); + } this.right.populateSymbols(symbols); }; @@ -311,12 +366,39 @@ } funcall.prototype.populateSymbols = function(symbols) { + var isll = false; if (this.name == 'foreign:') { if ((this.args[0] instanceof lambda) || (this.args[0] instanceof object) || (this.args[0] instanceof symbol)) { return; } else { throw new Error("Unexpected AST type for foreign:"); } + } else if (this.name == 'llProperty:withType:') { + if (this.args[0] instanceof symbol) { + if ((this.args[1] instanceof symbol) || (this.args[1] instanceof funcall)) { + symbols.defineLLProperty(this.args[0].name); + return; + } else { + throw new Error("Second argument to llProperty:withType: must be a symbol or funcall"); + } + } else { + throw new Error("First argument to llProperty:withType: must be a symbol"); + } + } else if (this.name == 'llMessage:withVars:andCode:') { + if (this.args[0] instanceof symbol) { + if (this.args[1] instanceof lambda) { + if (this.args[2] instanceof lambda) { + symbols.defineMsg(this.args[0].name, this.args[2]); + isll = true; + } else { + throw new Error("Third argument to llMessage:withVars:andCode: must be a lambda"); + } + } else { + throw new Error("Second argument to llMessage:withVars:andCode: must be a lambda"); + } + } else { + throw new Error("First argument to llMessage:withVars:andCode: must be a symbol"); + } } this.symbols = symbols; var name = this.name[this.name.length-1] == ':' ? this.name.substr(0, this.name.length-1) : this.name; @@ -325,26 +407,29 @@ symbols.find('self'); } for (var i in this.args) { - this.args[i].populateSymbols(symbols); + this.args[i].populateSymbols(symbols, undefined, isll); } if (this.receiver) { - this.receiver.populateSymbols(symbols); + this.receiver.populateSymbols(symbols, undefined, isll); } } funcall.prototype.populateSymbolsObject = function(symbols) { - this.populateSymbols(symbols.parent); + this.populateSymbols(symbols); } object.prototype.populateSymbols = function(symbols) { - symbols = new osymbols(symbols); + var symbols = new osymbols(symbols); for (var i in this.messages) { this.messages[i].populateSymbolsObject(symbols); } this.symbols = symbols; } - -lambda.prototype.populateSymbols = function(symbols, isobject) { +var lambdanum = 0; +lambda.prototype.populateSymbols = function(symbols, isobject, isll) { + if (!isll) { + this.name = 'lambda_' + lambdanum++; + } var args = this.args ? this.args.slice(0, this.args.length) : []; var exprs = this.expressions; var symbols = new lsymbols(symbols); @@ -394,3 +479,4 @@ this.fun = fun; } getter.prototype.args = [new symbol('self')]; + diff -r 6f8d868e8da0 -r a2d2d8e09291 editor.css --- a/editor.css Mon Aug 05 23:36:18 2013 -0700 +++ b/editor.css Mon Aug 05 23:37:17 2013 -0700 @@ -44,7 +44,12 @@ #src .selected { - background-color: yellow; + background-color: #FFFFB0; +} + +#src .selectParent +{ + background-color: #E0E0E0; } #editor @@ -78,7 +83,7 @@ box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; - height: 50%; + height: 33%; margin: 0; overflow: auto; } @@ -95,12 +100,12 @@ border-bottom-width: 0px; } -#editor #operators +#editor #operators, #editor #literals { display: none; } -#editor .showops > #operators +#editor .showops > #operators, #editor .showlit > #literals { display: block; } @@ -118,6 +123,11 @@ cursor: pointer; } +#nav > li +{ + min-width: 20mm; +} + /* AST Nodes */ #src span { @@ -153,6 +163,11 @@ display: block; } +.varname:after +{ + content: ' <-'; +} + .lambda { display: inline; @@ -200,6 +215,36 @@ color: #FF1030; } +.string:before, .string:after +{ + content: '"'; +} + +.listlit:before +{ + content: '['; +} + +.listlit:after, .arraylit:after +{ + content: ']'; +} + +.arraylit:before +{ + content: '#['; +} + +.funpart +{ + color: #108030; +} + +.args +{ + color: #1030C0; +} + .op > div { display: inline-block; diff -r 6f8d868e8da0 -r a2d2d8e09291 editor.js --- a/editor.js Mon Aug 05 23:36:18 2013 -0700 +++ b/editor.js Mon Aug 05 23:37:17 2013 -0700 @@ -1,122 +1,203 @@ -object.prototype.toHTML = function(node) { +object.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; var el = newEl('div', { - className: 'object' + className: 'object', + onclick: function(event) { + main_module.objectClick(this, astNode, event); + } }); + this.domNode = el; node.appendChild(el); for (var i in this.messages) { - this.messages[i].toHTML(el); + this.messages[i].toHTML(el, this); } }; -lambda.prototype.toHTML = function(node) { +lambda.prototype.toHTML = function(node, up) { + this.up = up var astNode = this; var el = newEl('div', { className: 'lambda', onclick: function(event) { - mainModule.lambdaClick(this, astNode, event); + main_module.lambdaClick(this, astNode, event); } }); + this.domNode = el; var args = newEl('div', { className: 'args' }); for (var i in this.args) { - this.args[i].toHTML(args); + this.args[i].toHTML(args, this); } var body = newEl('div', { className: 'lambdabody' }); for (var i in this.expressions) { - this.expressions[i].toHTML(body); + this.expressions[i].toHTML(body, this); } el.appendChild(args); el.appendChild(body); node.appendChild(el); }; -assignment.prototype.toHTML = function(node) { +assignment.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; var base = newEl('div', { - className: 'assignment' + className: 'assignment', + onclick: function(event) { + main_module.assignClick(this, astNode, event); + } }); var varName = newEl('span', { - textContent: this.symbol.name + ' <-' + textContent: this.symbol.name, + className: 'varname' }); + this.domNode = base; base.appendChild(varName); node.appendChild(base); - this.expression.toHTML(base); + this.expression.toHTML(base, this); }; -op.prototype.toHTML = function(node) { +op.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; var base = newEl('span', { - className: 'op' + className: 'op', + onclick: function(event) { + main_module.opClick(this, astNode, event); + } }); - this.left.toHTML(base); + this.domNode = base; + this.left.toHTML(base, this); base.appendChild(newEl('span', { textContent: this.op, className: 'opname' })); - this.right.toHTML(base); + if (this.op == '&&' || this.op == '||') { + this.right.expressions[0].toHTML(base, this); + } else { + this.right.toHTML(base, this); + } node.appendChild(base); }; -intlit.prototype.toHTML = function(node) { - node.appendChild(newEl('span', { +intlit.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; + this.domNode = newEl('span', { className: 'integer', - textContent: this.val - })); + textContent: this.val, + onclick: function(event) { + main_module.scalarClick(this, astNode, event); + } + }); + node.appendChild(this.domNode); }; -floatlit.prototype.toHTML = function(node) { - node.appendChild(newEl('span', { +floatlit.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; + this.domNode = newEl('span', { className: 'float', - textContent: this.val - })); + textContent: this.val, + onclick: function(event) { + main_module.scalarClick(this, astNode, event); + } + }); + node.appendChild(this.domNode); }; -strlit.prototype.toHTML = function(node) { - node.appendChild(newEl('span', { +strlit.prototype.toHTML = function(node, up) { + this.up = up; + var astNode = this; + this.domNode = newEl('span', { className: 'string', contentEditable: 'true', - textContent: this.val - })); + textContent: this.val, + onclick: function(event) { + main_module.scalarClick(this, astNode, event); + } + }); + node.appendChild(this.domNode); }; -funcall.prototype.toHTML = function(node) { +listlit.prototype.toHTML = function(node, up) { + this.up = up; + this.domNode = newEl('span', { + className: 'list', + }); + for (var i = 0; i < this.val.length; i++) { + this.val[i].toHTML(this.domNode, this); + } + node.appendChild(this.domNode); +}; + +arraylit.prototype.toHTML = function(node, up) { + this.up = up; + this.domNode = newEl('span', { + className: 'array', + }); + for (var i = 0; i < this.val.length; i++) { + this.val[i].toHTML(this.domNode, this); + } + node.appendChild(this.domNode); +}; + +funcall.prototype.toHTML = function(node, up) { + this.up = up; var astNode = this; var base = newEl('div', { - className: 'funcall' + className: 'funcall', + onclick: function(event) { + main_module.funClick(this, astNode, event); + } }); + this.domNode = base; if (this.receiver) { - this.receiver.toHTML(base); + this.receiver.toHTML(base, this); } var parts = this.name.split(':'); for (var i in parts ) { if(parts[i]) { base.appendChild(newEl('span', { textContent: parts[i] + (this.receiver && parts.length == 1 ? '' : ':'), - className: 'funpart', - onclick: function(event) { - mainModule.funClick(this, astNode, event); - }})); + className: 'funpart' + })); if (this.args[i]) { - this.args[i].toHTML(base); + this.args[i].toHTML(base, this); } } } for (; i < this.args.length; i++) { - this.args[i].toHTML(base); + this.args[i].toHTML(base, this); } node.appendChild(base); }; -symbol.prototype.toHTML = function(node) { +symbol.prototype.toHTML = function(node, up) { + this.up = up; var astNode = this; - node.appendChild(newEl('span', { + this.domNode = newEl('span', { className: 'symbol', textContent: this.name, onclick: function(event) { - mainModule.symbolClick(this, astNode, event); + main_module.symbolClick(this, astNode, event); } - })); + }) + node.appendChild(this.domNode); } + +function getEl(from, idx) +{ + return from[idx]; +} + +function setEl(to, idx, val) +{ + to[idx] = val; + return to; +} diff -r 6f8d868e8da0 -r a2d2d8e09291 index.html --- a/index.html Mon Aug 05 23:36:18 2013 -0700 +++ b/index.html Mon Aug 05 23:37:17 2013 -0700 @@ -21,7 +21,7 @@
    -
    +
    • <
    • >
    • @@ -32,7 +32,22 @@
    • &&
    • ||
    -
    • operators
    +
      +
    • object
    • +
    • list
    • +
    • array
    • +
    • string
    • +
    • number
    • +
    • new symbol
    • +
    +
      +
    • operators
    • +
    • literals
    • +
    +
    diff -r 6f8d868e8da0 -r a2d2d8e09291 jsbackend.js --- a/jsbackend.js Mon Aug 05 23:36:18 2013 -0700 +++ b/jsbackend.js Mon Aug 05 23:37:17 2013 -0700 @@ -3,18 +3,7 @@ function toobj(val) { - switch(typeof val) - { - case 'boolean': - if(val) { - return mainModule.strue; - } else { - return mainModule.sfalse; - } - case 'number': - return mainModule.snumber(val); - } - throw new Error("can't make val into object"); + return (typeof val == "boolean") ? (val ? module_true : module_false) : val; } function importSym(obj, src, key) @@ -45,9 +34,13 @@ } op.prototype.toJS = function(isReceiver) { - var ret = '(' + this.left.toJS() +' '+ (this.op == '=' ? '==' : this.op) +' '+ this.right.toJS() + ')'; - if (isReceiver) { - ret = 'toobj' + ret; + if (this.op == '&&') { + var ret = 'toobj(' + this.left.toJS() + ').sif(' + this.right.toJS() + ')'; + } else if(this.op == '||') { + var ret = 'toobj(' + this.left.toJS() + ').ifnot(' + this.right.toJS() + ')'; + } else { + var opmap = {'=': '==', '.': '+'}; + var ret = '(' + this.left.toJS() +' '+ (this.op in opmap ? opmap[this.op] : this.op) +' '+ this.right.toJS() + ')'; } return ret; }; @@ -80,8 +73,7 @@ pre += '.parent'; } } else if (info.type == 'toplevel') { - pre = 'modules.'; - modules[name] = false; + return toplevel.moduleVar(name); } return pre + escapeJSName(name); } @@ -122,22 +114,21 @@ args.splice(0, 0, this.receiver); } var funinfo = this.symbols.find(name); - if (!funinfo || funinfo.def instanceof setter) { + if (!funinfo || funinfo.def instanceof setter || funinfo.type == 'toplevel') { var receiver = args[0]; args.splice(0, 1); for (var i in args) { args[i] = args[i].toJS(); } - var rJS = receiver.toJS(true); + var rJS = (funinfo ? '' : 'toobj(') + receiver.toJS(true) + (funinfo ? '' : ')') ; if ((name[name.length-1] == '!' && args.length == 1) || (funinfo && funinfo.def instanceof setter)) { return '(' + rJS + '.' + escapeJSName(name.substr(0, name.length-1)) + ' = ' + args[0] + ', ' + rJS + ')'; } else { var callee = rJS + '.' + escapeJSName(name); - var callCode = callee + '(' + args.join(', ') + ')'; if (args.length == 0) { - return '(' + callee + ' instanceof Function ? ' + callCode + ' : ' + callee + ')'; + return callee; } else { - return callCode; + return callee + '(' + args.join(', ') + ')'; } } } @@ -145,7 +136,7 @@ switch(funinfo.type) { case 'self': - if (args.length < funinfo.def.args.length || funinfo.def.args[0].name != 'self') { + if (args.length < funinfo.def.args.length || funinfo.def.args.length == 0 || funinfo.def.args[0].name != 'self') { var receiver = new symbol('self', this.symbols); } else { var receiver = args[0]; @@ -160,7 +151,8 @@ ret = receiver.toJS(true) + '.'; break; case 'parent': - ret = 'this'; + var receiver = new symbol('self', this.symbols); + ret = receiver.toJS(true); for (var i = 0; i < funinfo.depth; ++i) { ret += '.parent'; } @@ -214,7 +206,7 @@ object.prototype.toJSModule = function() { this.populateSymbols(toplevel); - return '(function () {\n\tvar module = ' + indent(this.toJS()) + ';\n\treturn module;\n})' + return '(function () {\n\tvar module = ' + indent(this.toJS()) + ';\n\treturn module;\n})();' } lambda.prototype.toJS = function() { @@ -239,40 +231,162 @@ } return 'function (' + args.join(', ') + ') {\n\t' + (this.symbols.needsSelfVar ? 'var self = this;\n\t' : '') + exprs.join(';\n\t') + '\n}' }; +lambda.prototype.nonSelfArgs = function() { + var args = this.args ? this.args.slice(0, this.args.length) : []; + if (args.length && args[0].cleanName() == 'self') { + args.splice(0, 1); + } + return args; +}; lambda.prototype.toJSModule = function() { this.populateSymbols(toplevel); - return this.toJS(); + return this.toJS() + '();'; +} + +modulefile.prototype.toJSModule = function(){ + return this.ast.toJSModule(); +}; + +function processUsedToplevelJS(toplevel) +{ + var alwaysused = ['true', 'false']; + var ret = ''; + var modulenum = 0; + var visited = {}; + for (var i = 0; i < alwaysused.length; i++) { + toplevel.used[alwaysused[i]] = true; + } + var newused = Object.keys(toplevel.used); + var allused = newused; + while (newused.length) { + for (var i = 0; i < newused.length; i++) { + console.log(i, newused[i]); + toplevel.names[newused[i]].populateSymbols(toplevel); + visited[newused[i]] = true; + } + newused = []; + for (var symbol in toplevel.used) { + if (!(symbol in visited)) { + newused.push(symbol); + allused.push(symbol); + } + } + } + + for (var i = allused.length-1; i >= 0; i--) { + var symbol = allused[i]; + ret += 'var ' + toplevel.moduleVar(symbol) + ' = ' + toplevel.names[symbol].toJSModule() + '\n'; + } + return ret; +} + +function asyncProcessTopLevelJS(toplevel, whenDone) +{ + var alwaysused = ['true', 'false']; + var ret = ''; + var modulenum = 0; + var visited = {}; + for (var i = 0; i < alwaysused.length; i++) { + toplevel.used[alwaysused[i]] = true; + } + var newused = Object.keys(toplevel.used); + var allused = newused; + var i = -1; + var handler = function() { + i++; + while(newused.length) + { + if (i < newused.length) { + visited[newused[i]] = true; + toplevel.names[newused[i]].popuplateSymbolsAsync(toplevel, handler); + return; + } else { + newused = []; + for (var symbol in toplevel.used) { + if (!(symbol in visited)) { + newused.push(symbol); + allused.push(symbol); + } + } + i = 0; + } + } + whenDone(); + }; + handler(); +} + +function makeJSProg(mainmodule) +{ + return processUsedToplevelJS(toplevel) + 'main_module = ' + mainmodule.toJSModule() + '\n' + + 'Number.prototype.__defineGetter__("string", function() { return "" + this; });\n' + + 'String.prototype.__defineGetter__("string", function() { return this; });\n' + + 'String.prototype.__defineGetter__("print", function() { write(this); });\n' + + 'Object.defineProperty(Array.prototype, "foreach", {value: function(action) { var ret = module_false; for (var i = 0; i < this.length; i++) { ret = action(i, this[i]) }; return ret; }});\n' + + 'Function.prototype.whileCN_do = function(action) { var ret = module_false; while(toobj(this()) == module_true) { ret = action(); } return ret; };\n' + + 'module_true.valueOf = function() { return true; }\n' + + 'module_false.valueOf = function() { return false; }\n' + + 'function toobj(val) {\n' + + ' return (typeof val == "boolean") ? (val ? module_true : module_false) : val;\n' + + '}\n' + + 'var m = main_module.main;\n' + + 'if (m instanceof Function) {\n' + + ' m(arguments);\n' + + '}\n'; } assignment.prototype.toJS = function() { var existing = this.symbols.find(this.symbol.name); var prefix = ''; - if (!existing) { + /*if (!existing) { prefix = 'var '; } else { switch (existing.type) { case 'self': - prefix = 'this.'; + var self = new symbol('self', this.symbols); + prefix = self.toJS() + '.'; break; case 'parent': - prefix = 'this.'; + var self = new symbol('self', this.symbols); + prefix = self.toJS() + '.'; for (var i = 0; i < existing.depth; ++i) { prefix += 'parent.'; } break; } - } + }*/ var val = this.expression.toJS(); if (val === null) { return null; } + if ((existing.type == 'local' || existing.type == 'closedover') && !existing.isdeclared) { + prefix = 'var '; + this.symbols.declareVar(this.symbol.name); + } return prefix + this.symbol.toJS() + ' = ' + val; }; +function removeInitialFunction(str) +{ + var f = 'function'; + str = str.trim(); + if (str.substr(0, f.length) == f) { + return str.substr(f.length); + } + return str; +} assignment.prototype.toJSObject = function() { var val = this.expression.toJS(); if (val === null) { return null; } + if (this.expression instanceof lambda) { + var args = this.expression.nonSelfArgs(); + if (args.length == 0) { + return 'get ' + escapeJSName(this.symbol.name) + removeInitialFunction(val); + } else if(args.length == 1 && this.symbol.name[this.symbol.name.length-1] == '!') { + return 'set ' + escapeJSName(this.symbol.name.substr(0, this.symbol.name.length-1)) + removeInitialFunction(val); + } + } return escapeJSName(this.symbol.name) + ': ' + val; }; diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/array.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/array.tp Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,102 @@ +#{ + llProperty: size withType: uint32_t + llProperty: storage withType: uint32_t + llProperty: data withType: ((object ptr) ptr) + llMessage: get withVars: { + index <- obj_int32 ptr + } andCode: :index { + if: (index num) >= 0 && (index num) < size { + (self data) get: (index num) + } else: { + false + } + } + + llMessage: set withVars: { + index <- obj_int32 ptr + value <- object ptr + } andCode: :index value { + if: (index num) >= 0 && (index num) < size { + data set: (index num) value + } + self + } + + llMessage: foreach withVars: { + clos <- lambda ptr + i <- uint32_t + index <- obj_int32 ptr + } andCode: :clos { + i <- 0 + while: { i < size } do: { + index <- make_object: (addr_of: obj_int32_meta) NULL 0 + index num!: i + ccall: clos 2 index (data get: i) + i <- i + 1 + } + self + } + + llMessage: append withVars: { + value <- object ptr + tmp <- (object ptr) ptr + } andCode: :value { + if: storage = size { + storage <- storage * 2 + tmp <- GC_REALLOC: data storage * (sizeof: (object ptr)) + if: (not: tmp) { + fputs: "Failed to increase array size\n" stderr + exit: 1 + } + data <- tmp + } + data set: size value + size <- size + 1 + self + } + + llMessage: length withVars: { + intret <- obj_int32 ptr + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: size + intret + } + + fold:with <- :acc :fun { + foreach: self :idx el { + fun: acc el + } + } + + foldr:with <- :acc :fun { + idx <- length - 1 + while: {idx >= 0} do: { + fun: acc (get: idx) + } + } + + map <- :fun { + new <- #[] + foreach: self :idx el { + new append: (fun: el) + } + } + + find:withDefault <- :pred :default{ + idx <- 0 + l <- length + ret <- default + while: {idx < l} do: { + v <- get: idx + if: (pred: v) { + ret <- #{ + key <- idx + value <- v + } + idx <- l + } + } + ret + } +} diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/dict.tp --- a/modules/dict.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/modules/dict.tp Mon Aug 05 23:37:17 2013 -0700 @@ -42,6 +42,13 @@ (els get: idx) val } } + + foreach <- :l { + foreach: els :idx el { + l: (el key) (el val) + } + } + } } diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/false.tp --- a/modules/false.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/modules/false.tp Mon Aug 05 23:37:17 2013 -0700 @@ -2,6 +2,9 @@ if <- :self trueblock { self } + ifnot <- :self falseblock { + falseblock: + } if:else <- :self trueblock :elseblock { elseblock: } diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/sets.tp --- a/modules/sets.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/modules/sets.tp Mon Aug 05 23:37:17 2013 -0700 @@ -3,6 +3,8 @@ empty <- #{ empty? <- { true } } + size <- 0 + hashdiffs <- #[0] #{ buckets <- #[empty empty empty empty] size <- 0 @@ -10,11 +12,12 @@ hv <- object hash notdone <- true - hashes <- #[hv (hv + 1) (hv - 1)] + + basehash <- hv i <- 0 ret <- false - while: { if: notdone { i < 3 } } do: { - hv <- hashes get: i + while: { if: notdone { i < (hashdiffs length) } } do: { + hv <- basehash + (hashdiffs get: i) trunc <- hv % (buckets length) if: trunc < 0 { trunc <- 0 - trunc } bucketval <- (buckets get: trunc) @@ -42,10 +45,10 @@ } } notdone <- true - hashes <- #[hv (hv + 1) (hv - 1)] + basehash <- hv i <- 0 - while: { if: notdone { i < 3 } } do: { - hv <- hashes get: i + while: { if: notdone { i < (hashdiffs length) } } do: { + hv <- basehash + (hashdiffs get: i) trunc <- hv % (buckets length) if: trunc < 0 { trunc <- 0 - trunc } bucketval <- (buckets get: trunc) @@ -61,7 +64,13 @@ i <- i + 1 } if: notdone { - newsize <- (buckets length) * 3 + newsize <- (buckets length) * 3 + 1 + lastdiff <- hashdiffs get: ((hashdiffs length) - 1) + if: lastdiff <= 0 { + hashdiffs append: ((0 - lastdiff) + 1) + } else: { + hashdiffs append: (0 - lastdiff) + } newbucks <- #[] newbucks resize: newsize while: { (newbucks length) < newsize } do: { diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/string.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/string.tp Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,96 @@ +#{ + llProperty: len withType: uint32_t + llProperty: bytes withType: uint32_t + llProperty: data withType: (char ptr) + + llMessage: length withVars: { + intret <- (obj_int32 ptr) + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: len + intret + } + + llMessage: byte_length withVars: { + intret <- (obj_int32 ptr) + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: bytes + intret + } + + llMessage: EQ_ withVars: { + argb <- (string ptr) + } andCode: :argb { + if: len = (argb len) && bytes = (argb bytes) && (not: (memcmp: data (argb data) bytes)) { + true + } + } + + llMessage: NEQ_ withVars: { + argb <- (string ptr) + } andCode: :argb { + if: len != (argb len) || bytes != (argb bytes) || (memcmp: data (argb data) bytes) { + true + } + } + + llMessage: print withVars: {} andCode: { + fwrite: data 1 bytes stdout + self + } + + llMessage: string withVars: {} andCode: { + self + } + + llMessage: CAT_ withVars: { + argbo <- (object ptr) + argb <- (string ptr) + out <- (string ptr) + } andCode: :argbo { + argb <- mcall: string 1 argbo + out <- make_object: (addr_of: string_meta) NULL 0 + out bytes!: bytes + (argb bytes) + out len!: len + (argb len) + out data!: (GC_MALLOC_ATOMIC: (out bytes) + 1) + memcpy: (out data) data bytes + memcpy: (out data) + bytes (argb data) (argb bytes) + 1 + out + } + + llMessage: byte withVars: { + index <- (obj_int32 ptr) + intret <- (obj_int32 ptr) + } andCode: :index { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: (if: (index num) < bytes { data get: (index num) } else: {0}) + intret + } + + llMessage: int32 withVars: { + intret <- (obj_int32 ptr) + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: (atoi: data) + intret + } + + llMessage: hash withVars: { + intret <- (obj_int32 ptr) + i <- uint32_t + } andCode: { + intret <- make_object: (addr_of: obj_int32_meta) NULL 0 + intret num!: 0 + if: bytes { + intret num!: (data get: 0) * 128 + i <- 0 + while: { i < bytes } do: { + intret num!: (1000003 * (intret num)) xor (data get: i) + i <- i + 1 + } + intret num!: (intret num) xor bytes + } + intret + } +} diff -r 6f8d868e8da0 -r a2d2d8e09291 modules/true.tp --- a/modules/true.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/modules/true.tp Mon Aug 05 23:37:17 2013 -0700 @@ -2,6 +2,9 @@ if <- :self trueblock { trueblock: } + ifnot <- :self falseblock { + self + } if:else <- :self trueblock :elseblock { trueblock: } diff -r 6f8d868e8da0 -r a2d2d8e09291 mquery.js --- a/mquery.js Mon Aug 05 23:36:18 2013 -0700 +++ b/mquery.js Mon Aug 05 23:37:17 2013 -0700 @@ -1,6 +1,6 @@ function each(container, fun) { - if (container instanceof Array) { + if (container instanceof Array || container instanceof HTMLCollection || container instanceof NodeList) { for (var i = 0; i < container.length; i++) { fun(i, container[i]); } @@ -68,6 +68,11 @@ } } +function hasClass(el, classname) +{ + return el.className == classname || el.className.split(' ').indexOf(classname) > -1 +} + function ajax(method, url, data, onSuccess, onFail, onOthers) { var req; @@ -126,3 +131,14 @@ return parent; } +function bubble(el, event, handler) +{ + el.addEventListener(event, handler, false); +} + +function capture(el, event, handler) +{ + el.addEventListener(event, handler, true); +} + + diff -r 6f8d868e8da0 -r a2d2d8e09291 parser.js --- a/parser.js Mon Aug 05 23:36:18 2013 -0700 +++ b/parser.js Mon Aug 05 23:37:17 2013 -0700 @@ -50,6 +50,7 @@ function object(messages) { this.messages = messages; + this.name = null; } function lambda(args, expressions) @@ -69,30 +70,33 @@ return node instanceof lambda; } -var grammar = +var grammar = 'start = ws module:(object / lambda) ws { return module; };' + 'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n")*;' + 'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' + 'expr = e:(funcall / methcall / opexpr) ws { return e; };' + -'opexpr = left:addsub pieces:(hws ("<=" / ">=" / "<" / ">" / "=" / "!=") hws addsub)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ -'addsub = left:muldiv pieces:(hws ("+"/"-"/".") hws muldiv)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ +'opexpr = left:compareop pieces:(hws ("&&" / "||") hws compareop)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ +'compareop = left:addsub pieces:(hws ("<=" / ">=" / "<" / ">" / "=" / "!=") hws addsub)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ +'addsub = left:muldiv pieces:(hws ("+"/"-"/"xor"/".") hws muldiv)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ 'muldiv = left:primlitsym pieces:(hws ("*"/"/"/"%") hws primlitsym)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+ 'primlitsym = hws val:(float / hex / binary / int / string / symbol / object / array / list / lambda / "(" ws expr:expr hws ")" { return expr; }) { return val; };' + -'symbol = chars:[a-zA-Z_!?@]+ trailing:(":"? [a-zA-Z_!?@0-9])* ! ":" { for (var i in trailing) { trailing[i] = trailing[i].join(""); } return new symbol(chars.join("") + trailing.join("")); };' + +'symbol = chars:[a-zA-Z_!?@]+ trailing:(":"? [a-zA-Z_!?@0-9])* ! ":" { for (var i = 0; i < trailing.length; i++) { trailing[i] = trailing[i].join(""); } return new symbol(chars.join("") + trailing.join("")); };' + 'float = digits:[0-9]+ "." decimals:[0-9]+ { return new floatlit(parseFloat(digits.join("") + "." + decimals.join(""))); };' + 'binary = "0b" digits:[01]+ { return new intlit(parseInt(digits.join(""), 2)); };' + 'hex = "0x" digits:[0-9a-fA-F]+ { return new intlit(parseInt(digits.join(""), 16)); };' + 'int = sign:"-"? digits:[0-9]+ { return new intlit(parseInt(sign + digits.join(""), 10)); };' + 'string = "\\"" text:(strpart/escape)* "\\"" { return new strlit(text.join("")); };' + -'strpart = text:[^\\"\\\\]+ { return text.join(""); };' + +'strpart = text:[^\\"\\\\]+ { return text.join(""); };' + 'escape = "\\\\" char:[nt\\"r\\\\] { if (char == "n") { return "\\n"; } if (char == "r") { return "\\r"; } return char; };' + -'object = "#{" ws messages:(assignment / funcall)* "}" { return new object(messages); };' + +'object = "#{" ws messages:(assignment / funexpr)* "}" { return new object(messages); };' + 'array = "#[" ws els:opexpr* "]" { return new arraylit(els); };' + 'list = "[" ws els:opexpr* "]" { return new listlit(els); };' + -'assignment = ws sym:symbol hws "<-" expr:expr ws { return new assignment(sym, expr); }' + +'opsym = name:("&&" / "||" / "<=" / ">=" / "<" / ">" / "=" / "!=" / "+" / "-" / "." / "*" / "/" / "%") { return new symbol(name); };' + +'assignment = ws sym:(symbol / opsym) hws "<-" expr:expr ws { return new assignment(sym, expr); }' + 'lambda = args:((& ":") argname+ )? "{" ws exprs:(assignment / expr)* "}" { return new lambda(args[1], exprs); };' + 'argname = init:":"? chars:[a-zA-Z_!?@]+ trailing:[a-zA-Z_!?@0-9]* hws { return new symbol(init + chars.join("") + trailing.join("")); };' + -'funcall = hws parts: funcallpart+ { var fun = ""; var args = []; for (var i in parts) { fun += parts[i].name; args = args.concat(parts[i].args); } return new funcall(fun, args); };' + +'funexpr = f: funcall ws { return f; };' + +'funcall = hws parts: funcallpart+ { var fun = ""; var args = []; for (var i = 0; i < parts.length; i++) { fun += parts[i].name; args = args.concat(parts[i].args); } return new funcall(fun, args); };' + 'funcallpart = fun:funpart args:opexpr* hws { return { name: fun, args: args}; };' + 'funpart = chars:[a-zA-Z_!?@]+ middle:[a-zA-Z_!?@0-9]* ":" & [ \\t\\n\\r] { return chars.join("") + middle.join("") + ":"; };' + 'methcall = receiver:opexpr hws info:methcallrest { info.receiver = receiver; return info; };' + diff -r 6f8d868e8da0 -r a2d2d8e09291 runtime/progfoot.inc --- a/runtime/progfoot.inc Mon Aug 05 23:36:18 2013 -0700 +++ b/runtime/progfoot.inc Mon Aug 05 23:37:17 2013 -0700 @@ -43,7 +43,7 @@ for (i = 0; i < argc; ++i) { arg = (string *)make_object(&string_meta, NULL, 0); arg->data = argv[i]; - arg->bytes = arg->length = strlen(argv[i]); + arg->bytes = arg->len = strlen(argv[i]); arr->data[i] = &(arg->header); } object * ret = mcall(METHOD_ID_MAIN, 2, mainModule(), &(arr->header)); diff -r 6f8d868e8da0 -r a2d2d8e09291 samples/logical.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/logical.tp Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,28 @@ +#{ + + foo <- { + print: "foo\n" + true + } + + bar <- { + print: "bar\n" + false + } + + baz <- { + print: "baz\n" + true + } + + qux <- { + print: "shouldn't be printed\n" + true + } + + + main <- { + foo && bar || (baz || qux) + } + +} diff -r 6f8d868e8da0 -r a2d2d8e09291 samples/oper_impl.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/oper_impl.tp Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,20 @@ +#{ + pair <- :a b { + #{ + first <- a + second <- b + + <- :other { + pair: first + (other first) second + (other second) + } + } + } + + main <- { + foo <- pair: 5 7 + bar <- pair: 9 23 + baz <- foo + bar + print: ((baz first) string) . "\n" + print: ((baz second) string) . "\n" + 0 + } +} diff -r 6f8d868e8da0 -r a2d2d8e09291 samples/testarray.tp --- a/samples/testarray.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/samples/testarray.tp Mon Aug 05 23:37:17 2013 -0700 @@ -12,7 +12,7 @@ bar append: 30 bar append: 28 bar append: 42 - (sum: foo) + (sum: bar) + print: "" . ((sum: foo) + (sum: bar)) . "\n" } } diff -r 6f8d868e8da0 -r a2d2d8e09291 samples/testdict.tp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/samples/testdict.tp Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,11 @@ +#{ + main <- { + foo <- dict linear: + foo set: "key1" "val1" + foo set: "key2" "val2" + foreach: foo :k :v { + print: k . ", " . v . "\n" + } + + } +} diff -r 6f8d868e8da0 -r a2d2d8e09291 scripttags.js --- a/scripttags.js Mon Aug 05 23:36:18 2013 -0700 +++ b/scripttags.js Mon Aug 05 23:37:17 2013 -0700 @@ -2,8 +2,12 @@ function compileAndRun(src) { var ast = parser.parse(src); - var js = ast.toJSModule(); - mainModule = eval(js)(); + asyncProcessTopLevelJS(toplevel, function() { + var js = makeJSProg(ast); + eval(js); + }); + /*.toJSModule(); + mainModule = eval(js); if (mainModule.strue) { each(mainModule.strue, function(key, val) { if(val instanceof Function) { @@ -13,18 +17,25 @@ } }); } - mainModule.main(); + mainModule.main();*/ } onReady(function() { - var tags = qall('script[type="text/tabletprog"]'); - for (var i = 0; i < tags.length; ++i) { - if (tags[i].src) { - get(tags[i].src, function(req) { - compileAndRun(req.responseText); - }); - } else { - compileAndRun(tags[i].innerHTML); + toplevel.onReady( function() { + var tags = qall('script[type="text/tabletprog"]'); + + for (var i = 0; i < tags.length; ++i) { + if (tags[i].src) { + (function() { + var src = tags[i].src; + get(src, function(req) { + console.log('Compiling ' + src); + compileAndRun(req.responseText); + }); + })(); + } else { + compileAndRun(tags[i].innerHTML); + } } - } + }); }); diff -r 6f8d868e8da0 -r a2d2d8e09291 src/editor.tp --- a/src/editor.tp Mon Aug 05 23:36:18 2013 -0700 +++ b/src/editor.tp Mon Aug 05 23:37:17 2013 -0700 @@ -5,7 +5,10 @@ each <- foreign: :iterable fun {} addClass <- foreign: :node className {} removeClass <- foreign: :node className {} +hasClass <- foreign: :node className {} get <- foreign: :url onSuccess onFail onOther {} +getEl <- foreign: :from idx {} +setEl <- foreign: :to idx val {} newEl <- foreign: :tagname props {} //TP Parser @@ -24,9 +27,62 @@ } //kernel definitions -import: kernel +//import: kernel + +filter <- :arr pred { + output <- arr slice: 0 0 + each: arr :idx el { + if: (pred: el) { + output push: el + } else: {} + } + output +} //editor code +selection <- #{ + valid? <- false +} + +setSelection:withInNode <- :astnode :innode { + fakeEvent <- #{ + stopPropagation <- :Blah { + } + } + selection <- #{ + valid? <- true + in <- { + (innode domNode) onclick: fakeEvent + } + out <- { + fakeEvent <- #{ + stopPropagation <- :Blah { + } + } + ((astnode up) domNode) onclick: fakeEvent + } + } +} + +setSelection <- :astnode { + fakeEvent <- #{ + stopPropagation <- :Blah { + } + } + selection <- #{ + valid? <- true + in <- { + } + out <- { + fakeEvent <- #{ + stopPropagation <- :Blah { + } + } + ((astnode up) domNode) onclick: fakeEvent + } + } +} + editFile <- :path { get: path :request { addClass: (q: "body") "editorMode" @@ -59,9 +115,9 @@ selectParent <- :node { each: (qall: ".selectParent") :idx el { - removeClass: el "selected" + removeClass: el "selectParent" } - addClass: (node parentNode) "selectParent" + addClass: node "selectParent" } popInscope:onClick <- :syms :handler { @@ -70,34 +126,146 @@ each: syms :idx key { inscope appendChild: (newEl: "li" #{ textContent <- key - onclick <- { handler: key } + onclick <- :Event { handler: key } }) } } +scalarClick <- :domnode astnode event { + selectNode: domnode + setSelection: astnode + event stopPropagation: (foreign: undefined) + //TODO: set focus +} + symbolClick <- :domnode astnode event { - console log: astnode selectNode: domnode - popInscope: ((astnode symbols) allSymbols) onClick: :key { + popInscope: ((astnode symbols) allSymbols: (foreign: undefined)) onClick: :key { domnode textContent!: key astnode name!: key } - event stopPropagation + setSelection: astnode + event stopPropagation: (foreign: undefined) +} + +assignClick <- :domnode astnode event { + selectParent: domnode + selectQuery: ".selectParent > .varname" in: domnode + popInscope: ((astnode symbols) allSymbols: (foreign: undefined)) onClick: :key { + (domnode firstChild) textContent!: key + (astnode symbol) name!: key + } + setSelection: astnode withInNode: (astnode expression) + event stopPropagation: (foreign: undefined) +} + +opClick <- :domnode astnode event { + selectParent: domnode + selectQuery: ".selectParent > .opname" in: domnode + showOps + setSelection: astnode withInNode: (astnode left) + event stopPropagation: (foreign: undefined) } funClick <- :domnode astnode event { selectParent: domnode - selectQuery: ".selectParent > .funpart" in: (domnode parentNode) + selectQuery: ".selectParent > .funpart" in: domnode symtable <- astnode symbols - syms <- filter: (symtable allSymbols) :sym { + syms <- filter: (symtable allSymbols: (foreign: undefined)) :sym { isLambda: ((symtable find: sym) def) } - popInscope: syms onClick: {} - event stopPropagation + inner <- if: (astnode receiver) != (foreign: null) { + astnode receiver + } else: { + (astnode args) getEl: 0 + } + setSelection: astnode withInNode: inner + popInscope: syms onClick: :key { + astnode name!: key + parts <- key split: ":" + nodes <- [] + each: (domnode children) :idx val{ + nodes push: val + } + partIdx <- 0 + nodeIdx <- 0 + lastWasNamePart <- true + while: { partIdx < (parts length) || nodeIdx < (nodes length) } do: { + if: nodeIdx < (nodes length) { + node <-getEl: nodes nodeIdx + nodeIdx <- nodeIdx + 1 + if: (hasClass: node "funpart") { + if: partIdx < (parts length) { + postfix <- if: partIdx = 0 && nodeIdx = 2 && (parts length) = 1 && (nodes length) = 2 { "" } else: { ":" } + t <- (getEl: parts partIdx) + node textContent!: (getEl: parts partIdx) . postfix + partIdx <- partIdx + 1 + } else: { + domnode removeChild: node + } + lastWasNamePart <- true + } else: { + if: (not: lastWasNamePart) && partIdx < (parts length) && nodeIdx > 0 { + domnode insertBefore: (newEl: "span" #{ + className <- "funpart selected" + textContent <- (getEl: parts partIdx) . ":" + }) node + partIdx <- partIdx + 1 + } + lastWasNamePart <- false + } + } else: { + console log: "part: " . (getEl: parts partIdx) + domnode appendChild: (newEl: "span" #{ + className <- "funpart selected" + textContent <- (getEl: parts partIdx) . ":" + }) + partIdx <- partIdx + 1 + } + } + } + event stopPropagation: (foreign: undefined) } lambdaClick <- :domnode astnode event { - symbolClick: domnode astnode event + selectNode: domnode + popInscope: ((astnode symbols) allSymbols: (foreign: undefined)) onClick: :key { + console log: "foooobar!" + } + inner <- if: ((astnode args) length) > 0 { + (astnode args) getEl: 0 + } else: { + (astnode expressions) getEl: 0 + } + setSelection: astnode withInNode: inner + event stopPropagation: (foreign: undefined) +} + +objectClick <- :domnode astnode event { + selectNode: domnode + popInscope: ((astnode symbols) allSymbols: (foreign: undefined)) onClick: :key { + console log: "fooobar!" + } + setSelection: astnode withInNode: ((astnode messages) getEl: 0) + event stopPropagation: (foreign: undefined) +} + +visible <- "showops" + +showOps <- { + each: (qall: ".controls") :idx el { + removeClass: el visible + addClass: el "showops" + } + visible <- "showops" +} + +showLit <- { + each: (qall: ".controls") :idx el { + removeClass: el visible + addClass: el "showlit" + } + visible <- "showlit" } main <- { @@ -124,7 +292,7 @@ } } } - + //bind handlers for editor buttons each: (qall: ".controls li") :idx el { el onclick!: :event { @@ -133,15 +301,30 @@ } } (q: "#ops_button") onclick!: :event { - each: (qall: ".controls") :idx el { - addClass: el "showops" + showOps + } + (q: "#lit_button") onclick!: :event { + showLit + } + + (q: "#in") onclick!: :event { + console log: "inwards" + if: (selection valid?) { + selection in } } - + + (q: "#out") onclick!: :event { + console log: "outwards" + if: (selection valid?) { + selection out + } + } + path <- (window location) pathname if: (path indexOf: "/edit/") = 0 { editFile: (path substr: 5) - } else: {} + } } } diff -r 6f8d868e8da0 -r a2d2d8e09291 tpc.js --- a/tpc.js Mon Aug 05 23:36:18 2013 -0700 +++ b/tpc.js Mon Aug 05 23:37:17 2013 -0700 @@ -6,12 +6,14 @@ var includes = ['.']; var basedir = ''; var debugmode = false; +var backend = 'C'; for (var i = 0; i < arguments.length; i++) { switch (argtype) { case 'normal': switch (arguments[i]) { case '-basedir': case '-i': + case '-backend': argtype = arguments[i]; break; case '-compilerdebug': @@ -38,6 +40,10 @@ includes.push(arguments[i]); argtype = 'normal'; break; + case '-backend': + backend = arguments[i]; + argtype = 'normal'; + break; } } if (argtype != 'normal') { @@ -50,7 +56,7 @@ quit(1); } includes.push(basedir + 'modules'); -compileFile(file, basedir, includes, debugmode); +compileFile(file, basedir, includes, debugmode, backend); function parseFile(filename) @@ -78,19 +84,35 @@ } -function compileFile(filename, basedir, includes, debugmode) +function compileFile(filename, basedir, includes, debugmode, backend) { load(basedir + 'peg.js'); PEG = module.exports; load(basedir + 'parser.js'); load(basedir + 'compiler.js'); - load(basedir + 'cbackend.js'); + if (backend == 'C') { + load(basedir + 'cbackend.js'); + } else { + load(basedir + 'jsbackend.js'); + } + var parsed = parseFile(filename); if (debugmode) { debugprint = print; } toplevel = new topsymbols(includes); - var c = parsed.toCModule(); + switch(backend) + { + case 'C': + var c = parsed.toCModule(); + break; + case 'JS': + var c = makeJSProg(parsed); + break; + default: + print('Backend', backend, ' not recognized'); + quit(1); + } print(c); } diff -r 6f8d868e8da0 -r a2d2d8e09291 types.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/types.js Mon Aug 05 23:37:17 2013 -0700 @@ -0,0 +1,421 @@ +function anytype() {}; +anytype.prototype.satisfiedBy = function(type) { + return true; +}; +anytype.prototype.str = function(indent) { + if (indent === undefined) { + indent = ''; + } + return indent + 'any\n'; +}; +anytype.prototype.id = 0; +anytype.prototype.callable = true; +anytype.prototype.replaceParams = function() { return this; }; +var any = new anytype(); + +function typeparam(name, base) +{ + this.name = name; + this.base = base; + this.callable = base.callable; +} +typeparam.prototype.replaceParams = function(paramtypes) { + if (!(this.name in paramtypes)) { + return this; + } + if (!this.base.satisfiedBy(paramtypes[this.name])) { + throw new Error('param ' + this.name + ' has base type ' + this.base.str() + ' which is not satisfied by ' + paramtypes[this.name].str()); + } + return paramtypes[this.name]; +}; +typeparam.prototype.satisfiedBy = function(type) { + return this.base.satisfiedBy(type); +}; +typeparam.prototype.str = function(indent) { + return indent + 'param ' + this.name + '\n'; +}; + +var nexttypeid = 1; + +function objecttype() +{ + this.messages = {}; + this.id = nexttypeid++; + this.typeparams = []; + this.satisfies_cache = {}; + this.satisfies_cache[this.id] = true; +} + +objecttype.prototype.callable = false; + +objecttype.prototype.addMessage = function(name, type) +{ + this.messages[name] = type; +}; + +objecttype.prototype.satisfiedBy = function(type) { + if (type.id in this.satisfies_cache) { + return this.satisfies_cache[type.id]; + } + //temporarily set cache entry to true to prevent infinite recursion + this.satisfies_cache[type.id] = true; + var ret = true; + if (type.messages === undefined) { + ret = false; + } + if (ret) { + for (var msgname in this.messages) { + if (!(msgname in type.messages) || !this.messages[msgname].satisfiedBy(type.messages[msgname])) { + ret = false; + break; + } + } + } + this.satisfies_cache[type.id] = ret; + return ret; +}; + +objecttype.prototype.str = function(indent) { + if (indent === undefined) { + indent = ''; + } + if (indent.length > 6) { + return 'max depth reached\n'; + } + var newindent = indent + '\t'; + var childindent = newindent + '\t' + var ret = indent + 'objectype {\n'; + for (var msgname in this.messages) { + ret += newindent + msgname + ':\n' + this.messages[msgname].str(childindent) + } + return ret + indent + '}\n'; +}; + +objecttype.prototype.replaceParams = function(paramtypes, visited) { + if (visited === undefined) { + visited = {}; + } + if (this.id in visited) { + return visited[this.id]; + } + var me = visited[this.id] = this.clone(); + for (var msgname in this.messages) { + me.messages[msgname] = me.messages[msgname].replaceParams(paramtypes, visited); + } + return me; +}; + +objecttype.prototype.clone = function() { + var clone = new objecttype(); + for (var msgname in this.messages) { + clone.messages[msgname] = this.messages[msgname]; + } + clone.typeparams = this.typeparams; + return clone; +}; + +function lambdatype() +{ + this.id = nexttypeid++; + this.messages = {}; + this.params = []; + this.paramlu = {}; + this.typeparams = []; + this.returntype = any; + this.satisfies_cache = {}; + this.satisfies_cache[this.id] = true; +} + +lambdatype.prototype.callable = true; + +lambdatype.prototype.addParam = function(name) { + this.paramlu[name] = this.params.length; + this.params.push(any); +}; + +lambdatype.prototype.paramType = function(name, type) { + this.params[this.paramlu[name]] = type; +}; + +lambdatype.prototype.addMessage = function(name, type) +{ + this.messages[name] = type; +}; + +lambdatype.prototype.satisfiedBy = function(type) { + if (type.id in this.satisfies_cache) { + return this.satisfies_cache[type.id]; + } + //temporarily set cache entry to true to prevent infinite recursion + this.satisfies_cache[type.id] = true; + var ret = true; + if (!(type.callable) || this.params.length != type.params.length) { + ret = false; + } + if (ret) { + for (var i in this.params) { + if (i >= type.params.length || !type.params[i].satisfiedBy(this.params[i])) { + ret = false; + break; + } + } + } + if (ret) { + for (var msgname in this.messages) { + if (!(msgname in type.messages) || !this.messages[msgname].satisfiedBy(type.messages[msgname])) { + ret = false; + break; + } + } + } + ret = ret && this.returntype.satisfiedBy(type.returntype); + this.satisfies_cache[type.id] = ret; + return ret; +} + +lambdatype.prototype.str = function(indent) { + if (indent === undefined) { + indent = ''; + } + if (indent.length > 6) { + return 'max depth reached\n'; + } + var newindent = indent + '\t'; + var childindent = newindent + '\t' + var ret = indent + 'lambdatype {\n' + newindent + 'params: {\n'; + for (var i = 0; i < this.params.length; i++) { + ret += childindent + i + ':\n' + this.params[i].str(childindent + '\t'); + } + ret += newindent + '}\n' + newindent + 'returntype:\n' + this.returntype.str(childindent); + + for (var msgname in this.messages) { + ret += newindent + msgname + ':\n' + this.messages[msgname].str(childindent) + } + return ret + indent + '}\n'; +}; + +lambdatype.prototype.replaceParams = function(paramtypes, visited) { + if (visited === undefined) { + visited = {}; + } + if (this.id in visited) { + return visited[this.id]; + } + var me = visited[this.id] = this.clone(); + for (var msgname in me.messages) { + me.messages[msgname] = me.messages[msgname].replaceParams(paramtypes, visited); + } + for (var i in me.params) { + me.params[i] = me.params[i].replaceParams(paramtypes, visited); + } + me.returntype = me.returntype.replaceParams(paramtypes, visited); + return me; +}; + +lambdatype.prototype.clone = function() { + var clone = new lambdatype(); + for (var msgname in this.messages) { + clone.messages[msgname] = this.messages[msgname]; + } + clone.paramlu = this.paramlu; + clone.params = this.params.slice(0, this.params.length); + clone.returntype = this.returntype; + clone.typeparams = this.typeparams; + return clone; +}; + +function uniontype(a, b) +{ + this.a = a; + this.b = b; + this.id = nexttypeid++; + this.satisfies_cache = null; +} + +uniontype.prototype = { + lazyinit: function() { + if (this.satisfies_cache == null) { + this.satisfies_cache = {}; + this.satisfies_cache[this.id] = true; + this._messages = {}; + if (this.a.messages !== undefined && this.b.messages !== undefined) { + for (var msgname in this.a.messages) { + if (msgname in this.b.messages) { + this._messages[msgname] = mkunion(this.a.messages[msgname], this.b.messages[msgname]); + } + } + } + this._callable = false; + if (this.a.callable && this.b.callable && this.a.params.length == this.b.params.length) { + this._callable = true; + this._params = []; + for (var i = 0; i < this.a.params.length; i++) { + this._params.push(mkunion(this.a.params[i], this.b.params[i])); + } + this._returntype = mkunion(this.a.returntype, this.b.returntype); + } + } + }, + satisfiedBy: function(type) + { + this.lazyinit(); + if (type.id in this.satisfies_cache) { + return this.satisfies_cache[type.id]; + } + this.satisfies_cache[type.id] = true; + var ret = this.a.satisfiedBy(type) || this.b.satisfiedBy(type); + this.satisfies_cache[type.id] = ret; + return ret; + }, + str: function(indent) + { + if (indent === undefined) { + indent = ''; + } + if (indent.length > 6) { + return 'max depth reached\n'; + } + return indent + 'Union {\n\t' + indent + this.a.str(indent+'\t') + '\t' + indent + this.b.str(indent+'\t') + indent + '}\n'; + }, + replaceParams: function(paramtypes, visited) { + if (visited === undefined) { + visited = {}; + } + if (this.id in visited) { + return visited[this.id]; + } + var me = visited[this.id] = this.clone(); + me.a = me.a.replaceParams(paramtypes, visited); + me.b = me.b.replaceParams(paramtypes, visited); + return me; + }, + clone: function() { + return new uniontype(this.a, this.b); + }, + get messages() { + this.lazyinit(); + return this._messages; + }, + get params() { + this.lazyinit(); + return this._params; + }, + get returntype() { + this.lazyinit(); + return this._returntype; + }, + get callable() { + this.lazyinit(); + return this._callable; + } +}; + + +function mkunion(a, b) +{ + //if b is a subtype of a, then a | b is equivalent to a + if (a.satisfiedBy(b)) { + return a; + } + //if a is a subtype of b, then a | b is equivalent to b + if (b.satisfiedBy(a)) { + return b; + } + return new uniontype(a, b); +} + +function withtparams(type, params) +{ + this.type = type; + this.params = params; + this.replaced = false; +} + +withtparams.prototype = { + satisfiedBy: function(type) { + this.lazyinit(); + return this.type.satisfiedBy(type); + }, + str: function(indent) { + if (indent === undefined) { + indent = ''; + } + if (indent.length > 6) { + return 'max depth reached\n'; + } + return this.type.str(indent) + indent + '<' + this.params.map(function(p) { return p.str(indent); }).join(', ') + '>'; + }, + replaceParams: function(paramtypes) { + var replaced = false; + for (var i in this.params) { + var newp = this.params[i].replaceParams(paramtypes); + if (newp != this.params[i]) { + replaced = true; + this.params[i] = newp; + } + } + return this; + }, + lazyinit: function() { + if (!this.replaced) { + var childptypes = {}; + for (var i in this.type.typeparams) { + childptypes[this.type.typeparams[i]] = this.params[i] + } + this.type = this.type.replaceParams(childptypes, {}); + this.replaced = true; + } + }, + get messages() { + this.lazyinit(); + return this.type.messages; + } +}; + +function typetest() +{ + var foo = new objecttype(); + var msgtype = new lambdatype(); + msgtype.addParam('fo'); + msgtype.returntype = foo; + foo.addMessage('bar', msgtype); + var baz = new objecttype(); + var msgtype2 = new lambdatype(); + msgtype2.addParam('fo'); + msgtype2.paramType('fo', foo); + msgtype2.returntype = baz; + baz.addMessage('bar', msgtype2); + baz.addMessage('qux', msgtype); + var shizzle = new objecttype(); + shizzle.addMessage('bar', msgtype); + shizzle.addMessage('boo', msgtype); + return {foo: foo, baz: baz, shizzle: shizzle}; +} + +function paramtypetest() +{ + var empty = new objecttype(); + var tlnode = new objecttype(); + tlnode.typeparams = ['T']; + var t = new typeparam('T', any); + var q = new typeparam('Q', any); + var head = new lambdatype(); + head.returntype = t; + tlnode.addMessage('head', head); + var tail = new lambdatype(); + var econs = new lambdatype(); + econs.addParam('val'); + econs.typeparams = ['Q']; + econs.paramType('val', q); + econs.returntype = new withtparams(tlnode, [q]); + empty.addMessage('cons', econs); + tail.returntype = new uniontype(new withtparams(tlnode, [t]), empty); + tlnode.addMessage('tail', tail); + var cons = new lambdatype(); + cons.addParam('val'); + cons.paramType('val', t); + cons.returntype = new withtparams(tlnode, [t]); + tlnode.addMessage('cons', cons); + return {empty: empty, tlnode: tlnode}; +} +