diff --git a/Slakefile b/Slakefile index a2fabd801..dae76b625 100644 --- a/Slakefile +++ b/Slakefile @@ -34,12 +34,10 @@ minify = -> task \install 'install LiveScript via npm' -> shell 'npm install -g .' -docs = <[ doc.ls ]> - task \build 'build lib/ from src/' -> - ext = /\.ls$/; webs = docs + ext = /\.ls$/ sources = for file in dir \src - \src/ + file if ext.test file and file not in webs + \src/ + file if ext.test file run [\-bco \lib] +++ sources task \build:full 'build twice and run tests' -> @@ -54,12 +52,6 @@ task \build:parser 'build lib/parser.js from lib/grammar.js' -> .replace /(:[^]+?break;)(?=\ncase \d+\1)/g \: .replace /(:return .+)\nbreak;/g \$1 -task \build:doc 'build doc/' -> - <- docs.forEach - name = it.slice 0 -3; js = require(\./lib/livescript)compile slurp \src/ + it - fs.writeFile "doc/#name.raw.js" js - slobber "doc/#name.js" minify js - task \build:browser 'build extras/' -> LiveScript = require \./lib/livescript co = '' diff --git a/lib/ast.js b/lib/ast.js index dfd2f3c50..16eb9913c 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -28,7 +28,6 @@ var Node, Negatable, Block, Atom, Literal, Var, Key, Index, Chain, Call, List, O } fun = Fun([], Block(this)); call = Call(); - hasThis = hasArgs = false; this.traverseChildren(function(it){ switch (it.value) { case 'this': @@ -273,17 +272,15 @@ Negatable = { exports.Block = Block = (function(superclass){ Block.displayName = 'Block'; var prototype = __extend(Block, superclass).prototype, constructor = Block; - function Block(node){ + function Block(body){ var __this = this instanceof __ctor ? this : new __ctor; - __this.lines = []; - if (!node) { - return __this; - } - node = node.unparen(); - if (node instanceof Block) { - return node; + body || (body = []); + if ('length' in body) { + __this.lines = body; + } else { + __this.lines = []; + __this.add(body); } - __this.add(node); return __this; } function __ctor(){} __ctor.prototype = prototype; prototype.children = ['lines']; @@ -556,6 +553,9 @@ exports.Literal = Literal = (function(superclass){ val = 'void'; // fallthrough case 'void': + if (!level) { + return ''; + } val += ' 8'; // fallthrough case 'null': @@ -672,7 +672,7 @@ exports.Index = Index = (function(superclass){ } function __ctor(){} __ctor.prototype = prototype; prototype.children = ['key']; prototype.show = function(){ - return (this.soak || '') + this.symbol; + return [this.soak ? '?' : void 8] + this.symbol; }; prototype.isComplex = function(){ return this.key.isComplex(); @@ -761,7 +761,7 @@ exports.Chain = Chain = (function(superclass){ if (!(tail = (__ref = this.tails)[__ref.length - 1])) { return this.head.isAssignable(); } - if (!(tail instanceof Index) || tail.key instanceof List) { + if (!(tail instanceof Index) || tail.key instanceof List || tail.symbol === '.~') { return false; } for (__i = 0, __len = (__ref = this.tails).length; __i < __len; ++__i) { @@ -875,6 +875,9 @@ exports.Chain = Chain = (function(superclass){ node = __ref[i]; if (__ref1 = node.soak, delete node.soak, __ref1) { bust = Chain(this.head, this.tails.splice(0, i)); + if (node.assign && !bust.isAssignable()) { + node.carp('invalid accessign'); + } test = node instanceof Call ? (__ref1 = bust.cacheReference(o), test = __ref1[0], this.head = __ref1[1], JS("typeof " + test.compile(o, LEVEL_OP) + " === 'function'")) : (i && node.assign @@ -905,6 +908,9 @@ exports.Chain = Chain = (function(superclass){ } else { __ref1 = Chain(left).cacheReference(o), left = __ref1[0], this.head = __ref1[1]; } + if (op === '=') { + op = ':='; + } return __ref1 = Assign(left, this, op), __ref1.access = true, __ref1; } } @@ -1551,7 +1557,7 @@ exports.Unary = Unary = (function(superclass){ them[i] = sp ? lat = Splat(node) : node; } if (!lat && (this['void'] || !o.level)) { - it = (__ref = __clone(Block.prototype), __ref.lines = them, __ref.front = this.front, __ref['void'] = true, __ref); + it = (__ref = Block(them), __ref.front = this.front, __ref['void'] = true, __ref); } return it.compile(o, LEVEL_PAREN); }; @@ -1827,7 +1833,7 @@ exports.Binary = Binary = (function(superclass){ return this.compileMethod(it, 'String', 'split'); }; prototype.compileRepeat = function(o){ - var x, n, items, that, refs, i, item, q, __ref, __len; + var x, n, items, that, refs, i, item, q, __len, __ref; x = this.first, n = this.second; items = x.items; if (((that = items && Splat.compileArray(o, items)) && (x = JS(that))) || !(n instanceof Literal && n.value < 0x20)) { @@ -1840,7 +1846,7 @@ exports.Binary = Binary = (function(superclass){ } if (items) { if (n < 1) { - return (__ref = __clone(Block.prototype), __ref.lines = items, __ref).add(JS('[]')).compile(o); + return Block(items).add(JS('[]')).compile(o); } refs = []; for (i = 0, __len = items.length; i < __len; ++i) { @@ -1993,10 +1999,10 @@ exports.Assign = Assign = (function(superclass){ if (left.items) { return this.compileDestructuring(o, left); } + left.isAssignable() || left.carp('invalid assign'); if (this.logic) { return this.compileConditional(o, left); } - left.isAssignable() || left.carp('invalid assign'); op = this.op, right = this.right; if (op == '?=') { return this.compileMinMax(o, left, right); @@ -2370,12 +2376,8 @@ exports.Fun = Fun = (function(superclass){ } }; prototype.ripName = function(it){ - var __ref; this.name || (this.name = it.varName()); this.declared = it instanceof Var; - if (((__ref = it.head) != null ? __ref.value : void 8) === 'prototype' && it.tails.length === 1 && !it.tails[0].isComplex()) { - this.meth = it.tails[0]; - } }; prototype.compileNode = function(o){ var pscope, sscope, scope, that, inLoop, body, name, tab, code, __ref; @@ -2516,7 +2518,7 @@ exports.Class = Class = (function(superclass){ this.name = it.varName(); }; prototype.compile = function(o, level){ - var fun, title, decl, name, lines, i, node, proto, ctor, vname, that, args, clas, __len, __ref; + var fun, title, decl, name, lines, i, node, proto, prop, f, ctor, vname, that, args, clas, __len, __i, __ref, __len1, __ref1, __j, __len2; fun = this.fun, title = this.title; decl = title != null ? title.varName() : void 8; name = decl || this.name; @@ -2530,6 +2532,19 @@ exports.Class = Class = (function(superclass){ node = lines[i]; if (node instanceof Obj) { lines[i] = Import(proto || (proto = Var('prototype')), node); + for (__i = 0, __len1 = (__ref = node.items).length; __i < __len1; ++__i) { + prop = __ref[__i]; + if ((__ref1 = prop.key) instanceof Key || __ref1 instanceof Literal) { + if (prop.val instanceof Fun) { + prop.val.meth = prop.key; + } else if (prop.accessor) { + for (__j = 0, __len2 = (__ref1 = prop.val).length; __j < __len2; ++__j) { + f = __ref1[__j]; + f.meth = prop.key; + } + } + } + } } else if (node instanceof Fun && !node.statement) { ctor && node.carp('redundant constructor'); ctor = node; @@ -2558,15 +2573,14 @@ exports.Class = Class = (function(superclass){ }(Node)); exports.Super = Super = (function(superclass){ Super.displayName = 'Super'; - var constant, prototype = __extend(Super, superclass).prototype, constructor = Super; + var prototype = __extend(Super, superclass).prototype, constructor = Super; prototype.isCallable = YES; - constant = /^\.|^\[['"\d]/; prototype.compile = function(o){ - var scope, that, key, __ref; + var scope, that; scope = o.scope; for (; that = !scope.get('superclass') && scope.fun; scope = scope.parent) { - if (constant.test(key = (__ref = that.meth) != null ? __ref.compile(o) : void 8)) { - return "superclass.prototype" + key; + if (that = that.meth) { + return 'superclass.prototype' + Index(that).compile(o); } } return 'superclass'; @@ -2828,6 +2842,7 @@ exports.While = While = (function(superclass){ return this; }; prototype.makeReturn = function(it){ + var __ref; if (it) { if (this.objComp) { this.body = Block(this.body.makeObjReturn(it)); @@ -2836,6 +2851,9 @@ exports.While = While = (function(superclass){ } } else { this.body.makeReturn(it); + if ((__ref = this['else']) != null) { + __ref.makeReturn(it); + } } } else { this.getJump() || (this.returns = true); @@ -2843,25 +2861,33 @@ exports.While = While = (function(superclass){ return this; }; prototype.compileNode = function(o){ - var test, that, head, __ref; + var test, head, that, __ref; o.loop = true; this.test && (this.un ? this.test = this.test.invert() : this.anaphorize()); if (this.post) { - return 'do {' + this.compileBody((o.indent += TAB, o), this.test); + return 'do {' + this.compileBody((o.indent += TAB, o)); } test = ((__ref = this.test) != null ? __ref.compile(o, LEVEL_PAREN) : void 8) || ''; - head = (that = this.update) - ? "for (;" + (test && ' ' + test) + "; " + that.compile(o, LEVEL_PAREN) - : test ? "while (" + test : 'for (;;'; + if (!(this.update || this['else'])) { + head = test ? "while (" + test : 'for (;;'; + } else { + head = 'for ('; + if (this['else']) { + head += (this.yet = o.scope.temporary('yet')) + " = true"; + } + head += ";" + (test && ' ' + test) + ";"; + if (that = this.update) { + head += ' ' + that.compile(o, LEVEL_PAREN); + } + } return head + ') {' + this.compileBody((o.indent += TAB, o)); }; - prototype.compileBody = function(o, potest){ - var lines, ret, code, empty, res, run, that, __key; - o['break'] = true; - o['continue'] = true; - lines = this.body.lines; + prototype.compileBody = function(o){ + var lines, yet, tab, ret, code, empty, res, that, __key, __ref; + o['break'] = o['continue'] = true; + lines = this.body.lines, yet = this.yet, tab = this.tab; code = ret = ''; if (this.returns) { if (this.objComp) { @@ -2875,22 +2901,21 @@ exports.While = While = (function(superclass){ lines[__key] = lines[__key].makeReturn(res = o.scope.assign('__results', empty)); } ret = "\n" + this.tab + "return " + (res || empty) + ";"; + if ((__ref = this['else']) != null) { + __ref.makeReturn(); + } } - if (this['else']) { - lines.unshift(JS((run = o.scope.temporary('run')) + " = true;")); - } + yet && lines.unshift(JS(yet + " = false;")); if (that = this.body.compile(o, LEVEL_TOP)) { - code += "\n" + that + "\n" + this.tab; + code += "\n" + that + "\n" + tab; } code += '}'; - if (potest) { - code += " while (" + potest.compile((o.tab = this.tab, o), LEVEL_PAREN) + ");"; + if (this.post) { + code += " while (" + this.test.compile((o.tab = tab, o), LEVEL_PAREN) + ");"; } - if (run) { - if (this.returns) { - this['else'].makeReturn(); - } - code += " if (!" + run + ") " + this.compileBlock(o, this['else']); + if (yet) { + code += " if (" + yet + ") " + this.compileBlock(o, this['else']); + o.scope.free(yet); } return code + ret; }; @@ -2911,7 +2936,7 @@ exports.For = For = (function(superclass){ return this.index; }; prototype.compileNode = function(o){ - var temps, idx, pvar, step, tvar, tail, vars, eq, cond, svar, srcPart, lvar, head, body, __ref; + var temps, idx, pvar, step, tvar, tail, vars, eq, cond, svar, srcPart, lvar, head, that, body, __ref; o.loop = true; temps = this.temps = []; if (idx = this.index) { @@ -2955,13 +2980,24 @@ exports.For = For = (function(superclass){ } } } - head = 'for (' + (this.object - ? idx + " in " + srcPart - : (step === pvar || (vars += ', ' + step), (vars + "; " + cond + "; ") + (1 == Math.abs(pvar) + this['else'] && (this.yet = o.scope.temporary('yet')); + head = 'for ('; + if (this.object) { + head += idx + " in "; + } + if (that = this.yet) { + head += that + " = true, "; + } + if (this.object) { + head += srcPart; + } else { + step === pvar || (vars += ', ' + step); + head += (vars + "; " + cond + "; ") + (1 == Math.abs(pvar) ? (pvar < 0 ? '--' : '++') + idx : idx + (pvar < 0 ? ' -= ' + pvar.slice(1) - : ' += ' + pvar)))); + : ' += ' + pvar)); + } this.own && (head += ") if (" + o.scope.assign('__own', '{}.hasOwnProperty') + ".call(" + svar + ", " + idx + ")"); head += ') {'; this.infuseIIFE(); @@ -3377,13 +3413,16 @@ exports.Vars = Vars = (function(superclass){ prototype.children = ['vars']; prototype.makeReturn = THIS; prototype.compile = function(o, level){ - var v, __i, __ref, __len; + var v, value, __i, __ref, __len; for (__i = 0, __len = (__ref = this.vars).length; __i < __len; ++__i) { - v = __ref[__i]; + v = __ref[__i], value = v.value; if (!(v instanceof Var)) { v.carp('invalid variable declaration'); } - o.scope.declare(v.value, v); + if (o.scope.check(value)) { + v.carp("redeclaration of \"" + value + "\""); + } + o.scope.declare(value, v); } if (level) { return Literal('void').compile(o); @@ -3410,10 +3449,21 @@ exports.Decl = { ? Assign(Chain(out, [Index(Key(that))]), node) : Import(out, node); } - return __ref = __clone(Block.prototype), __ref.lines = lines, __ref; + return Block(lines); + }, + 'import': function(lines, all){ + var i, line, __len; + for (i = 0, __len = lines.length; i < __len; ++i) { + line = lines[i]; + lines[i] = Import(Literal('this'), line, all); + } + return Block(lines); + }, + importAll: function(lines){ + return this['import'](lines, true); }, 'const': function(lines){ - var node, __i, __len, __ref; + var node, __i, __len; for (__i = 0, __len = lines.length; __i < __len; ++__i) { node = lines[__i]; if (node.op !== '=') { @@ -3421,7 +3471,7 @@ exports.Decl = { } node['const'] = true; } - return __ref = __clone(Block.prototype), __ref.lines = lines, __ref; + return Block(lines); }, 'var': Vars }; @@ -3436,6 +3486,8 @@ __ref.add = function(name, type, node){ if (node && (t = this.variables[name + "."])) { if (that = this.READONLY[t] || this.READONLY[type]) { node.carp("redeclaration of " + that + " \"" + name + "\""); + } else if (t === type && type === 'arg') { + node.carp("duplicate parameter \"" + name + "\""); } if (t == 'arg' || t == 'function') { return name; diff --git a/lib/command.js b/lib/command.js index 21d1c42e8..31bab473b 100755 --- a/lib/command.js +++ b/lib/command.js @@ -75,18 +75,21 @@ default: argv[1] = 'eval'; compileScript('', __join.call($args, '\n')); break; + case !o.interactive: + repl(); + break; case !o.stdin: compileStdin(); break; case !$args.length: compileScripts(); break; - case !process.stdin.readable: - compileStdin(); + case !require('tty').isatty(0): + say(version() + '\n' + help() + '\n'); + repl(); break; default: - o.interactive || say(version() + '\n' + help() + '\n'); - repl(); + compileStdin(); } } function fshoot(name, arg, callback){ @@ -110,10 +113,10 @@ function compileScripts(){ } fs.stat(source, function(e, stats){ if (e) { - if (top) { + if (top && !/\.ls$/.test(source)) { return walk(source + ".ls"); } - die(e); + die("Can't find: " + source); } if (stats.isDirectory()) { fshoot('readdir', source, function(it){ @@ -282,18 +285,22 @@ function printTokens(tokens){ } } function repl(){ - var code, cont, readline, reset, prompt, that, vm, server, _ttyWrite, __ref; + var code, cont, rl, reset, prompt, that, vm, server, _ttyWrite, __ref; argv[1] = 'repl'; code = repl.infunc ? ' ' : ''; - cont = false; - readline = require('readline').createInterface(process.stdin, process.stdout); + cont = 0; + rl = require('readline').createInterface(process.stdin, process.stdout); reset = function(){ - readline.line = code = ''; - readline.prompt(); + rl.line = code = ''; + rl.prompt(); repl.inheredoc = false; }; - (_ttyWrite = readline._ttyWrite, readline)._ttyWrite = function(chr){ - cont = chr == '\n' || chr == '>'; + (_ttyWrite = rl._ttyWrite, rl)._ttyWrite = function(chr){ + if (char == '\n' || char == '>') { + cont += 1; + } else { + cont = 0; + } return _ttyWrite.apply(this, arguments); }; prompt = 'livescript'; @@ -301,7 +308,7 @@ function repl(){ prompt += " -" + that; } if (typeof LiveScript != 'undefined' && LiveScript !== null) { - LiveScript.history = readline.history; + LiveScript.history = rl.history; } if (!o.compile) { module.paths = module.constructor._nodeModulePaths(module.filename = process.cwd() + '/repl'); @@ -309,7 +316,7 @@ function repl(){ global.module = module; global.exports = exports; global.require = require; - server = (__ref = __clone(require('repl').REPLServer.prototype), __ref.context = global, __ref.commands = [], __ref.useGlobal = true, __ref.eval = function(code, __arg, __arg1, cb){ + server = (__ref = __clone(require('repl').REPLServer.prototype), __ref.context = global, __ref.commands = [], __ref.useGlobal = true, __ref.useColors = process.env.NODE_DISABLE_COLORS, __ref.eval = function(code, __arg, __arg1, cb){ var res, err; try { res = vm.runInThisContext(code, 'repl'); @@ -318,18 +325,19 @@ function repl(){ } cb(err, res); }, __ref); - readline.completer = __bind(server, 'complete'); + rl.completer = __bind(server, 'complete'); } - readline.on('attemptClose', function(){ - if (readline.line || code) { + rl.on('SIGCONT', rl.prompt); + rl.on('SIGINT', function(){ + if (this.line || code) { say(''); reset(); } else { - readline.close(); + this.close(); } }); - readline.on('close', __bind(process.stdin, 'destroy')); - readline.on('line', function(it){ + rl.on('close', __bind(process, 'exit')); + rl.on('line', function(it){ var isheredoc, ops, x; if (it.match(/^$/)) { repl.infunc = false; @@ -337,9 +345,9 @@ function repl(){ if (it.match(/(\=|\~>|->|do|import|switch)\s*$/) || (it.match(/^!?(function|class|if|unless) /) && !it.match(/ then /))) { repl.infunc = true; } - if ((cont || repl.infunc) && !repl.inheredoc) { + if (((0 < cont && cont < 3) || repl.infunc) && !repl.inheredoc) { code += it + '\n'; - readline.output.write(__repeatString('.', prompt.length) + '. '); + this.output.write(__repeatString('.', prompt.length) + '. '); return; } else { isheredoc = it.match(/(\'\'\'|\"\"\")/g); @@ -348,7 +356,7 @@ function repl(){ } if (repl.inheredoc) { code += it + '\n'; - readline.output.write(__repeatString('.', prompt.length) + '" '); + rl.output.write(__repeatString('.', prompt.length) + '" '); return; } } @@ -385,8 +393,15 @@ function repl(){ process.on('uncaughtException', function(it){ say("\n" + ((it != null ? it.stack : void 8) || it)); }); - readline.setPrompt(prompt + "> "); - readline.prompt(); + process.on('exit', function(){ + if (code && rl.output.isTTY) { + cont = 0; + say(''); + rl.emit('line', ''); + } + }); + rl.setPrompt(prompt + "> "); + rl.prompt(); } function forkNode(){ var args, i, that; diff --git a/lib/lang-ls.js b/lib/lang-ls.js index 33893d2f8..eef496970 100644 --- a/lib/lang-ls.js +++ b/lib/lang-ls.js @@ -9,9 +9,9 @@ tint = function(ext, shortcuts, fallthroughs){ } return PR.registerLangHandler(PR.createSimpleLexer(shortcuts, fallthroughs), [ext]); }; -ident = '(?:[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*)'; -kwend = '(?![$\\w\\x7f-\\uffff])'; -tint('ls', [['str', /^'(?:''[\S\s]*?''|[^\\']*(?:\\[\S\s][^\\']*)*)'/, '\''], ['lang-ls-qq', /(^"(?:""[\S\s]*?""|[^\\"]*(?:\\[\S\s][^\\"]*)*)")/, '"'], ['lang-ls-qr', /(^\/\/[\S\s]*?\/\/[gimy$?]{0,4})/, '/'], ['lang-ls-at', RegExp('(^@@?' + ident + '?)'), '@'], ['com', /^#.*/, '#'], ['typ', /^(?:0x[\dA-Fa-f][\dA-Fa-f_]*|(\d*)~([\dA-Za-z]\w*)|((\d[\d_]*)(\.\d[\d_]*)?(?:e[+-]?\d[\d_]*)?)[$\w]*)/i, '0123456789'], ['lang-js', /^`([^\\`]*(?:\\[\S\s][^\\`]*)*)`/, '`']], [['str', /^\\\S[^\s,;)}\]]*/], ['com', /^\/\*[\S\s]*\*\//], ['pln', RegExp('^(?:\\.{3}|(?:\\.\\s*(?:(?:[-+*/%&|^:]|>>>?|<<)?=|[~!@])?\\s*|[)}\\]?]|::)(?:' + ident + '[?~!@]?)+|' + ident + '[^\\n\\S]*:(?![:=]))')], ['kwd', RegExp('^(?:t(?:ry|h(?:row|en)|ypeof!?)|f(?:or(?:[^\\n\\S]+(?:own|ever))?|inally|unction)|n(?:ew|ot)|c(?:ontinue|a(?:se|tch)|lass)|i(?:[fs]|snt|n(?:stanceof)?|mport(?:[^\\n\\S]+all)?)|e(?:lse|x(?:tends|port))|d(?:e(?:fault|lete|bugger)|o)|un(?:less|til)|w(?:hile|ith|hen)|o[fr]|return|break|switch|and|let|loop)' + kwend)], ['typ', RegExp('^(?:true|false|null|void)' + kwend)], ['ctx', RegExp('^(?:t(?:h(?:is|at)|o|il)|f(?:rom|allthrough)|it|arguments|eval|by|super|prototype)' + kwend)], ['glb', RegExp('^(?:Array|Boolean|Date|Error|Function|JSON|Math|Number|Object|RegExp|S(?:tring|yntaxError)|TypeError|is(?:NaN|Finite)|parse(?:Int|Float)|(?:en|de)codeURI(?:Component)?)' + kwend)], ['var', RegExp('^' + ident)], ['str', /^<\[[\S\s]*?]>/], ['lang-ls-r', /^[^\/](\/(?![\s\/])[^[\/\n\\]*(?:(?:\\.|\[[^\]\n\\]*(?:\\.[^\]\n\\]*)*\])[^[\/\n\\]*)*\/[gimy$]{0,4})(?!\d)/]]); +ident = '(?:(?!\\d)(?:(?!\\s)[\\w$\\xAA-\\uFFDC])+)'; +kwend = '(?!(?!\\s)[$\\w\\xAA-\\uFFDC])'; +tint('ls', [['str', /^'(?:''[\S\s]*?''|[^\\']*(?:\\[\S\s][^\\']*)*)'/, '\''], ['lang-ls-qq', /(^"(?:""[\S\s]*?""|[^\\"]*(?:\\[\S\s][^\\"]*)*)")/, '"'], ['lang-ls-qr', /(^\/\/[\S\s]*?\/\/[gimy$?]{0,4})/, '/'], ['lang-ls-at', RegExp('(^@@?' + ident + '?)'), '@'], ['com', /^#.*/, '#'], ['typ', /^(?:0x[\da-f][\da-f_]*|(?:[2-9]|[12]\d|3[0-6])r[\da-z][\da-z_]*|\d[\d_]*(?:\.\d[\d_]*)?(?:e[+-]?\d[\d_]*)?[\w$]*)/i, '0123456789'], ['lang-js', /^`([^\\`]*(?:\\[\S\s][^\\`]*)*)`/, '`']], [['str', /^\\\S[^\s,;)}\]]*/], ['com', /^\/\*[\S\s]*\*\//], ['pln', RegExp('^(?:\\.{3}|(?:\\.\\s*(?:(?:[-+*/%&|^:]|>>>?|<<)?=|[~!@])?\\s*|[)}\\]?]|::)(?:' + ident + '[?~!@]?)+|' + ident + '[^\\n\\S]*:(?![:=]))')], ['kwd', RegExp('^(?:t(?:ry|h(?:row|en)|ypeof!?)|f(?:or(?:[^\\n\\S]+(?:own|ever))?|inally|unction)|n(?:ew|ot|o)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:[fs]|n(?:stanceof)?|mport(?:[^\\n\\S]+all)?)|e(?:lse|x(?:tends|port))|d(?:e(?:fault|lete|bugger)|o)|un(?:less|til)|w(?:hile|ith)|s(?:witch|uper)|o[frn]|off|return|break|and|let|var|loop|yes)' + kwend)], ['typ', RegExp('^(?:true|false|null|void)' + kwend)], ['ctx', RegExp('^(?:t(?:h(?:is|at)|o|il)|f(?:rom|allthrough)|it|arguments|eval|by|constructor|prototype|superclass)' + kwend)], ['glb', RegExp('^(?:Array|Boolean|Date|Error|Function|JSON|Math|Number|Object|RegExp|S(?:tring|yntaxError)|TypeError|is(?:NaN|Finite)|parse(?:Int|Float)|(?:en|de)codeURI(?:Component)?)' + kwend)], ['var', RegExp('^' + ident)], ['str', /^<\[[\S\s]*?]>/], ['lang-ls-r', /^[^\/](\/(?![\s\/])[^[\/\n\\]*(?:(?:\\.|\[[^\]\n\\]*(?:\\.[^\]\n\\]*)*\])[^[\/\n\\]*)*\/[gimy$]{0,4})(?!\d)/]]); interps = ['lang-ls', RegExp('^#({[\\S\\s]*?}|' + ident + ')'), '#']; regexes = ['lit', /^[\S\s]+?/]; tint('ls-qq', [interps], [['str', /^[\S\s]+?/]]); diff --git a/lib/lexer.js b/lib/lexer.js index 9fcafb5c8..abf5f70e2 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -93,7 +93,7 @@ exports.checkConsistency = function(camel, id){ } }; exports.doID = function(code, index){ - var match, input, id, last, tag, __ref; + var match, input, id, last, tag, that, __ref; input = (match = (ID.lastIndex = index, ID).exec(code))[0]; if (!input) { return 0; @@ -226,11 +226,15 @@ exports.doID = function(code, index){ tag = 'WHILE'; break; case 'import': - id = '<<<'; if (last[0] === '(') { + id = '<<<'; tag = 'BIOP'; } else { - able(this.tokens) || this.token('LITERAL', 'this'); + if (able(this.tokens)) { + id = '<<<'; + } else { + tag = 'DECL'; + } } break; case 'with': @@ -284,9 +288,9 @@ exports.doID = function(code, index){ } break; case 'all': - if (last[1] === '<<<') { - last[1] += '<'; - return 4; + if (that = last[1] === '<<<' && '<' || last[1] === 'import' && 'All') { + last[1] += that; + return 3; } break; case 'from': @@ -1612,11 +1616,10 @@ NONASCII = /[\x80-\uFFFF]/; OPENERS = ['(', '[', '{', 'CALL(', 'PARAM(', 'INDENT']; CLOSERS = [')', ']', '}', ')CALL', ')PARAM', 'DEDENT']; INVERSES = new function(){ - var i, o, c, __ref, __len; + var i, o, __ref, __len; for (i = 0, __len = (__ref = OPENERS).length; i < __len; ++i) { o = __ref[i]; - this[c = CLOSERS[i]] = o; - this[o] = c; + this[this[o] = CLOSERS[i]] = o; } }; CHAIN = ['(', '{', '[', 'ID', 'STRNUM', 'LITERAL', 'LET', 'WITH', 'WORDS']; diff --git a/lib/mode-ls.js b/lib/mode-ls.js new file mode 100644 index 000000000..8158a367c --- /dev/null +++ b/lib/mode-ls.js @@ -0,0 +1,243 @@ +define('ace/mode/coco', function(require, exports, module){ + var identifier, LiveScriptMode, keywordend, stringfill; + identifier = '(?!\\d)(?:(?!\\s)[\\w$\\xAA-\\uFFDC])+'; + exports.Mode = LiveScriptMode = (function(superclass){ + LiveScriptMode.displayName = 'LiveScriptMode'; + var indenter, prototype = __extend(LiveScriptMode, superclass).prototype, constructor = LiveScriptMode; + function LiveScriptMode(){ + var that; + this.$tokenizer = new (require('ace/tokenizer')).Tokenizer(LiveScriptMode.Rules); + if (that = require('ace/mode/matching_brace_outdent')) { + this.$outdent = new that.MatchingBraceOutdent; + } + } + indenter = RegExp('(?:[({[=:]|[-~]>|\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\s*all)?|const|var|let|new|catch(?:\\s*' + identifier + ')?))\\s*$'); + prototype.getNextLineIndent = function(state, line, tab){ + var indent, tokens; + indent = this.$getIndent(line); + tokens = this.$tokenizer.getLineTokens(line, state).tokens; + if (!(tokens.length && tokens[tokens.length - 1].type === 'comment')) { + if (state === 'start' && indenter.test(line)) { + indent += tab; + } + } + return indent; + }; + prototype.toggleCommentLines = function(state, doc, startRow, endRow){ + var comment, range, i, line, out; + comment = /^(\s*)#/; + range = new (require('ace/range')).Range(0, 0, 0, 0); + for (i = startRow; i <= endRow; ++i) { + if (out = comment.test(line = doc.getLine(i))) { + line = line.replace(comment, '$1'); + } else { + line = line.replace(/^\s*/, '$&#'); + } + range.end.row = range.start.row = i; + range.end.column = line.length + 1; + doc.replace(range, line); + } + return 1 - out * 2; + }; + prototype.checkOutdent = function(state, line, input){ + var __ref; + return (__ref = this.$outdent) != null ? __ref.checkOutdent(line, input) : void 8; + }; + prototype.autoOutdent = function(state, doc, row){ + var __ref; + return (__ref = this.$outdent) != null ? __ref.autoOutdent(doc, row) : void 8; + }; + return LiveScriptMode; + }(require('ace/mode/text').Mode)); + keywordend = '(?![$\\w]|\\s*:(?![:=]))'; + stringfill = { + token: 'string', + regex: '.+' + }; + LiveScriptMode.Rules = { + start: [ + { + token: 'keyword', + regex: '(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mport(?:\\s+all)?|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot|o)|un(?:less|til)|w(?:hile|ith)|o[frn]|off|return|break|let|var|loop|yes)' + keywordend + }, { + token: 'constant.language', + regex: '(?:true|false|null|void)' + keywordend + }, { + token: 'invalid.illegal', + regex: '(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)' + keywordend + }, { + token: 'language.support.class', + regex: '(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)' + keywordend + }, { + token: 'language.support.function', + regex: '(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)' + keywordend + }, { + token: 'variable.language', + regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by)' + keywordend + }, { + token: 'identifier', + regex: identifier + '\\s*:(?![:=])' + }, { + token: 'variable', + regex: identifier + }, { + token: 'keyword.operator', + regex: '(?:\\.{3}|\\s+\\?)' + }, { + token: 'keyword.variable', + regex: '(?:@+|::|\\.\\.)', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\.\\s*', + next: 'key' + }, { + token: 'string', + regex: '\\\\\\S[^\\s,;)}\\]]*' + }, { + token: 'string.doc', + regex: '\'\'\'', + next: 'qdoc' + }, { + token: 'string.doc', + regex: '"""', + next: 'qqdoc' + }, { + token: 'string', + regex: '\'', + next: 'qstring' + }, { + token: 'string', + regex: '"', + next: 'qqstring' + }, { + token: 'string', + regex: '`', + next: 'js' + }, { + token: 'string', + regex: '<\\[', + next: 'words' + }, { + token: 'string.regex', + regex: '//', + next: 'heregex' + }, { + token: 'comment.doc', + regex: '/\\*', + next: 'comment' + }, { + token: 'comment', + regex: '#.*' + }, { + token: 'string.regex', + regex: '\\/(?:[^[\\/\\n\\\\]*(?:(?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[\\/\\n\\\\]*)*)\\/[gimy$]{0,4}', + next: 'key' + }, { + token: 'constant.numeric', + regex: '(?:0x[\\da-fA-F][\\da-fA-F_]*|(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*|(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[\\w$]*)' + }, { + token: 'lparen', + regex: '[({[]' + }, { + token: 'rparen', + regex: '[)}\\]]', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\S+' + }, { + token: 'text', + regex: '\\s+' + } + ], + heregex: [ + { + token: 'string.regex', + regex: '.*?//[gimy$?]{0,4}', + next: 'start' + }, { + token: 'string.regex', + regex: '\\s*#{' + }, { + token: 'comment.regex', + regex: '\\s+(?:#.*)?' + }, { + token: 'string.regex', + regex: '\\S+' + } + ], + key: [ + { + token: 'keyword.operator', + regex: '[.?@!]+' + }, { + token: 'identifier', + regex: identifier, + next: 'start' + }, { + token: 'text', + regex: '.', + next: 'start' + } + ], + comment: [ + { + token: 'comment.doc', + regex: '.*?\\*/', + next: 'start' + }, { + token: 'comment.doc', + regex: '.+' + } + ], + qdoc: [ + { + token: 'string', + regex: ".*?'''", + next: 'key' + }, stringfill + ], + qqdoc: [ + { + token: 'string', + regex: '.*?"""', + next: 'key' + }, stringfill + ], + qstring: [ + { + token: 'string', + regex: '[^\\\\\']*(?:\\\\.[^\\\\\']*)*\'', + next: 'key' + }, stringfill + ], + qqstring: [ + { + token: 'string', + regex: '[^\\\\"]*(?:\\\\.[^\\\\"]*)*"', + next: 'key' + }, stringfill + ], + js: [ + { + token: 'string', + regex: '[^\\\\`]*(?:\\\\.[^\\\\`]*)*`', + next: 'key' + }, stringfill + ], + words: [ + { + token: 'string', + regex: '.*?\\]>', + next: 'key' + }, stringfill + ] + }; +}); +function __extend(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; +} \ No newline at end of file diff --git a/lib/slake.js b/lib/slake.js index 09a8730ec..1eaa1dce0 100755 --- a/lib/slake.js +++ b/lib/slake.js @@ -1,4 +1,4 @@ -var Tasks, Aliases, Flags, Options, args, filename, rec, __ref, __slice = [].slice; +var Tasks, Aliases, Flags, Options, args, filename, __ref, __slice = [].slice; Tasks = { __proto__: null }; @@ -47,7 +47,7 @@ global.spit = fs.writeFileSync; global.dir = fs.readdirSync; args = process.argv.slice(2); filename = ((__ref = args[0]) == '-f' || __ref == '--slakefile') && args.splice(0, 2)[1] || 'Slakefile'; -path.exists(filename, rec = function(affirmative){ +fs.exists(filename, function rec(affirmative){ var optparse; if (!affirmative) { if (process.cwd() === '/') { @@ -55,7 +55,7 @@ path.exists(filename, rec = function(affirmative){ process.exit(1); } process.chdir('..'); - return path.exists(filename, rec); + return fs.exists(filename, rec); } optparse = require('./optparse'); LiveScript.run(slurp(filename), { diff --git a/package.json b/package.json index 53b6929c5..609b1b41e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ } ], "engines": { - "node": ">= 0.6.14 < 0.9.0" + "node": ">= 0.8.0" }, "directories": { "lib": "./lib", diff --git a/package.ls b/package.ls index 5a848f77c..b55b72755 100644 --- a/package.ls +++ b/package.ls @@ -16,7 +16,7 @@ licenses : type: \MIT, url: \https://raw.github.com/gkz/LiveScript/master/LICENSE ... -engines : node: '>= 0.6.14 < 0.9.0' +engines : node: '>= 0.8.0' directories : lib: \./lib bin: \./bin diff --git a/src/ast.ls b/src/ast.ls index 84da325f0..163691553 100644 --- a/src/ast.ls +++ b/src/ast.ls @@ -27,7 +27,8 @@ # 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 @getJump! - fun = Fun [] Block this; call = Call!; hasThis = hasArgs = false + fun = Fun [] Block this; call = Call! + var hasArgs, hasThis @traverseChildren !-> switch it.value | \this => hasThis := true @@ -199,12 +200,12 @@ Negatable = #### Block # A list of expressions that forms the body of an indented block of code. class exports.Block extends Node - (node) ~> - @lines = [] - return this unless node - node.=unparen! - return node if node instanceof Block - @add node + (body || []) ~> + if \length of body + @lines = body + else + @lines = [] + @add body children: [\lines] @@ -340,7 +341,10 @@ class exports.Literal extends Atom switch val = "#{@value}" | \this => return o.scope.fun?bound or val | \undefined => val = 'void'; fallthrough - | \void => val += ' 8'; fallthrough + | \void => + return '' unless level + val += ' 8' + fallthrough | \null => @carp 'invalid use of ' + @value if level is LEVEL_CALL | \on \yes => val = 'true' | \off \no => val = 'false' @@ -394,7 +398,7 @@ class exports.Index extends Node children: [\key] - show: -> (@soak or '') + @symbol + show: -> ([\? if @soak]) + @symbol isComplex: -> @key.isComplex! @@ -444,7 +448,9 @@ class exports.Chain extends Node isAssignable: -> return @head.isAssignable! unless tail = @tails[*-1] - return false if tail not instanceof Index or tail.key instanceof List + 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 @@ -519,8 +525,9 @@ class exports.Chain extends Node if @head.unfoldSoak o that.then.tails.push ...@tails return that - for node, i in @tails then if delete node.soak + 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.isAssignable! test = if node instanceof Call [test, @head] = bust.cacheReference o JS "typeof #{ test.compile o, LEVEL_OP } === 'function'" @@ -547,6 +554,7 @@ class exports.Chain extends Node [rites[i], lefts[i]] = Chain node .cacheReference o else [left, @head] = Chain left .cacheReference o + op = \:= if op is \= return Assign(left, this, op) <<< {+access} expandSplat: !(o) -> @@ -910,7 +918,7 @@ class exports.Unary extends Node for op in ops by -1 then node = .. op.op, node, op.post them[i] = if sp then lat = Splat node else node if not lat and (@void or not o.level) - it = Block:: with {lines: them, @front, +void} + it = Block(them) <<< {@front, +void} it.compile o, LEVEL_PAREN # `v = delete o.k` @@ -1085,7 +1093,7 @@ class exports.Binary extends Node return x.compile o if 1 <= n < 2 # `[x] * 2` => `[x, x]` if items - if n < 1 then return (Block:: with lines: items)add(JS '[]')compile o + if n < 1 then return Block items .add JS '[]' .compile o refs = [] for item, i in items then [items[i], refs.*] = item.cache o, 1x items.push JS! <<< @@ -1175,8 +1183,8 @@ class exports.Assign extends Node @right = Binary left.op, @right, left.second left.=first return @compileDestructuring o, left if left.items - return @compileConditional o, left if @logic left.isAssignable! 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 <[ **= ^= %%= ]> @@ -1438,9 +1446,6 @@ class exports.Fun extends Node # `name = ->` @name ||= it.varName! @declared = it instanceof Var - # `::meth = ->` - @meth = it.tails.0 if it.head?value is \prototype and - it.tails.length is 1 and not it.tails.0.isComplex! compileNode: (o) -> pscope = o.scope @@ -1536,6 +1541,12 @@ class exports.Class extends Node for node, i in lines if node instanceof Obj lines[i] = Import proto||=Var(\prototype), node + for prop in node.items + if prop.key instanceof [Key, Literal] + if prop.val instanceof Fun + prop.val.meth = prop.key + else if prop.accessor + for f in prop.val then f.meth = prop.key else if node instanceof Fun and not node.statement ctor and node.carp 'redundant constructor' ctor = node @@ -1556,12 +1567,9 @@ class exports.Class extends Node class exports.Super extends Node isCallable: YES - constant = /^\.|^\[['"\d]/ - compile: ({scope}:o) -> while not scope.get \superclass and scope.fun, scope.=parent - if constant.test key = that.meth?compile o - return "superclass.prototype#key" + return \superclass.prototype + Index that .compile o if that.meth \superclass #### Parens @@ -1671,7 +1679,7 @@ class exports.Throw extends Jump #### Return class exports.Return extends Jump - ~> import {it} if it and it.value is not \void + ~> if it and it.value is not \void then import {it} getJump: THIS @@ -1685,7 +1693,7 @@ 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 (;;)` - import {test} if @post or test.value is not ''+!un + if @post or test.value is not ''+!un then import {test} children: <[ test body update else ]> @@ -1720,6 +1728,7 @@ class exports.While extends Node @body = If @guard, @body if @guard else @body.makeReturn it + @else?makeReturn it else @getJump! or @returns = true this @@ -1727,29 +1736,35 @@ class exports.While extends Node compileNode: (o) -> o.loop = true @test and if @un then @test.=invert! else @anaphorize! - return 'do {' + @compileBody (o.indent += TAB; o), @test if @post + return 'do {' + @compileBody (o.indent += TAB; o) if @post test = @test?compile o, LEVEL_PAREN or '' - head = if @update - then "for (;#{ test and ' ' + test }; #{ that.compile o, LEVEL_PAREN }" - else if test then "while (#test" else 'for (;;' + unless @update or @else + head = if test then "while (#test" else 'for (;;' + else + head = 'for (' + head += "#{ @yet = o.scope.temporary \yet } = true" if @else + head += ";#{ test and ' ' + test };" + head += ' ' + that.compile o, LEVEL_PAREN if @update head + ') {' + @compileBody (o.indent += TAB; o) - compileBody: (o, potest) -> - o <<< {+\break, +\continue} - {lines} = @body; code = ret = '' + compileBody: (o) -> + o.break = o.continue = true + {body: {lines}, yet, tab} = this + code = ret = '' if @returns @body = Block @body.makeObjReturn \__results if @objComp @body = If @guard, @body if @guard and @objComp empty = if @objComp then '{}' else '[]' lines[*-1]?=makeReturn res = o.scope.assign \__results empty ret = "\n#{@tab}return #{ res or empty };" - lines.unshift JS "#{ run = o.scope.temporary \run } = true;" if @else - code += "\n#that\n#{@tab}" if @body.compile o, LEVEL_TOP + @else?makeReturn! + yet and lines.unshift JS "#yet = false;" + code += "\n#that\n#tab" if @body.compile o, LEVEL_TOP code += \} - code += " while (#{ potest.compile o<<<{@tab} LEVEL_PAREN });" if potest - if run - @else.makeReturn! if @returns - code += " if (!#run) #{ @compileBlock o, @else }" + code += " while (#{ @test.compile o<<<{tab} LEVEL_PAREN });" if @post + if yet + code += " if (#yet) #{ @compileBlock o, @else }" + o.scope.free yet code + ret #### For @@ -1799,11 +1814,19 @@ class exports.For extends While temps.push lvar = o.scope.temporary \len vars = "#idx = 0, #lvar = #srcPart.length" cond = "#idx < #lvar" - head = 'for (' + if @object then "#idx in #srcPart" else + @else and @yet = o.scope.temporary \yet + head = 'for (' + head += "#idx in " if @object + head += "#that = true, " if @yet + if @object + head += srcPart + else step is pvar or vars += ', ' + step - "#vars; #cond; " + if 1 === Math.abs pvar - then (if pvar < 0 then \-- else \++) + idx - else idx + if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar + head += "#vars; #cond; " + if 1 === Math.abs pvar + then (if pvar < 0 then \-- else \++) + idx + else idx + if pvar < 0 + then ' -= ' + pvar.slice 1 + else ' += ' + pvar @own and head += ") if (#{ o.scope.assign \__own '{}.hasOwnProperty' } .call(#svar, #idx)" head += ') {' @@ -2060,9 +2083,10 @@ class exports.Vars extends Node makeReturn: THIS compile: (o, level) -> - for v in @vars + for {value}:v in @vars v.carp 'invalid variable declaration' unless v instanceof Var - o.scope.declare v.value, v + v.carp "redeclaration of \"#value\"" if o.scope.check value + o.scope.declare value, v if level then Literal \void .compile o else '' #### Parser Utils @@ -2084,13 +2108,19 @@ exports.Decl = Assign Chain(out, [Index Key that]), node else Import out, node - Block:: with {lines} + Block lines + + import: (lines, all) -> + for line, i in lines then lines[i] = Import Literal(\this), line, all + Block lines + + importAll: (lines) -> @import lines, true const: (lines) -> for node in lines node.carp 'invalid constant variable declaration' unless node.op is \= node.const = true - Block:: with {lines} + Block lines var: Vars @@ -2107,6 +2137,8 @@ Scope ::= if node and t = @variables"#name." if @READONLY[t] or @READONLY[type] node.carp "redeclaration of #that \"#name\"" + else if t is type is \arg + node.carp "duplicate parameter \"#name\"" return name if t in <[ arg function ]> # Dot-suffix to bypass `Object::` members. @variables"#name." = type diff --git a/src/command.ls b/src/command.ls index fd629f9ca..0f328c085 100644 --- a/src/command.ls +++ b/src/command.ls @@ -56,12 +56,17 @@ switch case o.eval argv.1 = \eval compileScript '' $args * \\n - case o.stdin then compileStdin! - case $args.length then compileScripts! - case process.stdin.readable then compileStdin! - default - o.interactive or say version! + \\n + help! + \\n + case o.interactive + repl! + case o.stdin + compileStdin! + case $args.length + compileScripts! + case require \tty .isatty 0 + say version! + \\n + help! + \\n repl! + default + compileStdin! # Calls a `fs` method, exiting on error. !function fshoot name, arg, callback @@ -79,8 +84,8 @@ switch fshoot \readFile source, !-> compileScript source, "#it", base e, stats <-! fs.stat source if e - return walk "#source.ls" if top - die e + return walk "#source.ls" if top and not /\.ls$/test source + die "Can't find: #source" if stats.isDirectory! <-! fshoot \readdir source <-! it.forEach @@ -194,18 +199,20 @@ switch argv.1 = \repl # ref. code = if repl.infunc then ' ' else '' - cont = false - readline = require(\readline)createInterface process.stdin, process.stdout + cont = 0 + rl = require(\readline)createInterface process.stdin, process.stdout reset = !-> - readline.line = code := '' - readline.prompt! + rl.line = code := '' + rl.prompt! repl.inheredoc = false - ({_ttyWrite} = readline)_ttyWrite = (chr) -> - cont := chr in [ \\n, \> ] + ({_ttyWrite} = rl)_ttyWrite = (chr) -> + if char in [\\n \>] + then cont += 1 + else cont := 0 _ttyWrite ... prompt = \livescript prompt += " -#that" if [\b if o.bare; \c if o.compile]join '' - LiveScript.history = readline.history if LiveScript? + LiveScript.history = rl.history if LiveScript? unless o.compile module.paths = module.._nodeModulePaths \ module.filename = process.cwd! + \/repl @@ -213,19 +220,21 @@ switch global <<< {module, exports, require} server = require(\repl)REPLServer:: with context: global, commands: [], useGlobal: true + useColors: process.env.NODE_DISABLE_COLORS eval: !(code,,, cb) -> try res = vm.runInThisContext code, \repl catch then err = e cb err, res - readline.completer = server~complete - readline.on \attemptClose !-> - if readline.line or code then say ''; reset! else readline.close! - readline.on \close process.stdin~destroy - readline.on \line !-> + rl.completer = server~complete + rl.on \SIGCONT rl.prompt + rl.on \SIGINT !-> + if @line or code then say ''; reset! else @close! + rl.on \close process~exit + rl.on \line !-> repl.infunc = false if it.match(/^$/) # close with a blank line without spaces repl.infunc = true if it.match(/(\=|\~>|->|do|import|switch)\s*$/) or (it.match(/^!?(function|class|if|unless) /) and not it.match(/ then /)) - if (cont or repl.infunc) and not repl.inheredoc + if (0 < cont < 3 or repl.infunc) and not repl.inheredoc code += it + \\n - readline.output.write \. * prompt.length + '. ' + @output.write \. * prompt.length + '. ' return else isheredoc = it.match /(\'\'\'|\"\"\")/g @@ -233,7 +242,7 @@ switch repl.inheredoc = not repl.inheredoc if repl.inheredoc code += it + \\n - readline.output.write \. * prompt.length + '" ' + rl.output.write \. * prompt.length + '" ' return repl.inheredoc = false code += it @@ -250,8 +259,14 @@ switch catch then say e reset! process.on \uncaughtException !-> say "\n#{ it?stack or it }" - readline.setPrompt "#prompt> " - readline.prompt! + process.on \exit !-> + # Handle `echo 42 | livescript -i` etc. + if code and rl.output.isTTY + cont := 0 + say '' + rl.emit \line '' + rl.setPrompt "#prompt> " + rl.prompt! # Start up a new __node.js__ instance with the arguments in `--nodejs` passed # to it, preserving the other options. diff --git a/src/grammar.ls b/src/grammar.ls index b2ca287af..5d2e89247 100644 --- a/src/grammar.ls +++ b/src/grammar.ls @@ -1,14 +1,17 @@ # The LiveScript parser is generated by [Jison](http://github.com/zaach/jison) # from this grammar file. Jison is a bottom-up parser generator, similar in -# style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript. -# It can recognize [LALR(1), LR(0), SLR(1), and LR(1)](http://en.wikipedia.org/wiki/LR_grammar) +# style to [Bison](http://www.gnu.org/software/bison), +# implemented in JavaScript. +# It can recognize +# [LALR(1), LR(0), SLR(1), and LR(1)](http://en.wikipedia.org/wiki/LR_grammar) # type grammars. To create the Jison parser, we list the pattern to match # on the left-hand side, and the action to take (usually the creation of syntax # tree nodes) on the right. As the parser runs, it # shifts tokens from our token stream, from left to right, and # [attempts to match](http://en.wikipedia.org/wiki/Bottom-up_parsing) # the token sequence against the rules below. When a match can be made, it -# reduces into the [nonterminal](http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols) +# reduces into the +# [nonterminal](http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols) # (the enclosing name at the top), and we proceed from there. # # If you run the `slake build:parser` command, Jison constructs a parse table @@ -42,7 +45,7 @@ o = (patterns, action, options) -> # dollar-sign variables are provided by Jison as references to the value of # their numeric position, so in this rule: # -# "Expression MATH Expression" +# Expression MATH Expression # # `$1` would be the value of the first _Expression_, `$2` would be the token # value for the _MATH_ terminal, and `$3` would be the value of the second @@ -102,11 +105,11 @@ bnf = o '( BACKTICK Chain BACKTICK Expression )' , -> Chain(Chain Var \__flip .add Call [$3]).flipIt!add Call [$5] - # Array/Object + # An array or object List: o '[ ArgList OptComma ]' -> L Arr $2 o '{ Properties OptComma }' -> L Obj $2 - # Can be labeled to perform named destructuring. + # can be labeled to perform named destructuring. o '[ ArgList OptComma ] LABEL' -> L Arr $2 .named $5 o '{ Properties OptComma } LABEL' -> L Obj $2 .named $5 @@ -150,14 +153,14 @@ bnf = o 'PARAM( ArgList OptComma )PARAM <- Expression' , -> Call.back $2, $6, $5.charAt(1) is \~, $5.length is 3 - # `var` `const` `export` + # `var`, `const`, `export`, or `import` o 'DECL Exprs' -> Decl[$1] $2 o 'DECL INDENT ArgList OptComma DEDENT' -> Decl[$1] $3 o \COMMENT -> L JS $1, true true # [yadayadayada](http://search.cpan.org/~tmtm/Yada-Yada-Yada-1.00/Yada.pm) - o \... -> L Throw JS "Error('unimplemented')" + o \... -> L Throw JS "Error('unimplemented')" # An indented block of expressions. # Note that [Lexer](#lexer) rewrites some single-line forms into blocks. @@ -229,19 +232,20 @@ bnf = o 'IfBlock ELSE Block' -> $1.addElse $3 o 'Expression POST_IF Expression' -> If $3, $1, $2 is \unless - # Loops can either be normal with a block of expressions - # to execute, postfix with a single expression, or postconditional. + # Loops can either be normal with a block of expressions to execute o 'LoopHead Block' -> $1.addBody $2 + # and an optional `else` clause, o 'LoopHead Block ELSE Block' -> $1.addBody $2 .addElse $4 + o 'DO Block WHILE Expression' , -> new While($4, $3 is \until, true)addBody $2 - # `return` or `throw` + # `return` or `throw`. o 'HURL Expression' -> Jump[$1] $2 o 'HURL INDENT ArgList OptComma DEDENT' -> Jump[$1] Arr.maybe $3 o \HURL -> L Jump[$1]! - # `break` or `continue` + # `break` or `continue`. o \JUMP -> L new Jump $1 o 'JUMP ID' -> L new Jump $1, $2 @@ -273,7 +277,7 @@ bnf = o '[ Expression TO Expression BY Expression ]' , -> new Parens new For from: $2, op: $3, to: $4, step: $6 - # Keys and values. + # The various forms of property. KeyValue: o \Key o \LITERAL -> Prop L(Key $1, $1 not in <[ arguments eval ]>), L Literal $1 @@ -297,11 +301,11 @@ bnf = # Properties within an object literal can be separated by # commas, as in JavaScript, or simply by newlines. Properties: - o '' -> [] - o \Property -> [$1] - o 'Properties , Property' -> $1 +++ $3 - o 'Properties OptComma NEWLINE Property' -> $1 +++ $4 - o 'INDENT Properties OptComma DEDENT' -> $2 + o '' -> [] + o \Property -> [$1] + o 'Properties , Property' -> $1 +++ $3 + o 'Properties OptComma NEWLINE Property' -> $1 +++ $4 + o 'INDENT Properties OptComma DEDENT' -> $2 Parenthetical: o '( Body )' -> Parens $2.chomp!unwrap!, false, $1 is \" diff --git a/src/lang-ls.ls b/src/lang-ls.ls index 4b70aa65e..403d0e49c 100644 --- a/src/lang-ls.ls +++ b/src/lang-ls.ls @@ -4,8 +4,8 @@ tint = (ext, shortcuts, fallthroughs) -> for rule in shortcuts when rule.length < 4 then rule.splice 2 0 0 PR.registerLangHandler PR.createSimpleLexer(shortcuts, fallthroughs), [ext] -ident = /(?:[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)/$ -kwend = /(?![$\w\x7f-\uffff])/$ +ident = /(?:(?!\d)(?:(?!\s)[\w$\xAA-\uFFDC])+)/$ +kwend = /(?!(?!\s)[$\w\xAA-\uFFDC])/$ ### Main tint \ls [ @@ -15,9 +15,9 @@ tint \ls [ [\lang-ls-at //(^ @@? #ident? )// \@] [\com /^#.*/ \#] [\typ // ^ (? - : 0x[\dA-Fa-f][\dA-Fa-f_]* - | (\d*) ~ ([\dA-Za-z]\w*) - | ( (\d[\d_]*)(\.\d[\d_]*)? (?:e[+-]?\d[\d_]*)? ) [$\w]* + : 0x[\da-f][\da-f_]* + | (?:[2-9]|[12]\d|3[0-6]) r [\da-z][\da-z_]* + | \d[\d_]*(?:\.\d[\d_]*)? (?:e[+-]?\d[\d_]*)? [\w$]* ) //i \0123456789] [\lang-js /^`([^\\`]*(?:\\[\S\s][^\\`]*)*)`/ \`] ] [ @@ -33,20 +33,21 @@ tint \ls [ [\kwd // ^ (? : t(?:ry|h(?:row|en)|ypeof!?) | f(?:or(?:[^\n\S]+(?:own|ever))?|inally|unction) - | n(?:ew|ot) - | c(?:ontinue|a(?:se|tch)|lass) - | i(?:[fs]|snt|n(?:stanceof)?|mport(?:[^\n\S]+all)?) + | n(?:ew|ot|o) + | c(?:on(?:tinue|st)|a(?:se|tch)|lass) + | i(?:[fs]|n(?:stanceof)?|mport(?:[^\n\S]+all)?) | e(?:lse|x(?:tends|port)) | d(?:e(?:fault|lete|bugger)|o) | un(?:less|til) - | w(?:hile|ith|hen) - | o[fr] | return | break | switch | and | let | loop + | w(?:hile|ith) + | s(?:witch|uper) + | o[frn] | off | return | break | and | let | var | loop | yes ) #kwend //] [\typ // ^ (?: true | false | null | void ) #kwend //] [\ctx // ^ (? : t(?:h(?:is|at)|o|il) | f(?:rom|allthrough) - | it | arguments | eval | by | super | prototype + | it | arguments | eval | by | constructor | prototype | superclass ) #kwend //] [\glb // ^ (? : Array | Boolean | Date | Error | Function | JSON | Math | Number diff --git a/src/lexer.ls b/src/lexer.ls index 8711ea3a5..b93c0e28f 100644 --- a/src/lexer.ls +++ b/src/lexer.ls @@ -156,11 +156,11 @@ exports import case \unless then tag = \IF case \until then tag = \WHILE case \import - id = \<<< if last.0 is \( + id = \<<< tag = \BIOP else - able @tokens or @token \LITERAL \this + if able @tokens then id = \<<< else tag = \DECL case \with tag = | able @tokens => \CLONEPORT | last.0 is \( => \BIOP @@ -187,9 +187,11 @@ exports import if last.0 in <[ CASE | ]> last.0 = \DEFAULT return id.length - case \all then if last.1 is \<<< - last.1 += \< - return 4 + case \all + if last.1 is \<<< and \< + or last.1 is \import and \All + last.1 += that + return 3 case \from then @forange! and tag = \FROM case \to \til @forange! and @tokens.push [\FROM '' @line] [\STRNUM \0 @line] @@ -723,7 +725,7 @@ exports import countLines: -> (while pos = 1 + it.indexOf \\n pos then ++@line); it # Checks FOR for FROM/TO. - forange: -> @tokens[*-2]?0 is \FOR and import {-seenFor, +seenFrom} + forange: -> @tokens[*-2]?0 is \FOR and @<<<{-seenFor, +seenFrom} # Complains on duplicate flag. validate: (flag) -> @@ -1132,7 +1134,7 @@ OPENERS = <[ ( [ { CALL( PARAM( INDENT ]> CLOSERS = <[ ) ] } )CALL )PARAM DEDENT ]> # The inverse mappings of {OPEN,CLOS}ERS to look things up from either end. -INVERSES = new -> for o, i in OPENERS then import (c = CLOSERS[i]): o, (o): c +INVERSES = new -> for o, i in OPENERS then @[@[o] = CLOSERS[i]] = o # Tokens that can start a dot/call chain. CHAIN = <[ ( { [ ID STRNUM LITERAL LET WITH WORDS ]> diff --git a/src/mode-ls.ls b/src/mode-ls.ls new file mode 100644 index 000000000..8af70a88a --- /dev/null +++ b/src/mode-ls.ls @@ -0,0 +1,245 @@ +# Defines an editing mode for [Ace](http://ace.ajax.org). +# +# Open [test/ace.htm](../test/ace.htm) to test. + +require, exports, module <-! define \ace/mode/coco + +identifier = /(?!\d)(?:(?!\s)[\w$\xAA-\uFFDC])+/$ + +exports.Mode = class LiveScriptMode extends require(\ace/mode/text)Mode + -> + @$tokenizer = + new (require \ace/tokenizer)Tokenizer LiveScriptMode.Rules + if require \ace/mode/matching_brace_outdent + @$outdent = new that.MatchingBraceOutdent + + indenter = // (? + : [({[=:] + | [-~]> + | \b (?: e(?:lse|xport) | d(?:o|efault) | t(?:ry|hen) | finally | + import (?:\s* all)? | const | var | + let | new | catch (?:\s* #identifier)? ) + ) \s* $ // + + getNextLineIndent: (state, line, tab) -> + indent = @$getIndent line + {tokens} = @$tokenizer.getLineTokens line, state + unless tokens.length and tokens[*-1]type is \comment + indent += tab if state is \start and indenter.test line + indent + + toggleCommentLines: (state, doc, startRow, endRow) -> + comment = /^(\s*)#/; range = new (require \ace/range)Range 0 0 0 0 + for i from startRow to endRow + if out = comment.test line = doc.getLine i + then line.=replace comment, \$1 + else line.=replace /^\s*/ \$&# + range.end.row = range.start.row = i + range.end.column = line.length + 1 + doc.replace range, line + 1 - out * 2 + + checkOutdent: (state, line, input) -> @$outdent?checkOutdent line, input + + autoOutdent: (state, doc, row) -> @$outdent?autoOutdent doc, row + +### Highlight Rules + +keywordend = /(?![$\w]|\s*:(?![:=]))/$ +stringfill = token: \string, regex: '.+' + +LiveScriptMode.Rules = + start: + * token: \keyword + regex: //(? + :t(?:h(?:is|row|en)|ry|ypeof!?) + |c(?:on(?:tinue|st)|a(?:se|tch)|lass) + |i(?:n(?:stanceof)?|mport(?:\s+all)?|[fs]) + |d(?:e(?:fault|lete|bugger)|o) + |f(?:or(?:\s+own)?|inally|unction) + |s(?:uper|witch) + |e(?:lse|x(?:tends|port)|val) + |a(?:nd|rguments) + |n(?:ew|ot|o) + |un(?:less|til) + |w(?:hile|ith) + |o[frn]|off|return|break|let|var|loop|yes + )//$ + keywordend + + * token: \constant.language + regex: '(?:true|false|null|void)' + keywordend + + * token: \invalid.illegal + regex: '(? + :p(?:ackage|r(?:ivate|otected)|ublic) + |i(?:mplements|nterface) + |enum|static|yield + )' + keywordend + + * token: \language.support.class + regex: '(? + :R(?:e(?:gExp|ferenceError)|angeError) + |S(?:tring|yntaxError) + |E(?:rror|valError) + |Array|Boolean|Date|Function|Number|Object|TypeError|URIError + )' + keywordend + + * token: \language.support.function + regex: '(? + :is(?:NaN|Finite) + |parse(?:Int|Float) + |Math|JSON + |(?:en|de)codeURI(?:Component)? + )' + keywordend + + * token: \variable.language + regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by)' + keywordend + + * token: \identifier + regex: identifier + /\s*:(?![:=])/$ + + * token: \variable + regex: identifier + + * token: \keyword.operator + regex: /(?:\.{3}|\s+\?)/$ + + * token: \keyword.variable + regex: /(?:@+|::|\.\.)/$ + next : \key + + * token: \keyword.operator + regex: /\.\s*/$ + next : \key + + * token: \string + regex: /\\\S[^\s,;)}\]]*/$ + + * token: \string.doc + regex: \''' + next : \qdoc + + * token: \string.doc + regex: \""" + next : \qqdoc + + * token: \string + regex: \' + next : \qstring + + * token: \string + regex: \" + next : \qqstring + + * token: \string + regex: \` + next : \js + + * token: \string + regex: '<\\[' + next : \words + + * token: \string.regex + regex: \// + next : \heregex + + * token: \comment.doc + regex: '/\\*' + next : \comment + + * token: \comment + regex: '#.*' + + * token: \string.regex + regex: // + /(?: [^ [ / \n \\ ]* + (?: (?: \\. + | \[ [^\]\n\\]* (?:\\.[^\]\n\\]*)* \] + ) [^ [ / \n \\ ]* + )* + )/ [gimy$]{0,4} + //$ + next : \key + + * token: \constant.numeric + regex: '(?:0x[\\da-fA-F][\\da-fA-F_]* + |(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]* + |(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*) + (?:e[+-]?\\d[\\d_]*)?[\\w$]*)' + + * token: \lparen + regex: '[({[]' + + * token: \rparen + regex: '[)}\\]]' + next : \key + + * token: \keyword.operator + regex: \\\S+ + + * token: \text + regex: \\\s+ + + heregex: + * token: \string.regex + regex: '.*?//[gimy$?]{0,4}' + next : \start + * token: \string.regex + regex: '\\s*#{' + * token: \comment.regex + regex: '\\s+(?:#.*)?' + * token: \string.regex + regex: '\\S+' + + key: + * token: \keyword.operator + regex: '[.?@!]+' + * token: \identifier + regex: identifier + next : \start + * token: \text + regex: '.' + next : \start + + comment: + * token: \comment.doc + regex: '.*?\\*/' + next : \start + * token: \comment.doc + regex: '.+' + + qdoc: + token: \string + regex: ".*?'''" + next : \key + stringfill + + qqdoc: + token: \string + regex: '.*?"""' + next : \key + stringfill + + qstring: + token: \string + regex: /[^\\']*(?:\\.[^\\']*)*'/$ + next : \key + stringfill + + qqstring: + token: \string + regex: /[^\\"]*(?:\\.[^\\"]*)*"/$ + next : \key + stringfill + + js: + token: \string + regex: /[^\\`]*(?:\\.[^\\`]*)*`/$ + next : \key + stringfill + + words: + token: \string + regex: '.*?\\]>' + next : \key + stringfill diff --git a/src/slake.ls b/src/slake.ls index e8d8b7976..012ede5f2 100644 --- a/src/slake.ls +++ b/src/slake.ls @@ -45,13 +45,13 @@ global import args = process.argv.slice 2 filename = args.0 in <[ -f --slakefile ]> and args.splice(0 2)1 or \Slakefile -path.exists filename, rec = (affirmative) -> +fs.exists filename, :rec(affirmative) -> unless affirmative if process.cwd! is \/ console.error 'no "%s"' filename process.exit 1 process.chdir \.. - return path.exists filename, rec + return fs.exists filename, rec optparse = require \./optparse LiveScript.run slurp(filename), {filename} Options := optparse Flags, args diff --git a/test/accessor.ls b/test/accessor.ls index 5c56360bf..77f96aa27 100644 --- a/test/accessor.ls +++ b/test/accessor.ls @@ -19,16 +19,14 @@ o <<< a:~ -> 1 eq 1, o.a :: = - p:~ - \ -> @z - (@z) -> + p: -> if it? then @_ = it else @_ class C extends {::} - spd = Object.getOwnPropertyDescriptor super::, \p - p:~ -> spd.get.call this - p:~ (z) -> spd.set.call this, z + p:~ + \ -> super! + (z) -> super z c = new C eq c.p = 3, c.p -ok c.hasOwnProperty \z +ok c.hasOwnProperty \_ compileThrows 'excess accessor parameter' 1 'p:~ (a, b) ->' diff --git a/test/ace.html b/test/ace.html new file mode 100644 index 000000000..545180959 --- /dev/null +++ b/test/ace.html @@ -0,0 +1,48 @@ +ace/mode/coco
+#!/usr/bin/env livescript
+
+try
+  throw URIError decodeURI 0xbad - 36rCoco_lang * 123456.7e+8f
+catch e
+  console.log \word + 'qstring' + "qqstring" + '''
+    qdoc
+  ''' + """
+    qqdoc
+  """
+
+do ->
+  switch it
+  case <[ quoted words ]>
+    return /regex/imgy.test //
+      heregex  # comment
+    //imgy
+  /* JS Comment */
+  this is: not JS
+  void
+
+implements illegal
+
diff --git a/test/assignment.ls b/test/assignment.ls index 0cd9a1ab2..4b2a1b25e 100644 --- a/test/assignment.ls +++ b/test/assignment.ls @@ -77,6 +77,10 @@ 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 */ @@ -255,6 +259,8 @@ eq \e a.b.c a.=b <<< {\c} eq \c a.c +compileThrows 'assignment to undeclared "a"' 1 'a.=b' + ### Subdestructuring a = [] diff --git a/test/chaining.ls b/test/chaining.ls index 5accd2cc4..404e5bd31 100644 --- a/test/chaining.ls +++ b/test/chaining.ls @@ -101,6 +101,8 @@ eq 42, do(0; parent.child.~method) eq 42, do(0; parent.child~"me#{'th'}od") eq 42, parent.child. ~ [\method] null +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} diff --git a/test/compilation.ls b/test/compilation.ls index 3d6c0f744..9effe335a 100644 --- a/test/compilation.ls +++ b/test/compilation.ls @@ -164,13 +164,12 @@ eq λ(), 7 compileThrows 'invalid identifier "♪"' 1 'ƒ ♪ ♯' -# [coffee#1195](https://github.com/jashkenas/coffee-script/issues/1195) -eq ''' -(function(){}); -null; -''' LiveScript.compile ''' --> void; -null +# - [coffee#1195](https://github.com/jashkenas/coffee-script/issues/1195) +# - Ignore top-level `void`s. +eq '(function(){});' LiveScript.compile ''' + -> void; + void; + void ''' bare # Dash seperated identifiers diff --git a/test/declaration.ls b/test/declaration.ls index 6d62a5934..2f2a569e2 100644 --- a/test/declaration.ls +++ b/test/declaration.ls @@ -77,10 +77,10 @@ let ok a is b is c is d is e is void compileThrows 'invalid variable declaration' 2 'var\n 0' +compileThrows 'redeclaration of "a"' 2 '(a) ->\n var a' ### 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' -> @@ -92,5 +92,3 @@ eq '''(function(n){ n == null && (n = 2); return n + 1; });''' LiveScript.compile '(n = 2) -> n + 1' {+\const, +bare} - - diff --git a/test/loop.ls b/test/loop.ls index 0bb9c056c..8c3f82c3d 100644 --- a/test/loop.ls +++ b/test/loop.ls @@ -325,6 +325,13 @@ for cond in [true false] else ok not cond +r = for i from 0 to 3 + while i &&& 1 + break + else + i +eq '0,2' ''+r + r = for i til 1 then i else [9] eq 0 r.0 diff --git a/test/oo.ls b/test/oo.ls index eb34cb4c2..9117e1085 100644 --- a/test/oo.ls +++ b/test/oo.ls @@ -10,7 +10,7 @@ class FirstChild extends Base func: -> super('one/') + it SecondChild = class extends FirstChild - ::func = -> super('two/') + it + func: -> (super).call(this, 'two/') + it thirdCtor = -> @array = [1, 2, 3] diff --git a/test/operator.ls b/test/operator.ls index 6fbff906d..937aeaa05 100644 --- a/test/operator.ls +++ b/test/operator.ls @@ -280,10 +280,6 @@ eq ''' ok ok.isPrototypeOf new []= (->) <<< prototype: ok -new - import life: 2, universe: 3, everything: 7 - eq @life * @universe * @everything, 42 - f = -> import it <<< new: -> new this it @@ -307,6 +303,14 @@ 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, 42 + ### {in,de}crement a = [0]