diff --git a/lib/ast.js b/lib/ast.js index 90acefc3..62e88485 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -118,6 +118,13 @@ Cola.AST_Command = Cola.DEFNODE("Command", "name args", { $propdoc: { name: "[string] Name of command", args: "[AST_Node*] List of arguments" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.args.forEach(function(arg){ + arg._walk(visitor); + }); + }); } }, Cola.AST_Node); @@ -406,6 +413,11 @@ Cola.AST_Namedarg = Cola.DEFNODE("Namedarg", "start end name value", { $documentation: "Named argument", $propdoc: { value: "Value of named argument" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.value._walk(visitor); + }); } }, Cola.AST_SymbolRef); @@ -415,6 +427,11 @@ Cola.AST_ArgDef = Cola.DEFNODE("ArgDef", "start end name type argtype defval", { $propdoc: { type: "Data type", argtype: "positional/named/splated" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.name._walk(visitor); + }); } }, Cola.AST_SymbolFunarg); @@ -712,12 +729,12 @@ Cola.AST_Cascade = Cola.DEFNODE("Cascade", "expression subexpressions", { _walk: function(visitor) { return visitor._visit(this, function(){ this.expression._walk(visitor); - this.subexpressions.forEach(function(el){ - el._walk(visitor); + this.subexpressions.forEach(function(subexpr){ + subexpr._walk(visitor); }); }); } -}); +}, Cola.AST_PropAccess); Cola.AST_Sub = Cola.DEFNODE("Sub", null, { $documentation: "Index-style property access, i.e. `a[\"foo\"]`", diff --git a/lib/translate.js b/lib/translate.js index 733f6f8c..37086fc6 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -770,6 +770,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ */ if((node instanceof Cola.AST_Function || node instanceof Cola.AST_Defun) && node.argnames.length != 0){ var posed = [], named = [], onfront = true, delQueue = [], pos = 0, splated, aftersplated = -1; + node.argnames.forEach(function(val, i){ if(val.argtype == "positional"){ if(val.defval instanceof Cola.AST_Noop && onfront) pos++, node.argnames[i] = val.name; @@ -788,7 +789,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ posed.push(splated = { pos : pos++, after : 0, val : val }); } }); - + if(delQueue.length != 0) delQueue.reverse().forEach(function(val){ node.argnames.splice(val, 1); diff --git a/lib/typecheck.js b/lib/typecheck.js index 6a4e7df2..c89d97a2 100644 --- a/lib/typecheck.js +++ b/lib/typecheck.js @@ -47,6 +47,16 @@ */ +// TypeAliases + +Cola.TypeAliases = { + "int": "Number", + "float": "Number", + "real": "Number", + "double": "Number", + "bool": "Boolean", + "void": "undefined" +}; // RefsStorage @@ -56,11 +66,21 @@ Cola.RefsStorage = function (parentStorage) { this.objs = []; }; -Cola.RefsStorage.Element = function (name, vartype, callable) { +Cola.RefsStorage.Element = function (name, vartype, callable, args) { this.name = name; this.vartype = vartype; this.callable = callable; this.props = new Cola.RefsStorage(); + this.args = callable ? new Cola.RefsStorage() : false; + + if(callable && args){ + args.posed.forEach(function(v, k){ + this.args.uniqueAdd(k, v, false, false); + }, this); + + for(var n in args.named) + this.args.uniqueAdd(n, args.named[n], false, false); + } }; Cola.RefsStorage.prototype.setSingleType = function (type) { @@ -72,40 +92,42 @@ Cola.RefsStorage.prototype.setSingleType = function (type) { Cola.RefsStorage.prototype.contains = function (name, callable) { return this.objs.some(function(el){ - return el.name == name && el.callable == callable; + return el.name == name && (el.callable = callable); }); }; -Cola.RefsStorage.prototype.remove = function (name, callable) { +Cola.RefsStorage.prototype.remove = function (name) { var pre = this.objs.length; this.objs = this.objs.filter(function(el){ - return !(el.name == name && el.callable == callable); + return !(el.name == name); }); return pre != this.objs.length; }; -Cola.RefsStorage.prototype.uniqueAdd = function (name, vartype, callable) { - if(!this.contains(name, callable)){ - this.objs.push(new Cola.RefsStorage.Element(name, vartype, callable)); +Cola.RefsStorage.prototype.uniqueAdd = function (name, vartype, callable, args) { + if(!this.contains(name)){ + if(callable) this.objs.push(new Cola.RefsStorage.Element(name, "Function", false, false)); + this.objs.push(new Cola.RefsStorage.Element(name, vartype, callable, args)); return true; } return false; }; -Cola.RefsStorage.prototype.owerwriteAdd = function (name, vartype, callable) { - var removed = this.remove(name, callable); - this.objs.push(new Cola.RefsStorage.Element(name, vartype, callable)); +Cola.RefsStorage.prototype.owerwriteAdd = function (name, vartype, callable, args) { + var removed = this.remove(name); + if(callable) this.objs.push(new Cola.RefsStorage.Element(name, "Function", false, false)); + this.objs.push(new Cola.RefsStorage.Element(name, vartype, callable, args)); return removed; }; -Cola.RefsStorage.prototype.get = function (name) { +Cola.RefsStorage.prototype.get = function (name, callable) { if(this.singletype) return this.objs[0]; var obj = false; this.objs.some(function(el){ - return el.name == name && (obj = el); + return el.name == name && el.callable == callable && (obj = el); }); if(!obj && this.parentStorage) obj = this.parentStorage.get(name); @@ -123,48 +145,162 @@ Cola.TypeChecker = function (parentStorage, om) { /* Defs: - first step ( simple ) + // first step ( simple ) - vars definition * AST_VarDef - done - function definition * AST_Defun - done - function argument definition * AST_SymbolFunarg - done - second step - - object's property assignment * AST_Assign - - prototypes ( classes methods ) * AST_Assign - after classes - - vars definition in constructor ( vars of class ) * AST_Assign - after classes + // second step + //- object's property assignment * AST_Assign + //- prototypes ( classes methods ) * AST_Assign - after classes + //- vars definition in constructor ( vars of class ) * AST_Assign - after classes */ -Cola.TypeChecker.prototype.reg = function (def) { +Cola.TypeChecker.prototype.reg = function (def, storage) { + if(!storage) storage = this.storage; + if(def.forEach) def.forEach(function(d){ - if(d instanceof Cola.AST_VarDef && !this.storage.uniqueAdd(d.name.name, d.type, false)){ + if(Cola.TypeAliases[d.type]) d.type = Cola.TypeAliases[d.type]; + + if(d instanceof Cola.AST_VarDef && d.type == "Function" && !storage.uniqueAdd(d.name.name, d.type, true)){ + this.printFuncRedef(d.name.start); + storage.owerwriteAdd(d.name.name, d.type, true); + } else + if(d instanceof Cola.AST_VarDef && !storage.uniqueAdd(d.name.name, d.type, false)){ this.printVarRedef(d.name.start); - this.storage.owerwriteAdd(d.name.name, d.type, false); - } else - if(d instanceof Cola.AST_SymbolFunarg) this.storage.owerwriteAdd(d.name, d.type, false); + storage.owerwriteAdd(d.name.name, d.type, false); + } + }, this); else + if(def instanceof Cola.AST_Defun){ - if(!this.storage.uniqueAdd(def.name.name, "Function", false)){ + if(Cola.TypeAliases[def.type]) def.type = Cola.TypeAliases[def.type]; + var argsTypes = { posed: [], named: {} }; + + def.argnames.forEach(function(val, i){ + if(val.argtype == "positional"){ + argsTypes.posed.push(val.type); + } else if(val.argtype == "named"){ + argsTypes.named[val.name.name] = val.type; + } else if(val.argtype == "splated"){ + argsTypes.posed.push(null); + } + }); + + if(!storage.uniqueAdd(def.name.name, def.type, true, argsTypes)){ this.printFuncRedef(def.start); - this.storage.owerwriteAdd(def.name.name, "Function", false); - } - this.storage.owerwriteAdd(def.name.name, def.type, true); + storage.owerwriteAdd(def.name.name, def.type, true, argsTypes); + } } }; /* - Nodes to check: + Nodes to check: - done * AST_Binary * AST_UnaryPrefix * AST_UnaryPostfix - * AST_Return * AST_Conditional - * AST_VarDef - * AST_ForIn - `in` left argument - * AST_Switch + * AST_Call - checking args + * AST_Defun && AST_Function - checking returned type + * AST_VarDef - checking def value + * AST_ForIn - checking `in` left arg with right + * AST_Switch - checking arg with braches args */ -Cola.TypeChecker.prototype.check = function () { +Cola.TypeChecker.prototype.check = function (node) { + if(node instanceof Cola.AST_Assign){ + var left = this.get(node.left), + right = this.get(node.right); + + if(left != "dynamic" && left != right) this.printMism(node.start); + } else + + if(node instanceof Cola.AST_Binary){ + var left = this.get(node.left), + right = this.get(node.right); + + switch(node.operator){ + case "===": case "!==": case "in": case "instanceof": break; + default: + if(left != "dynamic" && right != "dynamic" && left != right) + this.printMism(node.start); + } + + } else + + if(node instanceof Cola.AST_UnaryPrefix) + switch(node.operator){ + case "typeof": case "in": case "delete": case "new": break; + case "+": case "-": case "++": case "--": case "~": + if(this.get(node.expression) != "Number") this.printMism(node.start); + break; + case "!": + if(this.get(node.expression) != "Boolean") this.printMism(node.start); + break; + } + else + + if(node instanceof Cola.AST_UnaryPostfix) + switch(node.operator){ + case "+": case "-": case "++": case "--": + if(this.get(node.expression) != "Number") this.printMism(node.start); + break; + } + else + + if(node instanceof Cola.AST_Conditional){ + var cond = this.get(this.condition), + cons = this.get(this.consequent), + altr = this.get(this.alternative); + + if(cond != "Boolean" && cond != "dynamic" || cons != altr) this.printMism(node.start); + } else + + if(node instanceof Cola.AST_Call){ + var args = this.get(node, true).args; + //node.argmap + } else + + if((node instanceof Cola.AST_Function || node instanceof Cola.AST_Defun) && node.type != "dynamic"){ + var types = this.getReturnedTypes(node); + types.forEach(function(el){ + if(node.type != el[0]) this.printMism(el[1]); + }); + } else + + if(node instanceof Cola.AST_VarDef && node.type != "dynamic" && node.type != this.get(node.value)) + this.printMism(node.start); + else + + if(node instanceof Cola.AST_ForIn){ + var init = node.init instanceof Cola.AST_Var + ? node.init.type + : this.get(node.init), + + object = this.get(node.object, true); + + if(!object.storage.singletype || init != object.storage.get('every').vartype) this.printMism(node.start); + } else + + if(node instanceof Cola.AST_Switch){ + var expr = this.get(node.expression); + node.body.forEach(function(branch){ + if(branch.expression && this.get(branch.expression) != expr) this.printMism(branch.expression.start) + }); + } +}; + +Cola.TypeChecker.prototype.getReturnedTypes = function (node) { + var types = []; + + var tw = new Cola.TreeWalker(function(node, descend){ + if(node instanceof Cola.AST_Return) types.push([this.get(node), node.start]); + if(!(node instanceof Cola.AST_Defun || node instanceof Cola.AST_Function)) descend(); + }); + + node.walk(tw); + return types; }; /* @@ -172,13 +308,14 @@ Cola.TypeChecker.prototype.check = function () { * AST_Binary * AST_UnaryPrefix * AST_UnaryPostfix - * AST_Return * AST_Conditional * AST_Call * AST_Seq * AST_Dot * AST_Sub + * AST_Cascade + * AST_Proto * AST_Symbol @@ -186,9 +323,10 @@ Cola.TypeChecker.prototype.check = function () { * AST_Constant * AST_Array * AST_Object + * AST_StringTemplate */ -Cola.TypeChecker.prototype.get = function () { +Cola.TypeChecker.prototype.get = function (node) { }; @@ -241,7 +379,7 @@ Cola.AST_Toplevel.DEFMETHOD("check_types", function(options){ node.type_checker = new Cola.TypeChecker(node.parent_scope ? node.parent_scope.type_checker.storage : null, options.output_mode); if (node instanceof Cola.AST_Defun) currScope.type_checker.reg(node); - if (node instanceof Cola.AST_Function || node instanceof Cola.AST_Defun) node.type_checker.reg(node.argnames); + //if (node instanceof Cola.AST_Function || node instanceof Cola.AST_Defun) node.type_checker.reg(node.argnames); csDump = currScope; currScope = node;