diff --git a/lib/ast.js b/lib/ast.js index 89703352..b0a9b57b 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -394,6 +394,34 @@ Cola.AST_Class = Cola.DEFNODE("Class", "name extends", { } }, Cola.AST_Scope); +Cola.AST_Singleton = Cola.DEFNODE("Singleton", "name", { + $iscola: true, + $documentation: "Base class for singletons", + $propdoc: { + name: "[AST_SymbolSingleton] the name of this singleton" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.name._walk(visitor); + Cola.walk_body(this, visitor); + }); + } +}, Cola.AST_Scope); + +Cola.AST_Injector = Cola.DEFNODE("Injector", "name", { + $iscola: true, + $documentation: "Base class for injectors", + $propdoc: { + name: "[AST_SymbolInjector] the name of this injector" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.name._walk(visitor); + Cola.walk_body(this, visitor); + }); + } +}, Cola.AST_Scope); + Cola.AST_Lambda = Cola.DEFNODE("Lambda", "name argnames uses_arguments", { $documentation: "Base class for functions", $propdoc: { @@ -951,6 +979,16 @@ Cola.AST_SymbolClass = Cola.DEFNODE("SymbolClass", null, { $documentation: "Symbol defining a class", }, Cola.AST_SymbolDeclaration); +Cola.AST_SymbolSingleton = Cola.DEFNODE("SymbolSingleton", null, { + $iscola: true, + $documentation: "Symbol defining a singleton", +}, Cola.AST_SymbolDeclaration); + +Cola.AST_SymbolInjector = Cola.DEFNODE("SymbolInjector", null, { + $iscola: true, + $documentation: "Symbol defining an injector", +}, Cola.AST_SymbolDeclaration); + Cola.AST_SymbolFunarg = Cola.DEFNODE("SymbolFunarg", "type", { $documentation: "Symbol naming a function argument", }, Cola.AST_SymbolVar); diff --git a/lib/parse.js b/lib/parse.js index 5eaceaa6..1dd08e28 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1200,6 +1200,12 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "class": return this.class_(); + case "singleton": + return this.singleton_(); + + case "injector": + return this.injector_(); + case "function": return this.function_(Cola.AST_Defun); @@ -1436,7 +1442,7 @@ Cola.Parser.prototype.for_in = function (init) { Cola.Parser.prototype.class_ = function(){ if(this.S.in_class) - this.token_error(this.prev(), "You can't define class in other class."); + this.token_error(this.prev(), "You can define class, singleton or injector only in root scope."); this.S.in_class = true; @@ -1504,6 +1510,112 @@ Cola.Parser.prototype.class_ = function(){ }); }; +Cola.Parser.prototype.singleton_ = function(){ + if(this.S.in_class) + this.token_error(this.prev(), "You can define class, singleton or injector only in root scope."); + + this.S.in_class = true; + + var _this = this, + name = this.is("name") ? this.as_symbol(Cola.AST_SymbolSingleton) : this.unexpected(); + + this.S.class_name = name.name; + + return new Cola.AST_Singleton({ + name: name, + body: (function(loop, labels){ + ++_this.S.in_function; + _this.S.in_directives = true; + _this.S.in_loop = 0; + _this.S.labels = []; + + var tmp, a = _this.block_(); + + --_this.S.in_function; + _this.S.in_loop = loop; + _this.S.labels = labels; + + var mconstr = false; + a.forEach(function(member){ + if(member instanceof Cola.AST_Defun && member.name instanceof Cola.AST_SymbolDefun && member.name.name == _this.S.class_name){ + if(member.type != "dynamic") + _this.token_error(member.start, "Constructor can't have returned type."); + + if(member.mods.length != 0) + _this.token_error(member.start, "Constructor can't have modificators."); + + if(member.argnames.length) + _this.token_error(member.start, "Singleton's and injector's constructor can't have arguments."); + + if(mconstr) + _this.token_error(member.start, "Main constructor can be defined only one time"); + + mconstr = true; + } else if(member instanceof Cola.AST_Defun && member.name instanceof Cola.AST_Dot && member.mods.indexOf("static") != -1){ + _this.token_error(member.start, "Unexpected `static` modificator."); + } + }); + + _this.S.in_class = false; + _this.S.class_name = false; + return a; + })(this.S.in_loop, this.S.labels) + }); +}; + +Cola.Parser.prototype.injector_ = function(){ + if(this.S.in_class) + this.token_error(this.prev(), "You can define class, singleton or injector only in root scope."); + + this.S.in_class = true; + + var _this = this, + name = this.is("name") ? this.as_symbol(Cola.AST_SymbolInjector) : this.unexpected(); + + this.S.class_name = name.name; + + return new Cola.AST_Injector({ + name: name, + body: (function(loop, labels){ + ++_this.S.in_function; + _this.S.in_directives = true; + _this.S.in_loop = 0; + _this.S.labels = []; + + var tmp, a = _this.block_(); + + --_this.S.in_function; + _this.S.in_loop = loop; + _this.S.labels = labels; + + var mconstr = false; + a.forEach(function(member){ + if(member instanceof Cola.AST_Defun && member.name instanceof Cola.AST_SymbolDefun && member.name.name == _this.S.class_name){ + if(member.type != "dynamic") + _this.token_error(member.start, "Constructor can't have returned type."); + + if(member.mods.length != 0) + _this.token_error(member.start, "Constructor can't have modificators."); + + if(mconstr) + _this.token_error(member.start, "Main constructor can be defined only one time"); + + if(member.argnames.length) + _this.token_error(member.start, "Singleton's and injector's constructor can't have arguments."); + + mconstr = true; + } else if(member instanceof Cola.AST_Defun && member.name instanceof Cola.AST_Dot && member.mods.indexOf("static") != -1){ + _this.token_error(member.start, "Unexpected `static` modificator."); + } + }); + + _this.S.in_class = false; + _this.S.class_name = false; + return a; + })(this.S.in_loop, this.S.labels) + }); +}; + Cola.Parser.prototype.as_funcarg = function(splatedexist) { var name = this.as_symbol(Cola.AST_SymbolFunarg), type = name, argtype = 'positional', defval = new Cola.AST_Noop, required = true; diff --git a/lib/translate.js b/lib/translate.js index 32e0839a..b30d8d22 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -1436,7 +1436,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ else post_constructor.body.push(member); } }); - + if(!has_main_constr){ newNode.unshift(new Cola.AST_Defun({ mods : [], @@ -1475,8 +1475,11 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ newNode.push(pre_constructor); main_constructors.forEach(function(constr){ constr.body.unshift(new Cola.AST_Call({ - expression : new Cola.AST_Dot({ expression: new Cola.AST_This, property: "pre_constructor" }), - args : [] + expression : new Cola.AST_Dot({ + expression : new Cola.AST_Proto({ expression: node.name, property: "pre_constructor" }), + property : "call" + }), + args : [new Cola.AST_This] })); constr.body[0] = new Cola.AST_SimpleStatement({ body : constr.body[0] @@ -1488,8 +1491,11 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ newNode.push(post_constructor); main_constructors.forEach(function(constr){ constr.body.push(new Cola.AST_Call({ - expression : new Cola.AST_Dot({ expression: new Cola.AST_This, property: "post_constructor" }), - args : [] + expression : new Cola.AST_Dot({ + expression : new Cola.AST_Proto({ expression: node.name, property: "post_constructor" }), + property : "call" + }), + args : [new Cola.AST_This] })); constr.body[constr.body.length - 1] = new Cola.AST_SimpleStatement({ body : constr.body[constr.body.length - 1] @@ -1537,7 +1543,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ member.args.unshift(new Cola.AST_This); } else - if(member instanceof Cola.AST_SymbolRef && hmembers.indexOf(member.name) != -1 && node.extends){ + if(member instanceof Cola.AST_SymbolRef && hmembers.indexOf(member.name) != -1){ if(member.name == "super"){ with_super = true; member.name = "$uper"; @@ -1549,6 +1555,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ : new Cola.AST_This, property : member.name }); + if(lvl == 1) flvl_this.push(member); } else @@ -1564,6 +1571,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ : new Cola.AST_This, property : member.name }); + if(lvl == 1) flvl_this.push(member); } @@ -1601,6 +1609,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ }); } } + }); if(with_super){ @@ -1617,6 +1626,326 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ node = new Cola.AST_BlockStatement({ body: newNode }); } else + /* + singleton Test { + + const int i = 123; + + Test(){ // constructor + alert("test"); + } + + queryAll("ul li a").forEach((el) => + el.onclick = () => console.log("click") + ); + + covert String method1() => console.log("method"); + + get getter() => i; + set setter(int val) => console.log(val); + + selfExample(){ + query("*").onclick = () => console.log(self); + } + + } + + to + + Test(){ + this.pre_constructor(); + + alert("test") + + this.post_constructor(); + } + + Test::pre_constructor(){ + const int this.i = 123; + } + + Test::post_constructor(){ + queryAll("ul li a").forEach((el) => + el.onclick = () => console.log("click") + ); + } + + covert String Test::method1() => console.log("method"); + + get Test::getter() => i; + set Test::setter(int val) => console.log(val); + + Test::selfExample(){ + var self = this; + query("*").onclick = () => console.log(self); + } + + */ + if(node instanceof Cola.AST_Singleton){ + var pre_constructor, post_constructor, main_constructors = [], + is_pre = true, members = [], binder; + + newNode = []; + node.name = new Cola.AST_SymbolRef(node.name); + + pre_constructor = new Cola.AST_Defun({ + mods : ["covert"], + type : "dynamic", + name : new Cola.AST_Proto({ + expression : new Cola.AST_SymbolDefun(node.name), + property : "pre_constructor" + }), + argnames : [], + body : [] + }); + + post_constructor = new Cola.AST_Defun({ + mods : ["covert"], + type : "dynamic", + name : new Cola.AST_Proto({ + expression : new Cola.AST_SymbolDefun(node.name), + property : "post_constructor" + }), + argnames : [], + body : [] + }); + + var has_main_constr = false; + node.body.forEach(function(member){ + if(member instanceof Cola.AST_Defun && member.name instanceof Cola.AST_SymbolDefun && member.name.name == node.name.name){ + main_constructors.push(member); + newNode.push(member); + + has_main_constr = true; + + is_pre = false; + } else + + if(member instanceof Cola.AST_Lambda && member.name instanceof Cola.AST_SymbolDefun){ + if(members.indexOf(member.name.name) == -1) members.push(member.name.name); + member.name = new Cola.AST_Proto({ + expression : node.name, + property : member.name.name + }); + newNode.push(member); + } else + + if(member instanceof Cola.AST_Var && member.mods.indexOf("static") != -1){ + member.definitions.forEach(function(def){ + var texpr = def.name; + + if(!(texpr instanceof Cola.AST_Symbol)){ + while(!(texpr.expression instanceof Cola.AST_Symbol || texpr.expression instanceof Cola.AST_Constant)) texpr = texpr.expression; + } + + if(def.name instanceof Cola.AST_Symbol && (def.value || member.mods.indexOf("covert") != -1)){ + newNode.push(new Cola.AST_VarDef({ + name : new Cola.AST_Dot({ expression: node.name, property: def.name.name }), + value : def.value + })); + + member.mods.splice(member.mods.indexOf("static"), 1); + + newNode[newNode.length - 1] = new Cola.AST_Var({ + type : member.type, + mods : member.mods, + definitions : [newNode[newNode.length - 1]] + }); + } else + + if((def.value || member.mods.indexOf("covert") != -1) && !(texpr.expression instanceof Cola.AST_This)){ + texpr.expression = new Cola.AST_Dot({ expression: node.name, property: texpr.expression.name }); + newNode.push(new Cola.AST_VarDef({ + name : def.name, + value : def.value + })); + + member.mods.splice(member.mods.indexOf("static"), 1); + + newNode[newNode.length - 1] = new Cola.AST_Var({ + type : member.type, + mods : member.mods, + definitions : [newNode[newNode.length - 1]] + }); + } + }); + } + + else { + if(member instanceof Cola.AST_Var) + member.definitions.forEach(function(def){ + var texpr = def.name; + + if(!(texpr instanceof Cola.AST_Symbol)){ + while(!(texpr.expression instanceof Cola.AST_Symbol || texpr.expression instanceof Cola.AST_Constant)) texpr = texpr.expression; + texpr = texpr.expression; + } + + if(texpr instanceof Cola.AST_Symbol && !(texpr instanceof Cola.AST_This) && members.indexOf(texpr.name) == -1) + members.push(texpr.name); + }); + + if(is_pre) pre_constructor.body.push(member); + else post_constructor.body.push(member); + } + }); + + if(!has_main_constr){ + newNode.unshift(new Cola.AST_Defun({ + mods : [], + type : "dynamic", + name : new Cola.AST_SymbolDefun(node.name), + argnames : [], + body : [] + })); + + main_constructors.push(newNode[0]); + } + + if(pre_constructor.body.length != 0){ + newNode.push(pre_constructor); + main_constructors.forEach(function(constr){ + constr.body.unshift(new Cola.AST_Call({ + expression : new Cola.AST_Dot({ expression: new Cola.AST_This, property: "pre_constructor" }), + args : [] + })); + constr.body[0] = new Cola.AST_SimpleStatement({ + body : constr.body[0] + }); + }); + } + + if(post_constructor.body.length != 0){ + newNode.push(post_constructor); + main_constructors.forEach(function(constr){ + constr.body.push(new Cola.AST_Call({ + expression : new Cola.AST_Dot({ expression: new Cola.AST_This, property: "post_constructor" }), + args : [] + })); + constr.body[constr.body.length - 1] = new Cola.AST_SimpleStatement({ + body : constr.body[constr.body.length - 1] + }); + }); + } + + var scope, lvl = 0, hmembers, with_self = false, self_def = false, flvl_this = []; + binder = new Cola.TreeTransformer(function(member){ + var tmembers, tscope, tlvl, tself_def; + member = member.clone(); + + if(lvl > 1 && member instanceof Cola.AST_Var){ + member.definitions.forEach(function(def){ + if(def.name instanceof Cola.AST_Symbol){ + if(hmembers.indexOf(def.name.name) != -1) + hmembers.splice(hmembers.indexOf(def.name.name), 1); + if(def.name.name == "self") + self_def = true; + } + }); + } else + + if(lvl > 1 && (member instanceof Cola.AST_Defun || member instanceof Cola.AST_Getter || member instanceof Cola.AST_Setter) && member.name instanceof Cola.AST_Symbol && hmembers.indexOf(member.name.name) != -1){ + hmembers.splice(hmembers.indexOf(member.name.name), 1); + } else + + if(member instanceof Cola.AST_Scope) + scope = (lvl++, member); + else + + if(member instanceof Cola.AST_SymbolRef && hmembers.indexOf(member.name) != -1){ + + member = new Cola.AST_Dot({ + expression : lvl > 1 + ? (with_self = true, new Cola.AST_SymbolRef({ name: "self" })) + : new Cola.AST_This, + property : member.name + }); + + if(lvl == 1) flvl_this.push(member); + } else + + if(member instanceof Cola.AST_SymbolRef && member.name == "self" && !self_def){ + with_self = true; + } else + + if(member instanceof Cola.AST_SymbolVar && scope.name instanceof Cola.AST_Proto && (scope.name.property == "pre_constructor" || scope.name.property == "post_constructor") + && hmembers.indexOf(member.name) != -1){ + member = new Cola.AST_Dot({ + expression : lvl > 1 + ? (with_self = true, new Cola.AST_SymbolVar({ name: "self" })) + : new Cola.AST_This, + property : member.name + }); + + if(lvl == 1) flvl_this.push(member); + } + + tscope = scope; tlvl = lvl; tmembers = hmembers.slice(); tself_def = self_def; + member._descend(member, this); + scope = tscope; lvl = tlvl; hmembers = tmembers; self_def = tself_def; + + return member; + }); + + newNode.forEach(function(member, i){ + lvl = 0; + flvl_this = []; + with_self = false; + self_def = false; + hmembers = members.slice(); + + if(member instanceof Cola.AST_Lambda){ + newNode[i] = member.transform(binder); + newNode[i].mods ? newNode[i].mods.push("method") : (newNode[i].mods = ["method"]); + + if(with_self) { + newNode[i].body.unshift(new Cola.AST_Var({ + mods : [], + type : "dynamic", + definitions : [new Cola.AST_VarDef({ + type : "dynamic", + name : new Cola.AST_SymbolVar({ name: "self" }), + value : new Cola.AST_This + })] + })); + + flvl_this.forEach(function(th){ + th.expression = new Cola.AST_SymbolRef({ name: "self" }); + }); + } + } + + }); + + newNode.push(new Cola.AST_Return({ + value : new Cola.AST_New({ + args : [], + expression : node.name + }) + })); + //node = new Cola.AST_BlockStatement({ body: newNode }); + newNode = new Cola.AST_Function({ + type : node.name.name, + body : newNode, + argnames : [] + }); + + newNode = new Cola.AST_Call({ + args : [], + expression : newNode + }); + + node = new Cola.AST_Var({ + type : node.name.name, + mods : [], + definitions : [new Cola.AST_VarDef({ + type : node.name.name, + name : new Cola.AST_SymbolVar(node.name), + value : newNode + })] + }); + } else + /* func(String s, Number n:, Array list..., Boolean b = false, h: 123){