LiveScript-1.5.0/000775 000000 000000 00000000000 12716147631 013571 5ustar00rootroot000000 000000 LiveScript-1.5.0/.editorconfig000664 000000 000000 00000000265 12716147631 016251 0ustar00rootroot000000 000000 [*] indent_style = space end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true [*.ls] indent_size = 4 [*.js] indent_size = 2 [Makefile] indent_style = tab LiveScript-1.5.0/.gitignore000664 000000 000000 00000000032 12716147631 015554 0ustar00rootroot000000 000000 t.* coverage node_modules LiveScript-1.5.0/.travis.yml000664 000000 000000 00000000044 12716147631 015700 0ustar00rootroot000000 000000 language: node_js node_js: - 0.10 LiveScript-1.5.0/CHANGELOG000664 000000 000000 00000000045 12716147631 015002 0ustar00rootroot000000 000000 See http://livescript.net/#changelog LiveScript-1.5.0/LICENSE000664 000000 000000 00000002101 12716147631 014570 0ustar00rootroot000000 000000 Copyright (c) Jeremy Ashkenas, Satoshi Murakami, George Zahariev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. LiveScript-1.5.0/Makefile000664 000000 000000 00000002555 12716147631 015240 0ustar00rootroot000000 000000 default: all SRC = $(shell find src -name "*.ls" -type f | sort) LIB = $(SRC:src/%.ls=lib/%.js) lib/parser.js LSC = bin/lsc BROWSERIFY = node_modules/.bin/browserify UGLIFYJS = node_modules/.bin/uglifyjs ISTANBUL = node_modules/.bin/istanbul lib: mkdir -p lib/ lib/parser.js: lib/grammar.js ./scripts/build-parser > lib/parser.js lib/%.js: src/%.ls lib $(LSC) --output lib --bare --compile "$<" browser: mkdir browser/ browser/livescript.js: $(LIB) browser scripts/preroll { ./scripts/preroll ; $(BROWSERIFY) -r ./lib/browser.js:livescript ; } > browser/livescript.js browser/livescript-min.js: browser/livescript.js $(UGLIFYJS) browser/livescript.js --mangle --comments "all" > browser/livescript-min.js package.json: package.json.ls $(LSC) --compile package.json.ls .PHONY: build build-browser force full install dev-install test test-harmony coverage loc clean all: build build: $(LIB) package.json build-browser: browser/livescript.js browser/livescript-min.js force: make -B full: make force && make force && make test && make coverage install: build npm install -g . dev-install: package.json npm install . test: build ./scripts/test test-harmony: build node --harmony ./scripts/test coverage: build $(ISTANBUL) cover ./scripts/test loc: wc --lines src/* clean: rm -f ./*.js rm -rf lib rm -rf browser/*.js rm -rf coverage rm -f package.json LiveScript-1.5.0/README.md000664 000000 000000 00000001764 12716147631 015060 0ustar00rootroot000000 000000 # LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming. Check out **[livescript.net](http://livescript.net)** for more information, examples, usage, and a language reference. ### Build Status [![Build Status](https://travis-ci.org/gkz/LiveScript.svg?branch=master)](https://travis-ci.org/gkz/LiveScript) ### Install Have Node.js installed. `sudo npm install -g livescript` After, run `lsc -h` for more information. ### Source [git://github.com/gkz/LiveScript.git](git://github.com/gkz/LiveScript.git) ### Community If you'd like to chat, drop by [#livescript](irc://irc.freenode.net/livescript) on Freenode IRC. If you don't have IRC client you can use Freenode [webchat](https://webchat.freenode.net/?channels=#livescript). LiveScript-1.5.0/bin/000775 000000 000000 00000000000 12716147631 014341 5ustar00rootroot000000 000000 LiveScript-1.5.0/bin/lsc000775 000000 000000 00000000300 12716147631 015041 0ustar00rootroot000000 000000 #!/usr/bin/env node var path = require('path'), fs = require('fs'), lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); require(lib + '/command')(process.argv); LiveScript-1.5.0/logo.png000664 000000 000000 00000023132 12716147631 015240 0ustar00rootroot000000 000000 PNG  IHDR/tEXtSoftwareAdobe ImageReadyqe<diTXtXML:com.adobe.xmp H5"IDATx tTչg'A WĠUhV*Vޢ\bWCmEbEJCJA.G $LI2yg {` 3s9sdfuL{Μzө@p* Pj@ Pj @1c{ȎCH6v\Ŏ`EAHeGӇM! p B AhBnB `b gIfZB zԮ3$3>C6~j..UP;zX,;ѯ# Uô5)| B #[nx'W!BRuf%yIAt i͠?*[= šo%5&$QF?Qq5~ IvSQ=A!Ͽo{f 8hӑώ'aj|K|{O2 Xt:gNO ݝe&X(I_'X)Ѭ0tldGv{V_H!5Pg*rƤuɚE-p^? *5)岯4mG9ݯA1*x0iև^K1B3 T[(]V\.3 W,劵^0cz`$_Qju1zܹ6vvD|* eLLz~Pn:'B Li+I P[$m:H =p]E. HM^Lc]"H[W}L2%GwB LJ>)I˺NӺZfKiRvuR'R"҃}ݎ{[zj+.eM6eL2.&f0:o# B٤Ex eDTPate3MZX}$*IW}eGoK}wkYW/.}᾵%b8'V0DňLhFm] UvO&:%36.3;!q1%փg4YJ >-]:(i8:rdrEdVAa$߯Q3%NDJZ\PT&!;Qǎ BL$r؏C|R:#wЀix{bJu뱽ɗ]?Akם}+]B.뾏N7xۄ~gY\vIww|ōcD Or%'YZ!0Ě Q)43.hJ;:!5n Q 3I>Sqkr-f 6Ǹ_!Я*Cܴ1P#䡲YBYgdTfB- S8aʺwH@=}QaiM TAy+٠s5u 2o ~HY'+B4M W2HMl_!eϹ[DЛv棿6g>YYc=}M,] M.Evʴ[لdEBb ں_5e\-e#c6&|gE4&y/uq# FR/z D~j-6~&wF|65Ζ {((4؜ǒGY_4Z2ӹt Km,{ԗxMӝ_%?\_ePٺm3-Or1ǟoI2ymj X'7nZC C~&ԼxHtWzCn6}wT3f=;W(igmֆhXLdڹBHVz[*\[,1(I73~D`ͱ E3mA`ѵGielhtc_{$Ld¸0!+`LλHTڠW,"NuT!o!0F?CbzeXʉ`LLTZs[$*#~.ԠSN1tֽ[S噹nhl =.>Vw <-BD^s4^}j`Pzzj?juߒaom>*edTAB;jeVƢutJ*}i-+ޘ|Ir7&cBr.I.)׫3N+=V/aԜטriU.k5B3:{OsASx|[>pl#P^(jayuL)~| "]QI3cYZԴ='5Ls:XH IWN}<g7:ɎBd58{EmiQaj@YLk [S[W-I*w7O^x m;a[u|碴wd6%^My[paݻ峋z PfOSKRSoZ ØLGM)Wb3*ڔT;șҭiw̍MJAGC7TDx enjwj+0 ,: ,UNa,Yԉ/mZDd;:eTikJB]k\5D50&}"牌Ubo Yz>m#UV*Y9Ȃ{ڴ}M],{_>ɲP@otE>?#vѕQpjN[ϯ8ߺ.uh }.vr217*@$Ad]"Hz[zn3nnPٙxY|ftѺ)fmDB ~=>ic|Q=M,oo>%RL; >;kO_y %9R$4Mbq %e6aP%Y>,R.nlpH hIR&XpH ZNןAU<9fERx8[IiR h&2-$)1u,lT$"RNo0 `;ֽ. T6HwOsmUh*d"#Z6Q9 OzV;YڂP "CT6H7=ymBmD3xSB$ p:]j@G5>x}Jy+3|&^a3&(j B ;X -Avd#2&!րP< LeHs8Rػn~\^P@w˰ĤIpn՗MU;zNp vX"OMȇrX4{>ux Y1<]w>q+{^-V>=BzG&\(#;:%^!A5TM@}yUQ]})a% BZ5L$ wEWTHY[:ͻhֲl½*G[BN7v؟ɼ+"AAH@'0+-+a벳μTăcd3@[L FY\6p'RPd396,X#4Zix@Td.k-Q = 6ux@af2-7EϾո2<86wVjYS'!;)sĖc+} W e.J &oXj׬h֑ ح>,fB-OuOoNa?浭9_of |NL0x,nGx_{O$E6.+?g7tr>ذR9٤|>G/ uwX꼬DGyԟ6}_m ͎iS V#ӛ :_7JDzh~vͮr,l 5(lPFo1^M:RV۝l~cćASCb5m@)!#,L$CwLt.qD+5x.n ~zكuϑWTw9M2یk"aYyP+G;s*~݉YՆݝ~7&v7j &|4˝Zx9cCqZʓgm{w[n'X@U 1ZNG7SՇ\Zվfp42BHְWҰkUPFֱpO^1/XM{m_MUHwMۛB/>voB]Ś7^g W};&>?LP5fe?.{-:-G|f=(\ptekћƒSs^ғ0^ٲ ۡ^w:[Qu- pK kiwhGD֏WhpjӟV<.MGL XST^n7u<֍ɗ]r>CvPų<~ý95ޟD8ca;رZ*z`#<>s_ĩ+oIOtFє;v%oq5̾MHc6)h*:7X..]+\JABP֤([}Z#MVBkw6Mqc:;~Fm5,+wΓr:m0% $ uˎY'6e_m7OG%/UwD:k;JdDA 50b&}&@6 t۱}V&jI("uT;gƤ9eڭqL1c%(gSvG}6Gg[\{u9Pj ON+k摚4c5Q4M,6Dq[W}eGycwz9;p'y@sZ]Gàɮp(k DvLZVL\䡴 S=zv =WkivtӠCV"ևD1)Zs\fvNR+@s7/C|Ҹelze ]`Ջz:&bom4ضfmNɌ7b 1<ڤgR=iZKX==zjk]CK=WlP3#t;1 N5 Iծ麦Ae\D%xqzm=cwM8hm+ocVɣ8lUz>PiNu{Ł5yd-&u.'*7OUV!jb5 {a7PQ!xF kP+YjB 5 jB 5 jB 5 jB 5F|q:UAeRNs/B%{a(SΎTka' Qj@5 Pj@5 Pj@5 0WМN/IENDB`LiveScript-1.5.0/package.json.ls000664 000000 000000 00000002332 12716147631 016474 0ustar00rootroot000000 000000 name: 'livescript' version: '1.5.0' description: 'LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.' keywords: 'language' 'compiler' 'coffeescript' 'coco' 'javascript' 'functional' author: 'George Zahariev ' homepage: 'http://livescript.net' bugs: 'https://github.com/gkz/LiveScript/issues' license: 'MIT' engines: node: '>= 0.8.0' directories: lib: './lib' bin: './bin' files: 'lib' 'bin' 'README.md' 'LICENSE' main: './lib/' browser: './lib/browser.js' bin: lsc: './bin/lsc' scripts: pretest: 'make force && make force' test: 'make test' 'test-harmony': 'make test-harmony' posttest: 'git checkout -- lib' prefer-global: true repository: type: 'git' url: 'git://github.com/gkz/LiveScript.git' dependencies: 'prelude-ls': '~1.1.2' optionator: '~0.8.1' 'source-map': '^0.5.6' dev-dependencies: jison: '0.4.17' 'uglify-js': '~2.6.2' istanbul: '~0.4.3' browserify: '^13.0.1' LiveScript-1.5.0/scripts/000775 000000 000000 00000000000 12716147631 015260 5ustar00rootroot000000 000000 LiveScript-1.5.0/scripts/build-parser000775 000000 000000 00000000110 12716147631 017567 0ustar00rootroot000000 000000 #!/usr/bin/env node console.log(require("../lib/grammar").generate()); LiveScript-1.5.0/scripts/preroll000775 000000 000000 00000000603 12716147631 016664 0ustar00rootroot000000 000000 #!/usr/bin/env node var version = require("..").VERSION; console.log("// Generated by LiveScript " + version + "\n" + "\n// LiveScript " + version + "\n// Copyright (c) Jeremy Ashkenas, Satoshi Murakami, George Zahariev" + "\n// Released under the MIT License" + "\n// https://raw.githubusercontent.com/gkz/LiveScript/master/LICENSE"); LiveScript-1.5.0/scripts/test000775 000000 000000 00000012252 12716147631 016167 0ustar00rootroot000000 000000 #!/usr/bin/env node (function() { var fs = require("fs"); var path = require("path"); var assert = require("assert"); var prelude = require("prelude-ls"); global.LiveScript = require(".."); global.command = require("../lib/command"); var bold = "\33[0;1m"; var red = "\33[0;31m"; var green = "\33[0;32m"; var reset = "\33[0m"; function tint(text, color) { if (color == null) { color = green; } return color + text + reset; } var startTime = Date.now(); var passedTests = 0; var failedTests = 0; var key; for (key in assert) { (function (name, func){ global[name] = function(){ func.apply(global, arguments); ++passedTests; }; }(key, assert[key])); } global.eq = strictEqual; global.throws = function(msg, fun){ try { fun(); } catch (e) { return eq(e != null ? e.message : void 8, msg); } ok(false, "should throw: " + msg); }; global.compileThrows = function(msg, lno, code){ throws(msg + " on line " + lno, function(){ LiveScript.compile(code); }); }; var activeCommandTests = 0; global.commandEq = function(args, arr, afterTests) { activeCommandTests++; afterTests = afterTests || prelude.id; var i = 0; var res = []; // switch to mocha in the future, better way to deal with async tests var maybeTestResults = function() { if (i === arr.length - 1) { arr.forEach(function (x, j) { if (x instanceof RegExp) { ok(x.test(res[j]), "should pass regex:\n" + x + "\n.test\n" + res[j]); } else { eq(x, res[j], "should equal:\n" + x + "\n==\n" + res[j]); } }); afterTests(); activeCommandTests--; } else if (i >= arr.length) { console.error("more results than expected!\n" + args + "\n" + res[i-1]); } }; var log = function(msg) { res.push(msg); maybeTestResults(); i++; }; var die = function(msg) { res.push(msg); maybeTestResults(); i++; }; try { command(args, {say: log, warn: log, die: die, sayWithTimestamp: log}); } catch (e) { ok(false, e.message); } }; global.fileRead = function(filename) { try { return fs.readFileSync(filename, { encoding: "utf8" }); } catch (e) { return "Error: " + e.message; } }; global.fileExists = function(filename) { try { return fs.statSync(filename).isFile(); } catch (e) { console.error(e.message); return false; } }; global.fileDelete = function(filename) { try { fs.unlinkSync(filename); } catch (e) { console.error(e.message); } }; process.on("exit", function(){ var printResults = function() { var time = ((Date.now() - startTime) / 1e3).toFixed(2); var message = "passed " + passedTests + " tests in " + time + " seconds"; console.log(failedTests ? tint("failed " + failedTests + " and " + message, red) : tint(message)); if (failedTests) { process.exit(1); } }; var check = function() { if (activeCommandTests > 0) { setTimeout(wait, 5); } else { printResults(); } }; check(); }); var files = fs.readdirSync("test"); if (process.execArgv.indexOf("--harmony") < 0 && process.execArgv.indexOf("--harmony-generators") < 0) { files.splice(files.indexOf("generators.ls"), 1); } else { console.log("Testing with harmony."); } files.forEach(function(file){ var stk, msg, m, ref, num, row, col, that, lines, line; if (!/\.ls$/i.test(file)) { return; } var filename = path.join("test", file); var code = fs.readFileSync(filename, "utf8"); try { console.log(filename); LiveScript.run(code, { filename: filename }); } catch (e) { ++failedTests; if (!(stk = e != null ? e.stack : null)) { console.error(e); } msg = e.message || "" + /^[^]+?(?=\n at )/.exec(stk); m = /^(AssertionError:) "(.+)" (===) "(.+)"$/.exec(msg) if (m) { for (num = 2; num <= 4; num += 2) { m[num] = tint(m[num].replace(/\\n/g, "\n"), bold); } msg = m.slice(1).join("\n"); } if ((ref = RegExp(filename + ":(\\d+):(\\d+)\\)?$", "m").exec(stk)) != null) { row = ref[1]; col = ref[2]; } if (row && col) { console.error(tint(msg + "\n" + red + "at " + filename + ":" + row-- + ":" + col--, red)); code = LiveScript.compile(code, { bare: true }); } else if (that = /\bon line (\d+)\b/.exec(msg)) { console.error(tint(msg, red)); row = that[1] - 1; col = 0; } else { console.error(stk); return; } line = (lines = code.split("\n"))[row]; lines[row] = line.slice(0, col) + tint(line.slice(col), bold); console.error(lines.slice((ref = row - 8) > 0 ? ref : 0, row + 9).join("\n")); } }); }()); LiveScript-1.5.0/src/000775 000000 000000 00000000000 12716147631 014360 5ustar00rootroot000000 000000 LiveScript-1.5.0/src/ast.ls000664 000000 000000 00000351342 12716147631 015517 0ustar00rootroot000000 000000 # Contains all of the node classes for the AST (abstract syntax tree). # Most nodes are created as the result of actions in the [grammar](#grammar), # but some are created by other nodes as a method of code generation. # To convert the syntax tree into a string of JavaScript code, # call `Block::compile-root`. require! { 'prelude-ls': {fold} './util': {name-from-path, strip-string} 'source-map': {SourceNode, SourceMapGenerator} } sn = (node = {}, ...parts) -> try result = new SourceNode node.line, node.column, null, parts result.display-name = node.constructor.display-name result catch e console.dir parts throw e sn-empty = (node) -> if node instanceof SourceNode for child in node.children unless sn-empty(child) return false true else !node sn-safe = (code) -> if code instanceof SourceNode then code else code.to-string! sn-remove-left = (node, count) -> for i til node.children.length child = node.children[i] if child instanceof SourceNode count = sn-remove-left child, count else child = child.to-string! node.children[i] = child.slice count count -= child.length if count <= 0 return 0 count sn-autofill = (node, parents = []) -> if node instanceof SourceNode if node.line for p in parents p.line = node.line p.column = node.column parents.length = 0 else parents.push node for child in node.children sn-autofill child, parents node SourceNode::replace = (...args) -> new SourceNode @line, @column, @source, [..replace(...args) for @children], @name SourceNode::set-file = (filename) -> @source = filename for child in @children when child instanceof SourceNode child.set-file filename # Built-in version of this sucks, so replace it with our own SourceNode::to-string-with-source-map = (...args) -> gen = new SourceMapGenerator ...args gen-line = 1 gen-column = 0 stack = [] code = '' debug-output = '' debug-indent = '' debug-indent-str = ' ' gen-for-node = (node) -> if node instanceof SourceNode debug-output += debug-indent + node.display-name # Block nodes should essentially "clear out" any effects # from parent nodes, so always add them to the stack valid = node.line and 'column' of node if valid stack.push node debug-output += '!' debug-output += " #{node.line}:#{node.column} #{gen-line}:#{gen-column}\n" debug-indent += debug-indent-str for child in node.children gen-for-node child debug-indent := debug-indent.slice 0, debug-indent.length - debug-indent-str.length if valid stack.pop! else debug-output += "#{debug-indent}#{ JSON.stringify node }\n" code += node cur = stack[*-1] if cur gen.add-mapping do source: cur.source original: line: cur.line column: cur.column generated: line: gen-line column: gen-column name: cur.name for i til node.length c = node.char-at i if c == "\n" gen-column := 0 ++gen-line if cur gen.add-mapping do source: cur.source original: line: cur.line column: cur.column generated: line: gen-line column: gen-column name: cur.name else ++gen-column gen-for-node(this) {code: code, map: gen, debug: debug-output} /* # Use this to track down places where a SourceNode is being converted into a string and causing the location to be lost tmp-to-string = SourceNode::to-string SourceNode::to-string = (...args) -> console.log("toString(): ", new Error().stack) tmp-to-string.apply this, args */ ### Node # The abstract base class for all nodes in the syntax tree. # Each subclass implements the `compile-node` method, which performs the # code generation for that node. To compile a node to JavaScript, # call `compile` on it, which wraps `compile-node` in some generic extra smarts. # An options hash is passed and cloned throughout, containing information about # the environment from higher in the tree (such as if a returned value is # being requested by the surrounding function), information about the current # scope, and indentation level. (Node = -> ...):: = compile: (options, level) -> o = {} <<< options o.level? = level node = @unfold-soak o or this # If a statement appears within an expression, wrap it in a closure. return node.compile-closure o if o.level and node.is-statement! code = (node <<< tab: o.indent).compile-node o if node.temps then for tmp in that then o.scope.free tmp code compile-closure: (o) -> # A statement that _jumps_ out of current context (like `return`) can't # be an expression via closure-wrapping, as its meaning will change. that.carp 'inconvertible statement' if @get-jump! fun = Fun [] Block this call = Call! fun.generator = true if o.in-generator var hasArgs, hasThis @traverse-children !-> switch it.value | \this => hasThis := true | \arguments => hasArgs := it.value = \args$ if hasThis call.args.push Literal \this call.method = \.call if hasArgs call.args.push Literal \arguments fun.params.push Var \args$ # Flag the function as `wrapper` so that it shares a scope # with its parent to preserve the expected lexical scope. out = Parens(Chain fun<<<{+wrapper, @void} [call]; true) if o.in-generator out = new Yield 'yieldfrom', out out.compile o # Compiles a child node as a block statement. compile-block: (o, node) -> unless sn-empty(code = node?compile o, LEVEL_TOP) sn(null, "{\n", code, "\n#{@tab}}") else sn(node, '{}') # Spreads a transformation over a list and compiles it. compile-spread-over: (o, list, transform) -> ob = list instanceof Obj them = list.items for node, i in them node.=it if sp = node instanceof Splat node.=val if ob and not sp node = transform node node = lat = Splat node if sp if ob and not sp then them[i].val = node else them[i] = node if not lat and (@void or not o.level) list = Block(if ob then [..val for them] else them) <<< {@front, +void} list.compile o, LEVEL_PAREN # If the code generation wishes to use the result of a complex expression # in multiple places, ensure that the expression is only ever evaluated once, # by assigning it to a temporary variable. cache: (o, once, level) -> unless @is-complex! return [if level? then @compile o, level else this] * 2 sub = Assign ref = Var(o.scope.temporary!), this # Pass a `level` to precompile. if level? sub.=compile o, level o.scope.free ref.value if once return [sub, ref.value] # If flagged as `once`, the tempvar will be auto-freed. if once then [sub, ref <<< {+temp}] else [sub, ref, [ref.value]] # Compiles to a variable/source pair suitable for looping. compile-loop-reference: (o, name, ret, safe-access) -> if this instanceof Var and o.scope.check @value or this instanceof Unary and @op in <[ + - ]> and -1/0 < +@it.value < 1/0 or this instanceof Literal and not @is-complex! code = @compile o, LEVEL_PAREN code = "(#code)" if safe-access and this not instanceof Var return [code] * 2 asn = Assign Var(tmp = o.scope.temporary name), this ret or asn.void = true [tmp; asn.compile o, if ret then LEVEL_CALL else LEVEL_PAREN] # Passes each child to a function, returning its return value if exists. each-child: (fn) -> for name in @children when child = @[name] if \length of child for node, i in child then return that if fn(node, name, i) else return that if fn(child, name)? # Performs `each-child` on every descendant. # Overridden by __Fun__ not to cross scope by default. traverse-children: (fn, xscope) -> @each-child (node, name, index) ~> fn(node, this, name, index) ? node.traverse-children fn, xscope # Performs anaphoric conversion if a `that` is found within `@aTargets`. anaphorize: -> @children = @aTargets if @each-child hasThat if (base = this)[name = @a-source] instanceof Existence base.=[name] name = \it unless base[name]value is \that base[name] = Assign Var(\that), base[name] function hasThat it.value is \that or if it.a-source then hasThat that if it[that] else it.each-child hasThat delete @children @[@a-source] <<< {+cond} # Throws a syntax error, appending `@line` number to the message. carp: (msg, type = SyntaxError) -> throw type "#msg on line #{ @line or @traverse-children -> it.line }" # Defines delegators. delegate: !(names, fn) -> for let name in names @[name] = -> fn.call this, name, it # Default implementations of the common node properties and methods. Nodes # will override these with custom logic, if needed. children: [] terminator: \; is-complex: YES is-statement : NO is-assignable : NO is-callable : NO is-empty : NO is-array : NO is-string : NO is-regex : NO is-matcher: -> @is-string! or @is-regex! # Do I assign a certain variable? assigns: NO # Picks up name(s) from LHS. rip-name: VOID unfold-soak : VOID unfold-assign : VOID unparen : THIS unwrap : THIS maybe-key : THIS expand-slice : THIS var-name : String get-accessors : VOID get-call : VOID get-default : VOID # Digs up a statement that jumps out of this node. get-jump : VOID invert: -> Unary \! this, true invert-check: -> if it.inverted then @invert! else this add-else: (@else) -> this # Constructs a node that returns the current node's result. # If obj is true, interprets this node as a key-value pair to be # stored on ref. Otherwise, pushes this node into ref. make-return: (ref, obj) -> if obj then items = if this instanceof Arr if not @items.0? or not @items.1? @carp 'must specify both key and value for object comprehension' @items else kv = \keyValue$ for v, i in [Assign(Var(kv), this), Var(kv)] Chain v .add Index Literal i Assign (Chain Var ref).add(Index items.0, \., true), items.1 else if ref Call.make JS(ref + \.push), [this] else Return this # Extra info for `toString`. show: String # String representation of the node for inspecting the parse tree. # This is what `lsc --ast` prints out. to-string: (idt or '') -> tree = \\n + idt + @constructor.display-name tree += ' ' + that if @show! @each-child !-> tree += it.toString idt + TAB tree # JSON serialization stringify: (space) -> JSON.stringify this, null space to-JSON: -> {type: @constructor.display-name, ...this} # JSON deserialization exports.parse = (json) -> exports.from-JSON JSON.parse json exports.from-JSON = function return it unless it and typeof it is \object if it.type node = ^^exports[that].prototype for key, val of it then node[key] = from-JSON val return node if it.length? then [from-JSON v for v in it] else it #### Mixins Negatable = show: -> @negated and \! invert: -> !=@negated; this #### Block # A list of expressions that forms the body of an indented block of code. class exports.Block extends Node (body || []) ~> if \length of body @lines = body else @lines = [] @add body children: [\lines] to-JSON: -> delete @back; super! add: -> it.=unparen! switch | @back => that.add it | it.lines => @lines.push ...that | otherwise => @lines.push it @back = that if delete it.back this prepend: -> @lines.splice @neck!, 0, ...arguments this pipe: (target, type) -> args = if type is \|> then @lines.pop! else target args = [args] if typeof! args isnt \Array switch type | \|> => @lines.push Call.make(target, args, pipe: true) | \<| => @lines.push Call.make(@lines.pop!, args) this unwrap: -> if @lines.length is 1 then @lines.0 else this # Removes trailing comment nodes. chomp: -> {lines} = this i = lines.length while lines[--i] then break unless that.comment lines.length = i + 1 this # Finds the right position for inserting variable declarations. neck: -> pos = 0 for x in @lines break unless x.comment or x instanceof Literal ++pos pos is-complex: -> @lines.length > 1 or @lines.0?is-complex! ::delegate <[ isCallable isArray isString isRegex ]> -> @lines[*-1]?[it]! get-jump: -> for node in @lines then return that if node.get-jump it # **Block** does not return its entire body, rather it # ensures that the final line is returned. make-return: -> @chomp! if @lines[*-1]?=make-return ...& --@lines.length if that instanceof Return and not that.it this compile: (o, level ? o.level) -> return @compile-expressions o, level if level o.block = this tab = o.indent codes = [] for node in @lines node = node.unfold-soak o or node continue if sn-empty(code = (node <<< {+front})compile o, level) codes.push tab codes.push code node.is-statement! or codes.push node.terminator codes.push \\n codes.pop! sn(null, ...codes) # **Block** is the only node that can serve as the root. compile-root: (options) -> o = { level: LEVEL_TOP scope: @scope = Scope.root = new Scope ...options } if delete o.saveScope # use savedScope as your scope @scope = Scope.root = o.scope = that.savedScope or= o.scope delete o.filename o.indent = if bare = delete o.bare then '' else TAB if /^\s*(?:[/#]|javascript:)/test @lines.0?.code prefix = @lines.shift!code + \\n if delete o.eval and @chomp!lines.length if bare then @lines.push Parens @lines.pop! else @make-return! code = [(@compile-with-declarations o)] # Wrap everything in a safety closure unless requested not to. bare or code = ["(function(){\n", ...code, "\n}).call(this);\n"] result = sn(null, prefix || [], ...code) # Compile to a function body. compile-with-declarations: (o) -> o.level = LEVEL_TOP pre = [] if i = @neck! rest = @lines.splice i, 9e9 pre = [(@compile o), "\n"] @lines = rest return sn(this, pre.0 || []) if sn-empty(post = @compile o) sn(null, ...pre, if @scope then that.emit post, o.indent else post) # Compile to a comma-separated list of expressions. compile-expressions: (o, level) -> {lines} = @chomp! i = -1 while lines[++i] then lines.splice i-- 1 if that.comment lines.push Literal \void unless lines.length lines.0 <<< {@front} lines[*-1] <<< {@void} return lines.0.compile o, level unless lines.1 code = [] last = lines.pop! for node in lines code.push (node <<< {+void})compile(o, LEVEL_PAREN), ', ' code.push (last.compile o, LEVEL_PAREN) if level < LEVEL_LIST then sn(null, ...code) else sn(null, "(", ...code, ")") #### Atom # An abstract node for simple values. class Atom extends Node show: -> @value is-complex: NO #### Literal # `this`, `debugger`, regexes and primitives. class exports.Literal extends Atom (@value) ~> return JS "#value" true if value.js return new Super if value is \super is-empty : -> @value in <[ void null ]> is-callable : -> @value in <[ this eval .. ]> is-string : -> 0 <= '\'"'indexOf "#{@value}"char-at! is-regex : -> "#{@value}"char-at! is \/ is-complex : -> @is-regex! or @value is \debugger is-what : -> | @is-empty! => \empty | @is-callable! => \callable | @is-string! => \string | @is-regex! => \regex | @is-complex! => \complex | otherwise => void var-name: -> if /^\w+$/test @value then \$ + @value else '' make-return: -> if not it and @value is 'debugger' this else super ... maybe-key: -> if ID.test @value then Key @value else this compile: (o, level ? o.level) -> switch val = "#{@value}" | \this => return sn(this, o.scope.fun?bound or val) | \void => return sn(this, '') unless level val += ' 8' fallthrough | \null => @carp 'invalid use of ' + @value if level is LEVEL_CALL | \on \yes => val = 'true' | \off \no => val = 'false' | \* => @carp 'stray star' | \.. => @carp 'stray reference' unless val = o.ref @cascadee or val.erred = true | \debugger => return sn(this, "(function(){ debugger; }())") if level sn(this, sn-safe(val)) #### Var # Variables. class exports.Var extends Atom (@value) ~> ::is-assignable = ::is-callable = YES assigns: -> it is @value maybe-key: -> Key(@value) <<< {@line} var-name: ::show compile: (o) -> sn(this, if @temp then o.scope.free @value else @value) #### Key # A property name in the form of `{key: _}` or `_.key`. class exports.Key extends Node (name, @reserved or name.reserved) ~> @name = '' + name is-complex: NO assigns: -> it is @name var-name: -> {name} = this if @reserved or name in <[ arguments eval ]> then "$#name" else name show: -> if @reserved then "'#{@name}'" else @name compile: -> sn(this, @show()) #### Index # Dots and brackets to access an object's property. class exports.Index extends Node (key, symbol or \., init) ~> if init and key instanceof Arr switch key.items.length | 1 => key = Parens k unless (k = key.items.0) instanceof Splat switch symbol | '[]' => @vivify = Arr | '{}' => @vivify = Obj | _ => @assign = symbol.slice 1 if \= is symbol.slice -1 this <<< {key, symbol} children: [\key] show: -> [\? if @soak] + @symbol is-complex: -> @key.is-complex! var-name: -> @key instanceof [Key, Literal] and @key.var-name! compile: (o) -> code = @key.compile o, LEVEL_PAREN if @key instanceof Key and \' is not code.to-string!.char-at 0 then sn(this, ".", code) else sn(this, "[",code,"]") #### Slice # slices away at the target class exports.Slice extends Node ({@type, @target, @from, @to}) ~> @from ?= Literal 0 @to = Binary \+ @to, Literal \1 if @to and @type is \to children: [\target \from \to] show: -> @type compile-node: (o) -> @to = Binary \|| @to, Literal \9e9 if @to and @type is \to args = [@target, @from] args.push @to if @to Chain Var (util \slice) .add Index (Key \call), \. true .add Call args .compile o #### Chain # Acts as a container for property-access/function-call chains, by holding # __Index__ or __Call__ instances as `@tails`. class exports.Chain extends Node (head, tails) ~> return head if not tails and head instanceof Chain this <<< {head, tails or []} children: <[ head tails ]> add: -> if @tails.length last = @tails[*-1] # optimize `x |> f 1, _` to `f(1, x)` if last instanceof Call and last.partialized?length is 1 and it.args.length is 1 index = last.partialized.0.head.value # Chain Literal i delete last.partialized # extract the single arg from pipe call last.args[index] = it.args.0 return this if @head instanceof Existence {@head, @tails} = Chain @head.it it.soak = true @tails.push it bi = if @head instanceof Parens and @head.it instanceof Binary and not @head.it.partial then @head.it else if @head instanceof Binary and not @head.partial then @head if @head instanceof Super if not @head.called and it instanceof Call and not it.method it.method = \.call it.args.unshift Literal \this @head.called = true else if not @tails.1 and it.key?name is \prototype @head.sproto = true else if it instanceof Call and @tails.length is 1 and bi and bi.op in logics = <[ && || xor ]> call = it f = (x, key) -> y = x[key] if y instanceof Binary and y.op in logics then f y, \first; f y, \second else x[key] = Chain y .auto-compare call.args f bi, \first f bi, \second return bi this auto-compare: (target) -> test = @head switch | test instanceof Literal Binary \=== test, target.0 | test instanceof Unary and test.it instanceof Literal Binary \=== test, target.0 | test instanceof Arr, test instanceof Obj Binary \==== test, target.0 | test instanceof Var and test.value is \_ Literal \true | otherwise this .add Call target or [] flip-it: -> @flip = true; this # __Chain__ can be unwrapped as its inner node, if there are no subnodes. unwrap: -> if @tails.length then this else @head ::delegate <[ getJump assigns isStatement isString ]> , (it, arg) -> not @tails.length and @head[it] arg is-complex : -> @tails.length or @head.is-complex! is-callable : -> if @tails[*-1] then not that.key?items else @head.is-callable! is-array : -> if @tails[*-1] then that.key instanceof Arr else @head.is-array! is-regex : -> @head.value is \RegExp and not @tails.1 and @tails.0 instanceof Call is-assignable: -> return @head.is-assignable! unless tail = @tails[*-1] return false if tail not instanceof Index or tail.key instanceof List or tail.symbol is \.~ for tail in @tails when tail.assign then return false true # `@$` `o.0` is-simple-access: -> @tails.length is 1 and not @head.is-complex! and not @tails.0.is-complex! make-return: -> if @tails.length then super ... else @head.make-return ...& get-call: -> (tail = @tails[*-1]) instanceof Call and tail var-name: -> @tails[*-1]?var-name! # A reference has base part (`this` value) and name part. # We cache them separately for compiling complex expressions, so that e.g. # # a()[b()] ||= c # # compiles to # # (ref$ = a())[key$ = b()] || (ref$[key$] = c); # cache-reference: (o) -> name = @tails[*-1] # `a.b()` return @unwrap!cache o, true unless @is-assignable! # `a` `a.b` if @tails.length < 2 and not @head.is-complex! and not name?is-complex! return [this] * 2 base = Chain @head, @tails.slice 0 -1 # `a().b` if base.is-complex! ref = o.scope.temporary! base = Chain Assign Var(ref), base bref = Var(ref) <<< {+temp} # `a{}` return [base, bref] unless name # `a[b()]` if name.is-complex! ref = o.scope.temporary \key name = Index Assign Var(ref), name.key nref = Index Var(ref) <<< {+temp} [base.add name; Chain bref || base.head, [nref or name]] compile-node: (o) -> if @flip util \flip util \curry {head, tails} = this head <<< {@front, @newed} return head.compile o unless tails.length return that.compile o if @unfold-assign o for t in tails when t.partialized then has-partial = true; break if has-partial util \slice pre = [] rest = [] for t in tails broken = broken or t.partialized? if broken then rest.push t else pre .push t [partial, ...post] = rest if rest? @tails = pre context = if pre.length then Chain head, pre[til -1] else Literal \this return (Chain (Chain Var util \partialize .add Index Key \apply .add Call [context, Arr [this; Arr partial.args; Arr partial.partialized]]), post).compile o @carp 'invalid callee' if tails.0 instanceof Call and not head.is-callable! @expand-slice o @expand-vivify! @expand-bind o @expand-splat o @expand-star o if @splatted-new-args idt = o.indent + TAB func = Chain @head, tails.slice 0 -1 return sn(null, """ (function(func, args, ctor) { #{idt}ctor.prototype = func.prototype; #{idt}var child = new ctor, result = func.apply(child, args), t; #{idt}return (t = typeof result) == "object" || t == "function" ? result || child : child; #{TAB}})(""", (func.compile o), ", ", @splatted-new-args, """, function(){}) """) return @head.compile o unless @tails.length base = [(@head.compile o, LEVEL_CALL)] news = [] rest = [] for t in @tails news.push 'new ' if t.new rest.push t.compile o base.push ' ' if \. is rest.join("").char-at 0 and SIMPLENUM.test base.0.to-string! sn(null, ...news, ...base, ...rest) # Unfolds a soak into an __If__: `a?.b` => `a.b if a?` unfold-soak: (o) -> if @head.unfold-soak o that.then.tails.push ...@tails return that for node, i in @tails when delete node.soak bust = Chain @head, @tails.splice 0 i node.carp 'invalid accessign' if node.assign and not bust.is-assignable! if i and (node.assign or node instanceof Call) [test, bust] = bust.cache-reference o if bust instanceof Chain @tails.unshift ...bust.tails bust.=head @head = bust else [test, @head] = bust.unwrap!cache o test = if node instanceof Call JS "typeof #{ test.compile o, LEVEL_OP } == 'function'" else Existence test return If(test, this) <<< {+soak, @cond, @void} unfold-assign: (o) -> if @head.unfold-assign o that.right.tails.push ...@tails return that for index, i in @tails then if op = index.assign index.assign = '' left = Chain @head, @tails.splice 0 i .expand-slice o .unwrap! if left instanceof Arr # `[a, b].=reverse()` => `[a, b] = [a, b].reverse()` lefts = left.items {items: rites} = @head = Arr! for node, i in lefts [rites[i], lefts[i]] = Chain node .cache-reference o else [left, @head] = Chain left .cache-reference o op = \:= if op is \= return Assign(left, this, op) <<< {+access} expand-splat: !(o) -> {tails} = this i = -1 while call = tails[++i] continue unless args = call.args ctx = call.method is \.call and (args.=concat!)shift! continue unless !sn-empty(args = Splat.compile-array o, args, true) if call.new @splatted-new-args = args else if not ctx and tails[i-1] instanceof Index [@head, ctx] = Chain(@head, tails.splice 0 i-1)cache o, true i = 0 call <<< method: \.apply, args: [ctx or Literal \null; JS args] expand-vivify: !-> {tails} = this i = 0 while i < tails.length when delete tails[i++]vivify @head = Assign Chain(@head, tails.splice 0, i), that!, \= \|| i = 0 expand-bind: !(o) -> {tails} = this i = -1 while tails[++i] continue unless that.symbol is \.~ that.symbol = '' obj = Chain(@head, tails.splice 0 i)unwrap! {key} = tails.shift! call = Call.make Util(\bind), [obj, key <<< {+reserved}] @head = if @newed then Parens call, true else call i = -1 expand-star: !(o) -> {tails} = this i = -1 while tails[++i] continue if that.args or that.stars or that.key instanceof Key stars = that.stars = [] that.each-child seek continue unless stars.length [sub, ref, temps] = Chain(@head, tails.splice 0 i)unwrap!cache o value = Chain(ref, [Index Key \length])compile o for star in stars then star <<< {value, is-assignable: YES} @head = JS sub.compile(o, LEVEL_CALL) + tails.shift!compile o o.scope.free temps.0 if temps i = -1 !function seek if it.value is \* then stars.push it else unless it instanceof Index then it.each-child seek # `a[x, y] = b{z} = c` => `[a[x], a[y]] = {z: b.z} = c` expand-slice: (o, assign) -> {tails} = this i = -1 while tail = tails[++i] when tail.key?items tail.carp 'calling a slice' if tails[i+1] instanceof Call x = tails.splice 0 i+1 x = x.pop!key.to-slice o, Chain(@head, x)unwrap!, tail.symbol, assign @head = x <<< {@front} i = -1 this #### Call # `x(y)` class exports.Call extends Node (args || []) ~> if args.length is 1 and (splat = args.0) instanceof Splat if splat.filler @method = \.call args <<< [Literal \this; Splat Literal \arguments] else if splat.it instanceof Arr args = splat.it.items else for a, i in args when a.value is \_ args[i] = Chain Literal \void args[i].placeholder = true (@partialized ?= []).push Chain Literal i this <<< {args} children: [\args] show: -> [@new] + [@method] + [\? if @soak] compile: (o) -> code = [sn(this, (@method or ''), \() + (if @pipe then "\n#{o.indent}" else '')] for a, i in @args code.push (if i then ', ' else ''), a.compile o, LEVEL_LIST code.push sn(this, \)) sn(null, ...code) @make = (callee, args, opts) -> call = Call args call <<< opts if opts Chain(callee)add call @block = (fun, args, method) -> Parens(Chain fun, [Call(args) <<< {method}]; true) <<< {+calling} @back = (params, node, bound, curried, hushed, generator) -> fun = Fun params,, bound, curried, hushed, generator if node instanceof Label fun <<< {name: node.label, +labeled} node.=it node.=it if not fun.hushed and fun.hushed = node.op is \! node.get-call!?partialized = null {args} = node.get-call! or (node = Chain node .add Call!)get-call! index = 0 for a in args break if a.placeholder ++index node <<< back: (args[index] = fun)body @let = (args, body, generator = false) -> params = for a, i in args if a.op is \= and not a.logic and a.right args[i] = that continue if i is 0 and gotThis = a.left.value is \this a.left else Var a.var-name! || a.carp 'invalid "let" argument' gotThis or args.unshift Literal \this @block Fun(params, body, null, null, null, generator), args, \.call #### List # An abstract node for a list of comma-separated items. class List extends Node children: [\items] show : -> @name named : (@name) -> this is-empty: -> not @items.length assigns: -> for node in @items then return true if node.assigns it @compile = (o, items, deep-eq) -> switch items.length | 0 => return '' | 1 => return items.0.compile o, LEVEL_LIST {indent, level} = o o <<< indent: indent + TAB, level: LEVEL_LIST code = [items[i = 0]compile o] while items[++i] code.push ', ' target = that if deep-eq if target instanceof Var and target.value is \_ target = Obj [Prop (Key \__placeholder__), Literal true] else if target instanceof [Obj, Arr] target.deep-eq = true code.push target.compile o code = ["\n#{o.indent}", ...code, "\n#indent"] if ~code.join("").indexOf \\n o <<< {indent, level} sn(this, ...code) #### Obj # `{x: y}` class exports.Obj extends List (@items or []) ~> as-obj: THIS # `base{x: y}` => `{x: base.y}` to-slice: (o, base, symbol, assign) -> {items} = this if items.length > 1 [base, ref, temps] = base.cache o else ref = base for node, i in items continue if node.comment if node instanceof [Prop, Splat] node[name = node.children[*-1]] = chain = Chain base, [Index node[name]maybe-key!] else # `o{k or v}` => `{k: a.k or v}` node.=first if logic = node.get-default! if node instanceof Parens # `a{(++i)}` => `{(ref$ = ++i): a[ref$]}` [key, node] = node.cache o, true # `a{(++i)} = b` => `{(ref$): a[ref$ = ++i]} = b` # => `a[ref$ = ++i] = b[ref$]` [key, node] = [node, key] if assign key = Parens key else key = node val = chain = Chain base, [Index node.maybe-key!, symbol] val = logic <<< first: val if logic items[i] = Prop key, val base = ref chain or @carp 'empty slice' (chain.head = Var temps.0)temp = true if temps this compile-node: (o) -> {items} = this return sn(this, if @front then '({})' else '{}') unless items.length code = [] idt = \\n + o.indent += TAB dic = {} for node, i in items if node.comment code.push idt, node.compile o continue node.=first if logic = node.get-default! if node instanceof Splat or (node.key or node) instanceof Parens rest = items.slice i break if logic # `{@a or b}` => `{a: @a or b}` if node instanceof Prop then node.val = logic <<< first: node.val else node = Prop node, logic <<< first: node if @deep-eq and node instanceof Prop if node.val instanceof Var and node.val.value is \_ node.val = Obj [Prop (Key \__placeholder__), Literal true] else if node.val instanceof [Obj, Arr] node.val.deep-eq = true if multi then code.push \, else multi = true code.push idt if node instanceof Prop {key, val} = node if node.accessor code.push (node.compile-accessor o, key.=compile o) else val.rip-name key code.push (key.=compile o), ": ", (val.compile o, LEVEL_LIST) else code.push (key = node.compile o), ": ", key # Canonicalize the key, e.g.: `0.0` => `0` ID.test key or key = do Function "return #key" node.carp "duplicate property \"#key\"" unless dic"#key." .^.= 1 if code.join("") then code.push \\n + @tab code = sn(null, sn(this, "{"), ...code, sn(this, "}")) rest and code = Import(JS code; Obj rest)compile o <<< indent: @tab if @front and \{ is code.to-string!.char-at! then sn(null, "(", code, ")") else code #### Prop # `x: y` class exports.Prop extends Node (@key, @val) ~> return Splat @val if key.value is \... if val.get-accessors! @val = that for fun in that fun.x = if fun.hushed = fun.params.length then \s else \g this <<< {\accessor} children: <[ key val ]> show: -> @accessor assigns: -> @val.assigns? it compile-accessor: (o, key) -> funs = @val if funs.1 and funs.0.params.length + funs.1.params.length is not 1 funs.0.carp 'invalid accessor parameter' code = [] for fun in funs fun.accessor = true code.push fun.x, "et ", key, (fun.compile o, LEVEL_LIST .to-string!.slice 8), ',\n' + o.indent code.pop! sn(null, ...code) compile-descriptor: (o) -> obj = Obj! for fun in @val then obj.items.push Prop Key(fun.x + \et ), fun obj.items.push Prop Key(\configurable), Literal true obj.items.push Prop Key(\enumerable ), Literal true obj.compile o #### Arr # `[x, y]` class exports.Arr extends List (@items or []) ~> is-array: YES as-obj: -> Obj([Prop Literal(i), item for item, i in @items]) # `base[x, ...y]` => `[base[x], ...base[y]]` to-slice: (o, base, symbol) -> {items} = this if items.length > 1 then [base, ref] = base.cache o else ref = base for item, i in items item.=it if splat = item instanceof Splat continue if item.is-empty! chain = Chain base, [Index item, symbol] items[i] = if splat then Splat chain else chain base = ref chain or @carp 'empty slice' this compile: (o) -> {items} = this return sn(this, '[]') unless items.length unless sn-empty(code = Splat.compile-array o, items) return if @newed then sn(this, "(", code, ")") else sn(this, code) sn(null, sn(this, "["), (List.compile o, items, @deep-eq), sn(this, "]")) @maybe = (nodes) -> return nodes.0 if nodes.length is 1 and nodes.0 not instanceof Splat constructor nodes @wrap = -> constructor [Splat it <<< is-array: YES] class exports.Yield extends Node (@op, @it) ~> children: <[ it ]> show: -> if @op is 'yieldfrom' then 'from' else '' ::delegate <[ isCallable ]> -> yes compile-node: (o) -> code = [] if @op is \yieldfrom code.push 'yield*' else code.push 'yield' if @it code.push " #{@it.compile o, LEVEL_OP + PREC.unary}" sn(this, "(", ...code, ")") #### Unary operators class exports.Unary extends Node # `flag` denotes inversion or postcrement. (op, it, flag) ~> if it? if not flag and it.unaries that.push op return it switch op case \! break if flag return it <<< {+hushed} if it instanceof Fun and not it.hushed return it.invert! case \++ \-- then @post = true if flag case \new # `new C?` => `new C?()` if it instanceof Existence and not it.negated it = Chain(it)add Call! it.newed = true for node in it.tails or '' when node instanceof Call and not node.new node.args.shift! if node.method is \.call node <<< {\new, method: ''} return it case \~ then if it instanceof Fun and it.statement and not it.bound return it <<< bound: \this$ this <<< {op, it} children: [\it] show: -> [\@ if @post] + @op is-callable: -> @op in <[ do new delete ]> or not @it? is-array: -> @it instanceof Arr and @it.items.length or @it instanceof Chain and @it.is-array! is-string: -> @op in <[ typeof classof ]> invert: -> return @it if @op is \! and @it.op in <[ ! < > <= >= of instanceof ]> constructor \! this, true unfold-soak: (o) -> @op in <[ ++ -- delete ]> and @it? and If.unfold-soak o, this, \it get-accessors: -> return unless @op is \~ return [@it] if @it instanceof Fun if @it instanceof Arr {items} = @it return items if not items.2 and items.0 instanceof Fun and items.1 instanceof Fun function crement then {'++':\in '--':\de}[it] + \crement compile-node: (o) -> return @compile-as-func o if not @it? return that if @compile-spread o {op, it} = this switch op case \! then it.cond = true case \new then it.is-callable! or it.carp 'invalid constructor' case \do # `do f?` => `f?()` if o.level is LEVEL_TOP and it instanceof Fun and it.is-statement! return sn(this, (it.compile o), " ", (Unary \do Var it.name .compile o)) x = Parens if it instanceof Existence and not it.negated then Chain(it)add Call! else Call.make it return sn(this, (x <<< {@front, @newed})compile o) case \delete @carp 'invalid delete' if it instanceof Var or not it.is-assignable! return @compile-pluck o if o.level and not @void case \++ \-- it.is-assignable! or @carp 'invalid ' + crement op if it instanceof Var and o.scope.checkReadOnly it.value @carp "#{ crement op } of #that \"#{it.value}\"" ReferenceError it{front} = this if @post case \^^ then return sn(this, (util \clone), "(", (it.compile o, LEVEL_LIST), ")") case \jsdelete then return sn(this, "delete ", (it.compile o, LEVEL_LIST)) case \classof return sn(this, (util \toString), ".call( ", (it.compile o, LEVEL_LIST), ").slice(8, -1)") code = [(it.compile o, LEVEL_OP + PREC.unary)] if @post then code.push op else op += ' ' if op in <[ new typeof delete ]> or op in <[ + - ]> and op is code.join("").char-at! code.unshift op if o.level < LEVEL_CALL then sn(this, ...code) else sn(this, "(", ...code, ")") # `^delete ...o[p, ...q]` => `[^delete o[p], ...^delete o[q]]` # `^delete ...o{p, ...q}` => `{p: ^delete o[p], ...^delete o[q]}` compile-spread: (o) -> {it} = this ops = [this] while it instanceof constructor, it.=it then ops.push it return '' unless it instanceof Splat and it.=it.expand-slice(o)unwrap! instanceof List @compile-spread-over o, it, (node) -> for op in ops by -1 then node = constructor op.op, node, op.post node # `v = delete o.k` compile-pluck: (o) -> [get, del] = Chain @it .cache-reference o code = [ref = o.scope.temporary!, " = \ ", (get.compile o, LEVEL_LIST), ", delete \ ", (del.compile o, LEVEL_LIST), ", \ ", (o.scope.free ref)] if o.level < LEVEL_LIST then sn(this, ...code) else sn(this, "(", ...code, ")") compile-as-func: (o) -> if @op is \! then sn(this, util \not) else sn(this, "(", ((Fun [], Block Unary @op, Chain Var \it).compile o), ")") #### Binary operators class exports.Binary extends Node (op, first, second, destructuring) ~> if destructuring logic = op.logic logic = destructuring if typeof! destructuring is \String op = | logic => that | op is \= => \? | _ => \= @partial = not first? or not second? if not @partial if \= is op.char-at op.length-1 and op.char-at(op.length-2) not in <[ = < > ! ]> return Assign first.unwrap!, second, op switch op | \in => return new In first, second | \with => return new Import (Unary \^^ first), second, false | \<<< \<<<< => return Import first, second, op is \<<<< | \<| => return Block first .pipe second, op | \|> => return Block second .pipe first, \<| | \. \.~ => return Chain first .add Index second, op this <<< {op, first, second} children: <[ first second ]> show: -> @op is-callable: -> @partial or @op in <[ && || ? << >> ]> and @first.is-callable! and @second.is-callable! is-array: -> switch @op | \* => @first .is-array! | \/ => @second.is-matcher! is-string: -> switch @op | \+ \* => @first.is-string! or @second.is-string! | \- => @second.is-matcher! COMPARER = /^(?:[!=]=|[<>])=?$/ INVERSIONS = '===':'!==' '!==':'===' '==':'!=' '!=':'==' invert: -> if not COMPARER.test @second.op and INVERSIONS[@op] @op = that @was-inverted = true return this Unary \! Parens(this), true invertIt: -> @inverted = true; this get-default: -> switch @op | \? \|| \&& => this xor-children: (test) -> return false unless (first = test @first) xor test @second return if first then [@first, @second] else [@second, @first] compile-node: (o) -> return @compilePartial o if @partial switch @op case \? then return @compileExistence o case \* return @compileJoin o if @second.is-string! return @compileRepeat o if @first.is-string! or @first.is-array! case \- then return @compileRemove o if @second.is-matcher! case \/ then return @compileSplit o if @second.is-matcher! case \** \^ then return @compilePow o case \? then return @compileMinMax o case \<< \>> then return @compileCompose o case \++ then return @compileConcat o case \%% then return @compileMod o case \xor then return @compileXor o case \&& \|| @second.void = true if top = @void or not o.level if top or @cond @first .cond = true @second.cond = true case \instanceof {items}:rite = @second.expand-slice(o)unwrap! if rite instanceof Arr return @compileAnyInstanceOf o, items if items.1 @second = items.0 or rite @second.is-callable! or @second.carp 'invalid instanceof operand' case <[ ==== !=== ]> then @op.=slice 0 3; fallthrough case <[ <== >== <<= >>= ]> then return @compileDeepEq o default if COMPARER.test @op if @op in [\=== \!==] and @xor-children (.is-regex!) return @compileRegexEquals o, that if @op is \=== and (@first instanceof Literal and @second instanceof Literal) and @first.is-what! isnt @second.is-what! console?.warn "WARNING: strict comparison of two different types will always be false: #{@first.value} == #{@second.value}" return @compileChain o if COMPARER.test @op and COMPARER.test @second.op @first <<< {@front} code = [(@first .compile o, level = LEVEL_OP + PREC[@op]), " ", (@mapOp @op), " ", (@second.compile o, level)] if o.level <= level then sn(this, ...code) else sn(this, "(", ...code, ")") mapOp: (op) -> | op.match //\.([&\|\^] | << | >>>?)\.// => that.1 | op is \of => \in | otherwise => op # Mimic Python/Perl6's chained comparisons # when multiple comparison operators are used sequentially: # # $ livescript -pe '50 < 65 === 9r72 > 10' # true # # See . compileChain: (o) -> code = [(@first.compile o, level = LEVEL_OP + PREC[@op])] [sub, @second.first] = @second.first.cache o, true code.push " ", @op, " ", (sub.compile o, level), " && ", (@second.compile o, LEVEL_OP) if o.level <= LEVEL_OP then sn(this, ...code) else sn(this, "(", ...code, ")") compileExistence: (o) -> if @void or not o.level x = Binary \&& Existence(@first, true), @second return (x <<< {+void})compile-node o x = @first.cache o, true sn(this, If(Existence x.0; x.1)add-else(@second)compile-expression o) # `x instanceof [A, B]` => `x instanceof A || x instanceof B` compileAnyInstanceOf: (o, items) -> [sub, ref, @temps] = @first.cache o test = Binary \instanceof sub, items.shift! for item in items then test = Binary \|| test, Binary \instanceof ref, item sn(this, Parens test .compile o) compileMinMax: (o) -> lefts = @first .cache o, true rites = @second.cache o, true x = Binary @op.char-at!, lefts.0, rites.0 sn(this, If x, lefts.1 .add-else rites.1 .compile-expression o) compileMethod: (o, klass, method, arg) -> args = [@second] ++ (arg || []) if @first"is#klass"! sn(this, Chain(@first, [Index Key method; Call args])compile o) else args.unshift @first sn(this, Call.make(JS util(method) + \.call; args)compile o) compileJoin : -> @compileMethod it, \Array \join compileRemove : -> @compileMethod it, \String \replace JS "''" compileSplit : -> @compileMethod it, \String \split compileRepeat: (o) -> {first: x, second: n} = this {items} = x.=expand-slice o .unwrap! arr = x.is-array! and \Array if items and !sn-empty(arrCode = Splat.compile-array o, items) x = JS arrCode items = null if arr and not items or not (n instanceof Literal and n.value < 0x20) return sn(this, (Call.make Util(\repeat + (arr or \String)), [x, n] .compile o)) n = +n.value return sn(this, x.compile o) if 1 <= n < 2 # `[x] * 2` => `[x, x]` if items if n < 1 then return sn(this, (Block items .add JS '[]' .compile o)) refs = [] for item, i in items then [items[i], refs.*] = item.cache o, 1x items.push JS! <<< compile: -> sn(this, ...(([", ", (List.compile o, refs)] * (n-1))slice 1)) sn(this, x.compile o) # `'x' * 2` => `'xx'` else if x instanceof Literal sn(this, (q = (x.=compile o .to-string!)char-at!) + "#{ x.slice 1 -1 }" * n + q) # `"#{x}" * 2` => `(ref$ = "" + x) + ref$` else if n < 1 then return sn(this, Block(x.it)add(JS "''")compile o) x = (refs = x.cache o, 1, LEVEL_OP)0 + " + #{refs.1}" * (n-1) if o.level < LEVEL_OP + PREC\+ then sn(this, x) else sn(this, "(", x, ")") compilePow: (o) -> sn(null, Call.make(CopyL this, JS \Math.pow; [@first, @second])compile o) compileConcat: (o) -> f = (x) -> | x instanceof Binary and x.op is \++ => (f x.first) ++ (f x.second) | otherwise => [x] sn(null, (Chain @first .add(CopyL this, Index (Key \concat), \., true) .add Call(f @second) .compile o)) compileCompose: (o) -> op = @op functions = [@first] x = @second while x instanceof Binary and x.op is op and not x.partial functions.push x.first x = x.second functions.push x functions.reverse! if op is \<< sn(this, (Chain Var (util \compose) .add Call functions .compile o)) compileMod: (o) -> ref = o.scope.temporary! code = [sn(this, "((("), (@first.compile o), sn(this, ") % ("), sn(this, ref, " = "), (@second.compile o), sn(this, ") + ", ref, ") % ", ref, ")")] o.scope.free ref sn(null, ...code) compilePartial: (o) -> vit = Var \it switch case not @first? and not @second? x = Var \x$ y = Var \y$ sn(this, (Fun [x, y], Block((Binary @op, x, y).invert-check this), false, true).compile o) case @first? sn(this, "(", ((Fun [vit], Block((Binary @op, @first, vit) .invert-check this), true).compile o), ")") default sn(this, "(", ((Fun [vit], Block((Binary @op, vit, @second).invert-check this), true).compile o), ")") compileRegexEquals: (o, [regex, target]) -> if @op is \=== method = if @was-inverted then \test else \exec sn(this, (Chain regex .add Index Key method .add Call [target] .compile o)) else sn(this, (Unary \! (Chain regex .add Index Key \test .add Call [target]) .compile o)) compileDeepEq: (o) -> if @op in <[ >== >>= ]> [@first, @second] = [@second, @first] @op = if @op is \>== then \<== else \<<= if @op is \!== @op = \=== negate = true for x in [@first, @second] x.deep-eq = true if x instanceof [Obj, Arr] r = Chain Var (util \deepEq) .add Call [@first, @second, Literal "'#{@op}'"] sn(this, (if negate then Unary \! r else r).compile o) compileXor: (o) -> left = Chain @first .cache-reference o right = Chain @second .cache-reference o sn(this, (Binary \&& (Binary \!== (Unary \! left.0), (Unary \! right.0)) , (Parens Binary \|| left.1, right.1) .compile o)) #### Assign # Assignment to a variable/property. class exports.Assign extends Node (@left, rite, @op or \=, @logic or @op.logic, @defParam) ~> @opLoc = @op @op += '' @[if rite instanceof Node then \right else \unaries] = rite children: <[ left right ]> show: -> [,]concat(@unaries)reverse!join(' ') + [@logic] + @op assigns: -> @left.assigns it ::delegate <[ isCallable isRegex ]> -> @op in <[ = := ]> and @right and @right[it]! is-array: -> switch @op | \= \:= => @right and @right.is-array! | \/= => @right and @right.is-matcher! is-string: -> switch @op | \= \:= \+= \*= => @right and @right.is-string! | \-= => @right and @right.is-matcher! unfold-soak: (o) -> if @left instanceof Existence # `[a, b]? = c` => `[a, b] = c if c?` if delete (@left.=it)name then rite = @right; rite = Assign @right = Var(that), rite else [rite, @right, temps] = @right.cache o return If(Existence rite; this) <<< {temps, @cond, @void} If.unfold-soak o, this, \left unfold-assign: -> @access and this compile-node: (o) -> return @compileSplice o if @left instanceof Slice and @op is \= left = @left left.=it if sp = @left instanceof Splat left.=expand-slice(o, true)unwrap! if sp left instanceof List or @left.carp 'invalid splat' return @compile-spread o, left unless @right left.is-assignable! or left.carp 'invalid unary assign' [left, @right] = Chain left .cache-reference o for op in @unaries then @right = Unary op, @right return sn(null, (Parens(@right) <<< {@front, @newed})compile o) if left.is-empty! if left.get-default! @right = Binary left.op, @right, left.second left.=first return @compileDestructuring o, left if left.items left.is-assignable! or left.carp 'invalid assign' return @compileConditional o, left if @logic {op, right} = this return @compileMinMax o, left, right if op in <[ ?= ]> if op in <[ **= ^= %%= ++= |>= ]> or op is \*= and right.is-string! or op in <[ -= /= ]> and right.is-matcher! [left, reft] = Chain(left)cache-reference o right = Binary op.slice(0 -1), reft, right op = \:= op = (op.slice 1 -2) + \= if op in <[ .&.= .|.= .^.= .<<.= .>>.= .>>>.= ]> (right.=unparen!)rip-name left.=unwrap! sign = sn(@opLoc, " ", (op.replace \: ''), " ") name = ((left <<< {+front})compile o, LEVEL_LIST) if lvar = left instanceof Var if op is \= o.scope.declare name.to-string!, left, (@const or not @defParam and o.const and \$ isnt name.to-string!.slice -1) else if o.scope.checkReadOnly name.to-string! left.carp "assignment to #that \"#name\"" ReferenceError if left instanceof Chain and right instanceof Fun proto-split = name.to-string!.split '.prototype.' dot-split = name.to-string!.split \. if proto-split.length > 1 right.in-class = proto-split.0 else if dot-split.length > 1 right.in-class-static = dot-split[til -1].join '' code = if not o.level and right instanceof While and not right.else and (lvar or left instanceof Chain and left.is-simple-access!) # Optimize `a = while ...`. empty = if right.objComp then '{}' else '[]' [(res = o.scope.temporary \res), " = #empty;\n#{@tab}", (right.make-return(res)compile o), "\n#{@tab}", name, sign, o.scope.free res] else [name, sign, (right.compile o, LEVEL_LIST)] code = ["(", ...code, ")"] if o.level > LEVEL_LIST sn(null, ...code) compileConditional: (o, left) -> if left instanceof Var and @logic in <[ ? ]> and @op is \= o.scope.declare left.value, left lefts = Chain(left)cache-reference o # Deal with `a && b ||= c`. o.level += LEVEL_OP < o.level morph = Binary @logic, lefts.0, @<<<{-logic, left: lefts.1} sn(this, (morph <<< {@void})compile-node o) compileMinMax: (o, left, right) -> lefts = Chain(left)cache-reference o rites = right.cache o, true test = Binary @op.replace(\? ''), lefts.0, rites.0 put = Assign lefts.1, rites.1, \:= # `a `a <= b || a = b ` return Parens(Binary \|| test, put)compile o if @void or not o.level # `r = a `r = if a <= b then a else a = b` [test.first, left] = test.first.cache o, true sn(this, (If test, left .add-else put .compile-expression o)) # Implementation of recursive destructuring, # when assigning to an array or object literal. # See . compileDestructuring: (o, {{length: len}:items}:left) -> ret = o.level and not @void rite = @right.compile o, if len is 1 then LEVEL_CALL else LEVEL_LIST if left.name cache = sn(this, that, " = ", rite) o.scope.declare rite = that, left else if (ret or len > 1) and (not ID.test rite.to-string! or left.assigns rite.to-string!) cache = sn(this, (rref = o.scope.temporary!), " = ", rite) rite = rref if rite.to-string! is \arguments and not ret destructure-args = true if left not instanceof Arr @carp 'arguments can only destructure to array' list = @"rend#{ left.constructor.display-name }" o, items, rite, destructure-args o.scope.free rref if rref list.unshift cache if cache list.push rite if ret or not list.length code = [] sep = if destructure-args then '; ' else ', ' for item in list code.push item, sep code.pop! if list.length < 2 or o.level < LEVEL_LIST then sn(this, ...code) else sn(this, "(", ...code, ")") compileSplice: (o) -> [from-exp-node, from-exp] = Chain @left.from .cache-reference o [right-node, right] = Chain @right .cache-reference o to-exp = Binary \- @left.to, from-exp sn(this, (Block [Chain Var (util \splice) .add Index (Key \apply), \. true .add Call [@left.target, (Chain Arr [from-exp-node, to-exp] .add Index (Key \concat), \. true .add Call [right-node])]; right] .compile o, LEVEL_LIST)) compile-spread: (o, left) -> [rite, rref] = if @unaries then [that] * 2 else if left.items.length <= 1 then [@right] * 2 else @right.cache o, true @compile-spread-over o, left, ~> result = constructor it, rite, @op, @logic rite := rref result rendArr: (o, nodes, rite, destructure-args) -> ~function args-slice(begin, end) # [&[..] for from (begin) til (end)] new For {+ref, from: begin, op: \til, to: end} .make-comprehension (Chain Var \arguments .add Index Literal \..), [] ret = [] for node, i in nodes continue if node.is-empty! if node instanceof Splat len and node.carp 'multiple splat in an assignment' skip = (node.=it).is-empty! if i+1 is len = nodes.length break if skip if destructure-args val = args-slice do # from i to &length Literal(i) (Chain Var \arguments .add Index Key \length) else val = Arr.wrap JS do util(\slice) + \.call( + rite + if i then ", #i)" else \) else val = ivar = "#rite.length - #{ len - i - 1 }" # Optimize `[..., a] = b`. continue if skip and i+2 is len start = i+1 @.[]temps.push ivar = o.scope.temporary \i val = switch | skip Arr.wrap JS "#i < (#ivar = #val) ? #i : (#ivar = #i)" | destructure-args args-slice do JS "#i < (#ivar = #val) ? #i : (#ivar = #i)" Var ivar | _ Arr.wrap JS do "#i < (#ivar = #val) \ ? #{ util \slice }.call(#rite, #i, #ivar) \ : (#ivar = #i, [])" else (inc = ivar) and start < i and inc += " + #{ i - start }" val = Chain rcache||=Literal(rite), [Index JS inc || i] if node instanceof Assign node = Binary node.op, node.left, node.right, (node.logic or true) if destructure-args if node not instanceof Var and val instanceof For # avoid accidentally creating closure @.[]temps.push tmp = o.scope.temporary \ref vtmp = Var tmp ret.push (this with {left: vtmp, right: val, +void})compile o, LEVEL_TOP ret.push (this with {left: node, right: vtmp, +void})compile o, LEVEL_TOP else ret.push (this with {left: node, right: val, +void})compile o, LEVEL_TOP else ret.push (this with {left: node, right: val, +void})compile o, LEVEL_PAREN ret rendObj: (o, nodes, rite) -> for node in nodes node.=it if splat = node instanceof Splat # `{a or b} = c` => `a = c.a or b` node.=first if logic = node.get-default! if node instanceof Parens [node, key] = Chain(node.it)cache-reference o else if node instanceof Prop then node = ({key} = node)val else key = node node = CopyL node, Var node.name if node instanceof Key node = logic <<< first: node if logic val = Chain rcache||=Var(rite), [Index key.maybe-key!] val = Import Obj!, val if splat (this with {left: node, right: val, +void})compile o, LEVEL_PAREN #### Import # Copies properties from right to left. class exports.Import extends Node (@left, @right, @all and \All) ~> if not all and left instanceof Obj and right.items return Obj left.items ++ right.as-obj!items children: <[ left right ]> show: -> @all ::delegate <[ isCallable isArray ]> -> @left[it]! unfold-soak: (o) -> {left} = this if left instanceof Existence and not left.negated if left.=it instanceof Var {value} = @left = left unless o.scope.check value, true left = JS "typeof #value != 'undefined' && #value" else [left, @left, temps] = left.cache o return If(left, this) <<< {temps, +soak, @cond, @void} If.unfold-soak o, this, \left or (@void or not o.level) and If.unfold-soak o, this, \right compile-node: (o) -> {right} = this unless @all if right instanceof Chain right = right.unfold-soak o or right.unfold-assign o or right.expand-slice o .unwrap! return @compile-assign o, right.as-obj!items if right instanceof List (CopyL this, Call.make Util("import#{ @all or '' }"), [@left, right]) .compile-node o # If the right operand of `<<<` is an object or array literal, # expand it to a series of assignments. compile-assign: (o, items) -> return @left.compile o unless items.length top = not o.level if @proto or (items.length < 2 and (top or @void or items.0 instanceof Splat)) reft = @left reft = Parens reft if reft.is-complex! else [left, reft, @temps] = @left.cache o [delim, space] = if top then [\; \\n + @tab] else [\, ' '] delim += space code = if @temps then [left.compile(o, LEVEL_PAREN), delim] else [] for node, i in items i and code.push if com then space else delim if com = node.comment code.push node.compile o continue if node instanceof Splat code.push Import(reft, node.it)compile o continue node.=first if logic = node.get-default! if dyna = node instanceof Parens [key, val] = node.it.cache o, true else if node instanceof Prop {key, val} = node if node.accessor key = JS "'#{key.name}'" if key instanceof Key code.push "Object.defineProperty(", (reft.compile o, LEVEL_LIST), ", ", (key .compile o, LEVEL_LIST), ", ", (node.compile-descriptor o), ")" continue else key = val = node dyna or key.=maybe-key! logic and val = logic <<< first: val code.push (Assign(Chain reft, [Index key]; val)compile o, LEVEL_PAREN) return sn(null, ...code) if top @void or node instanceof Splat or code.push (if com then ' ' else ', '), (reft.compile o, LEVEL_PAREN) if o.level < LEVEL_LIST then sn(null, ...code) else sn(null, "(", ...code, ")") #### In # Handles `in` operation that tests if the left operand is included within # the right operand, arraywise. class exports.In extends Node implements Negatable (@item, @array) -> children: <[ item array ]> compile-node: (o) -> {items} = array = @array.expand-slice(o)unwrap! if array not instanceof Arr or items.length < 2 return sn(this, (if @negated then \! else ''), (util \in), "(", (@item.compile o, LEVEL_LIST), ", ", (array.compile o, LEVEL_LIST), ")") code = [] [sub, ref] = @item.cache o, false, LEVEL_PAREN [cmp, cnj] = if @negated then [' !== ' ' && '] else [' === ' ' || '] for test, i in items code.push cnj if code.length > 0 if test instanceof Splat code.push (new In(Var ref; test.it) <<< {@negated})compile o, LEVEL_TOP code = ["(#sub, ", ...code, ")"] unless i or sub is ref else code.push (if i or sub is ref then ref else "(#sub)"), cmp, (test.compile o, LEVEL_OP + PREC\== ) sub is ref or o.scope.free ref if o.level < LEVEL_OP + PREC\|| then sn(this, ...code) else sn(this, "(", ...code, ")") #### Existence # Checks a value for existence--not `undefined` nor `null`. class exports.Existence extends Node implements Negatable (@it, @negated) ~> children: [\it] compile-node: (o) -> node = @it.unwrap! <<< {@front} code = [(node.compile o, LEVEL_OP + PREC\==)] if node instanceof Var and not o.scope.check code.join(""), true [op, eq] = if @negated then <[ || = ]> else <[ && ! ]> code = ["typeof ", ...code, " #eq= 'undefined' #op ", ...code, " #eq== null"] else code.push " #{ op = if @negated then \== else \!= } null" if o.level < LEVEL_OP + PREC[op] then sn(this, ...code) else sn(this, "(", code, ")") #### Fun # A function definition. This is the only node that creates a `new Scope`. class exports.Fun extends Node (@params or [], @body or Block!, @bound and \this$, @curried or false, @hushed = false, @generator = false) ~> children: <[ params body ]> show: -> [@name] + ["~#that" if @bound] named: -> this <<< {name: it, +statement} is-callable: YES is-statement: -> !!@statement # Short-circuit `traverse-children` method to prevent it # from crossing scope boundaries by default. traverse-children: (, xscope) -> super ... if xscope make-return: -> if @statement then this <<< {+returns} else super ... rip-name: !-> @name ||= it.var-name! compile-node: (o) -> pscope = o.scope sscope = pscope.shared or pscope scope = o.scope = @body.scope = new Scope (if @wrapper then pscope else sscope), @wrapper && sscope scope.fun = this scope.assign \prototype "#{ that.compile o }.prototype" if @proto scope.assign \constructor that if @cname o.indent = @tab = '' if inLoop = delete o.loop o.indent += TAB {body, name, tab} = this code = [\function] if @generator @ctor and @carp "a constructor can't be a generator" o.in-generator = true code.push \* else if not @wrapper o.in-generator = false if @bound is \this$ if @ctor scope.assign \this$ 'this instanceof ctor$ ? this : new ctor$' body.lines.push Return Literal \this$ else if sscope.fun?bound then @bound = that else sscope.assign \this$ \this if @statement name or @carp 'nameless function declaration' pscope is o.block.scope or @carp 'misplaced function declaration' @accessor and @carp 'named accessor' pscope.add name, \function, this if @statement or name and @labeled code.push ' ', (scope.add name, \function, this) @hushed or @ctor or @newed or body.make-return! code.push "(", (@compile-params o, scope), ")" code = [sn(this, ...code)] code.push "{" code.push "\n", bodyCode, "\n#tab" unless sn-empty(bodyCode = body.compile-with-declarations o) code.push \} curry-code-check = ~> if @curried and @has-splats @carp 'cannot curry a function with a variable number of arguments' if @curried and @params.length > 1 and not @class-bound if @bound [(util \curry), "((", ...code, "), true)"] else [(util \curry), "(", ...code, ")"] else code if inLoop then return pscope.assign pscope.temporary(\fn), sn(null, ...curry-code-check!) if @returns code.push "\n#{tab}return ", name, ";" else if @bound and @ctor code.push ' function ctor$(){} ctor$.prototype = prototype;' code = curry-code-check! if @front and not @statement then sn(null, "(", ...code, ")") else sn(null, ...code) compile-params: (o, scope) -> {{length}:params, body} = this # Remove trailing placeholders. for p in params by -1 break unless p.is-empty! or p.filler --params.length for p, i in params if p.left instanceof Splat # splats + default/operator arguments = too ambiguous to support p.carp 'invalid splat' if p instanceof Splat @has-splats = true splace = i # `(a = x) ->` => `(a ? x) ->` else if p.op is \= params[i] = Binary (p.logic or \?), p.left, p.right # `(a, ...b, c) ->` => `(a) -> [[] ...b, c] = &` if splace? rest = params.splice splace, 9e9 else if @accessor that.carp 'excess accessor parameter' if params.1 else unless length or @wrapper params.0 = Var \it if body.traverse-children -> it.value is \it or null names = [] assigns = [] for p in params vr = p vr.=first if df = vr.get-default! if vr.is-empty! vr = Var scope.temporary \arg else if vr.value is \.. vr = Var o.ref = scope.temporary! else if vr not instanceof Var unaries = [] while vr instanceof Unary has-unary = true unaries.push vr vr.=it v = Var delete (vr.it || vr)name || vr.var-name! || scope.temporary \arg assigns.push Assign vr, switch | df => Binary p.op, v, p.second | has-unary => fold ((x, y) -> y.it = x; y), v, unaries.reverse! | otherwise => v vr = v else if df assigns.push Assign vr, p.second, \=, p.op, true names.push (scope.add vr.value, \arg, p), ', ' if rest while splace-- then rest.unshift Arr! assigns.push Assign Arr(rest), Literal \arguments @body.prepend ...assigns if assigns.length names.pop! sn(null, ...names) #### Class class exports.Class extends Node ({@title, @sup, @mixins, body}) -> @fun = Fun [] body children: <[ title sup mixins fun ]> is-callable: YES rip-name: !-> @name = it.var-name! compile: (o, level) -> {{{lines}:body}:fun, title} = this CopyL this, fun bound-funcs = [] curried-bound-funcs = [] decl = title?var-name! name = decl or @name if ID.test name || '' then fun.cname = name else name = \constructor proto = Var \prototype vname = fun.proto = Var fun.bound = name const ctor-name = \constructor$$ var ctor, ctor-place import-proto-obj = (node, i) -> j = 0 while j < node.items.length, j++ prop = node.items[j] key = prop.key if (key instanceof Key and key.name is ctor-name) or (key instanceof Literal and key.value is "'#ctor-name'") node.carp 'redundant constructor' if ctor ctor := prop.val node.items.splice j--, 1 ctor-place := i continue unless prop.val instanceof Fun or prop.accessor if key.is-complex! key = Var o.scope.temporary \key prop.key = Assign key, prop.key if prop.val.bound if prop.val.curried curried-bound-funcs.push prop.key else bound-funcs.push prop.key prop.val.bound = false # need to know whether bound param of curry$ should be true prop.val.class-bound = true for v in [] ++ prop.val v.meth = key if node.items.length Import(Chain vname .add Index Key \prototype; node) <<< {+proto} else Literal 'void' for node, i in lines if node instanceof Obj lines[i] = import-proto-obj node, i else if node instanceof Fun and not node.statement ctor and node.carp 'redundant constructor' ctor = node else if node instanceof Assign and node.left instanceof Chain and node.left.head.value is \this and node.right instanceof Fun node.right.stat = node.left.tails.0.key else node.traverse-children !-> if it instanceof Block for child, k in it.lines when child instanceof Obj it.lines[k] = import-proto-obj child, i ctor ||= lines.* = if @sup then Fun [] Block Chain(new Super).add Call [Splat Literal \arguments] else Fun! unless ctor instanceof Fun lines.splice ctor-place + 1, 0, Assign (Var ctor-name), ctor lines.unshift ctor = Fun [] Block Return Chain(Var ctor-name).add Call [Splat \arguments true] ctor <<< {name, +ctor, +statement} for f in bound-funcs ctor.body.lines.unshift do Assign (Chain Literal \this .add Index f), (Chain Var (util \bind) .add Call [Literal \this; Literal "'#{f.name}'"; Var \prototype]) for f in curried-bound-funcs ctor.body.lines.unshift do Assign (Chain Literal \this .add Index Key "_#{f.name}"), (Chain Var (util \curry) .add Call [Chain Var \prototype .add Index f; Var \true]) Assign (Chain Literal \this .add Index f), (Chain Var (util \bind) .add Call [Literal \this; Literal "'_#{f.name}'"]) lines.push vname args = [] if @sup args.push that imports = Chain Import (Literal \this), Var \superclass fun.proto = Util.Extends (if fun.cname then Block [Assign (imports.add Index Key 'displayName'), Literal "'#name'" ; Literal name] else imports) , fun.params.* = Var \superclass if @mixins imports = for args.* in that Import proto, JS("arguments[#{args.length-1}]"), true body.prepend ...imports body.prepend Literal "#name.displayName = '#name'" if fun.cname and not @sup clas = Parens Call.make(fun, args), true clas = Assign vname, clas if decl and title.is-complex! clas = Assign title, clas if title sn(null, (clas.compile o, level)) #### Super # Reference to the parent method or constructor. class exports.Super extends Node -> is-callable: YES compile: ({scope}:o) -> unless @sproto while not scope.get \superclass and scope.fun, scope.=parent result = that return sn(this, \superclass.prototype, (Index that .compile o)) if result.meth return sn(this, \superclass , (Index that .compile o)) if result.stat if scope.fun.in-class return sn(this, that, ".superclass.prototype.", scope.fun.name) else if scope.fun.in-class-static return sn(this, that, ".superclass.", scope.fun.name) return sn(this, that, ".superclass") if o.scope.fun?name sn(this, \superclass) #### Parens # An extra set of parentheses, # specifying evaluation order and/or forcing expression. class exports.Parens extends Node (@it, @keep, @string, @lb, @rb) ~> children: [\it] show: -> @string and '""' ::delegate <[ isComplex isCallable isArray isRegex ]> -> @it[it]! is-string: -> @string or @it.is-string! unparen: -> if @keep then this else @it.unparen! compile: (o, level ? o.level) -> {it} = this it{cond, \void} ||= this it.head.hushed = true if @calling and (not level or @void) unless @keep or @newed or level >= LEVEL_OP + PREC[it.op] return ((it <<< {@front})compile o, level || LEVEL_PAREN) if it.is-statement! then it.compile-closure o else sn(null, sn(@lb, "("), (it.compile o, LEVEL_PAREN), sn(@rb, ")")) #### Splat # A splat, either as an argument to a call, # the operand of a unary operator to be spread, # or as part of a destructuring assignment. class exports.Splat extends Node (@it, @filler) ~> ::{children, is-complex} = Parens:: is-assignable: YES assigns: -> @it.assigns it compile: -> @carp 'invalid splat' # Compiles a list of nodes mixed with splats to a proper array. @compile-array = (o, list, apply) -> expand list index = 0 for node in list break if node instanceof Splat ++index return sn(this, '') if index >= list.length unless list.1 return sn(this, ((if apply then Object else ensure-array) list.0.it .compile o, LEVEL_LIST)) args = [] atoms = [] for node in list.splice index, 9e9 if node instanceof Splat args.push Arr atoms.splice 0, 9e9 if atoms.length args.push ensure-array node.it else atoms.push node args.push Arr atoms if atoms.length sn(null, (if index then Arr list else args.shift!)compile(o, LEVEL_CALL), sn(this, ".concat("), (List.compile o, args), sn(this, ")")) function expand nodes index = -1 while node = nodes[++index] then if node instanceof Splat {it} = node if it.is-empty! nodes.splice index-- 1 else if it instanceof Arr nodes.splice index, 1, ...expand it.items index += it.items.length - 1 nodes function ensure-array node return node if node.is-array! Call.make JS(util(\slice) + \.call), [node] #### Jump # `break` `continue` class exports.Jump extends Node (@verb, @label) -> show: -> (@verb or '') + if @label then ' ' + that else '' is-statement : YES make-return : THIS get-jump: (ctx or {}) -> return this unless ctx[@verb] return that not in (ctx.labels ?= []) and this if @label compile-node: (o) -> if @label then that in (o.labels ?= []) or @carp "unknown label \"#that\"" else o[@verb] or @carp "stray #{@verb}" sn(this, @show! + \;) @extended = !(sub) -> sub::children = [\it] @[sub.display-name.toLowerCase!] = sub #### Throw class exports.Throw extends Jump (@it) ~> get-jump: VOID compile-node: (o) -> sn(this, "throw ", (@it?compile o, LEVEL_PAREN or \null), ";") #### Return class exports.Return extends Jump ~> if it and it.value is not \void then this <<< {it} get-jump: THIS compile-node: (o) -> sn(this, "return", ...(if @it then [' ', (that.compile o, LEVEL_PAREN)] else []), ";") #### While # The traditional `while`/`for`/`do` loop. # Returns an array of values collected from the last expression when requested. class exports.While extends Node (test, @un, mode) -> mode and if mode instanceof Node then @update = mode else @post = true # `while true` `until false` => `for (;;)` if @post or test.value is not ''+!un then this <<< {test} children: <[ test body update else ]> a-source: \test, aTargets: <[ body update ]> show: -> [\! if @un; \do if @post] * '' ::is-statement = ::is-array = YES make-comprehension: (toAdd, loops) -> @is-comprehension = true while loops.length toAdd = loops.pop!add-body Block toAdd toAdd <<< {+in-comprehension} if not toAdd.is-comprehension @add-body Block toAdd get-jump: (ctx or {}) -> ctx <<< {+\continue, +\break} for node in @body?.lines or [] then return node if node.get-jump ctx add-body: (@body) -> @body = Block If @guard, @body if @guard [top] = @body.lines @body.lines.length = 0 if top?verb is \continue and not top.label this add-guard: (@guard) -> this add-obj-comp: (@objComp = true) -> this make-return: -> return this if @has-returned if it if @objComp @body = Block @body.make-return it, true else unless @body or @index @add-body Block Var @index = \ridx$ last = @body.lines?[*-1] if (@is-comprehension or @in-comprehension) and not last?is-comprehension @body.make-return ...& @else?make-return ...& @has-returned = true else @res-var = it @else?make-return ...& else @get-jump! or @returns = true this compile-node: (o) -> o.loop = true @test and if @un then @test.=invert! else @anaphorize! return sn(null, sn(this, 'do {'), @compile-body (o.indent += TAB; o)) if @post test = @test?compile o, LEVEL_PAREN or '' unless @update or @else head = unless sn-empty(test) then [sn(this, "while ("), test] else [sn(this, 'for (;;')] else head = [sn(this, 'for (')] head.push (@yet = o.scope.temporary \yet), " = true" if @else head.push sn(this, ";"), (test.to-string! and ' '), test, sn(this, ";") head.push ' ', (that.compile o, LEVEL_PAREN) if @update sn(null, ...head, sn(this, ') {'), (@compile-body (o.indent += TAB; o))) compile-body: (o) -> o.break = o.continue = true {body: {lines}, yet, tab} = this code = [] ret = [] mid = [] empty = if @objComp then '{}' else '[]' var _result-name get-result-name = ~> _result-name ? _result-name := o.scope.temporary if @objComp then 'resultObj' else 'results' last = lines?[*-1] if not (@is-comprehension or @in-comprehension) or last?is-comprehension has-loop = false last?traverse-children !-> if it instanceof Block and it.lines[*-1] instanceof While has-loop := true if @returns and not @res-var @res-var = res = o.scope.assign get-result-name!, empty if @res-var and (last instanceof While or has-loop) temp = o.scope.temporary \lresult lines.unshift Assign (Var temp), (if lines[*-1].objComp then Obj! else Arr!), \= lines[*-1]?=make-return temp mid.push TAB, (Chain Var @res-var .add Index (Key \push), \., true .add Call [Chain Var temp] .compile o), ";\n#{@tab}" else @has-returned = true if @res-var @body.make-return @res-var if @returns if (not last instanceof While and not @has-returned) or @is-comprehension or @in-comprehension lines[*-1]?=make-return (res = o.scope.assign get-result-name!, empty), @objComp ret.push "\n#{@tab}return ", (res or empty), ";" @else?make-return! yet and lines.unshift JS "#yet = false;" code.push "\n", bodyCode, "\n#tab" unless sn-empty(bodyCode = @body.compile o, LEVEL_TOP) code.push ...mid code.push \} code.push sn(this, " while ("), (@test.compile o<<<{tab} LEVEL_PAREN), sn(this, ");") if @post if yet code.push sn(this, " if ("), yet, sn(this, ") "), (@compile-block o, Block @else) o.scope.free yet sn(null, ...code, ...ret) #### For # LiveScript's replacements for the `for` loop are array, object or range iterators. class exports.For extends While -> this <<<< it @item = null if @item instanceof Var and not @item.value for @kind or [] => @[..] = true @carp '`for own` requires `of`' if @own and not @object children: <[ item source from to step body ]> a-source: null show: -> ((@kind || []) ++ @index).join ' ' add-body: (body) -> has-yield = !!body.traverse-children (child) -> return true if child instanceof Yield if @let @item = Literal \.. if delete @ref body = Block Call.let do with [] ..push Assign Var(that), Literal \index$$ if @index ..push Assign that, Literal \item$$ if @item body has-yield super body if @guard and @let and (@index or @item) @body.lines[0].if.traverse-children !~> if it instanceof Var if @index and it.value is @index it.value = \index$$ if @item and it.value is @item.value it.value = \item$$ if @let @body := Block Yield \yieldfrom, body if has-yield delete @index delete @item this compile-node: (o) -> o.loop = true temps = @temps = [] if @object and @index then o.scope.declare idx = @index else temps.push idx = o.scope.temporary \i @add-body Block Var idx if not @body unless @object [pvar, step] = (@step or Literal 1)compile-loop-reference o, \step pvar is step or temps.push pvar if @from @item = Var idx if @ref [tvar, tail] = @to.compile-loop-reference o, \to fvar = @from.compile o, LEVEL_LIST vars = "#idx = #fvar" unless tail is tvar vars += ", #tail" temps.push tvar pvar = step = -1 if not @step and +fvar > +tvar eq = if @op is \til then '' else \= cond = if +pvar then "#idx #{ '<>'char-at pvar < 0 }#eq #tvar" else "#pvar < 0 ? #idx >#eq #tvar : #idx <#eq #tvar" else @item = Var o.scope.temporary \x if @ref if @item or @object and @own or @let [svar, srcPart] = @source.compile-loop-reference o, \ref, not @object, true svar is srcPart or temps.push svar else svar = srcPart = @source.compile o, LEVEL_PAREN unless @object if 0 > pvar and ~~pvar is +pvar # negative int vars = "#idx = #srcPart.length - 1" cond = "#idx >= 0" else temps.push lvar = o.scope.temporary \len vars = "#idx = 0, #lvar = #srcPart.length" cond = "#idx < #lvar" @else and @yet = o.scope.temporary \yet head = [sn(this, 'for (')] head.push idx, " in " if @object head.push that, " = true, " if @yet if @object head.push srcPart else step is pvar or vars += ', ' + step head.push vars, "; ", cond, "; " + if 1 ~= Math.abs pvar then (if pvar < 0 then \-- else \++) + idx else idx + if pvar < 0 then ' -= ' + pvar.to-string!.slice 1 else ' += ' + pvar @own and head.push sn(this, ") if ("), (o.scope.assign \own$ '{}.hasOwnProperty'), ".call(", svar, ", ", idx, ")" head.push sn(this, ') {') if @let @body.traverse-children !-> switch it.value | \index$$ => it.value = idx | \item$$ => it.value = "#svar[#idx]" o.indent += TAB if @index and not @object head.push \\n + o.indent, Assign(Var @index; JS idx).compile(o, LEVEL_TOP), \; if @item and not @item.is-empty! and not @from head.push \\n + o.indent, Assign(@item, JS "#svar[#idx]")compile(o, LEVEL_TOP), \; o.ref = @item.value if @ref body = @compile-body o head.push \\n + @tab if (@item or (@index and not @object)) and \} is body.to-string!.char-at 0 sn(null, ...head, body) #### Step slice # Slices a list in steps # Makes it possible to combine non-literals and the BY keyword in slices # E.g. list[1 to 10][f() to x by (1+1)] class exports.StepSlice extends For make-return: (@make-returnArg) -> super ... compile-node: (o) -> @index = o.scope.temporary \x [sub, ref, temps] = @target.unwrap!cache o @guard = Binary '<' (Literal @index), (Chain ref .add Index Key \length) @make-comprehension (Chain ref .add Index Literal @index), this if @make-returnArg? then @make-return @make-returnArg code = [] if temps then code.push sub.compile(o), \; + \\n + o.indent code.push super ... sn(this, ...code) #### Try # Classic `try`-`catch`-`finally` block with optional `catch`. class exports.Try extends Node (@attempt, @thrown, @recovery, @ensure) -> @recovery?lines.unshift Assign (@thrown or Var \e), Var \e$ children: <[ attempt recovery ensure ]> show: -> @thrown is-statement: YES is-callable: -> @recovery?is-callable! and @attempt.is-callable! get-jump: -> @attempt.get-jump it or @recovery?get-jump it make-return: -> @attempt .=make-return ...& @recovery?=make-return ...& this compile-node: (o) -> o.indent += TAB code = [sn(this, 'try '), (@compile-block o, @attempt)] if @recovery or not @ensure and JS '' code.push sn(that, ' catch (e$) '), (@compile-block o, that) if @ensure code.push sn(that, ' finally '), (@compile-block o, that) sn(null, ...code) #### Switch # Compiles to the regular JS `switch`-`case`-`default`, # but with forced `break` after each cases. class exports.Switch extends Node (@type, @topic, @cases, @default) -> if type is \match @target = Arr topic if topic @topic = null else if topic throw "can't have more than one topic in switch statement" if topic.length > 1 @topic.=0 if @cases.length and (last = @cases[*-1]).tests.length is 1 and last.tests.0 instanceof Var and last.tests.0.value is \_ @cases.pop! @default = last.body children: <[ topic cases default ]> a-source: \topic, aTargets: <[ cases default ]> show: -> @type is-statement: YES is-callable: -> for c in @cases when not c.is-callable! then return false if @default then @default.is-callable! else true get-jump: (ctx or {}) -> ctx.break = true for c in @cases then return that if c.body.get-jump ctx @default?get-jump ctx make-return: -> for c in @cases then c.make-return ...& @default?make-return ...& this compile-node: (o) -> {tab} = this [target-node, target] = Chain @target .cache-reference o if @target topic = if @type is \match t = if target then [target-node] else [] Block (t ++ [Literal \false]) .compile o, LEVEL_PAREN else !!@topic and @anaphorize!compile o, LEVEL_PAREN code = [sn(this, "switch (", sn-safe(topic), ") {\n")] stop = @default or @cases.length - 1 o.break = true for c, i in @cases code.push (c.compile-case o, tab, i is stop, (@type is \match or !topic), @type, target) if @default o.indent = tab + TAB code.push tab + "default:\n", that, "\n" if @default.compile o, LEVEL_TOP sn(null, ...code, tab + \}) #### Case class exports.Case extends Node (@tests, @body) -> children: <[ tests body ]> is-callable: -> @body.is-callable! make-return: -> @body.make-return ...& unless @body.lines[*-1]?value is \fallthrough this compile-case: (o, tab, nobr, bool, type, target) -> tests = [] for test in @tests test.=expand-slice(o)unwrap! if test instanceof Arr and type isnt \match for t in test.items then tests.push t else tests.push test tests.length or tests.push Literal \void if type is \match for test, i in tests tar = Chain target .add Index (Literal i), \., true tests[i] = Parens (Chain test .auto-compare (if target then [tar] else null)) if bool binary = if type is \match then \&& else \|| [t] = tests i = 0 while tests[++i] then t = Binary binary, t, that tests = [(@<<<{t, a-source: \t, aTargets: [\body]})anaphorize!invert!] code = [] for t in tests then code.push tab, sn(t, "case ", (t.compile o, LEVEL_PAREN), ":\n") {lines} = @body last = lines[*-1] lines[*-1] = JS '// fallthrough' if ft = last?value is \fallthrough o.indent = tab += TAB code.push bodyCode, \\n unless sn-empty(bodyCode = @body.compile o, LEVEL_TOP) code.push tab + 'break;\n' unless nobr or ft or last instanceof Jump sn(null, ...code) #### If # The `if`/`else` structure that acts as both statement and expression. class exports.If extends Node (@if, @then, @un) ~> children: <[ if then else ]> a-source: \if, aTargets: [\then] show: -> @un and \! terminator: '' ::delegate <[ isCallable isArray isString isRegex ]> -> @else?[it]! and @then[it]! get-jump: -> @then.get-jump it or @else?get-jump it make-return: -> @then.=make-return ...& @else?=make-return ...& this compile-node: (o) -> if @un then @if.=invert! else @soak or @anaphorize! if o.level then @compile-expression o else @compile-statement o compile-statement: (o) -> code = [sn(this, "if (", (@if.compile o, LEVEL_PAREN), ") ")] o.indent += TAB code.push (@compile-block o, Block @then) return sn(null, ...code) unless els = @else sn(null, ...code, sn(els, ' else '), (if els instanceof constructor then els.compile o <<< indent: @tab, LEVEL_TOP else @compile-block o, els)) compile-expression: (o) -> {then: thn, else: els or Literal \void} = this @void and thn.void = els.void = true if not @else and (@cond or @void) return Parens Binary \&& @if, thn .compile o code = [sn(this, @if.compile o, LEVEL_COND)] pad = if els.is-complex! then \\n + o.indent += TAB else ' ' code.push "#pad", sn(thn, "? "), (thn.compile o, LEVEL_LIST), "#pad", sn(els, ": "), (els.compile o, LEVEL_LIST) if o.level < LEVEL_COND then sn(null, ...code) else sn(null, "(", code, ")") # Unfolds a node's child if soak, # then tuck the node under the created **If**. @unfold-soak = (o, parent, name) -> if parent[name]unfold-soak o parent[name] = that.then that <<< {parent.cond, parent.void, then: Chain parent} #### Label # A labeled block or statement. class exports.Label extends Node (@label or \_, @it) -> if fun = it instanceof [Fun, Class] and it or it.calling and it.it.head fun.name or fun <<< {name: @label, +labeled} return it ::{children, is-callable, is-array} = Parens:: show: -> @label is-statement: YES get-jump: (ctx or {}) -> (ctx.labels ?= []).push @label @it.get-jump ctx <<< {+\break} make-return: -> @it.=make-return ...&; this compile-node: (o) -> {label, it} = this labels = o.labels = [...o.labels or []] @carp "duplicate label \"#label\"" if label in labels labels.push label it.is-statement! or it = Block it sn(null, sn(this, label, ": "), (if it instanceof Block then o.indent += TAB; @compile-block o, it else it.compile o)) #### Cascade class exports.Cascade extends Node (@input, @output, @prog1) ~> show: -> @prog1 children: <[ input output ]> terminator: '' ::delegate <[ isCallable isArray isString isRegex ]> -> @[if @prog1 then \input else \output][it]! get-jump: -> @output.get-jump it make-return: (@ret) -> this compile-node: ({level}:o) -> {input, output, prog1, ref} = this if prog1 and (\ret of this or level and not @void) output.add (Literal(\..) <<< {+cascadee}) if \ret of this output.=make-return @ret if ref then prog1 or output = Assign Var(ref), output else ref = o.scope.temporary \x if input instanceof Cascade then input <<< {ref} else input &&= Assign Var(ref), input o.level &&= LEVEL_PAREN code = [(input.compile o)] out = Block output .compile o <<< ref: new String ref @carp "unreferred cascadee" if prog1 is \cascade and not o.ref.erred return sn(null, ...code, input.terminator, "\n", out) unless level code.push ", ", out if level > LEVEL_PAREN then sn(null, "(", ...code, ")") else sn(null, ...code) #### JS # Embedded JavaScript snippets. class exports.JS extends Node (@code, @literal, @comment) ~> show: -> if @comment then @code else "`#{@code}`" terminator: '' ::is-assignable = ::is-callable = -> not @comment compile: -> sn(this, sn-safe(if @literal then entab @code, it.indent else @code)) #### Require class exports.Require extends Node (@body) ~> children: <[ body ]> compile: (o) -> get-value = (item, throw-error) ~> | item instanceof Key => item.name | item instanceof Var => item.value | item instanceof Literal => item.value | otherwise => if throw-error then @carp 'invalid require! argument' else item process-item = (item) -> [asg, value] = switch | item instanceof Prop => [item.val, item.key] | otherwise => [item, item] asg-value = get-value asg to-asg = if typeof! asg-value is 'String' then CopyL asg, Var name-from-path asg-value else asg value = strip-string get-value value, true main = Chain (CopyL this, Var 'require') .add Call [Literal "'#value'"] sn(item, (Assign to-asg, main .compile o)) if @body.items? code = [] for item in @body.items code.push (process-item item), ";\n#{o.indent}" code.pop! sn(null, ...code) else sn(null, process-item @body) #### Util # A wrapper node for utility functions. class exports.Util extends Node (@verb) ~> {(Jump::)show} is-callable: YES compile: -> sn(this, util @verb) ##### Util.Extends # An operator that sets up class-ical inheritance between two constructors, # returning the left one. @Extends = -> Call.make Util(\extend), &[0 1] #### Vars # Declares uninitialized variables. class exports.Vars extends Node (@vars) ~> children: [\vars] make-return: THIS compile: (o, level) -> for {value}:v in @vars v.carp 'invalid variable declaration' unless v instanceof Var v.carp "redeclaration of \"#value\"" if o.scope.check value o.scope.declare value, v sn(this, (Literal \void .compile o, level)) #### Parser Utils # Helpers for modifying nodes in [parser](../lib/parser.js). exports.L = (a, b, node) -> if node && typeof node == "object" node <<< first_line: a.first_line+1 first_column: a.first_column last_line: b.last_line+1 last_column: b.last_column line: a.first_line+1 column: a.first_column node exports.CopyL = CopyL = (a, node) -> if node && typeof node == "object" node <<< first_line: a.first_line first_column: a.first_column last_line: a.last_line last_column: a.last_column line: a.line column: a.column node exports.Box = (v) -> if typeof v == "object" v else new v.constructor(v) exports.Decl = (type, nodes, lno) -> throw SyntaxError "empty #type on line #lno" unless nodes.0 DECLS[type] nodes DECLS = export: (lines) -> i = -1 out = Util \out while node = lines[++i] if node instanceof Block lines.splice i-- 1 ...node.lines continue if node instanceof Fun and node.name lines.splice i++ 0 Assign Chain(out, [Index Key that]), Var that continue lines[i] = if node.var-name! or node instanceof Assign and node.left. var-name! or node instanceof Class and node.title?var-name! then Assign Chain(out, [Index Key that]), node else Import out, node Block lines import: (lines, all) -> for line, i in lines then lines[i] = Import Literal(\this), line, all Block lines import-all: -> @import it, true const: (lines) -> for node in lines node.op is \= or node.carp 'invalid constant variable declaration' node.const = true Block lines var: Vars ##### Scope # Regulates lexical scoping within LiveScript. As you # generate code, you create a tree of scopes in the same shape as the nested # functions. Each scope knows about the function parameters and the variables # declared within it, and has references to its parent/shared enclosing scopes. !function Scope @parent, @shared @variables = {} Scope ::= READ_ONLY: const:\constant function:\function undefined:\undeclared # Adds a new variable or overrides an existing one. add: (name, type, node) -> if node and t = @variables"#name." if @READ_ONLY[t] or @READ_ONLY[type] node.carp "redeclaration of #that \"#name\"" else if t is type is \arg node.carp "duplicate parameter \"#name\"" else if t is \upvar node.carp "accidental shadow of \"#name\"" return name if t in <[ arg function ]> # Dot-suffix to bypass `Object::` members. @variables"#name." = type name get: (name) -> @variables"#name." # Declares a variable unless declared already. declare: (name, node, constant) -> if @shared return if @check name scope = that else scope = this scope.add name, (if constant then \const else \var), node # Ensures that an assignment is made at the top of this scope. assign: (name, value) -> @add name, {value} # If we need to store an intermediate result, find an available name for a # compiler-generated variable. `var$`, `var1$`, and so on. temporary: (name || \ref) -> until @variables"#name\$." in [\reuse void] name = if name.length < 2 and name < \z then String.fromCharCode name.charCodeAt! + 1 else name.replace /\d*$/ -> ++it @add name + \$, \var # Allows a variable to be reused. free: (name) -> @add name, \reuse # Checks to see if a variable has already been declared. # Walks up the scope if `above` flag is specified. check: (name, above) -> return type if (type = @variables"#name.") or not above @parent?check name, above # Checks if a variable can be reassigned. check-read-only: (name) -> return that if @READ_ONLY[@check name, true] @variables"#name." ||= \upvar '' # Concatenates the declarations in this scope. emit: (code, tab) -> vrs = [] asn = [] fun = [] for name, type of @variables name.=slice 0 -1 if type in <[ var const reuse ]> vrs.push name, ", " else if type.value if ~(val = entab that, tab).to-string!.last-index-of \function( 0 if val instanceof SourceNode sn-remove-left(val, 8) else val = val.slice(8) fun.push "function ", name, val, "\n#tab" else asn.push name, " = ", val, ", " declCode = vrs.concat asn declCode.pop! fun.pop! code = sn(this, "#{tab}var ", ...declCode, ";\n", code) if declCode.length > 0 if fun.length > 0 then sn(this, code, "\n#tab", ...fun) else sn(this, code) ##### Constants function YES then true function NO then false function THIS then this function VOID then void UTILS = # Creates an object's prototypal child, ensuring `__proto__`. clone: '''function(it){ function fun(){} fun.prototype = it; return new fun; }''' # Sets up `.prototype` between a pair of constructors # as well as `.constructor` and `.superclass` references. extend: '''function(sub, sup){ function fun(){} fun.prototype = (sub.superclass = sup).prototype; (sub.prototype = new fun).constructor = sub; if (typeof sup.extended == 'function') sup.extended(sub); return sub; }''' # Creates a bound method. bind: '''function(obj, key, target){ return function(){ return (target || obj)[key].apply(obj, arguments) }; }''' # Copies properties from right to left. import: '''function(obj, src){ var own = {}.hasOwnProperty; for (var key in src) if (own.call(src, key)) obj[key] = src[key]; return obj; }''' import-all: '''function(obj, src){ for (var key in src) obj[key] = src[key]; return obj; }''' repeat-string: '''function(str, n){ for (var r = ''; n > 0; (n >>= 1) && (str += str)) if (n & 1) r += str; return r; }''' repeat-array: '''function(arr, n){ for (var r = []; n > 0; (n >>= 1) && (arr = arr.concat(arr))) if (n & 1) r.push.apply(r, arr); return r; }''' in: '''function(x, xs){ var i = -1, l = xs.length >>> 0; while (++i < l) if (x === xs[i]) return true; return false; }''' out: '''typeof exports != 'undefined' && exports || this''' curry: '''function(f, bound){ var context, _curry = function(args) { return f.length > 1 ? function(){ var params = args ? args.concat() : []; context = bound ? context || this : this; return params.push.apply(params, arguments) < f.length && arguments.length ? _curry.call(context, params) : f.apply(context, params); } : f; }; return _curry(); }''' flip: '''function(f){ return curry$(function (x, y) { return f(y, x); }); }''' partialize: '''function(f, args, where){ var context = this; return function(){ var params = slice$.call(arguments), i, len = params.length, wlen = where.length, ta = args ? args.concat() : [], tw = where ? where.concat() : []; for(i = 0; i < len; ++i) { ta[tw[0]] = params[i]; tw.shift(); } return len < wlen && len ? partialize$.apply(context, [f, ta, tw]) : f.apply(context, ta); }; }''' not: '''function(x){ return !x; }''' compose: '''function() { var functions = arguments; return function() { var i, result; result = functions[0].apply(this, arguments); for (i = 1; i < functions.length; ++i) { result = functions[i](result); } return result; }; }''' # modified version of underscore.js's _.isEqual and eq functions deep-eq: '''function(x, y, type){ var toString = {}.toString, hasOwnProperty = {}.hasOwnProperty, has = function (obj, key) { return hasOwnProperty.call(obj, key); }; var first = true; return eq(x, y, []); function eq(a, b, stack) { var className, length, size, result, alength, blength, r, key, ref, sizeB; if (a == null || b == null) { return a === b; } if (a.__placeholder__ || b.__placeholder__) { return true; } if (a === b) { return a !== 0 || 1 / a == 1 / b; } className = toString.call(a); if (toString.call(b) != className) { return false; } switch (className) { case '[object String]': return a == String(b); case '[object Number]': return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); case '[object Date]': case '[object Boolean]': return +a == +b; case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { return false; } length = stack.length; while (length--) { if (stack[length] == a) { return true; } } stack.push(a); size = 0; result = true; if (className == '[object Array]') { alength = a.length; blength = b.length; if (first) { switch (type) { case '===': result = alength === blength; break; case '<==': result = alength <= blength; break; case '<<=': result = alength < blength; break; } size = alength; first = false; } else { result = alength === blength; size = alength; } if (result) { while (size--) { if (!(result = size in a == size in b && eq(a[size], b[size], stack))){ break; } } } } else { if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { return false; } for (key in a) { if (has(a, key)) { size++; if (!(result = has(b, key) && eq(a[key], b[key], stack))) { break; } } } if (result) { sizeB = 0; for (key in b) { if (has(b, key)) { ++sizeB; } } if (first) { if (type === '<<=') { result = size < sizeB; } else if (type === '<==') { result = size <= sizeB } else { result = size === sizeB; } } else { first = false; result = size === sizeB; } } } stack.pop(); return result; } }''' # Shortcuts to speed up the lookup time for native methods. split : "''.split" replace : "''.replace" to-string: '{}.toString' join : '[].join' slice : '[].slice' splice : '[].splice' # Each level indicates a node's position in the AST. LEVEL_TOP = 0 # ...; LEVEL_PAREN = 1 # (...) LEVEL_LIST = 2 # [...] LEVEL_COND = 3 # ... ? x : y LEVEL_OP = 4 # !... LEVEL_CALL = 5 # ...() # Operator precedences. let @ = PREC = {unary: 0.9} @\&& = @\|| = @\xor = 0.2 @\.&. = @\.^. = @\.|. = 0.3 @\== = @\!= = @\~= = @\!~= = @\=== = @\!== = 0.4 @\< = @\> = @\<= = @\>= = @of = @instanceof = 0.5 @\<<= = @\>>= = @\<== = @\>== = @\++ = 0.5 @\.<<. = @\.>>. = @\.>>>. = 0.6 @\+ = @\- = 0.7 @\* = @\/ = @\% = 0.8 TAB = ' ' * 2 ID = /^(?!\d)[\w$\xAA-\uFFDC]+$/ SIMPLENUM = /^\d+$/ ##### Helpers # Declares a utility function at the top level. function util then Scope.root.assign it+\$ UTILS[it] function entab code, tab then code.replace /\n/g \\n + tab LiveScript-1.5.0/src/browser.ls000664 000000 000000 00000002212 12716147631 016400 0ustar00rootroot000000 000000 LiveScript = require '..' # `.run`s LiveScript code and calls back, passing error if any. LiveScript.stab = (code, callback, filename) !-> try LiveScript.run code, {filename, map: 'embedded'} catch callback? e # `.stab`s a remote script via `XMLHttpRequest`. LiveScript.load = (url, callback) -> xhr = new XMLHttpRequest xhr.open 'GET', url, true xhr.override-mime-type 'text/plain' if 'overrideMimeType' of xhr xhr.onreadystatechange = !-> if xhr.ready-state is 4 if xhr.status in [200 0] LiveScript.stab xhr.response-text, callback, url else callback? Error "#url: #{xhr.status} #{xhr.status-text}" xhr.send null xhr # Execute ` LiveScript-1.5.0/test/assignment.ls000664 000000 000000 00000014763 12716147631 017273 0ustar00rootroot000000 000000 # Can assign a conditional statement. getX = -> 10 if x = getX() then 100 eq x, 10 x = if getX() then 100 eq x, 100 # _thisprop_ assignment tester = -> @example = ok this eq ok, new tester().example num = 10 num -= 5 eq num, 5 num *= 10 eq num, 50 num /= 10 eq num, 5 num %= 3 eq num, 2 val = false val ||= 'value' val ||= 'eulav' eq val, 'value' val &&= 'rehto' val &&= 'other' eq val, 'other' val = null val ?= 'value' val ?= 'eulav' eq val, 'value' for nonref, i in <[ 0 f() this true ]> throws 'invalid assign on line ' + (i+1), -> x = if i then nonref else \... + nonref LiveScript.compile \\n * i + "[#{x}, y] = z" compileThrows 'assignment to undeclared "Math"' 1 'Math ||:= 0' # Power x = 2 x **= 2 eq 4 x x ^= 2 eq 16 x # Concat a = [1 2 3] a ++= [4 5] eq '1,2,3,4,5' String a a ++= 6 eq '1,2,3,4,5,6' String a # Pipe x = 3 x |>= (+ 7) eq 10 x # obj ::= obj2 as alias to obj::<< lala ::= prop: true fafa = new lala ok lala::prop ok !lala::other ok fafa.prop ok !fafa.other lala ::= other: true ok lala::other ok fafa.other compileThrows 'invalid assign' 1 'f() ?=x' compileThrows 'invalid accessign' 1 'f()?= x' # Empty assignments {} = -> /* will be front and should be wrapped */ eq 1, [] = 1 eq 9, 3 * [] = 2 + 1 eq ok, new []=(-> -> ok)() i = 0 [{}] = ++i eq i, 1 {}p = 0 [{}p] = 1 {}p++ ok 'LHS should take care frontness' ### Destructuring # simple variable swapping a = -1 b = -2 [a, b] = [b, a] eq a, -2 eq b, -1 eq "#{ do -> [a, b] := [b, a] }", '-1,-2' eq a, -1 eq b, -2 a = [0 1] [a, b] = a eq a, 0 eq b, 1 eq (onetwo = [1, 2]), [a, b] = [c, d] = onetwo ok a is c is 1 and b is d is 2 # fancy swapping a = [0 1]; i = 2 [a[--i], a[--i]].=reverse! eq 1 a.0 eq 0 a.1 # with splats [x, ...y, z] = [1, 2, 3, 4, 5] eq x, 1 eq y.length, 3 eq z, 5 [...heads, [head, ...tails], tail] = [1,2,3, [4, 5,6], 7] eq head, 4 eq tail, 7 eq heads + '', '1,2,3' eq tails + '', '5,6' # objects {a: a, b} = {a: 0, b: 1} eq a, 0 eq b, 1 {name: a, family: {'elder-brother': {addresses: [one, {city: b}]}}} = name : 'Moe' family: 'elder-brother': addresses: [ 'first' street: '101 Deercreek Ln.' city : 'Moquasset NY, 10021' ] eq a, 'Moe' eq b, 'Moquasset NY, 10021' {person: {address: [ignore, ...addr]}} = person: address: "------" "Street 101" "Apt 101" "City 101" eq addr.join(', '), 'Street 101, Apt 101, City 101' a = {\a \b} {a, b} = a eq a+b, \ab # with object shorthand {name, age, dogs: [first, second]} = name: 'Bob' age : 26 dogs: ['Prince', 'Bowie'] eq name , 'Bob' eq age , 26 eq first , 'Prince' eq second , 'Bowie' # on `for` persons = George : {name: 'Bob' } Bob : {name: 'Alice'} Christopher: {name: 'Stan' } join1 = ["#{key}: #{name}" for key, {name} of persons] eq join1.join(' / '), 'George: Bob / Bob: Alice / Christopher: Stan' persons = [ {name: 'Bob' , parent: {name: 'George' }} {name: 'Alice', parent: {name: 'Bob' }} {name: 'Stan' , parent: {name: 'Christopher'}} ] join2 = ["#{parent}: #{name}" for {name, parent: {name: parent}} in persons] eq join1.join(' '), join2.join(' ') persons = [['Bob', ['George']], ['Alice', ['Bob']], ['Stan', ['Christopher']]] join3 = ["#{parent}: #{name}" for [name, [parent]] in persons] eq join2.join(' '), join3.join(' ') [x] = {0: y} = {'0': z} = [Math.random()] ok x is y is z, 'destructuring in multiple' # into properties obj = func: (list, object) -> [@one, @two] = list {@a, @b} = object {@a} = object null obj.func [1, 2], a: 'a', b: 'b' eq obj.one, 1 eq obj.two, 2 eq obj.a, 'a' eq obj.b, 'b' x = 'y' {(x)} = y: 0xc0c0 eq x, 49344 # [coffee#870](https://github.com/jashkenas/coffee-script/issues/870) [void, null, v] = [1 to 3] eq v, 3 # [coffee#1108](https://github.com/jashkenas/coffee-script/issues/1108) [z] = [0] ? [1] eq 0 z # [coffee#1188](https://github.com/jashkenas/coffee-script/issues/1188) coffee1188 = coffee1188 := ok ok ### Accessign parent = child: str: 'test' parent.child.str.=replace /./, 'b' eq 'best', parent.child.str parent.child.str.='replace' /./, 'r' parent.=child.valueOf().str eq 'rest', parent parent.+=slice 1 eq 'restest', parent a = b: c: d: \e a.b.c?.=d eq \e a.b.c a.=b <<< {\c} eq \c a.c compileThrows 'assignment to undeclared "a"' 1 'a.=b' ### Subdestructuring a = [] a[0 [] [2]] = [0 1 [2]] eq a.0, 0 eq a.2, 2 i = 0; j = 2 a[i, j] = a[j, i] eq '2,,0' ''+a o = {}; k = \v o{k, 0, \1, (2), three: 3, (2*2): 4} = {k, 0, \1, (2), three: 3, (2*2): 4} eq o.k, \v eq o.0, 0 eq o.1, \1 eq o.2, 2 eq o.3, 3 eq o.4, 4 (i = 5; o){(i++), (i++)} = {5 6} eq o.5, 5 eq o.6, 6 o{a: [7 8], o: {9 \a}} = a: [7 8], o: {9 \a} eq o.7, 7 eq o.8, 8 eq o.9, 9 eq o.a, \a o[\b, ...\c, \d] = [0 to 3] eq o.b, 0 eq o.c+'' '1,2' eq o.d, 3 a = [0 1]; i = 2 a.reverse![--i, --i].=reverse! eq 0 a.0 eq 1 a.1 ### Destructuring Default new [x ? 2, [y] || [3], @p && 5] = [null, false, true] eq x * y * @p, 30 @p = @q = void [x = 2, [y] ||= [3], @p &&= 5] = [null, false, true] eq x * y * @p, 30 {a or 2, _: b or 3, @p or 5} = {} eq a * b * @p, 30 @a = @b = @c = void @{a ? 2, \b ? 3, ([\c]) ? 5} = {} eq @a * @b * @c, 30 @a = @b = @c = void @{a = 2, \b = 3, ([\c]) = 5} = {} eq @a * @b * @c, 30 @a = @b = @c = void @{a && 2, b || 3} = {a: 99} eq @a * @b, 6 @a = @b = @c = void @{a &&= 2, b ||= 3} = {a: 99} eq @a * @b, 6 ### Compound/Conditional Destructuring a = b = c = null [a, b] += [2 3] [b, c] ?= [4 5] eq '2,3,5' String [a,b,c] o = d: 0, e: 1 o{d, e} &&*= d: 2, e: 3 eq 0 o.d eq 3 o.e ### Named Destructuring [b, c]:a = [0 1] eq b, a.0 eq c, a.1 f = ({p, q}: o?) -> if o? eq p, o.p eq q, o.q else eq p, void eq q, void f {2 3} f ( ) o = a: {\b \c} {{b, c}:a, [d]:e ? [{}]} = o eq a, o.a eq b, \b eq c, \c eq d, e.0 ### Unary Assign o = {} eq 1, -~=o.0 eq false, !=o eq 0, -=o eq 1, ! += o eq true, !!=o ## Dash to camel hello-world = 2 eq hello-world, 2 a = 2 b = 3 aB = 99 eq 1 a-1 eq 1 4-b eq 99 a-b obj = ha-ha: 2 eq 2 obj.ha-ha eq 2 obj.haHa green = 5 eq 4 --green green-- eq 3 green eq 6, green-- * a eq \HELLO 'hello'.to-upper-case! ### Ill-shadow Protection compileThrows 'accidental shadow of "a"' 4 ''' a = 1 let a := 2 a = 3 ''' ## Function redfines iteself change-me = -> change-me := 2 eq \function typeof changeMe eq 2 changeMe! eq 2 changeMe ## Error when assigning to reserved LS word compileThrows 'cannot assign to reserved word \'match\'' 1 ''' match = 1 ''' LiveScript-1.5.0/test/browser.html000664 000000 000000 00000002270 12716147631 017122 0ustar00rootroot000000 000000 LiveScript browser tests

