diff --git a/README.md b/README.md index f406dd4d..b8966d3a 100644 --- a/README.md +++ b/README.md @@ -377,7 +377,7 @@ Future plans Object data = someData; int data.getFriendsCount() => this.friends.length; -- operator `?.` +- operator `?.`. status: done - Negate array accessor ( getter ) arr[-1]; // last element diff --git a/lib/parse.js b/lib/parse.js index 7b3a5a5f..6e9ca066 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -147,7 +147,7 @@ Cola.WHITESPACE_CHARS = Cola.makePredicate(Cola.characters(" \u00a0\n\r\t\f\u000 Cola.cPUNC_BEFORE_EXPRESSION = Cola.makePredicate(Cola.characters("[{(,.;:").concat(["::", "?."])); Cola.PUNC_BEFORE_EXPRESSION = Cola.makePredicate(Cola.characters("[{(,.;:")); -Cola.PUNC_CHARS = Cola.makePredicate(Cola.characters("[]{}(),;:?")); +Cola.PUNC_CHARS = Cola.makePredicate(Cola.characters("[]{}(),;:")); Cola.REGEXP_MODIFIERS = Cola.makePredicate(Cola.characters("gmsiy")); @@ -710,7 +710,6 @@ Cola.Tokenizer.prototype.next_token = function (force_regexp) { if (Cola.PUNC_CHARS(ch)){ if (!this.is_js && ch == ":" && this.peek(1) == ":") return this.next(), this.next(), this.token("punc", "::"); - if (!this.is_js && ch == "?" && this.peek(1) == ".") return this.next(), this.next(), this.token("punc", "?."); if (!this.is_js && this.S.string.at[this.S.string.level].inside && (this.S.string.at[this.S.string.level].inside_at || this.S.string.at[this.S.string.level].inside_braces)) { if (ch == '{') this.S.string.at[this.S.string.level].balance++; @@ -728,6 +727,7 @@ Cola.Tokenizer.prototype.next_token = function (force_regexp) { } if (!this.is_js) { + if (ch == "?" && this.peek(1) == ".") return this.next(), this.next(), this.token("punc", "?."); if (ch == 'r' && (this.peek(1) == '"' || this.peek(1) == "'" || this.peek(1) == '`')) return this.next(), this.read_string(true); if (ch + this.peek(1) == '=>') return this.next(), this.next(), this.token("punc", "=>"); if (ch == '@') return this.read_at(); @@ -2099,6 +2099,7 @@ Cola.Parser.prototype.maybe_unary = function(allow_calls) { return ex; } var val = this.expr_atom(allow_calls); + while (this.is("operator") && this.UNARY_POSTFIX(this.S.token.value) && !this.S.token.nlb) { if(!this.is_js && this.is("operator", "?") && !(this.next_is("punc", ";") || this.next_is("punc", ",") || this.next_is("punc", ":") || @@ -2169,6 +2170,7 @@ Cola.Parser.prototype.expr_ops = function (no_in) { Cola.Parser.prototype.maybe_conditional = function(no_in) { var start = this.S.token; var expr = this.expr_ops(no_in); + if (this.is("operator", "?")) { this.next(); diff --git a/lib/translate.js b/lib/translate.js index 33672867..eed87f3e 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -361,12 +361,17 @@ Cola.Constructions.NamedVarDef = function(name, type, defval, key){ }); }; -Cola.CondAccessContains = function(node){ +Cola.ContainCondAccess = function(node){ if(!(node instanceof Cola.AST_Call || node instanceof Cola.AST_PropAccess)) return false; + if(node instanceof Cola.AST_CondAccess) return node.expression; + var expr = node; while(expr.expression instanceof Cola.AST_Call || expr.expression instanceof Cola.AST_PropAccess){ expr = expr.expression; + if(expr instanceof Cola.AST_CondAccess) return expr.expression; } + + return false; }; @@ -472,11 +477,109 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ to - typeof a.b === "undefined" && a.b === null ? undefined : a.b.c(); + if(a.b === "undefined" && a.b === null) a.b.c(); */ - if(node instanceof ){ + if(node instanceof Cola.AST_SimpleStatement && (props = Cola.ContainCondAccess(node.body))){ + if(node.body instanceof Cola.AST_CondAccess) node.body = new Cola.AST_Dot(node.body); + else { + var expr = node.body; + while(expr.expression instanceof Cola.AST_Call || expr.expression instanceof Cola.AST_PropAccess){ + if(expr.expression instanceof Cola.AST_CondAccess){ + expr.expression = new Cola.AST_Dot(expr.expression); + break; + } + expr = expr.expression; + } + } + node = new Cola.AST_If({ + condition : Cola.Constructions.IsntSet(props), + body : node + }); + } else + + /* + alert(a.b?.c()); + + to + + alert(typeof a.b === "undefined" && a.b === null ? undefined : a.b.c()); + + */ + if(props = Cola.ContainCondAccess(node)){ + if(node instanceof Cola.AST_CondAccess) node = new Cola.AST_Dot(node); + else { + var expr = node; + while(expr.expression instanceof Cola.AST_Call || expr.expression instanceof Cola.AST_PropAccess){ + if(expr.expression instanceof Cola.AST_CondAccess){ + expr.expression = new Cola.AST_Dot(expr.expression); + break; + } + expr = expr.expression; + } + } + + node = new Cola.AST_Conditional({ + condition : Cola.Constructions.IsntSet(props), + consequent : new Cola.AST_Undefined, + alternative : node + }); + } else + + /* + a.b?.c = 123; + + to + + 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))){ + if(node.body.left instanceof Cola.AST_CondAccess) node.body.left = new Cola.AST_Dot(node.body.left); + else { + var expr = node.body.left; + while(expr.expression instanceof Cola.AST_Call || expr.expression instanceof Cola.AST_PropAccess){ + if(expr.expression instanceof Cola.AST_CondAccess){ + expr.expression = new Cola.AST_Dot(expr.expression); + break; + } + expr = expr.expression; + } + } + + node = new Cola.AST_If({ + condition : Cola.Constructions.IsntSet(props), + body : node + }); + } else + + /* + alert(a.b?.c = 123); + + to + + alert(typeof a.b === "undefined" && a.b === null ? undefined : a.b?.c = 123); + + */ + if(node instanceof Cola.AST_Assign && (props = Cola.ContainCondAccess(node.left))){ + if(node.left instanceof Cola.AST_CondAccess) node.left = new Cola.AST_Dot(node.left); + else { + var expr = node.left; + while(expr.expression instanceof Cola.AST_Call || expr.expression instanceof Cola.AST_PropAccess){ + if(expr.expression instanceof Cola.AST_CondAccess){ + expr.expression = new Cola.AST_Dot(expr.expression); + break; + } + expr = expr.expression; + } + } + + node = new Cola.AST_Conditional({ + condition : Cola.Constructions.IsntSet(props), + consequent : new Cola.AST_Undefined, + alternative : node + }); } else /* @@ -1457,7 +1560,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ var skiped = false, k = 0, is_arrayt = def instanceof Cola.AST_Array || def instanceof Cola.AST_ArrayTemplate, _ = is_arrayt ? def.elements : def.properties; _.forEach( is_arrayt ? function(el, j){ - if((el instanceof Cola.AST_SymbolRef || el instanceof Cola.AST_PropAccess && el.splated){ + if((el instanceof Cola.AST_SymbolRef || el instanceof Cola.AST_PropAccess) && el.splated){ skiped = true; defs.push(new Cola.AST_VarDef({ type : "int",