diff --git a/README.md b/README.md index cb8e930d..27072864 100644 --- a/README.md +++ b/README.md @@ -91,8 +91,9 @@ ColaScript is a language that compiles in JavaScript. This language is similar t - `@use` - @use strict, typing + @use strict @use asmjs + @use closure - `@if @end_if @else` diff --git a/bin/uglifyjs b/bin/cola similarity index 86% rename from bin/uglifyjs rename to bin/cola index 3a3318b2..7d234ff9 100755 --- a/bin/uglifyjs +++ b/bin/cola @@ -3,7 +3,7 @@ "use strict"; -var UglifyJS = require("../tools/node"); +var Cola = require("../tools/node"); var sys = require("util"); var optimist = require("optimist"); var fs = require("fs"); @@ -16,19 +16,21 @@ Use a single dash to read input from the standard input.\ \n\n\ NOTE: by default there is no mangling/compression.\n\ Without [options] it will simply parse input files and dump the AST\n\ -with whitespace and comments discarded. To achieve compression and\n\ +with whitespace and comments discarded. To achieve compression and\n\ mangling you need to use `-c` and `-m`.\ ") + .describe("j", "Work with JavaScript (by default Cola will expect ColaScript).") .describe("source-map", "Specify an output file where to generate source map.") .describe("source-map-root", "The path to the original source to be included in the source map.") .describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.") .describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.") .describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.") - .describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).") + .describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default Cola will try to be IE-proof).") .describe("expr", "Parse a single expression, rather than a program (for parsing JSON)") + .describe("n", "Disable `main` binding.") .describe("p", "Skip prefix for original filenames that appear in source maps. \ For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \ -You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \ +You can also specify -p relative, which will make Cola figure out itself the relative paths between original sources, \ the source map and the output file.") .describe("o", "Output file (default STDOUT).") .describe("b", "Beautify output/specify output options.") @@ -56,15 +58,18 @@ parsed, but the source map will adjust for its presence.") .describe("stats", "Display operations run time on STDERR.") .describe("acorn", "Use Acorn for parsing.") .describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).") - .describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)") + .describe("self", "Build itself (Cola) as a library (implies --wrap=Cola --export-all)") .describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \ You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.") - .describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.") + .describe("export-all", "Only used when --wrap, this tells Cola to add code to automatically export all globals.") .describe("lint", "Display some scope warnings") .describe("v", "Verbose") .describe("V", "Print version number and exit.") .describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.") + .alias("j", "js") + .alias("j", "javascript") + .alias("n", "no-main-binding") .alias("p", "prefix") .alias("o", "output") .alias("v", "verbose") @@ -88,7 +93,9 @@ You need to pass an argument to this option to specify the name that your module .string("wrap") .string("p") + .boolean("j") .boolean("expr") + .boolean("n") .boolean("source-map-include-sources") .boolean("screw-ie8") .boolean("export-all") @@ -109,7 +116,7 @@ You need to pass an argument to this option to specify the name that your module normalize(ARGS); if (ARGS.noerr) { - UglifyJS.DefaultsError.croak = function(msg, defs) { + Cola.DefaultsError.croak = function(msg, defs) { sys.error("WARN: " + msg); }; } @@ -121,7 +128,7 @@ if (ARGS.version || ARGS.V) { } if (ARGS.ast_help) { - var desc = UglifyJS.describe_ast(); + var desc = Cola.describe_ast(); sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2)); process.exit(0); } @@ -139,6 +146,10 @@ var COMPRESS = getOptions("c", true); var MANGLE = getOptions("m", true); var BEAUTIFY = getOptions("b", true); +if (ARGS.j) { + if (COMPRESS) COMPRESS.is_js = getOptions("j"); +} + if (ARGS.d) { if (COMPRESS) COMPRESS.global_defs = getOptions("d"); } @@ -159,7 +170,7 @@ if (ARGS.screw_ie8) { } if (BEAUTIFY) - UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY); + Cola.merge(OUTPUT_OPTIONS, BEAUTIFY); if (ARGS.comments) { if (/^\//.test(ARGS.comments)) { @@ -184,8 +195,8 @@ if (ARGS.self) { if (files.length > 0) { sys.error("WARN: Ignoring input files since --self was passed"); } - files = UglifyJS.FILES; - if (!ARGS.wrap) ARGS.wrap = "UglifyJS"; + files = Cola.FILES; + if (!ARGS.wrap) ARGS.wrap = "Cola"; ARGS.export_all = true; } @@ -222,7 +233,7 @@ var TOPLEVEL = null; var P_RELATIVE = ARGS.p && ARGS.p == "relative"; var SOURCES_CONTENT = {}; -var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({ +var SOURCE_MAP = ARGS.source_map ? new Cola.SourceMap({ file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE, root: ARGS.source_map_root, orig: ORIG_MAP, @@ -231,10 +242,10 @@ var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({ OUTPUT_OPTIONS.source_map = SOURCE_MAP; try { - var output = UglifyJS.OutputStream(OUTPUT_OPTIONS); - var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS); + var output = new Cola.OutputStream(OUTPUT_OPTIONS); + var compressor = COMPRESS && new Cola.Compressor(COMPRESS); } catch(ex) { - if (ex instanceof UglifyJS.DefaultsError) { + if (ex instanceof Cola.DefaultsError) { sys.error(ex.msg); sys.error("Supported options:"); sys.error(sys.inspect(ex.defs)); @@ -274,13 +285,16 @@ async.eachLimit(files, 1, function (file, cb) { } else { try { - TOPLEVEL = UglifyJS.parse(code, { + TOPLEVEL = Cola.parse(code, { filename : file, toplevel : TOPLEVEL, expression : ARGS.expr, + is_js : ARGS.j }); + + if (!ARGS.j) TOPLEVEL = TOPLEVEL.toJavaScript({ main_bindiing: !ARGS.n }); } catch(ex) { - if (ex instanceof UglifyJS.JS_Parse_Error) { + if (ex instanceof Cola.JS_Parse_Error) { sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col); sys.error(ex.message); sys.error(ex.stack); @@ -294,7 +308,7 @@ async.eachLimit(files, 1, function (file, cb) { }); }, function () { if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){ - TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL); + TOPLEVEL = Cola.AST_Node.from_mozilla_ast(TOPLEVEL); }); if (ARGS.wrap) { @@ -373,11 +387,11 @@ async.eachLimit(files, 1, function (file, cb) { } if (ARGS.stats) { - sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", { + sys.error(Cola.string_template("Timing information (compressed {count} files):", { count: files.length })); for (var i in STATS) if (STATS.hasOwnProperty(i)) { - sys.error(UglifyJS.string_template("- {name}: {time}s", { + sys.error(Cola.string_template("- {name}: {time}s", { name: i, time: (STATS[i] / 1000).toFixed(3) })); @@ -401,16 +415,17 @@ function getOptions(x, constants) { if (x !== true) { var ast; try { - ast = UglifyJS.parse(x, { expression: true }); + ast = Cola.parse(x, { expression: true, is_js: ARGS.j }); + if (!ARGS.j) ast = ast.toJavaScript({ main_bindiing: !ARGS.n }); } catch(ex) { - if (ex instanceof UglifyJS.JS_Parse_Error) { + if (ex instanceof Cola.JS_Parse_Error) { sys.error("Error parsing arguments in: " + x); process.exit(1); } } - ast.walk(new UglifyJS.TreeWalker(function(node){ - if (node instanceof UglifyJS.AST_Seq) return; // descend - if (node instanceof UglifyJS.AST_Assign) { + ast.walk(new Cola.TreeWalker(function(node){ + if (node instanceof Cola.AST_Seq) return; // descend + if (node instanceof Cola.AST_Assign) { var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_"); var value = node.right; if (constants) @@ -418,7 +433,7 @@ function getOptions(x, constants) { ret[name] = value; return true; // no descend } - if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) { + if (node instanceof Cola.AST_Symbol || node instanceof Cola.AST_Binary) { var name = node.print_to_string({ beautify: false }).replace(/-/g, "_"); ret[name] = true; return true; // no descend diff --git a/demo.cola b/demo.cola index 5a6bbaa2..c51d039e 100644 --- a/demo.cola +++ b/demo.cola @@ -112,4 +112,4 @@ Object Profile(String firstName, String secondName, String country = "Russia", A }; } -main(); \ No newline at end of file +//main(); \ No newline at end of file diff --git a/lib/ast.js b/lib/ast.js index c83b7fff..220fe279 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -111,6 +111,14 @@ Cola.AST_Node = Cola.DEFNODE("Node", "start end", { } }, null); +Cola.AST_Command = Cola.DEFNODE("Command", "name args", { + $documentation: "Compiler-command statement", + $propdoc: { + name: "[string] Name of command", + args: "[AST_Node*] List of arguments" + } +}, Cola.AST_Node); + Cola.AST_Noop = Cola.DEFNODE("Noop", null, null, Cola.AST_Node); Cola.AST_Node.warn_function = null; diff --git a/lib/output.js b/lib/output.js index b1f88c47..5736de7c 100644 --- a/lib/output.js +++ b/lib/output.js @@ -288,6 +288,7 @@ Cola.OutputStream.prototype.add_mapping = function (token, name) { if(!this.options.source_map) return; try { + if (token && !token.file) console.log(token); if (token) this.options.source_map.add( token.file || "?", this.current_line, this.current_col, diff --git a/lib/parse.js b/lib/parse.js index d3ea346e..3343d403 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -531,7 +531,7 @@ Cola.Tokenizer.prototype.read_at = function (){ this.S.string.at[this.S.string.level].inside_at = true; this.S.string.at[this.S.string.level].balance = 1; return this.token("punc", "@" + this.next()); - } + } else return this.token("punc", "@"); this.parse_error('Unexpected character "@"'); } @@ -1047,7 +1047,35 @@ Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() { case "punc": switch (this.S.token.value) { + case "@": + if (!this.is_js && this.next_is("name")) + return new Cola.AST_Command({ + name : this.next().value, + args : (function(){ + var args = []; + while (!this.peek().nlb && !this.next_is('eof')){ + if (this.next_is('punc', '{')) { + this.next(); + args.push(new Cola.AST_BlockStatement({ + start : this.S.token, + body : this.block_(), + end : this.prev() + })); + } else + args.push(this.next()); + } + this.next(); + return args; + }).call(this) + }); + case "{": + if (this.is_js) return new Cola.AST_BlockStatement({ + start : this.S.token, + body : this.block_(), + end : this.prev() + }); + this.dumpS(); var balance = 0, is_object = false; this.next_until(function(){ @@ -1655,7 +1683,7 @@ Cola.Parser.prototype.expr_atom = function(allow_calls) { 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]) { diff --git a/lib/std.js b/lib/std.js index b5db751b..5856abd6 100644 --- a/lib/std.js +++ b/lib/std.js @@ -34,6 +34,8 @@ ***********************************************************************/ +!this.Cola && (this.Cola = {}); + Cola.$_cola_is = function $_cola_is(_object, _type){ return _object === _type || _type.prototype && (_object instanceof _type || _object.__proto__ === _type.prototype) || isNaN(_object) && isNaN(_type); } diff --git a/tools/node.js b/tools/node.js index bef296ea..440c2ded 100644 --- a/tools/node.js +++ b/tools/node.js @@ -3,17 +3,13 @@ var fs = require("fs"); var vm = require("vm"); var sys = require("util"); -var UglifyJS = vm.createContext({ - sys : sys, - console : console, - MOZ_SourceMap : require("source-map") -}); +var MOZ_SourceMap = require("source-map"); function load_global(file) { file = path.resolve(path.dirname(module.filename), file); try { var code = fs.readFileSync(file, "utf8"); - return vm.runInContext(code, UglifyJS, file); + return eval(code); } catch(ex) { // XXX: in case of a syntax error, the message is kinda // useless. (no location information). @@ -31,26 +27,28 @@ var FILES = exports.FILES = [ "../lib/output.js", "../lib/compress.js", "../lib/sourcemap.js", - "../lib/mozilla-ast.js" + "../lib/mozilla-ast.js", + "../lib/translate.js", + "../lib/std.js" ].map(function(file){ return path.join(path.dirname(fs.realpathSync(__filename)), file); }); FILES.forEach(load_global); -UglifyJS.AST_Node.warn_function = function(txt) { +Cola.AST_Node.warn_function = function(txt) { sys.error("WARN: " + txt); }; -// XXX: perhaps we shouldn't export everything but heck, I'm lazy. -for (var i in UglifyJS) { - if (UglifyJS.hasOwnProperty(i)) { - exports[i] = UglifyJS[i]; +// XXX: perhaps we shouldn't export everything but heck, I'm lazy. +for (var i in Cola) { + if (Cola.hasOwnProperty(i)) { + exports[i] = Cola[i]; } } exports.minify = function(files, options) { - options = UglifyJS.defaults(options, { + options = Cola.defaults(options, { spidermonkey : false, outSourceMap : null, sourceRoot : null, @@ -59,16 +57,18 @@ exports.minify = function(files, options) { warnings : false, mangle : {}, output : null, - compress : {} + compress : {}, + is_js : false, + main_binding : true }); - UglifyJS.base54.reset(); + Cola.base54.reset(); // 1. parse var toplevel = null, sourcesContent = {}; if (options.spidermonkey) { - toplevel = UglifyJS.AST_Node.from_mozilla_ast(files); + toplevel = Cola.AST_Node.from_mozilla_ast(files); } else { if (typeof files == "string") files = [ files ]; @@ -77,19 +77,22 @@ exports.minify = function(files, options) { ? file : fs.readFileSync(file, "utf8"); sourcesContent[file] = code; - toplevel = UglifyJS.parse(code, { + toplevel = Cola.parse(code, { filename: options.fromString ? "?" : file, - toplevel: toplevel + toplevel: toplevel, + is_js : options.is_js }); + + if (!options.is_js) toplevel = toplevel.toJavaScript({ main_binding : options.main_binding }); }); } // 2. compress if (options.compress) { - var compress = { warnings: options.warnings }; - UglifyJS.merge(compress, options.compress); + var compress = { warnings: options.warnings, is_js : options.is_js }; + Cola.merge(compress, options.compress); toplevel.figure_out_scope(); - var sq = UglifyJS.Compressor(compress); + var sq = new Cola.Compressor(compress); toplevel = toplevel.transform(sq); } @@ -107,7 +110,7 @@ exports.minify = function(files, options) { inMap = fs.readFileSync(options.inSourceMap, "utf8"); } if (options.outSourceMap) { - output.source_map = UglifyJS.SourceMap({ + output.source_map = new Cola.SourceMap({ file: options.outSourceMap, orig: inMap, root: options.sourceRoot @@ -122,9 +125,9 @@ exports.minify = function(files, options) { } if (options.output) { - UglifyJS.merge(output, options.output); + Cola.merge(output, options.output); } - var stream = UglifyJS.OutputStream(output); + var stream = new Cola.OutputStream(output); toplevel.print(stream); return { code : stream + "", @@ -143,11 +146,11 @@ exports.minify = function(files, options) { // if (ctor.SUBCLASSES.length > 0) ret.sub = sub; // return ret; // } -// return doitem(UglifyJS.AST_Node).sub; +// return doitem(Cola.AST_Node).sub; // } exports.describe_ast = function() { - var out = UglifyJS.OutputStream({ beautify: true }); + var out = new Cola.OutputStream({ beautify: true }); function doitem(ctor) { out.print("AST_" + ctor.TYPE); var props = ctor.SELF_PROPS.filter(function(prop){ @@ -177,6 +180,6 @@ exports.describe_ast = function() { }); } }; - doitem(UglifyJS.AST_Node); + doitem(Cola.AST_Node); return out + ""; };