From 8e3914de57b761a90a076a367583d6637ff58fba Mon Sep 17 00:00:00 2001 From: Onoshko Dan Date: Sat, 16 Aug 2014 13:21:45 +0700 Subject: [PATCH] Var and func def modificators. covert int Math.some() => 123; to Object.defineProperty(Math, "some", { value: function some() { return 123; }, writable: true }); --- README.md | 9 +++ lib/ast.js | 12 +++- lib/parse.js | 71 +++++++++++++++---- lib/translate.js | 173 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 215 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index b42221db..4aa331b1 100644 --- a/README.md +++ b/README.md @@ -397,6 +397,15 @@ Future plans int index = -10; arr[%index] = 34; // arr[index %% arr.length]; +- var modificators. status: done + + readonly int Math.Pi = 3.14; + + list of modificators + * static: vars, funcs, getters and setters in class + * const: vars + * covert: vars, funcs, getters and setters in class and objects + - classes class A { diff --git a/lib/ast.js b/lib/ast.js index fc9d7804..566f7365 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -404,7 +404,15 @@ Cola.AST_Function = Cola.DEFNODE("Function", "type", { $documentation: "A function expression" }, Cola.AST_Lambda); -Cola.AST_Defun = Cola.DEFNODE("Defun", "type", { +Cola.AST_Defun = Cola.DEFNODE("Defun", "type mods", { + $documentation: "A function definition" +}, Cola.AST_Lambda); + +Cola.AST_Getter = Cola.DEFNODE("Getter", "type mods", { + $documentation: "A function definition" +}, Cola.AST_Lambda); + +Cola.AST_Setter = Cola.DEFNODE("Setter", "type mods", { $documentation: "A function definition" }, Cola.AST_Lambda); @@ -589,7 +597,7 @@ Cola.AST_Definitions = Cola.DEFNODE("Definitions", "definitions", { } }, Cola.AST_Statement); -Cola.AST_Var = Cola.DEFNODE("Var", "type", { +Cola.AST_Var = Cola.DEFNODE("Var", "type mods", { $documentation: "A `var` statement" }, Cola.AST_Definitions); diff --git a/lib/parse.js b/lib/parse.js index 661f909f..97326482 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -49,13 +49,13 @@ !this.Cola && (this.Cola = {}); Cola.KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; -Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' when clone is isnt class singleton injector'; +Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' static covert get set when clone is isnt class singleton injector'; Cola.KEYWORDS_ATOM = 'false null true'; Cola.cKEYWORDS_ATOM = Cola.KEYWORDS_ATOM + ' on yes off no'; Cola.RESERVED_WORDS = 'abstract boolean byte char double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'; -Cola.cRESERVED_WORDS = Cola.RESERVED_WORDS.replace(' class', '') + " " + Cola.cKEYWORDS_ATOM + " " + Cola.cKEYWORDS; +Cola.cRESERVED_WORDS = Cola.RESERVED_WORDS.replace(' class', '').replace(' static', '') + " " + Cola.cKEYWORDS_ATOM + " " + Cola.cKEYWORDS; Cola.RESERVED_WORDS += " " + Cola.KEYWORDS_ATOM + " " + Cola.KEYWORDS; Cola.KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; @@ -768,6 +768,8 @@ Cola.cUNARY_PREFIX = Cola.makePredicate([ "+" ]); +Cola.cVARS_MODIFICATORS = Cola.makePredicate([ "const", "covert", "static" ]); + Cola.UNARY_POSTFIX = Cola.makePredicate([ "--", "++" ]); Cola.cUNARY_POSTFIX = Cola.makePredicate([ "--", "++", "?" ]); @@ -1024,21 +1026,46 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "atom": return this.simple_statement(); - case "name": - if(!this.is_js && this.next_is("name")){ + case "name": case "keyword": + var mods = []; + + if(!this.is_js && this.is("keyword") && Cola.cVARS_MODIFICATORS(this.S.token.value)) + while(this.is("keyword") && Cola.cVARS_MODIFICATORS(this.S.token.value)){ + mods.push(this.S.token.value); + this.next(); + } + + if(!this.is_js && this.is("keyword", "var")){ + this.next(); + return tmp = this.var_(false, false, mods), this.semicolon(), tmp; + } + + if(!this.is_js && this.is("name") && (this.next_is("name") || this.next_is("keyword", "function") || this.next_is("keyword", "get") || this.next_is("keyword", "set"))){ type = this.S.token.value, this.next(); - var isfun = false; + var isfun = false, ctor = Cola.AST_Defun; + + if(this.is("keyword", "get")){ + ctor = Cola.AST_Getter; + this.next(); + } else if(this.is("keyword", "set")){ + cotr = Cola.AST_Setter; + this.next(); + } else if(this.is("keyword", "function")) + this.next(); + this.dumpS(); this.subscripts(this.as_symbol(Cola.AST_SymbolDefun), false); isfun = this.is("punc", "("); this.restoreS(); - if(isfun) return this.function_(Cola.AST_Defun, type); - return tmp = this.var_(false, type), this.semicolon(), tmp; + if(isfun){ + if(mods.indexOf("const") != -1) this.token_error(this.S.token, "Function can't have `const` modifer"); + return this.function_(ctor, type, mods); + } else return tmp = this.var_(false, type, mods), this.semicolon(), tmp; } - if(!this.is_js){ + if(!this.is_js && this.is("name")){ var _this = this, balance = 1, isfun = false; this.dumpS(); @@ -1055,10 +1082,16 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { } this.restoreS(); - if(isfun) return this.function_(Cola.AST_Defun); + if(isfun) { + if(mods.indexOf("const") != -1) this.token_error(this.S.token, "Function can't have `const` modifer"); + return this.function_(Cola.AST_Defun, false, mods); + } } - return this.next_is("punc", ":") + if(!this.is_js && mods.length != 0 && this.is("name")) + return tmp = this.var_(false, false, mods), this.semicolon(), tmp; + + if(this.is("name")) return this.next_is("punc", ":") ? this.labeled_statement() : this.simple_statement(); @@ -1132,7 +1165,7 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { this.next(); return new Cola.AST_EmptyStatement(); default: - this.unexpected(); + if(!this.is_js && !this.is("keyword")) this.unexpected(); } case "keyword": @@ -1165,6 +1198,12 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "function": return this.function_(Cola.AST_Defun); + case "get": + if(!this.is_js) return this.function_(Cola.AST_Getter); + + case "set": + if(!this.is_js) return this.function_(Cola.AST_Setter); + case "if": return this.if_(); @@ -1347,7 +1386,7 @@ Cola.Parser.prototype.as_funcarg = function(splatedexist) { }); }; -Cola.Parser.prototype.function_ = function(ctor, type) { +Cola.Parser.prototype.function_ = function(ctor, type, mods) { !type && (type = "dynamic"); var in_statement = ctor === Cola.AST_Defun, _this = this, splatedexist = false; @@ -1357,6 +1396,7 @@ Cola.Parser.prototype.function_ = function(ctor, type) { this.unexpected(); this.expect("("); return new ctor({ + mods: mods, type: type, name: name, argnames: (function(first, a){ @@ -1523,11 +1563,12 @@ Cola.Parser.prototype.vardefs = function (no_in, in_const, type) { return a; }; -Cola.Parser.prototype.var_ = function(no_in, type) { +Cola.Parser.prototype.var_ = function(no_in, type, mods) { (!type || type == "var") && (type = "dynamic"); return new Cola.AST_Var({ start : this.prev(), definitions : this.vardefs(no_in, false, type), + mods : mods, type : type, end : this.prev() }); @@ -1907,7 +1948,7 @@ Cola.Parser.prototype.object_ = Cola.Parser.embed_tokens(function(is_template, i ptype = name == "var" ? "dynamic" : name; name = this.as_name(); } else ptype = false; - if (type == "name" && !this.is("punc", ":")) { + if ((type == "name" || !this.is_js && type == "keyword") && !this.is("punc", ":")) { if (name == "get") { if (!this.is_js && is_object === false) this.unexpected(); is_object = true; @@ -2253,7 +2294,7 @@ Cola.Parser.prototype.cascade = function(expr, start) { || last instanceof Cola.AST_Call || last instanceof Cola.AST_PropAccess || last instanceof Cola.AST_Array - )) this.unexpected(); + )) this.unexpected(last.start); } if (!this.is("punc", "..")) break; } diff --git a/lib/translate.js b/lib/translate.js index 79a090a8..aec71248 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -374,6 +374,111 @@ Cola.ContainCondAccess = function(node){ return false; }; +Cola.DefPropWithMods = function(def, mods){ + if(mods.length == 0) return new Cola.AST_Assign({ + start : def.start, + end : def.end, + operator : '=', + left : def.name, + right : def.value + }); + + if(mods.indexOf("static") != -1) + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, def.start, "Prop definition outside of class can't contain `static` modifer"); + + if(mods.indexOf("const") != -1 && !def.value) + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, def.start, "`const` prop can't have `undefined` value"); + + var dp = { properties : [ + new Cola.AST_ObjectKeyVal({ + key : "value", + value : def.value + }) + ] }; + + if(mods.indexOf("const") == -1) + dp.properties.push(new Cola.AST_ObjectKeyVal({ + key : "writable", + value : new Cola.AST_True + })); + + if(mods.indexOf("covert") == -1) + dp.properties.push(new Cola.AST_ObjectKeyVal({ + key : "enumerable", + value : new Cola.AST_True + })); + + return new Cola.AST_Call({ + args : [ + def.name.expression, + def.name instanceof Cola.AST_Sub + ? def.name.property + : new Cola.AST_String({ value: def.name.property }), + new Cola.AST_Object(dp)], + expression : new Cola.AST_Dot({ + expression : new Cola.AST_SymbolRef({ name: "Object" }), + property : "defineProperty" + }) + }); +}; + +Cola.DefFunWithMods = function(func, mods){ + if(mods.length == 0) return new Cola.AST_Assign({ + start : func.start, + end : func.end, + operator : '=', + left : func.name, + right : (function(node){ + node.name = new Cola.AST_SymbolLambda({ + start : node.name.start, + end : node.name.end, + name : node.name.property + }); + return new Cola.AST_Function(node); + })(func) + }); + + if(mods.indexOf("static") != -1) + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, func.start, "Function definition outside of class can't contain `static` modifer"); + + var sname = func.name, dp = { properties : [ + new Cola.AST_ObjectKeyVal({ + key : "value", + value : (function(node){ + node.name = new Cola.AST_SymbolLambda({ + start : node.name.start, + end : node.name.end, + name : node.name.property + }); + return new Cola.AST_Function(node); + })(func) + }), + new Cola.AST_ObjectKeyVal({ + key : "writable", + value : new Cola.AST_True + }) + ] }; + + if(mods.indexOf("covert") == -1) + dp.properties.push(new Cola.AST_ObjectKeyVal({ + key : "enumerable", + value : new Cola.AST_True + })); + + return new Cola.AST_Call({ + args : [ + sname.expression, + sname instanceof Cola.AST_Sub + ? sname.property + : new Cola.AST_String({ value: sname.property }), + new Cola.AST_Object(dp)], + expression : new Cola.AST_Dot({ + expression : new Cola.AST_SymbolRef({ name: "Object" }), + property : "defineProperty" + }) + }); +}; + Cola.AST_Toplevel.prototype.toJavaScript = function(options){ if(this.language == 'js') return this; @@ -941,43 +1046,53 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ } else /* - int obj.num = 0 + const int num = 1, obj.num = 0; to - obj.num = 0 + const num - 1; + Object.defineProperty(obj, "num", { value: 0, enumerable: true }); */ if(node instanceof Cola.AST_Var){ var defCache = []; newNode = []; node.definitions.forEach(function(def, i){ - if(!(def.name instanceof Cola.AST_SymbolVar)){ + if(def.name instanceof Cola.AST_SymbolVar){ + if(!def.value) + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, def.start, "`const` var can't have `undefined` value"); + + defCache.push(def); + } else if(def.value){ if(defCache.length != 0){ - newNode.push(node.clone()); - newNode[newNode.length - 1].definitions = defCache; - defCache = []; + if(!node.mods || node.mods.length == 0 || node.mods.indexOf("const") != -1 && node.mods.length == 1){ + newNode.push(node.mods.length == 1 ? new Cola.AST_Const(node) : node.clone()); + newNode[newNode.length - 1].definitions = defCache; + defCache = []; + } else + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, node.start, "Var definition can contain only `const` modifer"); } - newNode.push(new Cola.AST_Assign({ - start : def.start, - end : def.end, - operator : '=', - left : def.name, - right : def.value - })); + var texpr = def.name; + while(!(texpr.expression instanceof Cola.AST_SymbolVar)) texpr = texpr.expression; + texpr.expression = new Cola.AST_SymbolRef(texpr.expression); + newNode.push(Cola.DefPropWithMods(def, node.mods)); newNode[newNode.length - 1] = new Cola.AST_SimpleStatement({ body : newNode[newNode.length - 1] }); - } else defCache.push(def); + } }); if(defCache.length != 0){ - newNode.push(node.clone()); - newNode[newNode.length - 1].definitions = defCache; - defCache = []; + if(!node.mods || node.mods.length == 0 || node.mods.indexOf("const") != -1 && node.mods.length == 1){ + newNode.push(node.mods && node.mods.length == 1 ? new Cola.AST_Const(node) : node.clone()); + newNode[newNode.length - 1].definitions = defCache; + defCache = []; + } else + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, node.start, "Var definition can contain only `const` modifer"); + } node = newNode; @@ -988,26 +1103,15 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ to - Math.rand = function(){} + Math.rand = function rand(){} */ if(node instanceof Cola.AST_Defun && !(node.name instanceof Cola.AST_SymbolDefun)){ var texpr = node.name; while(!(texpr.expression instanceof Cola.AST_SymbolDefun)) texpr = texpr.expression; texpr.expression = new Cola.AST_SymbolRef(texpr.expression); - node = new Cola.AST_Assign({ - start : node.start, - end : node.end, - operator : '=', - left : node.name, - right : (function(node){ - node.name = new Cola.AST_SymbolLambda({ - start : node.name.start, - end : node.name.end, - name : node.name.property - }); - return new Cola.AST_Function(node); - })(node) - }); + + node = Cola.DefFunWithMods(node, node.mods); + node = new Cola.AST_SimpleStatement({ body : node }); @@ -1074,6 +1178,9 @@ 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; + if(node.mods && node.mods.length != 0) + Cola.Parser.prototype.token_error.call(Cola.Parser.prototype, node.start, "Function definition outside of class can't contain any modifer"); + node.argnames.forEach(function(val, i){ if(val.argtype == "positional"){ if(val.defval instanceof Cola.AST_Noop && onfront && !val.required) pos++, node.argnames[i] = val.name; @@ -1105,7 +1212,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ posed.forEach(function(val, i){ var pos = val.pos; val = val.val; - console.log(val); + if(val.argtype == "splated"){ aftersplated = 0; props.definitions.push(new Cola.AST_VarDef({