From fb8ccc644ffb29b1c980b2c1343b2ca06872b882 Mon Sep 17 00:00:00 2001 From: Onoshko Dan Date: Thu, 22 May 2014 01:01:32 +0700 Subject: [PATCH] `@require` and `@include` done. --- README.md | 2 +- bin/cola | 11 ++++- demo.cola | 11 ++--- lib/ast.js | 2 +- lib/parse.js | 38 ++++++++++++---- lib/transform.js | 5 +-- lib/translate.js | 111 +++++++++++++++++++++++++++++++++++++++++++++-- lib/utils.js | 10 +++++ tools/node.js | 12 ++++- 9 files changed, 174 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 27072864..59086836 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ ColaScript is a language that compiles in JavaScript. This language is similar t ### Compiler - `@require` - @require "./library/jquery.js", "./library/underscore.js" + @require "./library/jquery.js" "./library/underscore.js" - `@use` diff --git a/bin/cola b/bin/cola index 7d234ff9..a0eb8697 100755 --- a/bin/cola +++ b/bin/cola @@ -292,7 +292,14 @@ async.eachLimit(files, 1, function (file, cb) { is_js : ARGS.j }); - if (!ARGS.j) TOPLEVEL = TOPLEVEL.toJavaScript({ main_bindiing: !ARGS.n }); + if (!ARGS.j) TOPLEVEL = TOPLEVEL.toJavaScript({ + main_binding: !ARGS.n, + parser: { + filename : file, + expression : ARGS.expr, + is_js : ARGS.j + } + }); } catch(ex) { if (ex instanceof Cola.JS_Parse_Error) { sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col); @@ -416,7 +423,7 @@ function getOptions(x, constants) { var ast; try { ast = Cola.parse(x, { expression: true, is_js: ARGS.j }); - if (!ARGS.j) ast = ast.toJavaScript({ main_bindiing: !ARGS.n }); + if (!ARGS.j) ast = ast.toJavaScript({ main_binding: !ARGS.n, parser: { expression: true, is_js: ARGS.j } }); } catch(ex) { if (ex instanceof Cola.JS_Parse_Error) { sys.error("Error parsing arguments in: " + x); diff --git a/demo.cola b/demo.cola index c51d039e..a67459de 100644 --- a/demo.cola +++ b/demo.cola @@ -1,7 +1,9 @@ // `main` functions may binding to a onload event // Functions can defined without `function` keyword, with and without `type` -main(){ +@require './sugar.min.js' +main(){ + @require './sugar.min.js' // Unary operators // Two variants of `isset` operator. Which is better? bool _seted = true, _empty; @@ -103,12 +105,7 @@ main(){ Object Profile(String firstName, String secondName, String country = "Russia", Array skills..., age:, petName: "Tux"){ skills.forEach((val) => val == "JavaScript" && console.log("JavaScript - It Awesome!")); return { - firstName : firstName, - secondName : secondName, - age : age, - country : country, - skills : skills, - petName : petName + firstName, secondName, age, country, skills, petName }; } diff --git a/lib/ast.js b/lib/ast.js index 220fe279..f9d4c268 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -462,7 +462,7 @@ Cola.AST_Continue = Cola.DEFNODE("Continue", null, { /* -----[ IF ]----- */ -Cola.AST_If = Cola.DEFNODE("If", "condition alternative", { +Cola.AST_If = Cola.DEFNODE("If", "condition alternative inline", { $documentation: "A `if` statement", $propdoc: { condition: "[AST_Node] the `if` condition", diff --git a/lib/parse.js b/lib/parse.js index 3343d403..a2083683 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1356,16 +1356,18 @@ Cola.Parser.prototype.function_ = function(ctor, type) { }); }; -Cola.Parser.prototype.if_ = function () { - var cond = this.parenthesised(), body = this.statement(), belse = null; +Cola.Parser.prototype.if_ = function (inline) { + var cond = this.parenthesised(), body = (inline && !this.is_js ? this.expression(true) : this.statement()), belse = null; if (this.is("keyword", "else")) { this.next(); - belse = this.statement(); + if (inline && !this.is_js) belse = this.is("keyword", "if") ? (this.next(), this.if_(true)) : this.expression(true); + else belse = this.statement(); } return new Cola.AST_If({ condition : cond, body : body, - alternative : belse + alternative : belse, + inline : inline }); }; @@ -1670,6 +1672,25 @@ Cola.Parser.prototype.expr_atom = function(allow_calls) { func.end = this.prev(); return this.subscripts(func, allow_calls); } + if (this.is("keyword", "if") && !this.is_js) { + this.next(); + var f = this.if_(true), s = f; + + /*while (true) { + if (s.body instanceof Cola.AST_BlockStatement && s.body.body.length != 1 || + !(s.body instanceof Cola.AST_BlockStatement) && !(s.body instanceof Cola.AST_SimpleStatement)) this.unexpected(s.body.start); + + if (s.alternative instanceof Cola.AST_If) s = f; + else if (s.alternative == null) break; + else { + if (s.alternative instanceof Cola.AST_BlockStatement && s.alternative.body.length != 1 || + !(s.alternative instanceof Cola.AST_BlockStatement) && !(s.alternative instanceof Cola.AST_SimpleStatement)) this.unexpected(s.alternative.start); + break; + } + }*/ + + return f; + } if (this.is("keyword", "switch") && !this.is_js) { this.next(); var swtch = { @@ -1680,8 +1701,7 @@ Cola.Parser.prototype.expr_atom = function(allow_calls) { }, _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); + if (branch.body.length != 1 || !(branch.body[0] instanceof Cola.AST_SimpleStatement)) _this.unexpected(branch.start); }); return new Cola.AST_Switch(swtch); @@ -1863,9 +1883,9 @@ Cola.Parser.prototype.object_ = Cola.Parser.embed_tokens(function(is_template, i } } if (!this.is_js && !this.is("punc",":")) { - if (is_object === true) this.unexpected(); - is_object = false; - val = new Cola.AST_Noop(); + //if (is_object === true) this.unexpected(); + //is_object = false; + val = new Cola.AST_SymbolRef({ name : name }); } else { this.expect(":"); diff --git a/lib/transform.js b/lib/transform.js index 978cce8d..ab4b4e80 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -83,9 +83,8 @@ Cola.TreeTransformer.prototype.__proto__ = new Cola.TreeWalker; var r = node.transform(tw, true); - r = r instanceof Array - ? Cola.MAP.splice(r) - : r; + if (!r) r = Cola.MAP.continue(); + else if (r instanceof Array) r = Cola.MAP.splice(r); return r; }); diff --git a/lib/translate.js b/lib/translate.js index 32f0e0bf..c2ddfa8c 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -43,10 +43,18 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ options = Cola.defaults(options, { main_binding : true, - main_event : 'DOMContentLoaded' + main_event : 'DOMContentLoaded', + parser : {}, + std : true }); - var $_cola_ast = Cola.parse(Cola.$_cola, { is_js : true }), $_cola_hash = {}, _this, + var $_cola_ast = Cola.parse(Cola.$_cola, { is_js : true }), + $_cola_hash = {}, + + _this, + + required = [], required_hash = {}, + tt = new Cola.TreeTransformer(function(node, descend, in_list){ var newNode, props = {}, parent = this.parent(); node = node.clone(); @@ -1517,6 +1525,50 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ }); } else + /* + var o = if(a) 'start'; else 'finish'; + + to + + var o = (function(){ + if(a) return 'start'; else return 'finish'; + }).apply(this, arguments); + + */ + if(node instanceof Cola.AST_If && node.inline && !(parent instanceof Cola.AST_If && parent.inline)){ + var s = node; + + while (true) { + s.inline = false; + s.body = new Cola.AST_Return({ + value : s.body + }); + + if (s.alternative instanceof Cola.AST_If) s = s.alternative; + else if (s.alternative == null) break; + else { + s.alternative = new Cola.AST_Return({ + value : s.alternative + }); + break; + } + } + + 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 + /* var o = switch { case b < 10: f(b); @@ -1535,7 +1587,7 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ 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] + value : branch.body[0].body }); }); @@ -1691,6 +1743,55 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ */ if(node instanceof Cola.AST_RegExp && (node.value.indexOf('\n') != -1 || /\/[\w]*x[\w]*$/.test(node.value))){ node.value = node.value.replace(/[\r\n\s]/g,'').replace(/(\/[\w]*)x([\w]*$)/, '$1$2'); + } else + + if(node instanceof Cola.AST_Command && node.name == "include"){ + var included = []; + + node.args.forEach(function(file){ + if (file.type != "string") Cola.Parser.prototype.unexpected.call(Cola.Parser.prototype, file); + else file = file.value; + + options.parser.is_js = /\.js$/.test(file); + options.parser.filename = file; + + var tl = Cola.parse(Cola.getSource(file), options.parser); + if (options.parser.is_js) tl = tl.toJavaScript({ + main_binding : options.main_binding, + main_event : options.main_event, + parser : options.parser, + std : false + }); + + included = included.concat(tl.body); + }); + + return included; + } else + + if(node instanceof Cola.AST_Command && node.name == "require"){ + node.args.forEach(function(file){ + if (file.type != "string") Cola.Parser.prototype.unexpected.call(Cola.Parser.prototype, file); + else file = file.value; + + if (required_hash[file]) return; + required_hash[file] = true; + + options.parser.is_js = /\.js$/.test(file); + options.parser.filename = file; + + var tl = Cola.parse(Cola.getSource(file), options.parser); + if (options.parser.is_js) tl = tl.toJavaScript({ + main_binding : options.main_binding, + main_event : options.main_event, + parser : options.parser, + std : false + }); + + required = required.concat(tl.body); + }); + + return false; } if(node instanceof Array){ @@ -1705,7 +1806,9 @@ Cola.AST_Toplevel.prototype.toJavaScript = function(options){ _this = this.transform(tt); - for(var i in $_cola_hash) if($_cola_hash.hasOwnProperty(i)) + _this.body = required.concat(_this.body); + + if(options.std) for(var i in $_cola_hash) if($_cola_hash.hasOwnProperty(i)) _this.body.unshift($_cola_ast.body[i]); return _this; diff --git a/lib/utils.js b/lib/utils.js index 5276ddbd..d4302fba 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -122,6 +122,7 @@ Cola.MAP = (function(){ var val = f(a[i], i); var is_last = val instanceof Last; if (is_last) val = val.v; + if (val instanceof Continue) return false; if (val instanceof AtTop) { val = val.v; if (val instanceof Splice) { @@ -156,10 +157,12 @@ Cola.MAP = (function(){ MAP.at_top = function(val) { return new AtTop(val) }; MAP.splice = function(val) { return new Splice(val) }; MAP.last = function(val) { return new Last(val) }; + MAP.continue = function() { return new Continue() }; var skip = MAP.skip = {}; function AtTop(val) { this.v = val }; function Splice(val) { this.v = val }; function Last(val) { this.v = val }; + function Continue() { }; return MAP; })(); @@ -340,4 +343,11 @@ Cola.clone = function (item) { Cola.nodeCompare = function(_a, _b){ return _a.__proto__ === _b.__proto__ && _a.start.pos === _b.start.pos && _a.end.pos === _b.end.pos; +}; + +Cola.getSource = function(url){ + var xhr = new XMLHttpRequest; + xhr.open('GET', url, false); + xhr.send(); + return xhr.responseText; }; \ No newline at end of file diff --git a/tools/node.js b/tools/node.js index 440c2ded..d1d0d9bd 100644 --- a/tools/node.js +++ b/tools/node.js @@ -40,6 +40,10 @@ Cola.AST_Node.warn_function = function(txt) { sys.error("WARN: " + txt); }; +Cola.getSource = function(file) { + return fs.readFileSync(path.join(process.cwd(), file), "utf8"); +}; + // XXX: perhaps we shouldn't export everything but heck, I'm lazy. for (var i in Cola) { if (Cola.hasOwnProperty(i)) { @@ -83,7 +87,13 @@ exports.minify = function(files, options) { is_js : options.is_js }); - if (!options.is_js) toplevel = toplevel.toJavaScript({ main_binding : options.main_binding }); + if (!options.is_js) toplevel = toplevel.toJavaScript({ + main_binding: options.main_binding, + parser: { + filename: options.fromString ? "?" : file, + is_js : options.is_js + } + }); }); }