From 3a8f96a40ef84e1b275fc116353edcf993725209 Mon Sep 17 00:00:00 2001 From: Onoshko Dan Date: Mon, 19 May 2014 16:58:17 +0700 Subject: [PATCH] `switch` done --- README.md | 10 +-- lib/ast.js | 13 +++- lib/index.html | 23 ++++++- lib/parse.js | 46 ++++++++++---- lib/std.js | 2 +- lib/translate.js | 162 +++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 219 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 64179da3..cb8e930d 100644 --- a/README.md +++ b/README.md @@ -104,12 +104,12 @@ ColaScript is a language that compiles in JavaScript. This language is similar t ## Expressions -- `switch` assignment +- `switch` assignment, status: done! String weather = switch(temperature){ - case -10: 'cold'; - case 20: 'normal'; - case 35: 'hot'; + when -10: 'cold'; + when 20: 'normal'; + when 35: 'hot'; }; @@ -314,4 +314,4 @@ ColaScript is a language that compiles in JavaScript. This language is similar t ### Statistic - 33 features ( without classes ) -- 29 done +- 30 done diff --git a/lib/ast.js b/lib/ast.js index eed6b658..c83b7fff 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -505,6 +505,10 @@ Cola.AST_Case = Cola.DEFNODE("Case", "expression", { } }, Cola.AST_SwitchBranch); +Cola.AST_When = Cola.DEFNODE("When", null, { + $documentation: "A `when` switch branch" +}, Cola.AST_Case); + /* -----[ EXCEPTIONS ]----- */ Cola.AST_Try = Cola.DEFNODE("Try", "bcatch bfinally", { @@ -915,12 +919,17 @@ Cola.AST_String = Cola.DEFNODE("String", "value", { } }, Cola.AST_Constant); -Cola.AST_StringTemplate = Cola.DEFNODE("StringTemplate", null, { +Cola.AST_StringTemplate = Cola.DEFNODE("StringTemplate", "body", { $documentation: "A string template", $propdoc: { body: "[AST_Statement*] the contents of this string template" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + Cola.walk_body(this, visitor); + }); } -}, Cola.AST_Block); +}, Cola.AST_Statement); Cola.AST_Number = Cola.DEFNODE("Number", "value", { $documentation: "A number literal", diff --git a/lib/index.html b/lib/index.html index 7d889dd7..53ea0a92 100644 --- a/lib/index.html +++ b/lib/index.html @@ -26,10 +26,17 @@ height: 100%; } + .triple textarea { + width: 33.333%; + } + + .double textarea { + width: 50%; + } + textarea { -moz-box-sizing: border-box; - box-sizing: border-box; - width: 33.333%; + box-sizing: border-box; height: 100%; font-size: 15px; float: left; @@ -75,7 +82,7 @@ - + + + @@ -212,12 +221,19 @@ main(); resultArea = document.getElementById("result"), isjs = document.getElementById("is_js"), mainbinding = document.getElementById("main_binding"), + compressed = document.getElementById("compressed"), source; if(!localStorage.source) localStorage.source = source = sourceArea.value; else sourceArea.value = source = localStorage.source; isjs.checked = localStorage.isjs == "t"; mainbinding.checked = localStorage.mainbinding == "t"; + compressed.checked = localStorage.compressed == "t"; + + function changeClass(){ + document.body.className = compressed.checked ? "triple" : "double"; + localStorage.compressed = compressed.checked ? "t" : "f"; + } function compile(){ source = sourceArea.value; @@ -280,5 +296,6 @@ main(); } compile(); + changeClass(); diff --git a/lib/parse.js b/lib/parse.js index db9d7e89..d3ea346e 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -49,7 +49,7 @@ !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', '') + ' clone isset is isnt class singleton injector'; +Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' when clone isset is isnt class singleton injector'; Cola.KEYWORDS_ATOM = 'false null true'; Cola.cKEYWORDS_ATOM = Cola.KEYWORDS_ATOM + ' on yes off no'; @@ -59,6 +59,7 @@ Cola.cRESERVED_WORDS = Cola.RESERVED_WORDS.replace(' class', '') + " " + Cola.cK 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.KEYWORDS = Cola.makePredicate(Cola.KEYWORDS); Cola.cKEYWORDS = Cola.makePredicate(Cola.cKEYWORDS); @@ -70,6 +71,7 @@ Cola.KEYWORDS_ATOM = Cola.makePredicate(Cola.KEYWORDS_ATOM); Cola.cKEYWORDS_ATOM = Cola.makePredicate(Cola.cKEYWORDS_ATOM); Cola.KEYWORDS_BEFORE_EXPRESSION = Cola.makePredicate(Cola.KEYWORDS_BEFORE_EXPRESSION); +Cola.cKEYWORDS_BEFORE_EXPRESSION = Cola.makePredicate(Cola.cKEYWORDS_BEFORE_EXPRESSION); Cola.OPERATOR_CHARS = Cola.makePredicate(Cola.characters("+-*&%=<>!?|~^")); @@ -271,11 +273,13 @@ Cola.Tokenizer = function ($TEXT, filename, is_js, html5_comments) { this.KEYWORDS_ATOM = Cola.KEYWORDS_ATOM; this.OPERATORS = Cola.OPERATORS; this.UNARY_POSTFIX = Cola.UNARY_POSTFIX; + this.KEYWORDS_BEFORE_EXPRESSION = Cola.KEYWORDS_BEFORE_EXPRESSION; } else { this.KEYWORDS = Cola.cKEYWORDS; this.KEYWORDS_ATOM = Cola.cKEYWORDS_ATOM; this.OPERATORS = Cola.cOPERATORS; this.UNARY_POSTFIX = Cola.cUNARY_POSTFIX; + this.KEYWORDS_BEFORE_EXPRESSION = Cola.cKEYWORDS_BEFORE_EXPRESSION; } this.is_js = !!is_js; @@ -351,7 +355,7 @@ Cola.Tokenizer.prototype.start_token = function () { Cola.Tokenizer.prototype.token = function (type, value, is_comment) { this.S.regex_allowed = ((type == "operator" && !this.UNARY_POSTFIX(value)) || - (type == "keyword" && Cola.KEYWORDS_BEFORE_EXPRESSION(value)) || + (type == "keyword" && this.KEYWORDS_BEFORE_EXPRESSION(value)) || (type == "punc" && Cola.PUNC_BEFORE_EXPRESSION(value))); this.prev_was_dot = (type == "punc" && value == "."); var ret = { @@ -930,7 +934,7 @@ Cola.Parser.prototype.prev = function () { }; Cola.Parser.prototype.croak = function (msg, line, col, pos) { - var ctx = this.S.input.context(); + var ctx = this.S ? this.S.input.context() : {}; Cola.js_error(msg, ctx.filename, line != null ? line : ctx.tokline, @@ -939,7 +943,7 @@ Cola.Parser.prototype.croak = function (msg, line, col, pos) { }; Cola.Parser.prototype.token_error = function (token, msg) { - this.croak(msg, token.line, token.col); + this.croak(msg, token.line, token.col, token.pos); }; Cola.Parser.prototype.unexpected = function (token) { @@ -1086,14 +1090,14 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "do": return new Cola.AST_Do({ - body : this.in_loop(function(){ return _this.statement() }), + body : this.in_loop(function(){ return this.statement() }), condition : (this.expect_token("keyword", "while"), tmp = this.parenthesised(), this.semicolon(), tmp) }); case "while": return new Cola.AST_While({ condition : this.parenthesised(), - body : this.in_loop(function(){ return _this.statement() }) + body : this.in_loop(function(){ return this.statement() }) }); case "for": @@ -1118,8 +1122,8 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "switch": return new Cola.AST_Switch({ - expression : this.parenthesised(), - body : this.in_loop(switch_body_) + expression : this.is('punc', '{') && !this.is_js ? new Cola.AST_Noop() : this.parenthesised(), + body : this.in_loop(this.switch_body_) }); case "throw": @@ -1229,7 +1233,7 @@ Cola.Parser.prototype.regular_for = function (init) { init : init, condition : test, step : step, - body : this.in_loop(function(){ return _this.statement() }) + body : this.in_loop(function(){ return this.statement() }) }); }; @@ -1242,7 +1246,7 @@ Cola.Parser.prototype.for_in = function (init) { init : init, name : lhs, object : obj, - body : this.in_loop(function(){ return _this.statement() }) + body : this.in_loop(function(){ return this.statement() }) }); }; @@ -1353,10 +1357,10 @@ Cola.Parser.prototype.switch_body_ = function () { var a = [], cur = null, branch = null, tmp; while (!this.is("punc", "}")) { if (this.is("eof")) this.unexpected(); - if (this.is("keyword", "case")) { + if (this.is("keyword", "case") || !this.is_js && this.is("keyword", "when")) { if (branch) branch.end = this.prev(); cur = []; - branch = new Cola.AST_Case({ + branch = new (!this.is_js && this.is("keyword", "when") ? Cola.AST_When : Cola.AST_Case)({ start : (tmp = this.S.token, this.next(), tmp), expression : this.expression(true), body : cur @@ -1638,6 +1642,22 @@ Cola.Parser.prototype.expr_atom = function(allow_calls) { func.end = this.prev(); return this.subscripts(func, allow_calls); } + if (this.is("keyword", "switch") && !this.is_js) { + this.next(); + var swtch = { + start : start, + expression : this.is('punc', '{') ? new Cola.AST_Noop() : this.parenthesised(), + body : this.in_loop(this.switch_body_), + end : this.prev() + }, _this = this; + + swtch.body.forEach(function(branch){ + if (branch.body.length > 1) _this.unexpected(branch.body[1].start); + else if (branch.body[0] && branch.body[0] instanceof Cola.AST_Var) _this.unexpected(branch.body[0].start); + }); + + return new Cola.AST_Switch(swtch); + } if (Cola.ATOMIC_START_TOKEN[this.S.token.type]) { return this.subscripts(this.as_atom_node(), allow_calls); } @@ -2125,7 +2145,7 @@ Cola.Parser.prototype.expression = function(commas, no_in, in_dd) { Cola.Parser.prototype.in_loop = function (cont) { ++this.S.in_loop; - var ret = cont(); + var ret = cont.call(this); --this.S.in_loop; return ret; }; diff --git a/lib/std.js b/lib/std.js index 4941d9cb..b5db751b 100644 --- a/lib/std.js +++ b/lib/std.js @@ -66,7 +66,7 @@ Cola.$_cola_clone = function $_cola_clone(_item){ var result, types = [ Number, String, Boolean ]; for (var i in types) if(types.hasOwnProperty(i) && _item instanceof types[i]) - return type( _item ); + return types[i]( _item ); if (_item.__proto__ === Array.prototype) { result = []; diff --git a/lib/translate.js b/lib/translate.js index 76a5b53b..32f0e0bf 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -760,7 +760,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ ; to - fix: this and arguments objects + (function($_cola_expr, arguments){ $_cola_expr[0] = yes; $_cola_expr.foo = bar; @@ -1043,6 +1043,8 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ if(node.body instanceof Cola.AST_Assign && (node.body.left instanceof Cola.AST_ArrayTemplate || node.body.left instanceof Cola.AST_ObjectTemplate || (node.body.left instanceof Cola.AST_Array || node.body.left instanceof Cola.AST_Object) && node.body.left.template) && node instanceof Cola.AST_SimpleStatement){ + if(node.body.left.vardef && node.body.operator != "=") Cola.Parser.prototype.unexpected.call(Cola.Parser.prototype, node.body.left.start); + node = node.body; var defs = []; @@ -1121,7 +1123,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ })); el.splated = undefined; defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el, right : new Cola.AST_Conditional({ condition : new Cola.AST_Binary({ @@ -1182,7 +1184,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ defs.push(el); } else if(el instanceof Cola.AST_SymbolRef || el instanceof Cola.AST_Sub || el instanceof Cola.AST_Dot) defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el, right : new Cola.AST_Sub({ expression : symbol, @@ -1241,7 +1243,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ }) })); else if(el.value instanceof Cola.AST_SymbolRef || el.value instanceof Cola.AST_Sub || el.value instanceof Cola.AST_Dot || el.value instanceof Cola.AST_Noop && el.start.type == "name") defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el.value instanceof Cola.AST_Noop ? new Cola.AST_SymbolRef({ name : el.key }) : el.value, right : el.start.type == "name" ? new Cola.AST_Dot({ @@ -1306,8 +1308,8 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ to func((function($_cola_expr, arguments){ - aname = obj.a; - bname = obj.b; + aname = $_cola_expr.a; + bname = $_cola_expr.b; return $_cola_expr; }).call(this, obj, arguments)) @@ -1315,13 +1317,11 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ */ if(node instanceof Cola.AST_Assign && (node.left instanceof Cola.AST_ArrayTemplate || node.left instanceof Cola.AST_ObjectTemplate || (node.left instanceof Cola.AST_Array || node.left instanceof Cola.AST_Object) && node.left.template)){ - if(node.vardef) Cola.Parser.prototype.unexpected.call({}, node.start); + if(node.left.vardef) Cola.Parser.prototype.unexpected.call(Cola.Parser.prototype, node.start); var defs = []; - var Symbol = node.right instanceof Cola.AST_SymbolRef - ? node.right - : new Cola.AST_SymbolRef({ name : "$_cola_expr" }); + var Symbol = new Cola.AST_SymbolRef({ name : "$_cola_expr" }); (function _rec(def, symbol, uid){ var skiped = false, k = 0, is_arrayt = def instanceof Cola.AST_Array || def instanceof Cola.AST_ArrayTemplate, _ = is_arrayt ? def.elements : def.properties; @@ -1336,7 +1336,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ })); el.splated = undefined; defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el, right : new Cola.AST_Conditional({ condition : new Cola.AST_Binary({ @@ -1384,7 +1384,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ })); } else if(el instanceof Cola.AST_SymbolRef || el instanceof Cola.AST_Sub || el instanceof Cola.AST_Dot) defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el, right : new Cola.AST_Sub({ expression : symbol, @@ -1428,7 +1428,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ } : function(el, j){ if(el.value instanceof Cola.AST_SymbolRef || el.value instanceof Cola.AST_Sub || el.value instanceof Cola.AST_Dot || el.value instanceof Cola.AST_Noop && el.start.type == "name") defs.push(new Cola.AST_Assign({ - operator : "=", + operator : node.operator, left : el.value instanceof Cola.AST_Noop ? new Cola.AST_SymbolRef({ name : el.key }) : el.value, right : el.start.type == "name" ? new Cola.AST_Dot({ @@ -1500,6 +1500,10 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ if(i == defs.length - 1) _(false); }); + props.body.push(new Cola.AST_Return({ + value : new Cola.AST_SymbolRef({ name : "$_cola_expr" }) + })); + props = { expression : new Cola.AST_Function(props), property : "call" @@ -1513,6 +1517,138 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ }); } else + /* + var o = switch { + case b < 10: f(b); + }; + + to + + var o = (function(){ + switch { + case b < 10: return f(b); + } + }).apply(this, arguments); + + */ + if(node instanceof Cola.AST_Switch && !(parent instanceof Cola.AST_Block)){ + node.body.forEach(function(branch){ + if(!branch.body.length) return; + branch.body[0] = new Cola.AST_Return({ + value : branch.body[0] instanceof Cola.AST_SimpleStatement ? branch.body[0].body : branch.body[0] + }); + }); + + props = { + expression : new Cola.AST_Function({ + type : "dynamic", + body : [node], + argnames : [] + }), + property : "apply" + } + + node = new Cola.AST_Call({ + args : [new Cola.AST_SymbolRef({ name : "this" }), new Cola.AST_SymbolRef({ name : "arguments" })], + expression : new Cola.AST_Dot(props) + }); + } else + + /* + switch { + when b < 10: f(b); + case b < 15: y(b); + } + + to + + switch (false) { + when !(b < 10): f(b); + case !(b < 15): y(b); + } + + */ + if(node instanceof Cola.AST_Switch && node.expression instanceof Cola.AST_Noop){ + node.expression = new Cola.AST_False; + + node.body.forEach(function(branch){ + if(branch instanceof Cola.AST_Default) return; + + if(branch.expression instanceof Cola.AST_Seq){ + var seq = branch.expression; + + while(true){ + seq.car = new Cola.AST_UnaryPrefix({ + operator : '!', + expression : seq.car + }); + if(seq.cdr instanceof Cola.AST_Seq) seq = seq.cdr; + else { + seq.cdr = new Cola.AST_UnaryPrefix({ + operator : '!', + expression : seq.cdr + }); + break; + } + } + + return; + } + + branch.expression = new Cola.AST_UnaryPrefix({ + operator : '!', + expression : branch.expression + }); + }); + } else + + /* + switch { + when b < 10: f(b); + } + + to + + switch { + case b < 10: + f(b); + break; + } + + */ + if(node instanceof Cola.AST_When){ + node = new Cola.AST_Case(node); + node.body.push(new Cola.AST_Break); + } + + /* + switch (g) { + case 11, 22: f(g); + } + + to + + switch (g) { + case 11: case 22: f(g); + } + + */ + if(node instanceof Cola.AST_SwitchBranch && !(node instanceof Cola.AST_Default) && node.expression instanceof Cola.AST_Seq){ + var branches = [], seq = node.expression; + + while(true){ + branches.push(new node.CTOR({ expression : seq.car, body : [] })); + if(seq.cdr instanceof Cola.AST_Seq) seq = seq.cdr; + else { + node.expression = seq.cdr + branches.push(node); + break; + } + } + + node = branches; + } + /* "test @a @{b} {{c}}"