LiveScript browser tests

LiveScript-1.5.0/test/chaining.ls000664 000000 000000 00000007756 12716147631 016707 0ustar00rootroot000000 000000 # Basic chained function calls. identityWrap = (x) -> -> x eq true, identityWrap(identityWrap(true))()() # Chained accesses split on dot + newline, backwards and forwards. $ = {} $.self = $ $.begin = $.do = $.end = -> this ok $. begin(). do(). self. self. end(). self # Leading dots introduce dummy blocks and/or close implicit indentations. r = $.self .begin() .do() .do -> 0; 1 .begin() .do -> 2 3 .end 4, -> .end() eq r, $ # Paren-free method chains eq \56, if \0 is 10.toString 2 .slice 3 \4 .replace /\d/ 5 .concat 6 eq 1, Array 0, 1 ?.1 # Ensure that indented array literals don't trigger whitespace rewriting. eq 2 [ [[[[], []], [[]]]], []].length eq 'Hello', String( """ Hello """) eq msg = 'special accessors should also continue lines', msg .~toString ?.valueOf()() eq 0, [0] ?.0 # Bracketless Accesses a = [0] eq 0, a.0 eq 0, a."0" eq 0, a."#{0}" eq 0, a.(0) eq 0, [a].0.0 eq a.* = 1, a.1 eq '0'.0, '10'.1 eq 1, [-> it]. 0 1 eq 1, [-> it].'0' 1 # `prototype` shorthand, `constructor` eq Array::toString, Array.prototype.toString eq 12345@@toString, 123@@toString eq 0 (:::0):: eq 0 (@@:0)@@ # Length Star a = [[1], 2, 3] eq 3, a[*-1] eq 1, a[0][*-*] eq 2, a[--*-1] eq 2, a.length compileThrows 'stray star' 1 '[*-1]' # Binding Access parent = child: method: -> @member member: 42 eq 42, do(0; parent.child.~method) eq 42, do(0; parent.child~"me#{'th'}od") eq 42, parent.child. ~ [\method] null eq 42, parent.child.~{method}.method! eq "42" 42~toString! compileThrows 'invalid assign' 1 'o~m=g' # Dots have to workaround syntax error when accessing a simple number. eq '0 .go;' , LiveScript.compile '0.go', {+bare,-header} # Brackets don't. eq "0['do'];", LiveScript.compile '0.do', {+bare,-header} # Array/Object Slice eq '2,3', '' + [3,2][1,0] eq '2,3', '' + [0,1,2,3][*-2,*-1] eq '2,3', '' + {2,3}<[2 3]> eq '-Infinity,Infinity', '' + Number[\NEGATIVE_INFINITY, \POSITIVE_INFINITY] k = \y o = {\x \y \z}{x, (k), 2: z} eq \x o.x eq \y o.y eq \z o.2 a = [0 1 2][[0 1], {2}] eq '0,1' ''+a.0 eq '0,1' ''+a[...0] eq 2 a.1.2 compileThrows 'calling a slice' 1 'a{0}()' compileThrows 'empty slice' 1 'o{}' compileThrows 'empty slice' 1 'o[,,]' if 0 then @front{ne,ss} x = 3 y = 1 l = [1 to 5] eq '2,3,4' "#{ l[1 to x] }" eq '2,3' "#{ l[1 til x] }" eq '2,3,4' "#{ l[y to 3] }" eq '2,3' "#{ l[y til 3] }" eq '2,3,4' "#{ l[y to x] }" eq '2,3' "#{ l[y til x] }" eq '3,4,5' "#{ l[2 til] }" eq '1,2' "#{ l[til 2] }" z = 2 eq '3,4,5' "#{ l[z til] }" eq '1,2' "#{ l[til z] }" eq '1,2,3,4,5' "#{ l[to] }" eq '1,2,3' "#{ l[til -2] }" eq '2,3' "#{ l[1 til -2] }" eq '1,2,3,4,5' "#{ l[to -1] }" eq '1,2,3,4' "#{ l[til -1] }" # splice l = [1 to 5] x = 3 eq '8,9' "#{ l[2 to x] = [8 9] }" eq '1,2,8,9,5' "#{ l }" y = -> 2 l = [1 to 5] eq '8,9' "#{ l[y! til 4] = [8 9] }" eq '1,2,8,9,5' "#{ l }" l = [1 to 5] eq '8,9' "#{ l[2 to 3] = [8 9] }" eq '1,2,8,9,5' "#{ l }" # `BY` keyword in Slices [x, f, list] = [2, (/2), [1 to 6]] eq '2,4' String list[1 til 4 by 2] eq '3,4,5' String list[x to 4 by 1] eq '2,4' String list[1 til 4 by x] eq '1,3' String list[to x by f 4][to x-1 by 1] # Automatic Dot Insertion eq @toString, @\toString eq @toString, @"to#{\S}tring" {ok}\ok 1 [ok]0 1 eq 0 [[0]]0.0 eq null [null]?0 eq void {0}?1?p v = void x = y: {\z} eq void v?=y.z eq void v eq \z x?=y.z eq \z x # Semiautovivification o = {} o.{}a.[]b .push 0 1 o.a{}c[]d .push 2 3 o?.{}a?.[]b?{}e?{}f.4 = 5 eq '0,1' ''+o.a.b eq '2,3' ''+o.a.c.d eq 5 o.a.b.e.f.4 a = [] eq 2 a{}[0, 1].length eq \object typeof a.0 eq \object typeof a.1 # Bang Call eq '' String! (-> ok true)! f = -> null eq null f?! eq void f!?p f = void eq void f?! # Keyword literals in object slices keywords = arguments: 1 eval: 2 void: 3 on: 4 debugger: 5 eq 1 keywords{arguments}arguments eq 2 keywords{eval}eval eq 3 keywords{void}void eq 4 keywords{on}on eq 5 keywords{debugger}debugger LiveScript-1.5.0/test/cli.ls000664 000000 000000 00000003403 12716147631 015657 0ustar00rootroot000000 000000 require! path: {normalize} # version command-eq '-v', ["LiveScript version #{LiveScript.VERSION}"] # eval print command-eq '-pe "2 + 2"', [4] # help command-eq '-h', [/^Usage: lsc (.|\n)*Misc:(.|\n)*Output control:(.|\n)*Version/] one-js = 'var f;\nf = function(x){\n return 1 + x;\n};' one-path-js = normalize 'test/data/one.js' one-path-ls = normalize 'test/data/one.ls' # compile print command-eq '-cpb --no-header test/data/one.ls', [one-js] # compile command-eq '-cb --no-header --debug test/data/one.ls', ["#one-path-ls => #one-path-js"], -> try ok file-exists one-path-js eq one-js, file-read one-path-js finally file-delete one-path-js # header command-eq '-cpb test/data/empty.ls', [ "// Generated by LiveScript #{LiveScript.VERSION}" ] # no-header command-eq '-cpb --no-header test/data/empty.ls', [ '' ] # not using bare command-eq '-cp --no-header test/data/empty.ls', [ '(function(){\n\n}).call(this);' ] # json json-content = '{\n "moo": 1,\n "foo": "string"\n}' # implicit json command-eq '-cp test/data/data.json.ls', [json-content ] # explicit json command-eq '-cp --json test/data/data.ls', [json-content] # eval print json, explicit command-eq '-je "@two" test/data/j.json', ['4'] # eval print json, implicit command-eq '-e "@two" test/data/j.json', ['4'] # map, basic command-eq '-c --debug --map linked test/data/empty.ls', [ "#{normalize 'test/data/empty.ls'} => #{normalize 'test/data/empty.js'}, #{normalize 'test/data/empty.js.map'}" ], -> try ok file-exists 'test/data/empty.js' ok file-exists 'test/data/empty.js.map' ok //empty\.js\.map//.test file-read 'test/data/empty.js' finally file-delete 'test/data/empty.js' file-delete 'test/data/empty.js.map' LiveScript-1.5.0/test/comment.ls000664 000000 000000 00000004014 12716147631 016551 0ustar00rootroot000000 000000 #! line comment #!comment func = -> #!comment false false #!comment false #!comment true switch 'string' #!comment case false then something() #!comment case null somethingElse() -> code() #!comment ok func() func func #!Line3 obj = { #!comment #!comment #!comment one: 1 #!comment two: 2 #!comment } result = if true # comment false ok not result result = if false false else # comment 45 eq result, 45 test = 'test ' + 'test ' + # comment 'test' eq test, 'test test test' /* This is a block comment. Just like JS. */ func = -> /* Another block comment. */ code /* debug code commented */ func = -> one = -> two = -> three = -> /* block. */ four = -> fn1 = -> oneLevel = null /* This is fine. */ ok ok # Spaced comments in if / elses. result = if false 1 #!comment else if false 2 #!comment else 3 eq result, 3 result = switch 'z' case 'z' then 7 #!comment eq result, 7 # Trailing-line comment before an outdent. func = -> if true true # comment 7 eq func(), 7 eq ''' /* leading block comments */ /* are placed before declarations */ var obj; obj = { /* * v */ key: val /* * ^ */ }; (function(){ /* no semicolon at end -> */ 1; return 2; }); /* trailing top level comment */ ''', LiveScript.compile ''' /* leading block comments */ /* are placed before declarations */ obj = { /* * v */ key : val /* * ^ */ } -> /* no semicolon at end -> */ 1 2 /* trailing block comments are */ /* removed when returning value */ /* trailing top level comment */ ''', {+bare,-header} # Block comments within non-statement `if`s. eq void, if true then /* 1 */ eq true, do if true /* 2 */ true eq true, do if true true /* 3 */ eq 0, [0]/* inline block comment */[0] # Ensure that backslash gobbles line comments as well as regular whitespace # [#550](https://github.com/gkz/LiveScript/issues/550) ({a, b, \ #comment c})-> /* Trailing block comment works. LiveScript-1.5.0/test/compilation.ls000664 000000 000000 00000016711 12716147631 017434 0ustar00rootroot000000 000000 bare = -> try LiveScript.compile it, {+bare,-header} catch console.error it throw e # Ensure that carriage returns don't break compilation on Windows. eq 'one;\ntwo;', bare 'one\r\ntwo' # Tab characters should work. eq '_(__);', bare '\n\t_\t__\t\n' # `{\eval}` forces the last value to be returned. eq 1, Function('return ' + LiveScript.compile 'delete @1' {\eval,-header}).call {1} eq ''' var ref$; ref$ = o.k, delete o.k, ref$; ''' LiveScript.compile 'delete o.k' {\eval, +bare,-header} compileThrows 'missing `"`' 2 '\n"\n' compileThrows 'unterminated string' 3 "\n\n'\n" compileThrows 'unterminated words' 3 '\n\n<[\n' compileThrows 'contaminated indent %20' 2 '1\n\t 2' compileThrows 'contaminated indent %09' 3 ' 1\n 2\n\t3' compileThrows 'unmatched dedent (1 for 2)' 3 '1\n 2\n 3' compileThrows 'unmatched `)`' 2 '()\n)' compileThrows 'unmatched `]`' 3 '[{\n\n]}' compileThrows 'missing `)CALL`' 1 'f(' throws ''' empty range on line 1 at filename ''' -> LiveScript.compile '[0 til 0]' {\filename,-header} eq ''' var k; for (k in o) {} ''' bare 'for k of o then' eq "a['in'] = this['in'];", bare 'a import {@in}' eq ''' while (0) { while (0) { ({}), {}; break; } } ''', bare 'while 0 then while 0 then {} = ({}; {}); break' compileThrows 'invalid use of null' 1 'null.po' compileThrows 'deprecated octal literal 0666' 1 '0666' tokens = LiveScript.lex ''' """ 1 #{ 2 3 } 4 """ ''' eq tokens.join('\n'), ''' NEWLINE, ,0,0 (,\",0,0 STRNUM,\"1 \",0,0 +-,+,0,0 (,(,1,4 INDENT,4,2,4 STRNUM,2,2,4 NEWLINE, ,3,4 STRNUM,3,3,4 DEDENT,4,4,2 NEWLINE, ,4,2 ),),4,2 +-,+,4,2 STRNUM,\" 4\",4,3 ),,5,3 NEWLINE, ,5,3 ''' # Indentation on line 1 should be valid. eq '1;\n2;', bare ' 1\n 2' eq 'STRNUM,0,0,0 ,,,,0,0 STRNUM,1,1,2' LiveScript.tokens(''' 0 \\ 1 ''').slice(0 3).join ' ' eq '!a;', bare '!!!a' eq '''(function(){ if ((function(){ debugger; }())) { debugger; } });''' bare '-> debugger if debugger' eq '1;\n2;\n3;\n4;', bare ''' 1 2 3 4 ''' # `__proto__` should be available as a variable name. eq 1, __proto__ = 1 # [#1](https://github.com/satyr/coco/issues/1) λ = -> 七 = 7 eq λ(), 7 compileThrows 'invalid identifier \'♪\'' 1 'ƒ ♪ ♯' # - [coffee#1195](https://github.com/jashkenas/coffee-script/issues/1195) # - Ignore top-level `void`s. eq '(function(){});' bare ''' -> void; void; void ''' # Dash seperated identifiers throws "Parse error on line 1: Unexpected 'ID'" -> LiveScript.compile 'a--b = 1' throws "Inconsistent use of encodeURL as encode-u-r-l on line 1" -> LiveScript.compile 'encode-URL is encode-u-r-l' # Optimize concat [#72](https://github.com/gkz/LiveScript/issues/72) eq '[1].concat([2], [3], [4]);' bare '[1] ++ [2] ++ [3] ++ [4]' # Error when attempting to curry a funciton using splats [#91](https://github.com/gkz/LiveScript/issues/91) compileThrows 'cannot curry a function with a variable number of arguments' 1 '(...args) --> args[0]' # destructuring assign sugar compileThrows 'invalid assign' 1 '{a **= b} = c' # require! eq "var a;\na = require('a');" bare 'require! [a]' eq "var a;\na = require('a');" bare 'require! <[a]>' eq "var a;\na = require('a');" bare 'require! {a}' eq "var b;\nb = require('b');" bare "require! {'b'}" eq "var c;\nc = require('d');" bare "require! d: c" eq "var e;\ne = require('f');" bare "require! f: e" eq "var g;\ng = require('h');" bare "require! 'h': 'g'" eq "var file;\nfile = require('file.js');" bare "require! 'file.js'" eq "var file;\nfile = require('./file.js');" bare "require! './file.js'" eq "var file;\nfile = require('./a/b/c/file.js');" bare "require! './a/b/c/file.js'" eq "var a;\na = require('file.js');" bare "require! 'file.js': a" eq "var b;\nb = require('./file.js');" bare "require! './file.js': b" eq "var c;\nc = require('./a/b/c/file.js');" bare "require! './a/b/c/file.js': c" eq "var preludeLs;\npreludeLs = require('prelude-ls');" bare "require! 'prelude-ls'" eq "var bar;\nbar = require('./file.js').bar;" bare "require! { './file.js': {bar} }" eq "var ref$, id, map;\nref$ = require('prelude-ls'), id = ref$.id, map = ref$.map;" bare "require! 'prelude-ls': {id, map}" eq "var a, b, c;\na = require('a');\nb = require('b');\nc = require('c');" bare 'require! [a, b, c]' eq "var a, b, c;\na = require('a');\nb = require('b');\nc = require('c');" bare 'require! <[ a b c ]>' eq "var a, b, c;\na = require('a');\nb = require('b');\nc = require('c');" bare 'require! { a, b, c }' compileThrows 'invalid require! argument' 1 'require! obj.key' # JS literal eq 'some js code!' bare '``some js code!``' # generators compileThrows "a constructor can't be a generator" 1 'class => ->*' # statement expression in generator, normal function still compiles fine code = '''(function*(){ var f, g; f = (yield* (function*(){ switch (false) { case !true: (yield 2); return g = function(){ return 3; }; } }())); });''' eq code, bare '!->* f = | true => yield 2; g = -> 3' # https://github.com/jashkenas/coffee-script/pull/3240#issuecomment-38344281 eq '(function*(){\n var body;\n body = (yield fn).body;\n});' bare '!->* {body} = yield fn' # [#237](https://github.com/satyr/coco/issues/237) LiveScript.compile 'class A; class B; class C' # [livescript#279](https://github.com/gkz/LiveScript/issues/279) ################################################################ jsonls = -> LiveScript.compile it, {+json} eq do ''' { "key": "value", "keyDash": 1, "key-dash": true, "object": { "strArray": [ "of", "strings" ], "objArray": [ { "index": 0, "name": "zero" }, { "index": 1, "name": "one" }, { "index": 2, "name": "two" } ], "mixedArray": [ { "key": "value" }, 1, true, "done" ], "nestedObjects": { "level1": { "level2": { "level3": { "key": true }, "levelThree": { "key": false } } }, "levelOne": { "nestedArrays": [ [ [ true, false ], [ false, true ] ] ] } } } } ''' jsonls ''' key: \\value key-dash: 1 'key-dash': on object: str-array: <[ of strings ]> # a comment obj-array: * index: 0 name: "zero" * index: 1 name: \\one * index: 2 name: 'two' mixed-array: key: "valu\#{\\e}" 1 yes \\done nested-objects: level1: level2: level3: {+key} level-three: {-key} level-one: nested-arrays: [ [ [ on off ] [ off on ] ] ] ''' # [LiveScript#48](https://github.com/gkz/LiveScript/issues/48) saveHere = {} LiveScript.compile 'x ?= 1', bare: true, saveScope: saveHere code = LiveScript.compile 'y ?= 2', bare: true, saveScope: saveHere ok 0 <= code.indexOf 'var x, y' # The presence of the full "ClassName.prototype.methodName =" in the compiled # code is relevant to post-processors like Google's Closure Compiler as a cue # that these are class methods. compiled = LiveScript.compile ''' class A one: -> 1 two: -> 2 class B extends A three: -> 3 four: -> 4 ''' for <[ A.prototype.one A.prototype.two B.prototype.three B.prototype.four ]> ok compiled.indexOf(..) >= 0 "missing #{..}" LiveScript-1.5.0/test/cs-classes.ls000664 000000 000000 00000026603 12716147631 017157 0ustar00rootroot000000 000000 ### The following are modified from CoffeeScript - test/classes.coffee # classes with a four-level inheritance chain class Base func: (string) -> "zero/#string" @static = (string) -> "static/#string" class FirstChild extends Base func: (string) -> super('one/') + string SecondChild = class extends FirstChild func: (string) -> super('two/') + string thirdCtor = -> @array = [1, 2, 3] class ThirdChild extends SecondChild -> thirdCtor.call this # Gratuitous comment for testing. func: (string) -> super('three/') + string result = (new ThirdChild).func 'four' eq 'zero/one/two/three/four' result eq 'static/word' Base.static 'word' FirstChild::func = (string) -> super 'one/' .length + string result = (new ThirdChild).func 'four' eq '9two/three/four' result eq '1 2 3' (new ThirdChild).array.join ' ' # constructors with inheritance and super identity = (f) -> f class TopClass (arg) -> @prop = 'top-' + arg class SuperClass extends TopClass (arg) -> identity super 'super-' + arg class SubClass extends SuperClass -> identity super 'sub' eq 'top-super-sub' (new SubClass).prop #Overriding the static property new doesn't clobber Function::new class OneClass (name) -> @name = name @new = 'new' function: 'function' class TwoClass extends OneClass delete TwoClass.new Function.prototype.new = -> new this ...arguments eq \three (TwoClass.new('three')).name eq \function (new OneClass).function eq \new OneClass.new delete Function.prototype.new #basic classes, again, but in the manual prototype style Base = -> Base::func = (string) -> 'zero/' + string Base::['func-func'] = (string) -> "dynamic-#{string}" FirstChild = -> SecondChild = -> ThirdChild = -> @array = [1, 2, 3] this ThirdChild extends SecondChild extends FirstChild extends Base FirstChild::func = (string) -> super('one/') + string SecondChild::func = (string) -> super('two/') + string ThirdChild::func = (string) -> super('three/') + string result = (new ThirdChild).func 'four' eq 'zero/one/two/three/four' result eq 'dynamic-thing' (new ThirdChild)['func-func']('thing') #super with plain ol' functions as the original constructors TopClass = (arg) -> @prop = 'top-' + arg this SuperClass = (arg) -> super 'super-' + arg this SubClass = -> super 'sub' this SuperClass extends TopClass SubClass extends SuperClass eq 'top-super-sub' (new SubClass).prop #'@' referring to the current instance, and not being coerced into a call class ClassName amI: -> @ instanceof ClassName obj = new ClassName ok obj.amI! #super() calls in constructors of classes that are defined as object properties class Hive (name) -> @name = name class Hive.Bee extends Hive (name) -> super ... maya = new Hive.Bee 'Maya' eq 'Maya' maya.name #classes with JS-keyword properties class Class class: 'class' name: -> @class instance = new Class eq \class instance.class eq \class instance.name! #Classes with methods that are pre-bound to the instance, or statically, to the class class Dog (name) -> @name = name bark: ~> "#{@name} woofs!" @static = ~> new this('Dog') spark = new Dog('Spark') fido = new Dog('Fido') fido.bark = spark.bark eq 'Spark woofs!' fido.bark! obj = func: Dog.static eq 'Dog' obj.func!name #a bound function in a bound function class Mini num: 10 generate: ~> for i in [1 to 3] ~> @num m = new Mini eq '10 10 10' [func! for func in m.generate!].join ' ' #contructor called with varargs class Connection (one, two, three) -> [@one, @two, @three] = [one, two, three] out: -> "#{@one}-#{@two}-#{@three}" list = [3, 2, 1] conn = new Connection ...list ok conn instanceof Connection ok '3-2-1' conn.out! #calling super and passing along all arguments class Parent method: (...args) -> @args = args class Child extends Parent method: -> super ... c = new Child c.method 1, 2, 3, 4 eq '1 2 3 4' c.args.join ' ' #classes wrapped in decorators func = (klass) -> klass::prop = 'value' klass func class Test prop2: 'value2' eq 'value' (new Test).prop eq 'value2' (new Test).prop2 # anonymous classes obj = klass: class method: -> 'value' instance = new obj.klass eq \value instance.method! #Implicit objects as static properties class Static @static = one: 1 two: 2 eq 1 Static.static.one eq 2 Static.static.two #nothing classes c = class ok c instanceof Function #classes with static-level implicit objects class A @static = one: 1 two: 2 class B @static = one: 1, two: 2 eq A.static.one, 1 eq A.static.two, void eq (new A).two, 2 eq B.static.one, 1 eq B.static.two, 2 eq (new B).two, void #classes with value'd constructors counter = 0 classMaker = -> inner = ++counter -> @value = inner class One constructor$$: classMaker! class Two constructor$$: classMaker! eq 1 (new One).value eq 2 (new Two).value eq 1 (new One).value eq 2 (new Two).value #exectuable class bodies class A if true b: 'b' else c: 'c' a = new A eq \b a.b eq void a.c #mild metaprogramming class Base @attr = (name) -> @::[name] = (val) -> if arguments.length > 0 @["_#{name}"] = val else @["_#{name}"] class Robot extends Base @attr 'power' @attr 'speed' robby = new Robot eq void robby.power! robby.power 11 robby.speed Infinity eq 11 robby.power! eq Infinity, robby.speed! # FAIL #namespaced classes do not reserve their function name in outside scope /* let one = {} two = {} class one.Klass @label = "one" class two.Klass @label = "two" eq typeof Klass, 'undefined' eq one.Klass.label, 'one' eq two.Klass.label, 'two' */ #nested classes class Outer -> @label = 'outer' class @Inner -> @label = 'inner' eq \outer (new Outer).label eq \inner (new Outer.Inner).label #variables in constructor bodies are correctly scoped class A x = 1 -> x := 10 y = 20 y = 2 captured: -> {x, y} a = new A eq 10 a.captured!x eq 2 a.captured!y #Issue #924: Static methods in nested classes class A @B = class @c = -> 5 eq 5 A.B.c! #`class extends this` class A func: -> 'A' B = null makeClass = -> B := class extends this func: -> (super ...) + ' B' makeClass.call A eq 'A B' (new B!).func! #ensure that constructors invoked with splats return a new object args = [1, 2, 3] Type = (@args) -> type = new Type args ok type and type instanceof Type ok type.args and type.args instanceof Array for v, i in type.args then eq v, args[i] Type1 = (@a, @b, @c) -> type1 = new Type1 ...args ok type1 instanceof Type1 eq type1.constructor, Type1 ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2] # Ensure that constructors invoked with splats cache the function. called = 0 get = -> if called++ then false else class Type new get() ...args #`new` shouldn't add extra parens eq new Date!constructor, Date # FAIL # doesn't implicitly return #`new` works against bare function /* eq Date, new -> eq this, new ~> this Date */ #1182: a subclass should be able to set its constructor to an external function ctor = -> @val = 1 class A class B extends A constructor$$: ctor eq 1 (new B).val #1182: external constructors continued ctor = -> class A class B extends A method: -> constructor$$: ctor ok B::method #1313: misplaced __extends nonce = {} class A class B extends A prop: nonce -> eq nonce, B::prop #1182: execution order needs to be considered as well counter = 0 makeFn = (n) -> eq n, ++counter; -> class B extends (makeFn 1) @B = makeFn 2 constructor$$: makeFn 3 #1182: external constructors with bound functions fn = -> {one: 1} this class B class A constructor$$: fn method: ~> this instanceof A ok (new A).method.call(new B) #1372: bound class methods with reserved names class C delete: ~> ok C::delete #1380: `super` with reserved names class C do: -> super ... ok C::do class B 0: -> super ... ok B::[0] #1464: bound class methods should keep context nonce = {} nonce2 = {} class C (@id) -> @boundStaticColon = ~> new this(nonce) @boundStaticEqual = ~> new this(nonce2) eq nonce, C.boundStaticColon().id eq nonce2, C.boundStaticEqual().id #1009: classes with reserved words as determined names (-> # had to modify from using word boundaries because we use the $ and CS uses _ b = '[^a-zA-Z$]' ok //#{b}eval#b//.test 'function eval(){}' ok not //#{b}eval#b//.test 'function $eval(){}' eq 'function' typeof (class @for) ok not //#{b}eval#b//.test (class @eval).toString! ok not //#{b}arguments#b//.test (class @arguments).toString! ).call {} #1482: classes can extend expressions id = (x) -> x nonce = {} class A then nonce: nonce class B extends id A eq nonce, (new B).nonce #1598: super works for static methods too class Parent method: -> 'NO' @method = -> 'yes' class Child extends Parent @method = -> 'pass? ' + super ... eq 'pass? yes' Child.method! #1842: Regression with bound functions within bound class methods class Store @bound = ~> do ~> eq this, Store Store.bound! # And a fancier case: class Store eq this, Store @bound = ~> do ~> eq this, Store @unbound = -> eq this, Store instance: ~> ok this instanceof Store Store.bound! Store.unbound! (new Store).instance! #1876: Class @A extends A class A class @A extends A ok (new @A) instanceof A #1813: Passing class definitions as expressions ident = (x) -> x result = ident class A then x = 1 eq result, A result = ident class B extends A x = 1 eq result, B #1966: external constructors should produce their return value ctor = -> {} class A then constructor$$: ctor ok (new A) not instanceof A #1980: regression with an inherited class with static function members class A class B extends A @static = ~> 'value' eq \value B.static! ## NOTE: this test is actually supposed to be after the following one, but INDENT DEDENT aren't added after class A for some reason if it is after and it fails to compile - very weird #2052: classes should work in strict mode try do -> 'use strict' class A catch ok 0 #1534: class then 'use strict' # [14.1 Directive Prologues and the Use Strict Directive](http://es5.github.com/#x14.1) throws = -> try it! ok 0 catch ok 1 does-not-throw = -> try it! ok 1 catch ok 0 nonce = {} error = 'do -> ok this' strictTest = "do ->'use strict';#{error}" eq (try LiveScript.run strictTest, bare: yes catch e then nonce), nonce throws -> LiveScript.run "class then 'use strict';#{error}", bare: yes does-not-throw -> LiveScript.run "class then #{error}", bare: yes does-not-throw -> LiveScript.run "class then #{error};'use strict'", bare: yes # comments are ignored in the Directive Prologue comments = """ class /* comment */ 'use strict' #{error}""" """ class /* comment 1 */ /* comment 2 */ 'use strict' #{error}""" """ class /* comment 1 */ /* comment 2 */ 'use strict' #{error} /* comment 3 */""" for comment in comments throws (-> LiveScript.run comment, bare: yes) # [ES5 §14.1](http://es5.github.com/#x14.1) allows for other directives directives = """ class 'directive 1' 'use strict' #{error}""" """ class 'use strict' 'directive 2' #{error}""" """ class /* comment 1 */ 'directive 1' 'use strict' #{error}""" """ class /* comment 1 */ 'directive 1' /* comment 2 */ 'use strict' #{error}""" for directive, i in directives throws (-> LiveScript.run directive, bare: yes) LiveScript-1.5.0/test/data/000775 000000 000000 00000000000 12716147631 015461 5ustar00rootroot000000 000000 LiveScript-1.5.0/test/data/data.json.ls000664 000000 000000 00000000025 12716147631 017677 0ustar00rootroot000000 000000 moo: 1 foo: 'string' LiveScript-1.5.0/test/data/data.ls000664 000000 000000 00000000025 12716147631 016727 0ustar00rootroot000000 000000 moo: 1 foo: 'string' LiveScript-1.5.0/test/data/empty.ls000664 000000 000000 00000000000 12716147631 017145 0ustar00rootroot000000 000000 LiveScript-1.5.0/test/data/j.json000664 000000 000000 00000000047 12716147631 016606 0ustar00rootroot000000 000000 { "one": [1, 2, 3], "two": 4 } LiveScript-1.5.0/test/data/one.ls000664 000000 000000 00000000021 12716147631 016573 0ustar00rootroot000000 000000 f = (x) -> 1 + x LiveScript-1.5.0/test/declaration.ls000664 000000 000000 00000003742 12716147631 017403 0ustar00rootroot000000 000000 ### `export` out = exports ? this {random} = Math a = random! export a eq out.a, a eq do export b = random! out.b export class C @d = random! export @d, @e = random! ok new out.C instanceof C eq out.d, C.d eq out.e, C.e export function f g export function g then h function h then f eq out.f!, g eq out.g!, h eq out.h!, f export I: i = random! J: j = random! eq out.I, i eq out.J, j o = k: random! export do -> o eq out.k, o.k ### `const` let const a = 1 const b = 2, c = 3 const d = 4 [e, f] = [5, 6] export const g = 7 export const h = 8 const i = 9 eq '1,2,3,4,5,6,7,8,9' ''+[a, b, c, d, e, f, g, h, i] eq out.g, g eq out.h, h eq out.i, i compileThrows 'redeclaration of constant "a"' 2 ''' const a = 0 a = 1 ''' compileThrows 'redeclaration of constant "a"' 2 ''' a = 2 const a = 3 ''' compileThrows 'redeclaration of constant "a"' 2 ''' const a = 4 function a then 5 ''' compileThrows 'assignment to constant "a"' 2 ''' const a = 6 -> a := 7 ''' compileThrows 'increment of constant "a"' 2 ''' const a = 8 ++a ''' compileThrows 'invalid constant variable declaration' 2 'const\n a' ### `var` let var a var b, c var d e ok a is b is c is d is e is void eq void var f compileThrows 'invalid variable declaration' 2 'var\n 0' compileThrows 'redeclaration of "a"' 2 '(a) ->\n var a' compileThrows 'empty var' 2 '\nvar' ### with const flag throws 'redeclaration of constant "z" on line 2' -> LiveScript.compile 'z = 1\nz = 2' {+\const} throws 'increment of constant "z" on line 2' -> LiveScript.compile 'z = 9\nz++' {+\const} throws 'assignment to constant "z" on line 2' -> LiveScript.compile 'z = 1\nz := 2' {+\const} eq '''(function(n){ n == null && (n = 2); return n + 1; });''' LiveScript.compile '(n = 2) -> n + 1' {+\const, +bare,-header} eq '''var ref$; 1 < 2 && 2 === (ref$ = 4 / 2) && ref$ > 0;''' LiveScript.compile '1 < 2 == 4/2 > 0' {+\const, +bare,-header} LiveScript-1.5.0/test/existence.ls000664 000000 000000 00000006612 12716147631 017104 0ustar00rootroot000000 000000 ok not mySpecialVariable? mySpecialVariable = false ok mySpecialVariable? # Existential assignment. a = null a ?= 10 eq a, 10 # Binary form. z = null eq 1, z ? 1 # Evaluate only once. i = 9 func = -> i += 1 result = func() ? 101 eq 10, result eq 10, i counter = 0 getNextNode = -> throw "up" if counter counter++ eq true, getNextNode()? # Existence chains, soaking up undefined properties. obj = prop: "hello" eq obj?.prop, "hello" eq obj?.'prop', "hello" eq obj.prop?.length, 5 eq obj?.prop?.'length', 5 eq void, obj? .prop?. nonexistent? .property # Soak and cache method calls as well. arr = ["--", "----"] eq arr.pop()?.length, 4 eq arr.pop()?.length, 2 eq arr.pop()?.length, void eq arr.pop()?.length?.non?.existent()?.property, void # Soak method calls safely. value = null eq value?.toString().toLowerCase(), void value = 10 eq value?.toString().toLowerCase(), '10' eq ''.nothing?.property() || 101, 101 counter = 0 func = -> counter += 1 'prop' obj = prop : -> this value: 5 eq 5, obj[func()]()[func()]()[func()]()?.value eq 3, counter ident = (obj) -> obj eq ident(non?.existent().method()), void, 'soaks inner values' # Soak constructor invocations. a = 0 class Foo -> a += 1 bar: "bat" eq (new Foo())?.bar, 'bat' eq a, 1 ok not value?.property?, 'safely checks existence true soaks' eq nothing?.value, void, 'safely calls values false of non-existent variables' eq !nothing?.value && 1, 1, 'corresponding operators work as expected' # Assign to the result of an exsitential operation with a minus. eq null ? - 1, -1 # Things that compile to ternaries should force parentheses, like operators do. duration = if options?.animated then 150 else 0 eq duration, 0 ### Soak Call plus1 = (x) -> x + 1 count = 0 obj = { counter: -> count += 1; this returnThis: -> this } eq plus1?(41), 42 eq (plus1? 41), 42 eq plus2?(41), void eq (plus2? 41), void eq obj.returnThis?(), obj eq obj.returnSelf?(), void eq obj.returnThis?().flag = true, true eq obj.returnSelf?().flag = true, void eq obj.counter().counter().returnThis?(), obj eq count, 2 maybe_close = (f, arg) -> if typeof f is 'function' then -> f(arg) else -1 eq maybe_close(plus1, 41)?(), 42 eq (maybe_close plus1, 41)?(), 42 eq (maybe_close 'string', 41)?(), void eq JSON?(3), void eq new Number?(42) .|. 0, 42 eq new Bumper?(42) .|. 0, 0 # [coffee#726](https://github.com/jashkenas/coffee-script/issues/726) eq calendar?.(Date()), void # [coffee#733](https://github.com/jashkenas/coffee-script/issues/733) a = b: {c: null} eq a.b?.c?(), void a.b?.c ||= (it) -> it eq a.b?.c?(1), 1 eq a.b?.c?(...[2, 3]), 2 # [coffee#756](https://github.com/jashkenas/coffee-script/issues/756) a = null ok isNaN a?.b.c + 1 eq void, a?.b.c += 1 eq void, ++a?.b.c eq void, delete a?.b.c a = b: {c: 0} eq 1, a?.b.c + 1 eq 1, a?.b.c += 1 eq 2, ++a?.b.c eq 2, delete a?.b.c eq (1 or 0)?, true, 'postfix `?` should unwrap correctly' # [coffee#1424](https://github.com/jashkenas/coffee-script/issues/1424) A = -> ok eq ok, new A? eq ok, do A? A = null eq void new A? eq void do A? # [coffee#1513](https://github.com/jashkenas/coffee-script/issues/1513) {}frontness? ### Soak Assign a = [0 null] b? = a.0 b? = a.1 eq b, 0 let [x, y?]? = a, {z}? = a.1 eq x, 0 eq y, void eq z, void ### on function call f = -> 42 g = -> void ok f!? ok not g!? # #471 obj = f: -> @xs.toString! + [...&]toString! xs: [1 5] eq '1,51,5', obj.f? ...obj.xsLiveScript-1.5.0/test/function.ls000664 000000 000000 00000030706 12716147631 016743 0ustar00rootroot000000 000000 {map} = require 'prelude-ls' I = (x) -> x M = (x) -> x x eq I, M I # Bare calls. eq I (), I( ) # The empty function should not cause a syntax error. -> (while 0 then 0).pop() # Multiple nested function declarations mixed with implicit calls should not # cause a syntax error. (one) -> (two) -> three four, (five) -> six seven, eight, (nine) -> obj = { bound : -> do ~> this unbound : -> do -> this nested : -> do ~> do ~> do ~> this } eq obj, obj.bound() ok obj is not obj.unbound() eq obj, obj.nested() # Multi-blocks with optional parens. result = I( -> I -> "Wrapped" ) eq result()(), 'Wrapped' eq 'lo', ("hello".slice) 3 # Nested single-line functions. func = (x) -> (x) -> (x) -> x eq func(1)(2)(3), 3 # Trailing comma/semicolon in a function parameter. eq 1, ((f,) -> f()) (,) -> 1 eq 2, ((f;) -> f()) (;) -> 2 # Implicit calls in functions in parens. eq 10, ((val) -> [].push val val )(10) # Passing multiple multiline functions without paren-wrapping. sum = (one, two) -> one() + two() eq 6, sum(-> 1 + 2 , -> 2 + 1 ) eq 20, sum -> 7 + 9 , -> /* spacer */ 1 + 3 # Implicit calls against trailing conditional branches. eq 2, if 0 then 1 else 2 eq 6, switch 3 case 4 then 5 default 6 # Implicit calls using leading commas. eq 1 , 1 eq \ 2 , 2 a = Array 0 , do -> 1 , do -> 2; 2 , 3 eq a+'' '0,1,2,3' # Assignment to an inner variable that shares a name with # an `Object.prototype` member should not leak. (-> @@ = 'word')() ok @@ is not 'word' # Implicit call including an implicit object and a trailing function. meth = (arg, obj, func) -> String [obj.a, arg, func()] eq '13,apple,orange', meth 'apple', b: 1, a: 13, -> 'orange' # Ensure that empty functions don't return mistaken values. o = func: (@param, ...@rest) -> eq void , o.func(101, 102, 103, 104) eq 101 , o.param eq '102,103,104', '' + o.rest (-> this it, it @ it, it ).call ok, '`this` should be able to call implicitly' # `new` should apply to the first function of a call chain, args = [1 2] eq 3, new (-> fun: (x, y) -> x + y)().fun ...args eq 3, new (-> (x, y) -> x + y)() ...args eq 4 (new new Function 'this.p = 4')p eq 5 new new Function('this.q = 5')()q # but not to helper functions. eq 1, new [...[-> [1]]].0().0 # slice$ eq 'object', typeof new {f: Number}.~f() # bind$ # Chained blocks, with proper indentation levels: counter = results: [] tick: (func) -> @results.push func() this counter .tick -> 3 .tick -> 2 .tick -> 1 eq counter.results.join(' '), '3 2 1' # Newline-supressed call chains with nested functions. obj = call: -> this func = -> obj .call -> one two .call -> three four 101 eq func(), 101 ok new Date!@@ is Date , '`new` should not add extra parens' ok new (Object C: Number).C instanceof Number , '`new` should not unwrap parenthesized operand' # `new` against bare function prevents implicit return. o = new -> @0 = 1 [2] eq o.0, 1 # Implicit calls against non-spaced unary plus/minus. eq +5, +5 eq -5, -5 # Implicit calls against precrements. n = 0 eq ++n, 1 eq --n, 0 eq ok, do -> ok /* Should `return` implicitly */ /* even with trailing comments. */ throws 'misplaced function declaration on line 1', -> LiveScript.compile 'if 1 then function F then' # Returns with multiple branches. func = -> if it for n in [1, 2] then return n else 0 eq func(0), 0 eq func(1), 1 # Don't gather results from a loop that _jumps_ out of a closure. findIt = (items) -> for item in items then return item if item is 'bacon' eq 'bacon', findIt [1, 2, 3, 'bacon', 4, 5] eq void , findIt [] # When a closure wrapper is generated for expression conversion, make sure # that references to "this" within the wrapper are safely converted as well. obj = method: -> (switch case 1 then this) eq obj.method(), obj eq 3, do -> (1; 2; 3) eq 3, do -> return (1; 2; 3) compileThrows 'inconvertible statement' 1 'b = break' compileThrows 'inconvertible statement' 2 ''' r = return ''' compileThrows 'inconvertible statement' 3 ''' r = if 1 2 + return ''' eq '(function(){})(function(){});', LiveScript.compile '(->return)(->void)', {+bare,-header} # `@it` isn't `it` eq ''' (function(){ return this.it; }); ''', LiveScript.compile '-> @it', {+bare,-header} # Simple functions require no parens when comma-listed. funs = [->, -> 1, -> it, -> this, null] eq 1, +funs.3.call funs.2 funs.1() # [#81](https://github.com/satyr/coco/issues/81) ok I(->), 'be careful when specialcasing `-> X ,`' eq 0, (new do -> Array).length x = y = 10; x1 = y1 = 20 area = (x, y, x1, y1) -> (x - x1) * (x - y1) eq area(x, y, x1, y1), 100 eq(area( x y x1, y1 ), 100) sumOfArgs = -> sum = 0 for val in arguments then sum += val sum eq 15, sumOfArgs(1, 2, 3, 4, 5) ((@arg) ->).call context = {}, 1 eq 1, context.arg ((...splat, @arg) ->).call context, 1, 2, 3 eq 3, context.arg ((...@arg) ->).call context, 1, 2, 3 eq '1,2,3', '' + context.arg eq 1, new (class then (@do) -> eq @do, $do)(1).do # Parameter destructuring ((...[{a: [b], c}]) -> eq b, 123 eq c, 456 ) {a: [123], c: 456} y = false ((a, ...[x]:b, c) -> eq 1, a deep-equal [2, 3], b eq 4, c eq 2, x y := true ) 1, 2, 3, 4 ok y # Parameter default values obj = f: (q = 123, @p = 456) -> q eq obj.f(), 123 eq obj.p , 456 withSplats = (a = 2, ...b, c = 3, d ? 5) -> a * (b.length + 1) * c * d eq 30, withSplats() eq 15, withSplats 1 eq 5, withSplats 1, 1 eq 1, withSplats 1, 1, 1 eq 2, withSplats 1, 1, 1, 1 f = (a || 2, b && 5) -> a + b eq 7, f 0, 1 eq 1, f 1, 0 eq 6, f 1, 1 eq 2, f 0, 0 f = (a ||= 2, b &&= 5) -> a + b eq 7, f 0, 1 eq 1, f 1, 0 eq 6, f 1, 1 eq 2, f 0, 0 do (a ? I(1)) -> eq a, 1 eq 1, do []= (a || 0 || 1) -> a eq arguments, switch case 1 eq arguments, (for i to 0 then arguments)0 arguments ok (@arguments, @eval) -> compileThrows 'duplicate parameter "a"' 1 '(a, a) ->' # Fun with radical parameters. obj = {} set = (name, obj[name]) -> set \key \value eq obj.key, \value # Call takes precedence over function parameter. eq 0, I(-> it()) -> 0 eq void Function() -> # Ignore trailing placeholder parameters. eq 0 ((,,...,,...,,) -> it)length ### Invalid call detection compileThrows 'invalid callee' 1 '[]()' compileThrows 'invalid constructor' 1 'new 42' ### `new` block o = new @1 = \_ @length = 3 {} eq '0_0' o * \0 ### Backcalls g = (...a, f) -> f ...a h = (f, ...a) -> f a eq ok, do (a, b) <- g \a, \b eq b, \b ...d <- h _, a eq d.0.0, \a ok new me = this f <~ M eq me, this eq \function typeof f eq 3 do do <- I 1 2 3 eq 6 (a <- g 6; a) # [#192](https://github.com/satyr/coco/issues/192) eq '192' do <- '081'replace /./g -~it /* ignore trailing */ /* block comments */ addArr = do (x, y) <-- map _, [2 3 4] x + y eq 5 addArr.0 3 eq 5 addArr.1 2 eq 5 addArr.2 1 t-obj = z: 10 bound: -> (x, y) <~~ map _, [2 3 4] x * y * this.z unbound: -> (x, y) <-- map _, [2 3 4] x * y * this.z timesArr = t-obj.bound! eq 60 timesArr.0 3 eq 60 timesArr.1 2 eq 60 timesArr.2 1.5 timesArr = t-obj.unbound! ok isNaN timesArr.0 3 ok isNaN timesArr.1 2 ok isNaN timesArr.2 1.5 g = (x, y, z, f) --> eq 6, x + y + z f! f = -> <-! g 1, _, 3 <| 2 true eq void f! ### `function` new function undef1 then function undef2 void eq void undef1() eq void undef2() ~function bound then this eq this, bound.call \this f = act: function (n) if n < 2 then 1 else n * act n-1 eq 120 f.act 5 ok function ok then throw eq void, do f = function then function double(a) then a * 2 function triple a then a * 3 eq 4, double 2 eq 9, triple 3 compileThrows 'redeclaration of function "f"' 2 ''' f = 0 function f then ''' compileThrows 'redeclaration of function "f"' 2 ''' function f f = 1 ''' compileThrows 'redeclaration of function "f"' 2 ''' function f then f = 2 ''' compileThrows 'redeclaration of function "f"' 1 ''' function f f then ''' compileThrows 'increment of function "f"' 2 ''' function f then ++f ''' compileThrows 'misplaced function declaration' 2 'if 1\n function F then' ### `let` new x = y = 1; @z = 2 let x = 0, y, @z eq x, 0 eq y, 1 eq z, 2 x = y = 3 eq x, 1 eq y, 1 eq \chainable, let \chain .concat \able eq ''' (function(){ this; }.call(this)); ''' LiveScript.compile 'let() then this # top-level and should not return' {+bare,-header} ok let [it] = [ok] it is ok let this = eq this eq, this ### `&` let 0 eq & , arguments eq &0, 0 eq &1, void ### thisplat f = (x, y) -> [this, x, y] let @ = 0, x = 1, y = 2 eq '0,1,2' ''+ f ... eq ',' ''+ f(...[])slice 1 ### do-`not`-return eq void do !-> true eq void do !~> true eq void do <-! M <~! M !function C then C ok new C instanceof C eq void do not function f => f true eq false !!-> eq void do (x) !-> true eq void do (x) !--> true eq void do (x) !~> true eq void do (x) !~~> true ### auto currying magic times = (x, y) --> x * y timesTwo = times 2 eq 12 times 2 6 eq 8 timesTwo 4 boundAdd = (x, y) ~~> x + y addThree = boundAdd 3 eq 12 boundAdd 6 6 eq 7 addThree 4 threeParams = (x, y, z) --> x * y + z eq 10 threeParams 2 3 4 5 multByTwo = threeParams 2 eq 7 multByTwo(3)(1) addNine = threeParams 3 3 eq 16 addNine 7 f4 = ((a, b, c, d) --> a * b * c * d)(2)(3) g = f4 5 h = f4 7 eq 330 g 11 eq 546 h 13 ### explicit naming let do a = :b -> eq a, b do c = :d!-> eq c, d do e = !:f-> eq e, f let a <-:b M c <-:d! M e <-!:f M eq a, b eq c, d eq e, f ### composing timesTwo = -> it * 2 plusOne = -> it + 1 timesTwoPlusOne = timesTwo >> plusOne plusOneTimesTwo = timesTwo << plusOne eq 5 timesTwoPlusOne 2 eq 6 plusOneTimesTwo 2 pott = timesTwo . plusOne eq 6 pott 2 eq 'true,false,true,false' "#{ map (is \function) . (typeof), [->, 2, ~>, 3] }" even = (x) -> x % 2 == 0 odd = (not) . even ok odd 3 ok not odd 2 f = (+ 1) >> (* 2) >> (- 10) eq 12, f 10 f = (+ 1) << (* 2) << (- 10) eq 1, f 10 f = (+ 1) >> (* 2) << (- 10) eq 2, f 10 f = (+ 1) << (* 2) >> (- 10) eq 11, f 10 do -> a = -> 1 b = (* 2) c = a >> b a = -> 100 eq 2, c! f = ((x) -> (* x)) >> ((+ 1) >>) eq 10 (f 2) 4 f1 = do -> timesTwo >> plusOne eq 5 f1 2 f2 = do -> timesTwo >> plusOne eq 5 f2 2 f3 = do -> timesTwo >> plusOne eq 5 f3 2 ### infix calls add = (x, y) --> x + y times = (x, y) --> x * y elem = (x, xs) --> x in xs eq 7, 3 `add` 4 eq 8, 3 + 2 `add` add 2 1 eq 25, 2 `add` 3 + 4 `times` 5 eq 25, 2 `add` 3 `times` 5 ok 3 `elem` [1 to 10] eq 5 (`add`) 2 3 eq 5 (2 `add`) 3 eq 5 (`add` 3) 2 ok (`elem` [1 to 10]) 3 ### implicit call/lookup obj = a: 2 b: -> 5 c: (x, y) -> x + y eq 2 (.a) obj eq 5 (.b!) obj eq 7 (.c 3 4) obj eq '5,1,7' "#{ map (.length), [[1 to 5] [1] [1 to 7]] }" eq '1|2|3,1,1|2' "#{ map (.join \|), [[1 to 3] [1] [1 to 2]] }" eq '3,2,,0' "#{ map (?p), [{p: 3}, {p: 2}, , {p: 0}] }" eq 2 (obj.) \a eq 7 ((obj <<< d: 7).) \d eq 2 (.) obj, \a eq 2 ((.) obj) \a ary = [1 2] eq '1,2,3' "#{(.~concat) ary <| 3}" concat = (.~) ary, 'concat' eq '1,2,3' "#{concat 3}" ### partialization three-add = (x, y, z) -> x + y + z g = three-add 2, _, 10 eq 20 g 8 eq 19 g 7 h = three-add 2, _, _ f = h _, 6 eq 10 f 2 two-add = (x = 10, y) -> x + y g = two-add _, 4 eq 14 g! obj = three-add: (x, y, z) -> x + y + z f = obj.three-add 1, _, 3 eq 6 f 2 eq 9 (6 |> obj.three-add 1, _, 2) # preserve context of partially applied function obj = offset: 5 add: (x, y) -> @offset + x + y eq 16 (10 |> obj.add _, 1) # do a named func do -> i = 0 ok named-func eq 1 named-func 1 eq 1 i do function named-func x ++i x eq 2 i # bound and curried class A (@list = \middle) -> enclose: (head, tail) ~~> [head, @list, tail].join! enclose-not-bound: (head, tail) --> [head, @list, tail].join! a = new A fn = a.enclose \head curried = fn \tail eq 'head,middle,tail' curried # multiple instances a2 = new A \middle2 fn2 = a2.enclose \head curried2 = fn2 \tail eq 'head,middle2,tail' curried2 # not bound obj = list: \haha fn: a.enclose-not-bound \head fn2: a.enclose-not-bound eq 'head,haha,tail' obj.fn \tail obj.fn3 = obj.fn2 \h eq 'h,haha,t' obj.fn3 \t # unary ops in parameters f = (!x) -> x ok f false g = (+x) -> x eq 1 g '1' h = (^^x) -> x <<< a: 9, c: 6 obj = a: 1, b: 2 obj2 = h obj eq 9 obj2.a eq 6 obj2.c eq 1 obj.a # original obj hasn't been modified ok not obj.c k = (!!x) -> x eq true k 1 l = (!!@x) -> x obj = {-x} l.call obj, 'hello' eq true obj.x LiveScript-1.5.0/test/generators.ls000664 000000 000000 00000004522 12716147631 017264 0ustar00rootroot000000 000000 # Generators # ----------------- # # * Generator Definition # generator as argument ok ->* 1 # named generator function ok <| :fn ->* 2 # generator definition x = ->* yield 0 yield 1 yield 2 y = x! z = y.next! eq z.value, 0 eq z.done, false z = y.next! eq z.value, 1 eq z.done, false z = y.next! eq z.value, 2 eq z.done, false z = y.next! eq z.value, void eq z.done, true # function declaration generator function* f yield 0 yield 1 yield 2 y = f! z = y.next! eq z.value, 0 eq z.done, false z = y.next! eq z.value, 1 eq z.done, false z = y.next! eq z.value, 2 eq z.done, false z = y.next! eq z.value, void eq z.done, true # yield from first = ->* i = 0 loop => yield i++ second = ->* yield from first! list = second! for i to 3 {value} = list.next! eq value, i # curried bound generators class A val: 5 curried: (x, y) ~~>* yield @val + x + y fn = (new A).curried yield-add = fn 2 y = yield-add 3 z = y.next! eq z.value, 10 eq z.done, false z = y.next! eq z.value, void eq z.done, true # bound generator obj = bound: -> do ~>* yield this unbound: -> do ->* yield this eq obj, obj.bound().next().value ok obj isnt obj.unbound().next().value # yield as expression, yield precendence f1 = ->* x = yield "foo" yield x + 2 g1 = f1! eq "foo" g1.next!.value eq 5 g1.next(3).value # generator returns f2 = ->* yield 1 2 g2 = f2! eq 1 g2.next!.value eq 2 g2.next!.value # backcall test-val = 0 do f3 = (gen) -> g3 = gen! test-val := g3.next!.value *<- f3 yield 1 eq 1 test-val # don't spread f4 = ->* yield [1, 2] g4 = f4! deep-equal [1, 2] g4.next!.value # parens, consumer yield f5 = ->* if (yield) and not (yield) ok true else ok false g5 = f5! g5.next! g5.next true g5.next false # yield expression as argument, as callable is-two = -> it == 2 f6 = ->* is-two yield 1 ok (yield 1)(2) ok (2 |> yield 1) g6 = f6! eq 1 g6.next(2).value g6.next is-two g6.next is-two # in switch f7 = (x) ->* y = switch x | true => yield 1 | _ => yield 2 y g7 = f7 true eq 1 g7.next!.value f8 = ->* result = for let i in [1,2,3] yield i -> i * 2 result g8 = f8! eq 1 g8.next!value eq 2 g8.next!value eq 3 g8.next!value g8_result = g8.next!value eq 2 g8_result[0]() eq 4 g8_result[1]() eq 6 g8_result[2]() LiveScript-1.5.0/test/heredoc.ls000664 000000 000000 00000001746 12716147631 016531 0ustar00rootroot000000 000000 eq ?= (a,b,msg="#a != #b") -> console.assert a == b, msg eq ' here there', ''' here ''' + """there""" eq ' here there', ''' here there ''' eq ' here there', ''' here''' + """ there """ # function inside heredoc eq ''' (function(){ var a; a = arguments[arguments.length - 1]; }); ''', LiveScript.compile '(..., a) ->', {+bare,-header} eq ''' a = -> 3 ''', 'a = ->\n 3' # heredoc with `then` function help then """ Usage: livescript [options] [files] [arguments] Options: #o """ eq 34, help.toString().indexOf("livescript") eq help.toString(), 'function help(){\n return "Usage: livescript [options] [files] [arguments]\\n\\nOptions:\\n" + o;\n}' eq '''function help(){ return "Usage: livescript [options] [files] [arguments]\\n\\nOptions:\\n" + o; }''', help.toString() # heredoc inside function need lines to be indented. function helper """ Usage: livescript [options] [files] [arguments] Options: #o """ eq 36, helper.toString().indexOf("livescript") LiveScript-1.5.0/test/if.ls000664 000000 000000 00000004302 12716147631 015505 0ustar00rootroot000000 000000 t = true # As implicit argument, nested, and/or one-liner. eq 1, if yes if on if no then false else if t 1 ok if 0 0 else if 0/0 0/0 else if void void else if null null else true ok if false then false else if false then false else true eq 100, Number if false then 300 else 100 # `unless` eq 1, unless true 0 else 1 # Returning if-else. eq -1, do -> if 1 < 0.5 then 1 else -1 # `else`-less `if` returns `undefined` with falsy condition. eq void, if 0 then eq void, do -> if 0 then # As part of a larger operation. eq 2, 1 + if false then 10 else 1 # Indented within an assignment. eq 5, do -> a = if false 3 else 5 101 a # Unmatched `then` should close implicit calls. i = 1 if Boolean 1 then ++i eq i, 2 # Unmatched `else` should close implicit blocks. eq 2, do -> if 0 then -> 1 else 2 # Outer `then` should skip `else`. eq 3, if 1 then if 0 then 2 else 3 # With suppressed indentations. eq 6, if 0 then 1 \ else 2 + if 3 then 4 \ else 5 # With leading `then`. if 0 then ok false else eq 2, if 1 then 2 else 3 # Post-condition should accept trailing non-`if` block. ok true if -> ok true if do true ok true if let true ok true if do function f true # [coffee#738](https://github.com/jashkenas/coffee-script/issues/738) ok if true then -> 1 # [coffee#1026](https://github.com/jashkenas/coffee-script/issues/1026) throws "Parse error on line 2: Unexpected 'ELSE'", -> LiveScript.ast ''' if a then b else then c else then d ''' eq 2, [if 1 then 2 , 3].0 eq 2, [if 0 then 1 else 2, 3].0 # Compile conditonal expression chains neatly. eq ''' var r; r = a ? b : c ? d : e(); ''' LiveScript.compile ''' r = if a then b else if c then d else e! ''' {+bare,-header} ### Anaphoric `if` eq ''' var that; if (1) { if (that = 2) { if (3) { 4; } if (that) { 5; } } } if ((that = 6) != null) { that; } ''', LiveScript.compile ''' if 1 if 2 4 if 3 5 if that that if 6? ''', {+bare,-header} # Soaks should not `that`-aware. a = [0 1] if 1 eq 1 a?[that] # then => if false => ok 0 else if true => ok 1 else if true => ok 0 else ok 0 LiveScript-1.5.0/test/label.ls000664 000000 000000 00000000565 12716147631 016175 0ustar00rootroot000000 000000 r = '' :PLAIN :WHILE while r.length < 9 break PLAIN if r.length > 1 :FOR for i til 2 r += i continue WHILE ok 0 eq r, \00 eq void, :if 1 switch break _ true r = :outer :inner break inner break outer if false 1 eq r, 1 compileThrows 'unknown label "a"' 1 'break a' compileThrows 'duplicate label "b"' 2 ''' :b :b break b ''' LiveScript-1.5.0/test/lib.ls000664 000000 000000 00000000074 12716147631 015657 0ustar00rootroot000000 000000 eq (require '../package.json' .version), LiveScript.VERSION LiveScript-1.5.0/test/literal.ls000664 000000 000000 00000020143 12716147631 016544 0ustar00rootroot000000 000000 ### Boolean ok true ok !false ok yes ok on ok !off ok !no throws 'invalid assign on line 1' -> LiveScript.compile 'yes = 6' ### Identifiers eq encodeURIComponent, encode-URI-component eq ''.toLowerCase, ''.to-lower-case function no-op then eq no-op(), void eq noOp.length, 0 try throw 0 catch e-r then eq eR, 0 ### Numbers eq 3-4, -1 # Decimal eq 1.0, 0.0 + -0.25 - -0.75 + 0.5 eq 2011_04_24, 20110424 # Hex eq 255, 0xff eq 0xB_C__D___, 0xBCD # With radix ok 2~101010 == 8~0644/10 == 42 eq 36~O_o, 888 # With comment eq 1year * 365.25days * 24hours, 8766_total_hours eq 36, 0XF + 36RT eq 100m2, 10m ** 2 eq 3000c, 30$ * 100 eq 36rpm 36 # Ranges start = 1 end = 5 step = 2 eq '1,2,3,4,5' String [start to end] eq '1,2,3,4' String [start til end] eq '2,3,4,5' String [3 - 1 to end] eq '3,4' String [3 til end] eq '3,5' String [start + 2 to end by step ] eq '1,3,5' String [1 to end by step ] eq '1,3' String [start til 5 by step ] eq '1,5' String [start to end by 4 ] eq '5,3' String [5 til 1 by -step] eq '1,3,5' String [start to 5 by 2 ] eq '1,3,5' String [1 to 5 by 2 ] eq '0,1,2,3' String [to 3] eq '0,1,2' String [til 3] eq '0,2,4' String [to 4 by 2] eq '0,2' String [til 4 by 2] to = 3 eq 3 to eq 4 [1 to end].3 eq 5 [1 to end].length r = [1 to end] eq '1,2,3,4,5' String r # [coffee#764](https://github.com/jashkenas/coffee-script/issues/764) # Boolean/Number should be indexable. ok 42['toString'] ok true['toString'] ### Arrays a = [((x) -> x), ((x) -> x * x)] eq a.length, 2 sum = 0 for n in [ 1, 2, 3, 4 5 6 7, 8 9 ] then sum += n eq sum, 45 # Trailing commas. eq '1,2' String [1, 2,] # Funky indentation within non-comma-seperated arrays. result = [['a'] {b: 'c'}] eq 'a', result.0.0 eq 'c', result.1.b #### Words eq '<[ quoted words ]>', <[ <[ quoted words ]\> ]>.join ' ' eq \\ <[\]>0 eq 0 <[ ]>length eq \1 [String]<[0]> 1 compile-throws 'unterminated words' 5 ''' <[ a b ]> <[ ''' #### Implicit arrays o = atom: 0 list: 1 2 eq 0 o.atom eq 2 o.list.length a = 3 a = a, 4 a = ...a eq '3,4' ''+a points = * x: 0 y: 1 * x: 2, y: 3 eq 0 points.0.x eq 3 points.1.y I2 = * 1 0 * 0 1 eq I2.0.0, I2.1.1 eq I2.0.1, I2.1.0 a = [] <<< 0, 1 2; 3 a += 4 5 eq '0,1,2,34,5' a eq '0,1' ''+ do -> return 0, 1 try throw 2, 3 catch eq '2,3' ''+e ### Objects o = {k1: "v1", k2: 4, k3: (-> true),} ok o.k3() and o.k2 is 4 and o.k1 is "v1" eq 10, {a: Number}.a 10 moe = { name: 'Moe' greet: (salutation) -> salutation + " " + @name hello: -> @['greet'] "Hello" 10: 'number' } eq moe.hello() ,"Hello Moe" eq moe[10] ,'number' moe.hello = -> this['greet'] "Hello" eq moe.hello(), 'Hello Moe' # Keys can include keywords. obj = { is : -> true, not : -> false, } ok obj.is() ok not obj.not() obj = {class: 'hot'} obj.function = 'dog' eq obj.class + obj.function, 'hotdog' # Property shorthands. new a = 0; @b = 1; x = {a, @b, 2, \3, +4, -5} eq x.a, 0 eq x.b, 1 eq x.2, 2 eq x.3, \3 eq x.4, true eq x.5, false c = null; d = 0; y = {a || 1, @b && 2, c ? 3} eq y.a, 1 eq y.b, 2 eq y.c, 3 z = {true, false, on, off, yes, no, null, void, this, arguments, eval, -super, +debugger} eq z.true , true eq z.false , false eq z.on , on eq z.off , off eq z.yes , yes eq z.no , no eq z.null , null eq z.void , void eq z.this , this eq z.arguments , arguments eq z.eval , eval eq z.super , false eq z.debugger , true # [coffee#542](https://github.com/jashkenas/coffee-script/issues/542): # Objects leading expression statement should be parenthesized. {f: -> ok true }.f() + 1 # [#19](https://github.com/satyr/coco/issues/19) compileThrows 'duplicate property "a"' 1 '{a, b, a}' compileThrows 'duplicate property "0"' 1 '{0, "0"}' compileThrows 'duplicate property "1"' 1 '{1, 1.0}' #### Implicit/Braceless config = development: server: 'localhost' timeout: 10 production: server: 'dreamboat' timeout: 1000 eq config.development.server ,'localhost' eq config.production.server ,'dreamboat' eq config.development.timeout ,10 eq config.production.timeout ,1000 o = a: 1 b: 2, c: d: 3 e: f: 'g': 4 0: 5 eq '1,2,3,4,5' String [o.a, o.b, o.c.d, o.e.f.g, o.0] # Implicit call should step over INDENT after `:`. o = Object a: b: 2, c: 3, eq 6, o.a.b * o.a.c /* Top-level braceless object */ obj: 1 /* doesn't break things. */ # With number arguments. k: eq 1, 1 # With wacky indentations. obj = 'reverse': (obj) -> Array.prototype.reverse.call obj abc: -> @reverse( @reverse @reverse ['a', 'b', 'c'].reverse() ) one: [1, 2, a: 'b' 3, 4] red: orange: yellow: green: 'blue' indigo: 'violet' oddent: [[], [], [], []] eq obj.abc() + '' ,'a,b,c' eq obj.one.length ,5 eq obj.one[4] ,4 eq obj.one[2].a ,'b' eq obj.red.indigo ,'violet' eq obj.oddent + '' ,',,,' eq obj.red.orange.yellow.green, 'blue' eq 2, [key for key of obj.red].length # As part of chained calls. pick = -> it.$ eq 9, pick pick pick $: $: $: 9 # [coffee#618](https://github.com/jashkenas/coffee-script/issues/618): # Should not consume shorthand properties. a = Array do 1: 2 3 eq 2, a.length eq 2, (Array 1: 2, 3).length # With leading comments. obj = /* comment one */ /* comment two */ one: 1, two: 2 fun: -> [zero: 0; three: @one + @two][1] eq obj.fun().three, 3 # [coffee#1871](https://github.com/jashkenas/coffee-script/issues/1871), # [coffee#1903](https://github.com/jashkenas/coffee-script/issues/1903): # Should close early when inline. o = p: 0 q: 1 o = q: 1 if false ok \q not of o # Inline with assignment/import. o = p: t = q: 0 r: t <<< s: 1 eq o.p, o.r eq o.p.q, 0 eq o.r.s, 1 #### Dynamic Keys i = 0 o = splat: 'me' obj = { /* leading comment */ (4 * 2): 8 /* cached shorthand */ (++i) (--i) or 'default value' /* splat */ ...o ...: splatMe: 'too' /* normal keys */ key: ok 's': ok 0.5: ok "#{'interpolated'}": """#{"nested"}""": 123: 456 /* traling comment */ } eq obj.interpolated.nested[123], 456 eq obj[8], 8 eq obj[1], 1 eq obj[0], 'default value' eq obj.splat , 'me' eq obj.splatMe, 'too' ok obj.key is obj.s is obj[1/2] eq 'braceless dynamic key', [key for key of """braceless #{ 0 of ((0):(0)) and 'dynamic' } key""": 0][0] obj = one: 1 (1 + 2 * 4): 9 eq obj[9], 9, 'trailing dynamic property should be braced as well' obj.key = 'val' obj.val = ok {(obj.key)} = obj eq ok, obj.key {(frontness?)} ### `void` eq void, [][0] eq void+'', 'undefined' eq [,,].length, 2 [, a, , b,] = [2 3 5 7] eq a * b, 21 eq 11, ((, a) -> a)(, 11) # undefined not a keyword... undefined = 52 eq 52 undefined ### ACI eq null null eq \1 \1 eq 2 [{} {}].length eq 3*[4] 12 eq '0,true,2,3' String [0 true \2 (3)] o = {0 \1 \2 3 4 (5)} eq o.1, \1 eq o.3, 3 eq o.5, 5 ### Numeric/Character Ranges show = -> it * ' ' eq '-1 0 1 2' show [-1 to +2] eq '1 0 -1' show [+1 to -1 by -1] eq '4 3 2' show [4 to 2] eq '999 1000' show [999 til 1001] eq '1e-9' show [1e-9 til 1e-8] eq '9999999' show [9999999 til 1e7] eq '10000000' show [1e7 til 9999999 by -1] eq '1 2 3' show [1 til 3.00001] eq '0.5 0.75 1' show [0.5 til 1.2 by 0.25] eq 'A F K P U Z' show [\A to \Z by 5] eq 'y u q m i e' show [\y til \a by -4] ok [\\u2028 to \\u2029] compileThrows 'range limit exceeded' 2 '\n[0 to 1 by 1e-5]' compileThrows 'empty range' 2 '\n[1 til 1]' compileThrows 'empty range' 2 '\n[2 to 3 by -1]' compileThrows 'bad "to" in range' 2 '\n[0 to "q"]' compileThrows 'bad "by" in range' 2 '\n[0 to 9 by "2"]' compileThrows 'bad string in range' 2 '\n["a" to "bc"]' ### yadayadayada throws \unimplemented -> ... ### Cascade a = with [2 7 1 8 2] ..push 3 ..sort! ..shift! ..pop! .join '' eq \2237 a ok .. .., .. (->) ..(.., ..) ok ..value-of! # quick map eq \2718, for [1 6 0 7] .. + 1 .join '' # errors compileThrows 'stray reference' 2 '\n..' compileThrows 'unreferred cascadee' 1 'a\n b' LiveScript-1.5.0/test/loop.ls000664 000000 000000 00000032671 12716147631 016072 0ustar00rootroot000000 000000 i = 5 list = while i -= 1 i * 2 deep-equal [8,6,4,2], list i = 5 list = [i * 3 while i -= 1] deep-equal [12,9,6,3] list i = 5 func = -> i -= it assert = -> unless 0 < i < 5 then ok false results = while func 1 assert() i deep-equal [4,3,2,1] results value = false i = 0 results = until value value = true if i is 5 i += 1 eq 6 i i = 5 list = [] for ever i -= 1 break if i is 0 list.push i * 2 deep-equal [8 6 4 2] list j = 5 list2 = [] loop j -= 1 break if j is 0 list2.push j * 2 deep-equal [8 6 4 2] list #759: `if` within `while` condition [2 while if 1 then 0] # https://github.com/jashkenas/coffee-script/issues/843 eq void, do -> while 0 then return # Basic array comprehensions. nums = for n in [1, 2, 3] then n * n if n .&. 1 results = [n * 2 for n in nums] deep-equal [2,18] results eq 11 [x for x to 10]length # Basic 'of' comprehensions. obj = {one: 1, two: 2, three: 3} names = [prop + '!' for prop of obj] odds = for prop, value of obj then prop + '!' if value .&. 1 deep-equal <[ one! two! three! ]> names deep-equal <[ one! three! ]> odds # Object comprehensions result = {[key, val * 2] for key, val of obj} deep-equal {one: 2, two: 4, three: 6}, result result = {[val, key] for key, val of obj} deep-equal {1: 'one', 2: 'two', 3: 'three'}, result f = -> {[key, val * 2] for key, val of {a:1, b:2}} obj = f! deep-equal {a: 2, b: 4} obj r = {[key, val] for key, val of {a:1, b:2} when val isnt 2} deep-equal {a: 1} r ok not r.b? input = a: b: 1, c: 2 d: e: 3, f: 4 result = { ["#{k1}#{k2}", v] for k1, x of input for k2, v of x } deep-equal {ab: 1, ac: 2, de: 3, df: 4} result eq \Object typeof! result result = [ { ["#{k1}#{k2}", v] for k2, v of x } for k1, x of input ] deep-equal [ { ab: 1, ac: 2 } { de: 3, df: 4} ] result eq \Array typeof! result eq \Object typeof! result.0 result = { ["#k#x", x + v] for x from 0 to 1 for k, v of {a: 1, b: 2} } deep-equal { a0: 1, a1: 2, b0: 2, b1: 3 } result eq \Object typeof! result result = { ["#k#x", x + v] for k, v of {a: 1, b: 2} for x from 0 to 1 } deep-equal { a0: 1, a1: 2, b0: 2, b1: 3 } result eq \Object typeof! result result = { ["#k#x", x] for k in <[ a b ]> for x from 0 to 1 } deep-equal { a0: 0, a1: 1, b0: 0, b1: 1 } result eq \Object typeof! result # obj comp [livescript#639](https://github.com/gkz/LiveScript/issues/639) i = 0 f = -> i++ true o = {[k, v] for k, v of {a: 1} when f!} deep-equal {a: 1} o eq 1 i i = 0 {a} = {[k, v] for k, v of {a: 1} when f!} eq 1 i eq 1 a i = 0 o = null g = -> o := it g {[k, v] for k, v of {a: 1} when f!} deep-equal {a: 1} o eq 1 i i = 0 a = {[k, v] for k, v of {a: 1} when f!}.a eq 1 i eq 1 a i = 0 g = -> {[k, v] for k, v of {a: 1} when f!} deep-equal {a: 1} g! eq 1 i # Basic range comprehensions. nums = [i * 3 for i from 1 to 3] negs = [x for x from -14 to -5*2] four = [4 for x to 3] deep-equal [3,6,9] nums deep-equal [-14,-13,-12,-11,-10] negs deep-equal [4,4,4,4] four deep-equal [1,2,3], [i for i from 1 til 4 ] deep-equal [0,3,6], [i for i from 0 til 9 by 3] # Auto-descend when obvious. deep-equal [0,-1,-2] [i for i til -3] # Almost never mess with binary `in`/`of` and variable `by`. all = from = to = by = 1 for i to 0 ok 0 of [0] ok 0 in [0] ok by = true for by in [1] then by ok by for by til 1 then by ok not by # With range comprehensions, you can loop in steps. deep-equal [0 3 6 9] [x for x from 0 to 9 by 3] deep-equal [9 6 3 0] [x for x from 9 to 0 by -3] deep-equal [9 6 3 0] [x for x from 3*3 to 0*0 by 0-3] # Multiline array comprehension with filter. evens = for num in [1, 2, 3, 4, 5, 6] then if num % 2 is 0 num *= -1 num -= 2 num * -1 deep-equal [4 6 8] evens # Backward traversing. odds = [num for num in [0, 1, 2, 3, 4, 5] by -2] deep-equal [5 3 1] odds # Multiline nested loops result in nested output eq 9 (for x from 3 to 5 for y from 3 to 5 x * y).0.0 multiLiner = for x from 3 to 5 for y from 3 to 5 x * y singleLiner = [x * y for y from 3 to 5 for x from 3 to 5] eq 3 multiLiner.length eq 9 singleLiner.length eq 25, multiLiner[*-1][*-1] eq 25, singleLiner[*-1] xs = for x to 5 for y to 5 if x is y for z to 2 x + y + z else for z from 10 to 15 x + y + z eq 6 xs.length eq 6 xs.0.length eq 3 xs.0.0.length eq 6 xs.0.1.length # Nested comprehensions. comp = ["#x#y" for x in [1 2 3] for y in [\a \b \c]] deep-equal <[ 1a 1b 1c 2a 2b 2c 3a 3b 3c ]> comp pythagoreanTriples = [[x,y,z] for x in [1 to 20] for y in [x to 20] for z in [y to 20] when x^2 + y^2 == z^2] eq "#{ pythagoreanTriples * \_ }", '3,4,5_5,12,13_6,8,10_8,15,17_9,12,15_12,16,20' # Comprehensions in comprehensions zs = [[x + y for y til 5] for x til 5] eq 5 zs.length eq 8 zs.4.4 obs = [{[x, i + y] for x, i in <[ one two ]>} for y to 5] eq 6 obs.length eq 0 obs.0.one eq 6 obs[*-1].two # Object comprehension in comprehension, see: # https://github.com/gkz/LiveScript/issues/538 obs = [{[key + i, val] for key, val of obj} for obj, i in [{+a, +b}, {-a, -b}]] deep-equal [{a0: true, b0: true}, {a1: false, b1: false}] obs ok typeof! obs[0] is \Object # Object and list comprehensions in the same scope should not share an empty value: # https://github.com/gkz/LiveScript/issues/294 using-if = -> if it {[key, value] for key, value of {a:1}} else [value for value in [1]] deep-equal {a:1} using-if true eq \Object typeof! using-if true deep-equal [1] using-if false eq \Array typeof! using-if false using-switch = -> switch it | true => {[key, value] for key, value of {}} | false => [value for value in []] eq \Object typeof! using-switch true eq \Array typeof! using-switch false # super nested comprehensions crazy = [{[y, [{[k + y, x + 1] for k, v of {a: 1}} for x from 1 to 2]] for y in <[ x y ]>} for z to 1] deep-equal [ * x: [{ax: 2}, {ax: 3}] y: [{ay: 2}, {ay: 3}] * x: [{ax: 2}, {ax: 3}] y: [{ay: 2}, {ay: 3}] ], crazy # Comprehensions returned xs = do -> [[x + y for x to 1] for y to 1] deep-equal [[0 1], [1 2]] xs eq 2 xs.length eq 2 xs.1.length ys = let for y to 1 for x to 1 x eq 2 ys.length eq 2 ys.1.length zs = do -> [x + y for x to 1 for y to 1] eq 4 zs.length deep-equal [0,1,1,2] zs # Comprehensions with cascade deep-equal [3,4,5] [.. + 2 for [1 2 3]] deep-equal [3,5] [.. + 2 for [1 2 3] when .. % 2 isnt 0] deep-equal [5,4,3] [.. + 2 for [1 2 3] by -1] deep-equal [5,3] [.. + 2 for [1 2 3] by -1 when .. % 2 isnt 0] deep-equal [3,4,5] [.. + 2 for from 1 to 3] deep-equal [3,5] [.. + 2 for from 1 to 3 when .. % 2 isnt 0] deep-equal [5,4,3] [.. + 2 for from 3 to 1 by -1] deep-equal [5,3] [.. + 2 for from 3 to 1 by -1 when .. % 2 isnt 0] # gkz/LiveScript#854 deep-equal [2,3,4,5] [.. + 2 for [to 3]] list-of-obj = * ha: 1 mo: 8 * ha: 4 la: 2 deep-equal [1,4] [..ha for list-of-obj] ys = [\A to \D] ++ [\H to \K] ++ [\Z] deep-equal <[ A B C D H I J K Z ]> [.. for [\A to \Z] when .. in ys] # Cascade comprehension doesn't prevent from using `in` later [.. for [0]] ok 0 in [0] # Comprehensions in loops xs = for x to 5 [x + y for y to 5] eq 6 xs.length eq 10 xs[*-1][*-1] xs = for x to 5 if x % 2 is 0 [x + y for y to 2] else [x + y for y to 5] eq 6 xs.length eq 6 xs[*-2][*-1] eq 10 xs[*-1][*-1] xs = for x to 5 if x % 2 is 0 w = [x + y for y to 2] w else v = [x + y for y to 5] v eq 6 xs.length eq 6 xs[*-2][*-1] eq 10 xs[*-1][*-1] xs = for i to 5 while 0 => while 0 => i deep-equal [0,1,2,3,4,5] xs xs = for x to 3 [y] = [z for z from 1 to 2] y + x deep-equal [1 2 3 4] xs # Multiline comprehensions res = [x + y for x to 4 for y to 3] eq 7 res[*-1] res = [x + y for x to 4 for y to 3 ] eq 7 res[*-1] res = [x + y + z for x to 4 for y to 3 for z to 2] eq 9 res[*-1] res = [x + y + z for x to 4 for y to 3 for z to 2 ] eq 9 res[*-1] res = [( a = 1 b = a + 2 a + b + x + y + z ) for x to 4 for y to 3 for z to 2] eq 13 res[*-1] # Comprehensions within parentheses. result = null store = -> result := it store [x * 2 for x in [3, 2, 1]] ok result.join(' ') is '6 4 2' # Closure-wrapped comprehensions that refer to the "arguments" object. expr = -> result = [item * item for item in arguments] ok expr(2, 4, 8).join(' ') is '4 16 64' # Fast object comprehensions over all properties, including prototypal ones. class Cat -> @name = 'Whiskers' breed: 'tabby' hair: 'cream' whiskers = new Cat own = [value for own key, value of whiskers] all = [value for key, value of whiskers] ok own.join(' ') is 'Whiskers' ok all.sort().join(' ') is 'Whiskers cream tabby' f = -> [-> ok false, 'should cache source'] for k of [f] = f() then ok true # Allow non-last lines to have `continue` or `break`. func = -> for i from 1 to 2 break if i is 2 for j in [3] then i * j eq func!0.0, 3 i = 6 odds = while i-- continue unless i .&. 1 i deep-equal [5,3,1], odds r = for i from 0 to 2 switch i case 0 then continue case 1 then i default break deep-equal [1], r eq (while 1 then break; 1).length, 0 copy = {} for k, copy[k] of [4, 2] then continue eq copy.0 * copy.1, 8 new -> do ~> me = this [] = for ever eq me, this eq me, do ~> this break 1 compileThrows 'stray break' 1 \break compileThrows 'stray continue' 1 \continue # Play nice with implicit calls. ok true, while 0 then ok [] = for i to 0 then for i from Number 2 to Number 3 by Number 4 then void eq 2 i let i, j = i eq ...for k to 1 then i # Non-variable assignees. o = i: 0, count: -> @i++ for o.p, i in [0] then eq o.p, +i for i, o.p of [0] then eq o.p, +i for o.count!_ in [1 2] then continue eq o.i, 2 for, o.count!_ of [1 2] then continue eq o.i, 4 # [#195](https://github.com/satyr/coco/issues/195) for [0] ok 0 of {0} for [1] then ok 1 in [1] for [2] => ok 2 in [2] ok 3 not of [] ### Line folding before/after `for` prepositions for x of {2} for y in [3] for z from 5 to 7 by 11 eq x*y*z, 30 ### Function Plucking # Function literals in loops are defined outside. them = [] until them.1 then them.push(->) eq ...them them = [] until them.1 then them.push((x, y) --> x + y) eq 5 them.1(2) 3 ### Post-`for` chains eq "#{ [a * b * c * d \ for a of {1} \ for b in [2] \ for c in [3, 4] by -1 \ for d from 5 to 6 \ for _ of {7}] }", '40,48,30,36' ### Anaphoric while 1 for ever => break eq that, 1 break ### Destructuring `for`-`of` r = 0 for [a, b] i in [[2 3] [5 7]] then r += a * b * i for {a, b} i in [{\a \b}] then r += a + b + i eq r, '35ab0' ### Post condition i = 0 do do ++i until true while ++i < 2 eq i, 2 (-> deep-equal [4,2,0] it) do i * 2 while i-- ### Post condition with when i = 0 list = [1 to 5] do list[i] = list[i] + 1 until ++i > 3 when i isnt 2 deep-equal [2,3,3,5,5], list i = 0 list = [1 to 5] do list[i] = list[i] + 1 while ++i < 3 when i isnt 2 deep-equal [2,3,3,4,5], list ### Update clause i = 0; evens = [i while i < 9, i += 2] deep-equal [0,2,4,6,8] evens i = 1; odds = until i > 9, ++i continue unless i .&. 1 i deep-equal [1,3,5,7,9] odds a = [1 2 3] b = [] while a.pop(), b.push that => continue deep-equal [3,2,1] b ### `else` clause for cond in [true false] while cond break else ok not cond r = for i from 0 to 9 while i .&. 1 break else if i .&. 2 i deep-equal [[],[],[2],[],[],[],[6],[],[],[]] r r = for i til 1 then i else [9] eq 0 r.0 r = for i til 0 then i else [9] eq 9 r.0 ### Omission of `for`'s first assignment for , i in [0] => eq i, 0 for , v of {1} => eq v, 1 for own, v of {2} => eq v, 2 ### When evens = [x for x from 1 to 10 | x % 2 is 0] eq 5 evens.length eq 4 evens.1 for x in <[ amy bibs ashley charlie danny alex ]> when x.charAt(0) is \a ok x in <[ amy ashley alex ]> while i < evens.length, ++i when evens[i] * 2 is 8 eq 4 evens[i] eq '1 3 7 9' [y for y from 1 to 10 when y isnt 5 by 2].join ' ' ### No vars at all i = 0 f = -> i++ for til 2 then f! eq 2 i i = 0 for from 2 to 5 then f! eq 4 i i = 0 eq '0 1 2 3' [f! for til 4].join ' ' i = 0 eq '2 4 6' [f! for til 4 when f!].join ' ' x = [] for <[one two three]> by -1 then x.push .. eq 'three two one' x.join ' ' x = [.. for <[one two three]> by -1] eq 'three two one' x.join ' ' # index var outside loop for v, k in [1] void ok v ok not k # for-let i = v = 7 for let v, k in [0] ok true for let k, v of {a: \b} ok true ok 7 is i is v fns = for let <[foo bar]> for let x in [6 7 8 9] when x % 2 == 0 -> .. + x eq \foo6 fns.0.0! eq \bar8 fns.1.1! xs = for let x, i in [1 to 10] by 2 when x % 3 == 0 -> i + x eq 5, xs[0]! eq 17, xs[1]! xs = for own let key, value of {a: 1, b: 2, c: 3, d: 4} when value % 2 == 0 -> key + value eq 'b2', xs[0]! eq 'd4', xs[1]! arr = [1,3,5,7] o = for let i in (if true => arr else arr) => i eq "1,3,5,7", o.join ',' i = 0 inc = -> i += 1 [1 3 5] o = for let x in inc() => x eq "1,3,5", o.join ',' eq 1, i o = { [k, -> v] for let k, v of {a: 1, b: 2} } eq 1 o.a! eq 2 o.b! # Certain literals could result in illegal JavaScript if not carefully # handled. These are all nonsensical use cases and could just as easily # be LiveScript syntax errors. The thing to avoid is for them to be JavaScript # syntax errors; lsc should never produce illegal JavaScript on any input, # silly or otherwise. deep-equal [] [0 for x in 42] deep-equal [] [0 for x in -42] throws "Cannot read property 'length' of null" -> [0 for x in null] throws "Cannot read property 'length' of undefined" -> [0 for x in void] LiveScript-1.5.0/test/null.ls000664 000000 000000 00000000144 12716147631 016061 0ustar00rootroot000000 000000 ok null == null ok null === null ok void == void ok void === void ok void !== null ok null !== void LiveScript-1.5.0/test/oo.ls000664 000000 000000 00000017037 12716147631 015535 0ustar00rootroot000000 000000 # Test classes with a four-level inheritance chain. class Base func: (string) -> "zero/#{string}" @static = (string) -> "static/#{string}" class FirstChild extends Base func: -> super('one/').concat it SecondChild = class extends FirstChild func: -> (super).call(this, 'two/') + it thirdCtor = -> @array = [1, 2, 3] class ThirdChild extends SecondChild -> thirdCtor ... name = -> 'func' # `super` from a dynamically named method and an inner function. (name!): -> let it super('three/') + it eq (new ThirdChild).func('four'), 'zero/one/two/three/four' eq Base.static('word'), 'static/word' eq (new ThirdChild).array.join(' '), '1 2 3' # `extends` operator First = -> Second = -> Second extends First extends Base ok new Second(2).func(), 'zero/2' # `@` referring to the current instance, and not being coerced into a call. ok (new class I then amI: -> @ instanceof I).amI() # `super` calls in constructors of classes that are defined as object properties. Bee = class then (@name, ...@attributes) -> Bee.Honey = class extends Bee then -> super ... bee = new Bee.Honey 'Maya' \adventurous \flighty eq "#{bee.name} is #{ bee.attributes.join ' and ' }." , 'Maya is adventurous and flighty.' # Test calling super and passing along all arguments. class Parent method: (...args) -> @args = args class Child extends Parent method: -> super ... c = new Child c.method 1, 2, 3, 4 eq c.args.join(' '), '1 2 3 4' # `superclass` injection let superclass = Object eq super, Object # Class with JS-keyword properties. class Class class: 'class' name: -> @class instance = new Class eq instance.class, 'class' eq instance.name(), 'class' # Static method as a bound function. class Dog (@name) -> @static = ~> new this 'Dog' eq {func: Dog.static}.func().name, 'Dog' # A bound function in a bound method. class Mini -> @generate = ~> for let i from 1 to 3 ~> @num * i num: 10 eq [func() for func in new Mini().generate()] + '', '10,20,30' # Test classes wrapped in decorators. func = (klass) -> klass::prop = 'value' klass func class Test prop2: 'value2' eq (new Test).prop , 'value' eq (new Test).prop2, 'value2' # Test anonymous classes. obj = klass: class method: -> 'value' instance = new obj.klass eq instance.method(), 'value' # Implicit objects as static properties. class Static @static = one: 1 two: 2 eq Static.static.one, 1 eq Static.static.two, 2 # Namespaced, Namespace = {} Class = null # but undeclared. Namespace.Class = class eq Namespace.Class.displayName, 'Class' eq Class, null # and declared. class Namespace.Class eq Class, Namespace.Class class BoundCtor extends (-> {@attr}) (@attr, ret) ~> return this if ret eq super(...).attr, @attr @method = ~> this class BoundChild extends BoundCtor ~> super ... # Auto-`return this` even with backcall. <- Object for C in [BoundCtor, BoundChild] bc = C 'attr' eq bc.attr, 'attr' eq [bc.method][0](), bc eq BoundCtor(8, true).attr, 8 class Importer -> this import it method1: this method2: Array class NewSuper extends Importer -> eq new super({ok}).ok, ok method1: -> new super it ns = new NewSuper eq ns.method1({1})[1], 1 eq ns.method2(2).length, 2 # [coffee#1009](https://github.com/jashkenas/coffee-script/issues/1009) # Class names are "$"-prefixed when reserved. new class @in class @eval ok $in? ok $eval? class OddSuper super: me = -> this $uper: me 1234 : me 5.67 : me '""' : me class OddSuperSub extends OddSuper super: -> super() $uper: -> super() 1234 : -> do super 5.67 : -> super 8 '""' : -> super ...@@ oss = new OddSuperSub eq oss.super(), oss eq oss.$uper(), oss eq oss[1234](), oss eq oss[5.67](), oss eq oss['""'](), oss eq \declared (class declared)displayName ok declared? eq \named (new -> return @named = class)displayName ok not named? 'should not leak to global when undeclared' # `super` with nested classes class Sup class @Sub extends this eq super, Sup method: function class extends @.@@ eq super, Sup method: -> eq super, method ok new Sup.Sub instanceof Sup (new (new Sup!method!))method! # `prototype`/`constructor`/`superclass` under class body new class extends Object eq ::, prototype eq ::, @:: eq @@, @ ok super is superclass is Object -> eq ::, @.@@:: eq @@, @.@@ # `super::method` class method: -> true class C extends this method: -> false test : -> super::method! ok new C!test! # `extended` hook class NameEater @subnames = [] @extended = -> eq it.superclass, this @subnames.push it.displayName class A extends NameEater (class B) extends NameEater eq 'A,B' ''+NameEater.subnames # Line folding around `extends` class Inject extends Object class Reject extends Object ok true #### `implements` Mover = x: 0, y: 0 moveTo: (@x, @y) -> this moveBy: (dx, dy) -> @x += dx; @y += dy; this Magnitude = lt : -> ... gt : -> it.lt this lte : -> @lt it or @eq it gte : -> @gt it or @eq it eq : -> not @neq it neq : -> @lt it or @gt it Measurable = lt: -> @measure! < it.measure! class Point implements Mover isAt: -> @x is it.x and @y is it.y class Rect extends Point implements Magnitude, Measurable (@w, @h) -> measure: -> @w * @h r0 = new Rect 2 3 r1 = new Rect 5 7 r0.moveTo 1, 1 r1.moveBy 1, 1 ok r0.isAt r1 ok r0.neq r1 ok r1.gte r0 ok r0.eq new Rect 1 6 ok class extends Function '' implements {} class implements {} void ### Clone bird = {+wing, fly: -> @wing} wingless = {-wing} duck = ^^bird dodo = ^^bird <<< {...wingless, +extinct } donaldo = ^^duck <<< {...wingless, +domestic} ok bird.fly() ok duck.fly() ok not donaldo.fly() ok ^^new Number instanceof Number eq (^^new Number)@@, Number # Inherit constructors class A -> @x = 5 class B extends A getX: -> @x eq 5 (new B)getX! # No body class C extends B eq 5 (new C)getX! class D extends C eq 5 (new D)getX! # Bound methods are bound to instance not class, # however bound static funcs are still bound to class class G -> @x = 5 @y = 6 getX: -> @x getY: ~> @y @x = \staticX @y = \staticY @getStatX = -> @x @getStatY = ~> @y g = new G obj = x: 0, y: 0 obj{getX, getY} = g obj{getStatY, getStatX} = G eq 0 obj.getStatX! eq \staticY obj.getStatY! eq 0 obj.getX! eq 6 obj.getY! class H extends G h = new H obj = x: 0, y: 0 obj{getX, getY} = h eq 0 obj.getX! eq 6 obj.getY! # Inherit static class A @stat = -> 2 class B extends A eq 2 B.stat! class C extends B @stat = -> 2 + super! eq 4 C.stat! # Super outside of class defintions class A meth: -> it + 5 @stat = -> it + 5 a = new A eq 10 a.meth 5 eq 10 A.stat 5 class B extends A b = new B eq 10 b.meth 5 eq 10 B.stat 5 B::meth = -> super it + 2 B.stat = -> super it + 2 eq 12 b.meth 5 eq 12 B.stat 5 B::meth = (x) -> func = -> super x + 2 func! B.stat = (x) -> func = -> super x + 2 func! eq 12 b.meth 5 eq 12 B.stat 5 # can define external constructor f = -> @y = @x + it class A x: 8 constructor$$: f eq 13 (new A 5).y # using @@ to reference static props class A @static = 2 meth: -> @@static * 2 eq 4 (new A).meth! # @@@ references @.@@ rather than the nonsensical @@.@ let @ = constructor: 10 eq 10 @@@ # Executable class bodies and let class A a: 2 -> b: 2 eq 2 (new A).a eq void (new A).b # complex extends and auto super x = A: class -> @y = \ha class B extends x.A y: \no eq \ha (new B).y class C extends NON-EXISTANT ? (->) y: 4 eq 4 (new C).y LiveScript-1.5.0/test/operator.ls000664 000000 000000 00000036472 12716147631 016757 0ustar00rootroot000000 000000 {filter, even, map, fold} = require 'prelude-ls' # Newline suppression for binary operators. eq 32, 1 * 2 + 3 - 4 .<<. 5 if false or true and null ? true eq 3 1 .&. 2 .|. 3 else ok 0 'leading logical/bitwise operators should continue lines' # Chained Comparisons ok 500 > 50 > 5 > -5 ok 0 is 0 is not 50 is 50 ok 10 < 20 > 10 ok 50 > 10 > 5 is parseInt('5', 10) eq 1, 1 .|. 2 < 3 < 4 ok 1 == 1 <= 1, '`x == y <= z` should become `x == y && y <= z`' i = 0 ok 1 > i++ < 1, 'chained operations should evaluate each value only once' # [coffee#891](https://github.com/jashkenas/coffee-script/issues/891) eq 1, (1 unless 1 < 0 == 0) # (In)equality a = 1 b = '1' ok a ~= b ok not a !~= b ok not (a == b) ok not (a is b) ok a != b ok a is not b ok a isnt b ok true is true ok true is not false ok true isnt false # `and`/`or` closes implicit calls, eq('1', String 0 and String 1) eq('0', String 0 or String 1) # unless it's inside block. eq 3, if 0 or 1 then 2 and 3 ok 'a' of obj = a: true ok 'b' not of obj, 'allow `x not of y`' ok new String instanceof String ok new Number not instanceof String, 'allow `x not instanceof Y`' eq true, [] instanceof [String, Number and Array] eq true, 0 not instanceof [String, Array or Number] eq true, 2 in [0 or 1, 2, 3] eq true, 2 in array = [1, 2, 3] eq true, 4 not in array eq true, 1 not in [] eq true, [3]pop() in [0, ...array] eq true, [4]pop() in [...array, 4] eq 1, +( 0 in [0]) eq 0, +(10 in [ ]) ok array[0]++ in [0, 1] 'should cache testee' a = [1 2 3] ok not ("2" in a) ok not ("2" in [1 2 3]) # Non-spaced values still work. x = 10 y = -5 eq x-9, 1 eq y+9, 4 eq x*-y, 50 eq x*+y, -50 # Conditional assignments. one = 1 two = 0 one || = 2 two || = 2 eq one, 1 eq two, 2 zero = 0 zero &&= 'one' one &&= 'one' eq zero, 0 eq one , 'one' n ?= void n ?= true eq n, true # does not work in REPL if not on first line never-used-before ?= 3 eq never-used-before, 3 # Conditional assignments should be careful about caching variables. count = 0 list = [] list[++count] ||= 1 eq list[1], 1 eq count, 1 list[++count] ?= 2 eq list[2], 2 eq count, 2 list[count++] &&= 'two' eq list[2], 'two' eq count, 3 base = -> ++count; base base().four ||= 4 eq base.four, 4 eq count, 4 base().five ?= 5 eq base.five, 5 eq count, 5 # Ensure that RHS is treated as a group. a = b = false a &&= b or true eq a, false # Conditional assignments with implicit objects. obj = void obj ?= one: 1 eq obj.one, 1 obj &&= two: 2 ok not obj.one eq obj.two, 2 # Compound assignment as a sub expression. [a, b, c] = [1, 2, 3] eq (a + b += c), 6 eq a, 1 eq b, 5 eq c, 3 # Bitwise operators: eq (10 .&. 3), 2 eq (10 .|. 3), 11 eq (10 .^. 3), 9 eq (10 .<<. 3), 80 eq (10 .>>. 3), 1 eq (10 .>>>. 3), 1 num = 10; eq (num .<<.= 3), 80 num = 10; eq (num .>>.= 3), 1 num = 10; eq (num .>>>.= 3), 1 num = 10; eq (num .&.= 3), 2 num = 10; eq (num .^.= 3), 9 num = 10; eq (num .|.= 3), 11 # [coffee#737](https://github.com/jashkenas/coffee-script/issues/737) eq 1, 1 in [1] && 1 # [coffee#768](https://github.com/jashkenas/coffee-script/issues/768) share = 0 a = -> share++ if share is 0 b = -> share++ if share is 1 c = -> share++ if share is 2 ok a() not in [b(),c()] and share is 3 # Operators should respect new lines as spaced. a = (123) + 456 eq a, 579 a = "1#{2}3" + "456" eq a, '123456' # [coffee#2506](https://github.com/jashkenas/coffee-script/issues/2506) a = 0 eq false, false && a ||= 1 eq a, 0 ### Unary `+`/`-` eq 0, +[] eq -1, -true # Should space themselves when repeated. eq(+ +1, - -1) eq (-1), - --[2]0 ### `throw` throws 'up' -> throw Error 'up' # from anywhere. try [throw 0] catch eq e, 0 # `null` when empty. try throw catch eq e, null ### `do` eq '', do do do -> -> -> do String eq 1, do -> 1 eq @, do ~> @ eq 1, do then 0; 1 eq 3, do 2 3 a = Array do a: 0, b: 1 2, 3 eq 1, a.0.b eq 3, a.2 fn = null eq void fn? do then 4;5 loaders = a: -> 1 b: -> 2 more: -> c: 3, d: 4 x = do ...loaders{a, b, ...more} deep-equal {a: 1 b: 2 c: 3 d: 4} x ### `import` x = 'xx' o = (-> {} import {42, '', x, @X, (x), ...([0])}).call {X: 'XX'} eq o[42], 42 eq o[''], '' eq o.x, 'xx' eq o.X, 'XX' eq o.xx, 'xx' eq o[0], 0 o import all new class then deep: 'copy' eq o.deep, 'copy' o import all: \base eq o.all, \base i = 0 ++i import {} eq i, 1 x = {}; a = 0; b = 1; c = null; i = 0 x <<< {a || 1, b && 2, c ? 3, (i++) or 4} eq x.a, 1 eq x.b, 2 eq x.c, 3 eq x.0, 4 eq ',1,2,3' "#{ [] <<< [void 1 2 3] }" eq ',1,2,3' "#{ [] <<< (^^{0}<<<{1}) <<<< (^^{2}<<<{3}) }" eq ''' ({ a: a, b: b, c: c }); ''', LiveScript.compile '{a}<<<{b}<<<{c}', {+bare,-header} ok ok.isPrototypeOf new []= (->) <<< prototype: ok f = -> import it <<< new: -> new this it o = f.new {2, 3} eq o.2 * o.3, 6 o = q = null eq o?p <<< q?r, void o = p: {} eq o?p <<< q?r, o.p q = r: s: \t o?p <<< q?r eq o.p.s, \t o = null eq o? <<< {4}, void o = {} eq o? <<< {4}, o eq 4 o.4 # Declaration Form new import life: 2, (universe: 3) import all everything: 7 new class then answer: 42 eq @life * @universe * @everything, @answer ### {in,de}crement a = [0] ok ++a.0 # Don't confuse with concat op f = -> it x = 4 eq 5, f ++x # Can be spaced. eq(-- a[0], a[0] ++) eq 1 a.0 # Infix after postcrement. eq a.0++ * 2, 2 eq a.0-- / 2, 1 ok a.0++ != 2 compileThrows 'increment of undeclared "C"' 1 'C++' compileThrows 'invalid decrement' 1 'q.=p--' ### `delete` i = 0 O = -> switch ++i case 1 then {7} case 2 then new String 7 default ok 0, 'returning delete should cache correctly' eq delete (o = new O)[new O], 7 eq o[7], void compileThrows 'invalid delete' 1 'delete a' compileThrows 'invalid delete' 1 'delete a.=b' # [#273](https://github.com/gkz/LiveScript/issues/273) a = b = ^^{0} <<< [1] a = delete a.0 eq 1 a eq 0 b.0 x = a: 1 b: 2 c: 3 y = delete ...x{a, z: b} deep-equal {c: 3} x deep-equal {a: 1, z: 2} y ### `jsdelete` x = a: 1 ok delete! x.1 ok not delete! Math.PI x = a: 1 b: 2 c: 3 Object.defineProperty x, \d, writable: false, enumerable: true deep-equal {+a, +b} delete! ...x{a, b} deep-equal {c: 3 d: undefined} x deep-equal {+c, -d} delete! ...x{c, d} deep-equal {d: undefined} x ### [[Class]] sniffing eq \RegExp typeof! /^/ ### Pow eq -256, -2**2**3 # -((2)**(2**3)) eq -256, -2^2^3 eq 17, 1+2*2**3 # 1+(2*(2**3)) eq 32, 2*4**2 eq 32, 2*4^2 a = [2]; i = 0 a[i++] **= 3 eq a.0, 8 ### Min/Max eq 0, 0 ? 1 eq 2, 2 ? 4 eq 5, 5 >? 4 eq \a, \a ? \b u = 42 eq u, u*1 ? u-2 >? u/3 u ?= 0 eq 9, u eq 99, u >?= 33*3 eq 99, u ?= 5 ok o.a is o.b is 5 o.c ?= 5 o.d >?= 7 eq o.c * o.d, 14 ### Pipe reverse = -> it.split '' .reverse! * '' upCase = -> it.toUpperCase! eq \OLLEH ('hello' |> reverse |> upCase) eq \OLLEH (upCase <| reverse <| \hello) eq 8 ((+ 2) << (* 2) <| 3) x = 3 |> (- 2) |> ([\a \b \c].) eq \b x x = [1, 2, 3, 4, 5] |> filter even |> map (* 2) |> fold (+), 0 eq 12 x # Pipe and assign result1 = 'hello' |> reverse |> upCase eq 'OLLEH', result1 result2 = upCase <| reverse <| \hello eq 'OLLEH', result2 # Pipe and return ok do -> return false |> (not) # Pipe and throw try throw false |> (not) catch => ok e # Assign and return eq 5 do -> return a = 5 # Assign and throw try throw a = 5 catch => eq 5 e ### Unary spread eq 'number,string' ''+ typeof do ...[Number, String] eq 'number,string' ''+ typeof ... 0 \1 o = 2: [3 4] 5: 6 a = delete ...o[5 ...2] eq '6,3,4' "#a" eq 3 a.length ok o.2 is o.5 is void eq '8,9' ''+ -~...[7 8] a = 1 b = 2 x = ++...[a, b] deep-equal [2 3] x eq 2 a eq 3 b a = 1 b = 2 x = ...[a, b]++ deep-equal [1 2] x eq 2 a eq 3 b a = 1 b = 2 x = ...[a, b] += 5 deep-equal [6 7] x eq 6 a eq 7 b o = a: 1 b: 2 c: 3 x = ...o{a, b}++ deep-equal {a: 1 b: 2} x deep-equal {a: 2 b: 3 c: 3} o o = a: 1 b: 2 c: 3 x = ++...o{a, b} deep-equal {a: 2 b: 3} x deep-equal {a: 2 b: 3 c: 3} o o = a: 1 b: 2 c: 3 cache-me = -> cache-me := (-> ok false "didn't cache the RHS"); 5 x = ...o{a, b} += cache-me! deep-equal {a: 6 b: 7} x deep-equal {a: 6 b: 7 c: 3} o o = [true false true] deep-equal [false true], != ...o[0 1] deep-equal [false true true] o ### Overloaded a = b = [0 1] #### Join eq '0==1' a * \== eq '0101' [... a] * 2 * '' eq '(01)' <[( )]> * "#{a * ''}" eq '0@@1' (-> arguments * \@@) 0 1 eq '0.1' b *= \. eq '0.1' b #### Remove eq '01' b - \. eq \. b -= /\d/g eq '0.1' b = 0.1 - //#{2}// #### Split eq '0,1' String a / \, eq 2 (/abc/ / /[^/]+/)length eq "#{ x = ''+ Math.random() }"/'.'*'.' x eq '0,1' ''+ b /= /\D/ eq '0,1' ''+ b ### Repeat x = \x n = 4 eq '' 'x'*0 eq \x 'x'*1 eq \xx "x"*2 eq \xxx \x *3 eq \xxxx \x *n eq '' "#{x}" * 0 eq \x "#{x}" * 1 eq \xx "#{x}" * 2 eq \xxx "#{x}" * 3 eq \xxxx "#{x}" * n i = -1 eq '' ''+ [i++]*0 eq '0' ''+ [i++]*1 eq '1,1' ''+ [i++]*2 eq '2,3,2,3,2,3' ''+ [i++, i++] * 3 eq '4,5,4,5,4,5' ''+ [i++, i++] * (n-1) a = [1] eq '0,1,0,1' ''+ [0 ...a] * 2 eq '1,1,1,1' ''+ [ ...a] * n eq '1,1,1,1' ''+ a[0 , 0] * 2 eq '1,1,1,1' ''+ a[0 ...] * n eq '0,1,0,1' ''+ [i for i to 1] * 2 eq '0,0,0,0' ''+ [i for i to 0] * n ##### ++ concat a = [0 1] c = [2 3] eq '0,1,5' String a++5 eq '0,1,5' String a ++ 5 eq '0,1,2,3' String a++c eq '0,1,2,3' String a ++ c eq '0,1,2,3' String a ++ c ### Mod eq -3, -3 % 4 eq 1, -3 %% 4 eq 1, 7 % 2 eq -1, 7 %% -2 x = 7; x %%= -2 eq -1 x eq '9', (-1 %% 10).toString! eq +2, +5 %% +3 eq -1, +5 %% -3 eq +1, -5 %% +3 eq -2, -5 %% -3 eq +0.25, +1.75 %% +0.5 eq -0.25, +1.75 %% -0.5 eq +0.25, -1.75 %% +0.5 eq -0.25, -1.75 %% -0.5 eq 1/+0, 1 / (0 %% +1) eq 1/-0, 1 / (0 %% -1) ok isNaN 1 %% 0 # fail #o = i: 7, valueOf: -> @i -= 2 #eq 2, o %% o #eq 3, o.i (a = [7 0])[a.1++] %%= 5 eq a.0, 2 ### Partially applied binary ops addTwo = (+ 2) eq 5 addTwo 3 eq 7 (+) 3, 4 eq 3 (+)(1) 2 eq 3 (1+) 2 eq 2 (-)(4) 2 eq 4 (- 5) 9 eq -4 (5-) 9 eq -2 (-2) # not spaced, not paritally applied eq 2 (+2) ok (~=) '2' 2 ok (!~= 2) 9 ok (2 ==) 2 ok (!=) 2 '2' ok (2!=) 3 ok (!=2) 3 ok (<) 2 3 ok (2<) 3 ok (<3) 2 ok (<=) 2 2 ok (2<=) 4 ok (<=2) 2 ok (>) 3 2 ok (3>) 2 ok (>2) 3 ok (>=) 2 2 ok (2>=) 1 ok (>=1) 1 ok (&&) true true ok not ((and false) true) ok (true and) true ok (or) false true ok (false or) true ok (or true) false ok (or)(true) false eq 6 (*) 2 3 eq 6 (2*) 3 eq 6 (*3) 2 eq 2 (/) 6 3 eq 2 (6/) 3 eq 2 (/3) 6 eq 0 (%) 4 2 eq 0 (4%) 2 eq 0 (%2) 4 eq -1 (%%) 7 -2 eq -1 (7%%) -2 eq -1 (%%-2) 7 eq 8 (^) 2 3 eq 8 (2**) 3 eq 8 (^3) 2 eq '1,2,3' "#{ (++) [1] [2 3] }" eq '1,2,3' "#{ ([1] ++) [2 3] }" eq '1,2,3' "#{ (++ [2 3]) [1] }" eq 2 (>?) 2 1 eq 2 (2 >?) 1 eq 2 (>? 1) 2 eq 1 () 3 (+ 2) eq 5 (|> (+ 2)) 3 eq 5 (3 |>) (+ 2) eq 5 (<| 3 2 ) (+) eq 5 (3 2 |>) (+) eq 2 (.&.) 10 3 eq 2 (10 .&.) 3 eq 2 (.&. 3) 10 x = 2 (x +=) 2 eq 4 x (x -=) 3 eq 1 x (x :=) 5 eq 5 x eq \--- (\- *) 3 eq '4,2' "#{ (/ '') 42 }" x = 10 eq 12 (x +) 2 a = [1 2] eq '1,2,3,4' String (++) a, [3 4] eq '3,4,1,2' String (++ a) [3 4] eq '1,2,3,4' String (a ++) [3 4] # partially bound binary operators should also bind `this` # see [#634](https://github.com/gkz/LiveScript/issues/634) o = x: 1 y: 2 f: -> ok (@x ==)(1) ok (== @x)(1) eq 2 (+ @x)(1) (@y =)(3) eq 3, @y o.f! # Unary ops as functions ok (not) false ok (!).call(null, false) x = 3 eq 2 (--) x eq '1,3,5' "#{ filter (not) << even, [1 to 5] }" eq '1,3,5' "#{filter ((<<) (not), even), [1 to 5] }" ### cloneport personA = name: \matias age: 20 job: 'a cool job' personB = personA with name: \john eq \john personB.name eq \matias personA.name personC = personA with name: \amy age: 19 hair: \blonde eq \amy personC.name eq 19 personC.age eq \blonde personC.hair eq \matias personA.name eq 20 personA.age ok not personA.hair? ### xor ok not (0 xor 0) ok not (1 xor 1) ok (0 xor 1) ok (1 xor 0) x = -> 1 y = -> 0 ok not (y! xor y!) ok not (x! xor x!) ok (y! xor x!) ok (x! xor y!) ok (x 0 xor y!) eq 'moo' (0 xor 'moo') ### Regex overloaded == if /[aeuio]*/ == 'ee' then eq 'ee' that.0 else ok 0 if /^e(.*)/ == 'enter' then ok 'enter,nter' String that else ok 0 if /^e(.*)/ == 'zx' then ok 0 else ok 1 if /moo/ != 'loo' then ok 1 else ok 0 switch | /moo/ != 'loo' => ok 1 | _ => ok 0 ### Deep Equals NaN === NaN /moo/gi === /moo/gi xs = [1 to 5] obj = {+opt, -goo, inp: \haha} ok [1 2 3 4 5] === xs ok not ([1 2 8 4 5] === xs) ok not ([1 2 3 4 6] === xs) ok not ([1 2 3 4 5] !== xs) ok [1 2 8 4 5] !== xs ok [1 2 3 4 6] !== xs ok not ([1 2 3 4 5] <<= xs) ok [1 2 3] <<= xs ok [1 2 3] <== xs ok [1 2 3 4 5] <== xs ok not ([1 2 3 4 5 6] <== xs) ok [1 2 3 4 5 6] >== xs ok [1 2 3 4 5] >== xs ok not ([1 2 3 4] >== xs) ok not ([1 2 3 4 5] >>= xs) ok [1 2 3 4 5 6] >>= xs ok {opt: true, goo: false, inp: 'haha'} === obj ok not ({opt: false, goo: false, inp: 'haha'} === obj) ok not ({opt: true, goo: false} === obj) ok not ({opt: true, goo: false, inp: 'haha', da: 4} === obj) ok not ({opt: true, goo: false, inp: 'haha'} !== obj) ok {opt: false, goo: false, inp: 'haha'} !== obj ok {opt: true, goo: false} !== obj ok {opt: true, goo: false, inp: 'haha', da: 4} !== obj ok {opt: true, goo: false} <<= obj ok not ({opt: true, goo: false, inp: 'haha'} <<= obj) ok {opt: true, goo: false} <== obj ok {opt: true, goo: false, inp: 'haha'} <== obj ok not ({opt: true, goo: false, inp: 'haha', da: 6} <== obj) ok {opt: true, goo: false, inp: 'haha', moo: 45} >>= obj ok not ({opt: true, goo: false, inp: 'haha'} >>= obj) ok {opt: true, goo: false, inp: 'haha', moo: 45} >== obj ok {opt: true, goo: false, inp: 'haha'} >== obj ok not ({opt: true, goo: false} >== obj) ok [[4, 3] {name: \moo, k: [NaN]} /[ae]/g] === [[4, 3] {name: \moo, k: [NaN]} /[ae]/g] ok !([[4, 3] {name: \mooo, k: [NaN]} /[ae]/g] === [[4, 3] {name: \moo, k: [NaN]} /[ae]/g]) ok [[4, 3] {name: \noo, k: [NaN]} /[ae]/g] <== [[4, 3] {name: \noo, k: [NaN]} /[ae]/g] ok [[4, 3] {name: \loo, k: [NaN]}] <== [[4, 3] {name: \loo, k: [NaN]} /[ae]/g] ok [[4, 3] {name: \koo, k: [NaN]}] <<= [[4, 3] {name: \koo, k: [NaN]} /[ae]/g] ok !([[4, 3] {name: \moo, k: [NaN]} /[ae]/g] <<= [[4, 3] {name: \moo, k: [NaN]} /[ae]/g]) ok [1, _, 3] === [1 2 3] ok {a: 1, b:_} === {a: 1, b: 2} ok {a: [1, _, 3]} === {a: [1 4 3]} ok {a: {b: _}} === {a: {b: 9}} ok [9 [1, _, 3]] === [9 [1 4 3]] ### Calling binary logic f = (- 1) g = (+ 1) h = (- 1) even = -> it % 2 == 0 odd = (not) . even eq 2 (f or g) 1 eq 1 (f or g) 2 ok not (f and g) 1 eq 2 (f or h or g) 1 ok (even or 1) 2 ok (odd or 2) 2 ok not (even or 1) 3 ok ((.length > 4) or [1 2 3]) [1 2 3] eq 8 ((-> &0 + &1 is 5) and (**)) 2 3 LiveScript-1.5.0/test/regex.ls000664 000000 000000 00000003212 12716147631 016220 0ustar00rootroot000000 000000 ok /x/.test 'x' ok 'x'.match /x/ eq /\\/ + '', '/\\\\/' eq /^/.source, '^' # Should not be mixed-up with the division operator. g = h = i = 2 eq g / h / i, 2 / 2 / 2 eq g/h/i, 2/2/2 eq [g][0]/h/i, (2)/2/2 eq 2, g /= h / i eq \=, /=/. source eq ' ' (/ /)source compileThrows 'unterminated regex' 1 '/1' # Should be cached aptly. eq 0 (/_/ <<< {0}).0 # Should accuse duplicate flag. compileThrows 'duplicate regex flag `g`' 1 '/^/gg' # Should be ASI safe. / / [0][0] # ACI interaction eq \/1/ '0'.replace //#{0}// ''+/1/ \g # ADI interaction eq true, /a/itest \A ### Heregex eq /^I'm\s+Heregex?\/\//gim + '', // ^ I'm \s+ Heregex? / / # or not //gim + '' eq '\\\\#{}\\\\\\\"', // #{ "#{ '\\' }" # normal comment } # regex comment \#{} \\ \" //.source # [coffee#3059](https://github.com/jashkenas/coffee-script/pull/3059) # Keep escaped whitespaces. ok //^ a \ b \ c \ d $//test 'a b\u3000c\nd' eq '(?:)' ////source eq // _ #{if 1 then \g else \m}//? + '', '/_/g' eq /\//source, //\///source # Should work nested. eq \01234 // 0 #{ // 1 #{ //2//source } 3 //source } 4 //source let this = \THIS ok //^ \\##@#this $//test //\#THISTHIS//source # [coffee#584](https://github.com/jashkenas/coffee-script/issues/584) # Unescaped slashes in character classes. ok /:\/[/]goog/.test 'http://google.com' # [coffee#764](https://github.com/jashkenas/coffee-script/issues/764) # Should be indexable. eq /0/['source'], //#{0}//['source'] ### $ flag eq \string typeof /^$/$ eq \string typeof //^$//$ eq \string typeof //^#{''}$//$ eq /\\\//$ /\\\//source eq //\\\///$ //\\\///source eq //#{\\}\///$ //#{\\}\///source LiveScript-1.5.0/test/splat.ls000664 000000 000000 00000004540 12716147631 016236 0ustar00rootroot000000 000000 fn = (first, ...rest) -> '' + rest eq fn(1,2,3,4,5), '2,3,4,5' eq fn(6,7), '7' fn = (...heads, last) -> '' + heads eq fn(1,2,3,4,5), '1,2,3,4' eq fn(6,7), '6' fn = (first, second, ...middles, last) -> '' + middles eq fn(1,2,3,4,5), '3,4' eq fn(6,7), '' a = [0 method: -> this is a.1] ok a[++a.0]method(...a), 'should cache base value' trio = [1 2 3] eq '1234' [...trio, 4] * '' eq '4123' [4, ...trio] * '' eq '1234321' [...trio, 4, ...trio.reverse!] * '' # Splats with `super`. class Parent meth: (...args) -> ''+ args class Child extends Parent nums: [0, 1] meth: -> super ...@nums, 2 eq '0,1,2' new Child()meth() # Array splat expansions with assigns. eq '0,1,2,3,4' String [a = 0, ...[1 2 3], b = 4] eq a, 0 eq b, 4 o = x: {0}, (y = 1): {2} {...x, ...(y)} = o eq x[0], 0 eq y[2], 2 ok x is not o.x , 'should copy o.x' ok y is not o[y], 'should copy o[y]' compileThrows 'multiple splat in an assignment' 1 '[...a, ...b] = c' class Thisplat -> [me, [a0, a1, a2]] = @f ... eq me, this eq a0 * a2, 21 f: -> [this, arguments] class Thisplat2 extends Thisplat ~> super ... f: -> super ... new Thisplat2 3 5 7 eq 0, [...[...[0]]][0] [...onetwo, [], {}, five] = [1 to 5] eq onetwo + '', '1,2' eq five, 5 eq '0.0', 0.toFixed ...[1] # Multiple splats in the same chain. o = f: -> @a.push ...arguments; this a: [1] o.f(...o.a).f(...o.a) eq '1,1,1,1' o.a + '' (-> o.f(...).f(...))call o, 2 eq '1,1,1,1,2,2' o.a + '' # [coffee#870](https://github.com/jashkenas/coffee-script/issues/870) [...[], a] = [1] eq a, 1 # `...` is same as `...[]` [..., a] = [1 to 3] eq a, 3 [a, ..., b] = [1 2] eq a, 1 eq b, 2 [a, ..., b] = [1 to 5] eq a, 1 eq b, 5 # [LiveScript#858](https://github.com/gkz/LiveScript/issues/858) [a, ..., b, c, d] = [1 2 3] eq a, 1 eq b, 2 eq c, 3 eq ''' (function(){ var a; a = arguments[arguments.length - 1]; }); ''', LiveScript.compile '(..., a) ->', {+bare,-header} # Don't call `slice$` on array literals. eq '[a, a].concat([b]);' LiveScript.compile '[...[a]*2 b]' {+bare,-header} # splatted new class A (x, y = 0, z = 0) -> @res = x + y + z eq 6 (new A ...[1 2 3]).res arg = [1 2 3] eq 6 (new A ...arg).res eq 5 (new A ...[5]).res x = [5] eq 5 (new A ...x).res eq 8 (new A 3 ...x).res eq 9 (new A 3 ...x, 1).res a = {} a.b = {} class a.b.A (x, y, z) -> @res = x + y + z eq 6 (new a.b.A ...[1 2 3]).res LiveScript-1.5.0/test/string.ls000664 000000 000000 00000010564 12716147631 016424 0ustar00rootroot000000 000000 eq '({[dollars]})', '\(\{\[dollars\]\}\)' eq 'one two three', "one t wo t hree" eq "four five", ' four \ five ' eq 'six seven' ' six \ seven ' hello = 'Hello' world = 'World' eq '#{hello} #{world}!', '#{hello} #{world}!' eq "#{hello} #{world}!", 'Hello World!' eq "#hello #world!", 'Hello World!' eq "[#{hello}#{world}]", '[HelloWorld]' eq "#{hello}##{world}", 'Hello#World' eq "Hello #{ 1 + 2 } World", 'Hello 3 World' eq "#{hello} #{ 1 + 2 } #{world}", "Hello 3 World" eq "#{hello + world}", 'HelloWorld' eq "#{hello + ' ' + world + '!'}", 'Hello World!' eq helloWorld = hello + world, "#hello-world" eq "\#{Escaping} first", '#{Escaping} first' eq "Escaping \#{in} middle", 'Escaping #{in} middle' eq "Escaping \#{last}", 'Escaping #{last}' eq "\#Esc\#a\#ping", '#Esc#a#ping' eq "##", '##' eq "#{}", '' eq "#{1}#{2}", '12' eq "#{}A#{} #{} #{}B#{}", 'A B' eq "\\\#{}", '\\#{}' eq "#{ }", '' eq "I won ##{20} last night.", 'I won #20 last night.' eq "I won ##{'#20'} last night.", 'I won ##20 last night.' list = [0 to 5] eq "values: #{ list.join ( ) }", 'values: 0,1,2,3,4,5' eq "values: #{ list.join ' ' }", 'values: 0 1 2 3 4 5' obj = name: 'Joe' toString: -> @name hi: -> "Hello #this." eq obj.hi(), "Hello Joe." eq "With #{"quotes"}", 'With quotes' eq 'With #{"quotes"}', 'With #{"quotes"}' eq "Where is #{obj["name"] + '?'}", 'Where is Joe?' eq "Where is #{"the nested #{obj["name"]}"}?", 'Where is the nested Joe?' eq "Hello #{world ? "#{hello}"}", 'Hello World' eq "Hello #{"#{"#{obj["name"]}" + '!'}"}", 'Hello Joe!' eq "#{"hello".replace("\"", "")}", 'hello' a = """ Hello #{ "Joe" } """ eq a, "Hello Joe" a = """ basic heredoc on two lines """ eq a, "basic heredoc\non two lines" a = ''' a "b c ''' eq a, "a\n \"b\nc" a = """ a b c """ eq a, "a\n b\n c" eq '''one-liner''', 'one-liner' a = """ out here """ eq a, "out\nhere" a = ''' a b c ''' eq a, " a\n b\nc" a = ''' a b c ''' eq a, "a\n\n\nb c" eq '''more"than"one"quote''', 'more"than"one"quote' # [coffee#647](https://github.com/jashkenas/coffee-script/issues/647) eq "''Hello, World\\''", ''' '\'Hello, World\\\'' ''' eq '""Hello, World\\""', """ "\"Hello, World\\\"" """ eq 'Hello, World\n', ''' Hello, World\ ''' a = """ basic heredoc #{10} on two lines """ b = ''' basic heredoc #{10} on two lines ''' eq a, "basic heredoc 10\non two lines" eq b, "basic heredoc \#{10}\non two lines" eq '''here's an apostrophe''', "here's an apostrophe" # Blank lines are ignored for indentation detection. eq """ one zero """, " one\n\nzero\n" # Space only lines count as indentation. eq '" one\\n \\nzero\\n";' LiveScript.compile ''' """ one \ zero \ """ ''' {+bare,-header} eq ''' line 0 should not be relevant to the indent level ''', ' line 0 \nshould not be relevant \n to the indent level' eq ''' '\\\' ''', " '\\' " eq """ "\\\" """, ' "\\" ' eq ''' <- keep these spaces -> ''', ' <- keep these spaces -> ' eq 'multiline nested "interpolations" work', """multiline #{ "nested #{ ok true "\"inter" }" + """polations\"""" } work""" compileThrows 'unterminated interpolation' 2 '"#{\n' throws "Parse error on line 1: Unexpected ')'" -> LiveScript.compile '"(#{+})"' compileThrows 'invalid variable interpolation \'if\'' 1 '"#if"' compileThrows 'malformed character escape sequence' 1 '"\\x"' compileThrows 'malformed character escape sequence' 1 '"\\u"' hi-there = 'Hi there!' one-two-three = 123 # Dash separated var interpolation eq 'Hi there! How are you?' "#hi-there How are you?" eq 'ha 123 ha' "ha #one-two-three ha" # Character/Word Literal eq 'word', \word eq \c, 'c' eq('+', \+) eq '\\', [\\\].0 eq '$', {\$}.\$ # [coffee#923](https://github.com/jashkenas/coffee-script/issues/923) eq "#{ "{" }", "{" eq "#{ '#{}}' } }", '#{}} }' # Automatic Dot Insertion o = k: ok; k = \k eq o.k, o\k eq o.k, o'k' eq o.k, o"#{k}" o\k true o'k' true o"#{k}" true o"""k""" true # Automatic Comma Insertion eq "#{0}" \0 eq \2 '1'.replace "#{1}" -> 2 # Safe Octals let 'use strict' eq '\1' '\x01' eq '\02' '\x02' eq '\377' '\xFF' eq '\08\09' '\x008\x009' # Unjoined x = 0 y = 1 a = %"#x/#y" eq a.0, 0 eq a.1, \/ eq a.2, 1 eq a.length, 3 # Trailing backslashes are themselves. eq '''\''' '\\' eq \\\\ \\ + \\ LiveScript-1.5.0/test/switch.ls000664 000000 000000 00000010133 12716147631 016407 0ustar00rootroot000000 000000 switch 10 case 5 then ok 0 case 'a' true false ok 0 case 10 then ok 1 #! Mid-switch comment with whitespace #! and multi line case 11 then ok 0 default ok 0 func = (num) -> switch num case 2, 4, 6 Boolean true case [1, 3, 5] Boolean false default eq func(2), true eq func(6), true eq func(3), false eq func(8), void # One-liner eq void, switch case 1 then break eq 1 , switch case 0 then break default 1 eq 2 , switch case 1 then (while 0 then continue); 2 eq 3 , do -> switch 0 case 1 then -> 2 default 3 eq 4 , if 1 then switch 2 case 3 then default 4 compileThrows 'inconvertible statement' 3 ''' for ever !switch continue ''' ok switch \words case (<[ nonbare words ]>) then false case <[ bare words ]> switch Function::toString case ok<[valueOf toString]> then true , '`case` expands bare arrays' # Sans-topic eq ok, switch case null then 0 case !1 then 1 case '' not of {''} then 2 case [] not instanceof Array then 3 case true is false then 4 case 'x' < 'y' > 'z' then 5 case 'a' in <[ b c ]> then 6 case 'd' in (<[ e f ]>) then 7 default ok eq ''' var that; switch (false) { case !1: return; case !2: throw me; case !3: break; case !4: // fallthrough case !(that = 5): that; break; case !(6 || 7 || 8): break; case !void 8: break; default: 9; } ''', LiveScript.compile ''' switch case 1 then return case 2 then throw me case 3 then break case 4 then fallthrough case 5 then that case 6 [7 8] then case[] then default 9 ''', {+bare,-header} # `that` eq 1, switch 1 case 1 then that while 1 eq 3, switch 3 case 3 then that break switch case [0, 2, 4,] then eq 2, that # `that` with default - #508 switch 10 | _ => that # Sans-condition switch ok 1 'caseless switch is allowed' break if true ok 0 'for early breaking' # case | switch | false then ok 0 | false then ok 0 | false ok 0 | true ok 1 | true then ok 0 # then => switch | false => ok 0 | false => ok 0 | true => ok 1 | true => ok 0 # otherwise, _ eq otherwise?, false switch | false => ok 0 | otherwise => ok 1 switch 2 + 3 case 6 then ok 0 case _ then ok 1 switch | false => ok 0 | _ => ok 1 switch 2 + 3 case 6 then ok 0 case otherwise then ok 1 # implicit switches boom1 = -> case false => 1 case otherwise => 2 3 eq 3 boom1! do -> | false => ok 0 | true => ok 1 do ~> | false => ok 0 | true => ok 1 boom2 = -> | false => 1 | otherwise => 2 3 eq 3 boom2! # when switch when false then ok 0 when true then ok 1 # else switch | false => ok 0 else ok 1 #### match x = 2 match x | (== 3) => ok 0 | (== 2) => ok 1 | _ => ok 0 match ++x | (== 4) => ok 0 | (== 3) or (==8) => ok 1 | _ => ok 0 false-func = -> false true-func = -> true # no subject match | false-func => ok 0 | true-func => ok 1 | otherwise => ok 0 # multiple topics even = --> it % 2 == 0 odd = (not) . even x = 1 y = 2 match x, y | odd, odd => ok 0 | even, even => ok 0 | odd, even => ok 1 | otherwise => ok 0 # literals x = 5 y = \moo z = true match x, y, z | 5, \moo, false => ok 0 | 4, \moo, true => ok 0 | 5, \moo, true => ok 1 | otherwise => ok 0 x = [1 2 3] y = 'haha' z = {+foo, moo: 2, g: {hi: \?}} match x | [2 4 6] => ok 0 | [1 2 3 4] => ok 0 | [1 2 _] => ok 1 | otherwise => ok 0 match z | {-foo, goo: 23, g: {hi: \?}} => ok 0 | {+foo, moo: 2, g: _} => ok 1 | otherwise => ok 0 match x, y, z | [1 2 3], /^ha/g, {foo: true, moo: 2, g: {hi: \!}} => ok 0 | [1 2 3], /^ha/g, {foo: true, moo: 2, g: {hi: \?}} => ok 1 | otherwise => ok 0 match 2 | even and 2 => ok 1 | otherwise => ok 0 match 3, \haha | _, 'muhaha' => ok 0 | even, _ => ok 0 | _, 'haha' => ok 1 | _ => ok 0 take = (n, [x, ...xs]:list) -> match n, list | (<= 0), _ => [] | _ , [] => [] | otherwise => [x] ++ take n - 1, xs eq '1,2,3' "#{ take 3, [1 to 10] }" x = -2 match x | -2 => ok 1 | _ => ok 0 match 1, 3, 3 | 1, 1, 2 or 3 => ok 0 | 1, 2 or 3, 3 => ok 1 | _ => ok 0 LiveScript-1.5.0/test/try.ls000664 000000 000000 00000001447 12716147631 015734 0ustar00rootroot000000 000000 n = 1 try n *= 2 throw \error n *= 3 catch error n *= 5 finally n *= 7 eq n, 70 # Hoist `catch`ee. eq error, \error # Allow one-liners. try x = 0 catch _ then ok false finally ++x eq x, 1 # Declare `e` by default. try throw 0 catch eq e, 0 # Return results. eq 1 let try 1 finally 2 eq 2 let try throw 1 catch then 2 finally 3 eq 3 try 3 eq 4 try throw 4 catch # Tolerate empty blocks. try try catch try finally try catch then finally try #!nothing catch then #!nothing finally #!nothing # Tolerate nested implicit blocks. eq 1, do -> try 1 eq 2, do -> try do -> throw 1 catch then do -> 2 finally # Destructure try throw {msg: 'error', val: 99} catch {msg, val} eq \error msg eq 99 val # Call result f = (x) -> x r = f try throw 0 catch 10 eq 10 r