From a8d8da556f9d045dca5629c63dfcd212660d87e1 Mon Sep 17 00:00:00 2001 From: Dan Onoshko Date: Wed, 31 Dec 2014 20:25:29 +0700 Subject: [PATCH] async function definiton is done --- README.md | 37 +++++++++++++ lib/ast.js | 10 ++++ lib/parse.js | 34 ++++++++---- lib/translate.js | 135 +++++++++++++++++++++++++++++++++++++++++++++-- lib/utils.js | 25 +++++++-- 5 files changed, 223 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 608c0f36..6788ffa3 100644 --- a/README.md +++ b/README.md @@ -497,6 +497,43 @@ Future plans @export each as forEach from "underscore" @export "mylib" +- `async` function modificator + + async GET(String url) { + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange() { + if (xhr.readyState != 4) return; + if (xhr.status == 200) resolve xhr.response; + + reject false; + } + + xhr.open("GET", url, true); + xhr.send(); + } + + to + + function GET(url) { + var _ColaRuntime$$arguments = arguments; + return new Promise(function(_ColaRuntime$$resolve, _ColaRuntime$$reject) { + arguments = _ColaRuntime$$arguments; + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (xhr.readyState != 4) return; + if (xhr.status == 200) return _ColaRuntime$$resolve(xhr.response); + + return _ColaRuntime$$reject(false); + } + + xhr.open("GET", url, true); + xhr.send(); + }.bind(this)); + } + + - `await` operator (only with Promise support) String name = await getNameFromServer(id); diff --git a/lib/ast.js b/lib/ast.js index d7e30906..becf41cf 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -503,6 +503,16 @@ Cola.AST_Throw = Cola.DEFNODE("Throw", null, { $documentation: "A `throw` statement" }, Cola.AST_Exit); +Cola.AST_Resolve = Cola.DEFNODE("Resolve", null, { + $iscola: true, + $documentation: "A `resolve` statement" +}, Cola.AST_Exit); + +Cola.AST_Reject = Cola.DEFNODE("Reject", null, { + $iscola: true, + $documentation: "A `reject` statement" +}, Cola.AST_Exit); + Cola.AST_LoopControl = Cola.DEFNODE("LoopControl", "label", { $documentation: "Base class for loop control statements (`break` and `continue`)", $propdoc: { diff --git a/lib/parse.js b/lib/parse.js index d5b5780c..1f2541cb 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -49,17 +49,17 @@ !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', '') + ' static covert export get set when clone is isnt class extends singleton'; +Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' static covert export async get set when clone is isnt class extends singleton resolve reject await'; 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', '').replace(' extends', '').replace(' static', '') + " " + Cola.cKEYWORDS_ATOM + " " + Cola.cKEYWORDS; +Cola.cRESERVED_WORDS = Cola.RESERVED_WORDS.replace(' class', '').replace(' extends', '').replace(' static', '').replace(' export', '') + " " + Cola.cKEYWORDS_ATOM + " " + Cola.cKEYWORDS; Cola.RESERVED_WORDS += " " + Cola.KEYWORDS_ATOM + " " + Cola.KEYWORDS; Cola.KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; -Cola.cKEYWORDS_BEFORE_EXPRESSION = Cola.KEYWORDS_BEFORE_EXPRESSION += ' when'; +Cola.cKEYWORDS_BEFORE_EXPRESSION = Cola.KEYWORDS_BEFORE_EXPRESSION += ' when resolve reject await'; Cola.KEYWORDS = Cola.makePredicate(Cola.KEYWORDS); Cola.cKEYWORDS = Cola.makePredicate(Cola.cKEYWORDS); @@ -770,7 +770,7 @@ Cola.cUNARY_PREFIX = Cola.makePredicate([ "+" ]); -Cola.cVARS_MODIFICATORS = Cola.makePredicate([ "const", "covert", "static", "export" ]); +Cola.cVARS_MODIFICATORS = Cola.makePredicate([ "const", "covert", "static", "export", "async" ]); Cola.UNARY_POSTFIX = Cola.makePredicate([ "--", "++" ]); Cola.cUNARY_POSTFIX = Cola.makePredicate([ "--", "++", "?" ]); @@ -850,6 +850,7 @@ Cola.Parser = function ($TEXT, options) { prev : null, peeked : null, in_function : 0, + in_async_function : 0, in_directives : true, in_loop : 0, labels : [] @@ -1330,10 +1331,18 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "if": return this.if_(); - case "return": + case "return": case "resolve": case "reject": if (this.S.in_function == 0) - this.croak("'return' outside of function"); - return new Cola.AST_Return({ + this.croak("'" + tmp + "' outside of function"); + + if (tmp != "return" && this.S.in_async_function == 0) + this.croak("'" + tmp + "' outside of async function"); + + var ctor = Cola.AST_Return; + if (tmp == "resolve") ctor = Cola.AST_Resolve; + else if (tmp == "reject") ctor = Cola.AST_Reject; + + return new ctor({ value: ( this.is("punc", ";") ? (this.next(), null) : this.can_insert_semicolon() @@ -1733,12 +1742,12 @@ Cola.Parser.prototype.function_ = function(ctor, type, mods) { if(!this.S.in_class){ if(name instanceof Cola.AST_Symbol){ - if(mods.length > 1 || mods[0] && mods[0] != "export") - this.token_error(name.start, "Function definition outside of class can contain only `export` modifer"); + if(!Cola.modsVerifi(mods, ["export", "async"])) + this.token_error(name.start, "Function definition outside of class can contain only `export` and `async` modifers"); } else { - if(mods.length != 0 && (mods.indexOf("static") != -1 || mods.indexOf("export") != -1)) + if(Cola.modsContains(mods, ["export", "static"])) this.token_error(name.start, "Prop definition can't contain `static` and `export` modifers"); } } else if (mods.indexOf("export") != -1) @@ -1763,6 +1772,7 @@ Cola.Parser.prototype.function_ = function(ctor, type, mods) { })(true, []), body: (function(loop, labels){ ++_this.S.in_function; + if (mods.indexOf("async") != -1) ++_this.S.in_async_function; _this.S.in_directives = true; _this.S.in_loop = 0; _this.S.labels = []; @@ -1783,6 +1793,7 @@ Cola.Parser.prototype.function_ = function(ctor, type, mods) { : _this.block_(); --_this.S.in_function; + if (mods.indexOf("async") != -1) --_this.S.in_async_function; _this.S.in_loop = loop; _this.S.labels = labels; return a; @@ -1922,6 +1933,9 @@ Cola.Parser.prototype.vardefs = function (no_in, in_const, type, mods) { if (mods && mods.indexOf("export") != -1 && (!(last.name instanceof Cola.AST_Symbol) || this.S.in_class)) this.token_error(last.start, "Unexpected `export` modificator."); + if (mods && mods.indexOf("async") != -1) + this.token_error(last.start, "Unexpected `async` modificator."); + if (!this.is("punc", ",")) break; this.next(); diff --git a/lib/translate.js b/lib/translate.js index b19b732d..7d2a41a9 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -482,6 +482,8 @@ Cola.DefFunWithMods = function(func, mods){ }); } + if (mods.indexOf("async") != -1) func = Cola.FuncAsync(func); + var sname = func.name, dp = { properties : [ new Cola.AST_ObjectKeyVal({ key : (function(node){ @@ -536,6 +538,55 @@ Cola.DefFunWithMods = function(func, mods){ }); }; +Cola.FuncAsync = function(func) { + if (!func.body.length) return func; + var newBody = []; + + if (func.argnames.length) newBody.push(new Cola.AST_Var({ + mods : [], + type : "dynamic", + definitions : [new Cola.AST_VarDef({ + type : "dynamic", + name : new Cola.AST_SymbolVar({ name: "_ColaRuntime$$arguments" }), + value : new Cola.AST_SymbolRef({ name: "arguments" }) + })] + })); + + newBody.push(new Cola.AST_Return({ + value : new Cola.AST_New({ + args : [], + expression : new Cola.AST_SymbolRef({ name: "Promise" }) + }) + })); + + newBody[newBody.length - 1].value.args.push(new Cola.AST_Call({ + args : [new Cola.AST_This], + expression : new Cola.AST_Dot({ expression: false, property: "bind" }) + })); + + func.body.unshift(new Cola.AST_SimpleStatement({ + body : new Cola.AST_Assign({ + left : new Cola.AST_SymbolRef({ name: "arguments" }), + operator : "=", + right : func.argnames.length + ? new Cola.AST_SymbolRef({ name: "_ColaRuntime$$arguments" }) + : new Cola.AST_Object + }) + })); + + newBody[newBody.length - 1].value.args[0].expression.expression = new Cola.AST_Function({ + body : func.body, + argnames : [ + new Cola.AST_SymbolFunarg({ name : "_ColaRuntime$$resolve" }), + new Cola.AST_SymbolFunarg({ name : "_ColaRuntime$$reject" }) + ] + }); + + func.body = newBody; + + return func; +}; + Cola.PushModuleToCommonWrapper = function(module, commonWrapper, name, id) { commonWrapper.body.args[0].elements.push(new Cola.AST_Function({ body : module.body, @@ -669,7 +720,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ to - if(a.b === "undefined" && a.b === null) a.b.c(); + if(!(typeof a.b === "undefined" || a.b === null)) a.b.c(); */ if(node instanceof Cola.AST_SimpleStatement && (props = Cola.ContainCondAccess(node.body))){ @@ -686,7 +737,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ } node = new Cola.AST_If({ - condition : Cola.Constructions.IsntSet(props), + condition : Cola.Constructions.IsSet(props), body : node }); } else @@ -724,7 +775,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ to - if(a.b === "undefined" && a.b === null) a.b.c = 123; + if(a.b !== "undefined" && a.b !== null) a.b.c = 123; */ if(node instanceof Cola.AST_SimpleStatement && node.body instanceof Cola.AST_Assign && (props = Cola.ContainCondAccess(node.body.left))){ @@ -741,7 +792,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ } node = new Cola.AST_If({ - condition : Cola.Constructions.IsntSet(props), + condition : Cola.Constructions.IsSet(props), body : node }); } else @@ -951,6 +1002,40 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ node = new Cola.AST_Call(props); } else + /* + resolve a + + to + + return _ColaRuntime$$resolve(a) + + */ + if(node instanceof Cola.AST_Resolve){ + node = new Cola.AST_Return({ + value : new Cola.AST_Call({ + args : [node.value], + expression : new Cola.AST_SymbolRef({ name: "_ColaRuntime$$resolve" }) + }) + }); + } else + + /* + reject a + + to + + return _ColaRuntime$$reject(a) + + */ + if(node instanceof Cola.AST_Reject){ + node = new Cola.AST_Return({ + value : new Cola.AST_Call({ + args : [node.value], + expression : new Cola.AST_SymbolRef({ name: "_ColaRuntime$$reject" }) + }) + }); + } else + /* arr[] = 123 @@ -1207,6 +1292,46 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ }); } else + /* + async GET(String url) { + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange() { + if (xhr.readyState != 4) return; + if (xhr.status == 200) resolve xhr.response; + + reject false; + } + + xhr.open("GET", url, true); + xhr.send(); + } + + to + + function GET(url) { + var _ColaRuntime$$arguments = arguments; + return new Promise(function(_ColaRuntime$$resolve, _ColaRuntime$$reject) { + arguments = _ColaRuntime$$arguments; + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (xhr.readyState != 4) return; + if (xhr.status == 200) return _ColaRuntime$$resolve(xhr.response); + + return _ColaRuntime$$reject(false); + } + + xhr.open("GET", url, true); + xhr.send(); + }.bind(this)); + } + + */ + if (node instanceof Cola.AST_Defun && node.mods.indexOf("async") != -1) { + node = Cola.FuncAsync(node); + } else + /* func(a, b, name : name, c) @@ -3159,7 +3284,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ })})); }); - if (options.root && !options.is_node) { + if (options.root && !options.is_node && _this.requiredModules.length && !exports.length) { var commonWrapper = _ColaRuntime$$ast.body[17].clone(), moduleId = 1; Cola.PushModuleToCommonWrapper(_this, commonWrapper); diff --git a/lib/utils.js b/lib/utils.js index d5c24411..25dec3df 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -162,6 +162,23 @@ Cola.bootstrap = function () { window.dispatchEvent(event); }; +Cola.modsVerifi = function(mods, allowedMods) { + mods = mods.slice(); + + allowedMods.forEach(function(mod){ + Cola.remove(mods, mod); + }); + + return !mods.length; +}; + +Cola.modsContains = function(mods, allowedMods) { + for (var i in allowedMods) + if (allowedMods.hasOwnProperty(i) && mods.indexOf(allowedMods[i]) != -1) return true; + + return false; +}; + "use strict"; Cola.array_to_hash = function (a) { @@ -298,9 +315,11 @@ Cola.string_template = function (text, props) { }; Cola.remove = function (array, el) { - for (var i = array.length; --i >= 0;) { - if (array[i] === el) array.splice(i, 1); - } + var i = array.indexOf(el); + if (i > -1) array.splice(i, 1); + // for (var i = array.length; --i >= 0;) { + // if (array[i] === el) array.splice(i, 1); + // } }; Cola.mergeSort = function (array, cmp) {