From 6b4e437c93715a08b9f05b8423a953dda3a10a93 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 27 Oct 2010 22:50:20 -0400 Subject: [PATCH] CoffeeScript now compiled 'undefined' as 'void 0' --- lib/browser.js | 2 +- lib/cake.js | 2 +- lib/command.js | 8 ++++---- lib/grammar.js | 2 +- lib/lexer.js | 12 ++++++------ lib/nodes.js | 39 +++++++++++++++++++++------------------ lib/parser.js | 2 +- lib/repl.js | 2 +- lib/rewriter.js | 18 +++++++++--------- lib/scope.js | 4 ++-- src/grammar.coffee | 23 ++++++++++------------- src/lexer.coffee | 12 ++++++------ src/nodes.coffee | 7 ++++--- 13 files changed, 67 insertions(+), 66 deletions(-) diff --git a/lib/browser.js b/lib/browser.js index 075f6b43..b2eae701 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -22,7 +22,7 @@ xhr.overrideMimeType('text/plain'); } xhr.onreadystatechange = function() { - return xhr.readyState === 4 ? CoffeeScript.run(xhr.responseText, options) : undefined; + return xhr.readyState === 4 ? CoffeeScript.run(xhr.responseText, options) : void 0; }; return xhr.send(null); }; diff --git a/lib/cake.js b/lib/cake.js index 4ff2f4cb..7070d121 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -65,7 +65,7 @@ desc = task.description ? "# " + task.description : ''; console.log("cake " + name + spaces + " " + desc); } - return switches.length ? console.log(oparse.help()) : undefined; + return switches.length ? console.log(oparse.help()) : void 0; }; missingTask = function(task) { console.log("No such task: \"" + task + "\""); diff --git a/lib/command.js b/lib/command.js index 61970393..2eb7c03b 100644 --- a/lib/command.js +++ b/lib/command.js @@ -74,7 +74,7 @@ fs.readFile(source, function(err, code) { return compileScript(source, code.toString(), base); }); - return opts.watch ? watch(source, base) : undefined; + return opts.watch ? watch(source, base) : void 0; } }); }); @@ -110,7 +110,7 @@ } else { t.output = CoffeeScript.compile(t.input, t.options); CoffeeScript.emit('success', task); - return o.print ? console.log(t.output.trim()) : o.compile ? writeJs(t.file, t.output, base) : o.lint ? lint(t.file, t.output) : undefined; + return o.print ? console.log(t.output.trim()) : o.compile ? writeJs(t.file, t.output, base) : o.lint ? lint(t.file, t.output) : void 0; } } catch (err) { CoffeeScript.emit('failure', err, task); @@ -129,7 +129,7 @@ code = ''; stdin = process.openStdin(); stdin.on('data', function(buffer) { - return buffer ? code += buffer.toString() : undefined; + return buffer ? code += buffer.toString() : void 0; }); return stdin.on('end', function() { return compileScript('stdio', code); @@ -163,7 +163,7 @@ js = ' '; } return fs.writeFile(jsPath, js, function(err) { - return err ? console.log(err.message) : opts.compile && opts.watch ? console.log("Compiled " + source) : undefined; + return err ? console.log(err.message) : opts.compile && opts.watch ? console.log("Compiled " + source) : void 0; }); }; return path.exists(dir, function(exists) { diff --git a/lib/grammar.js b/lib/grammar.js index 6763eb0a..9d6e4394 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -66,7 +66,7 @@ }), o('REGEX', function() { return new Literal($1); }), o('BOOL', function() { - return new Literal($1); + return new Literal($1 === 'undefined' ? 'void 0' : $1); }) ], Assign: [ diff --git a/lib/lexer.js b/lib/lexer.js index 0d2b6b20..59b76736 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -94,7 +94,7 @@ if (COFFEE_ALIASES.hasOwnProperty(id)) { id = COFFEE_ALIASES[id]; } - tag = id === '!' ? 'UNARY' : id === '==' || id === '!=' ? 'COMPARE' : id === '&&' || id === '||' ? 'LOGIC' : id === 'true' || id === 'false' || id === 'null' ? 'BOOL' : tag; + tag = id === '!' ? 'UNARY' : id === '==' || id === '!=' ? 'COMPARE' : id === '&&' || id === '||' ? 'LOGIC' : id === 'true' || id === 'false' || id === 'null' || id === 'undefined' ? 'BOOL' : tag; } this.token(tag, id); if (colon) { @@ -228,7 +228,7 @@ tokens.push(['+', '+']); } tokens.pop(); - if (((_ref3 = tokens[0]) != null ? _ref3[0] : undefined) !== 'STRING') { + if (((_ref3 = tokens[0]) != null ? _ref3[0] : void 0) !== 'STRING') { this.tokens.push(['STRING', '""'], ['+', '+']); } (_this = this.tokens).push.apply(_this, tokens); @@ -277,7 +277,7 @@ var dent, len; while (moveOut > 0) { len = this.indents.length - 1; - if (this.indents[len] === undefined) { + if (this.indents[len] === void 0) { moveOut = 0; } else if (this.indents[len] === this.outdebt) { moveOut -= this.outdebt; @@ -357,7 +357,7 @@ tag = 'UNARY'; } else if (__indexOf.call(SHIFT, value) >= 0) { tag = 'SHIFT'; - } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : undefined)) { + } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) { tag = 'LOGIC'; } else if (prev && !prev.spaced) { if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0)) { @@ -565,7 +565,7 @@ return Lexer; })(); JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'try', 'catch', 'finally', 'class', 'extends', 'super']; - COFFEE_KEYWORDS = ['then', 'unless', 'until', 'loop', 'of', 'by', 'when']; + COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when']; for (op in COFFEE_ALIASES = { and: '&&', or: '||', @@ -608,7 +608,7 @@ COMPARE = ['==', '!=', '<', '>', '<=', '>=']; MATH = ['*', '/', '%']; RELATION = ['IN', 'OF', 'INSTANCEOF']; - BOOL = ['TRUE', 'FALSE', 'NULL']; + BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']; NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']']; CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']; INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL'); diff --git a/lib/nodes.js b/lib/nodes.js index 16119bdf..a4919e63 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -76,7 +76,7 @@ contains = false; this.traverseChildren(false, function(node, arg) { var rearg; - return (rearg = block(node, arg)) === true ? !(contains = true) : arg != null ? rearg : undefined; + return (rearg = block(node, arg)) === true ? !(contains = true) : arg != null ? rearg : void 0; }, arg); return contains; }; @@ -311,7 +311,7 @@ Return.prototype.makeReturn = THIS; Return.prototype.compile = function(o, level) { var _ref2, expr; - expr = (_ref2 = this.expression) != null ? _ref2.makeReturn() : undefined; + expr = (_ref2 = this.expression) != null ? _ref2.makeReturn() : void 0; return expr && !(expr instanceof Return) ? expr.compile(o, level) : Return.__super__.compile.call(this, o, level); }; Return.prototype.compileNode = function(o) { @@ -384,7 +384,7 @@ Value.prototype.cacheReference = function(o) { var base, bref, name, nref; name = last(this.properties); - if (this.properties.length < 2 && !this.base.isComplex() && !(name != null ? name.isComplex() : undefined)) { + if (this.properties.length < 2 && !this.base.isComplex() && !(name != null ? name.isComplex() : void 0)) { return [this, this]; } base = new Value(this.base, this.properties.slice(0, -1)); @@ -1029,7 +1029,7 @@ empty = this.body.expressions.length === 0; delete o.bare; delete o.globals; - splat = undefined; + splat = void 0; params = []; _ref2 = this.params; for (i = 0, _len = _ref2.length; i < _len; i++) { @@ -1091,7 +1091,7 @@ return this.tags.front ? "(" + func + ")" : func; }; Code.prototype.traverseChildren = function(crossScope, func) { - return crossScope ? Code.__super__.traverseChildren.call(this, crossScope, func) : undefined; + return crossScope ? Code.__super__.traverseChildren.call(this, crossScope, func) : void 0; }; return Code; })(); @@ -1152,7 +1152,7 @@ o.scope.assign(len, 'arguments.length'); variadic = o.scope.freeVariable('result'); o.scope.assign(variadic, len + ' >= ' + this.arglength); - end = this.trailings.length ? ", " + len + " - " + this.trailings.length : undefined; + end = this.trailings.length ? ", " + len + " - " + this.trailings.length : void 0; _ref2 = this.trailings; for (idx = 0, _len = _ref2.length; idx < _len; idx++) { trailing = _ref2[idx]; @@ -1201,8 +1201,8 @@ While = (function() { function While(condition, opts) { While.__super__.constructor.call(this); - this.condition = (opts != null ? opts.invert : undefined) ? condition.invert() : condition; - this.guard = opts != null ? opts.guard : undefined; + this.condition = (opts != null ? opts.invert : void 0) ? condition.invert() : condition; + this.guard = opts != null ? opts.guard : void 0; return this; } return While; @@ -1428,7 +1428,7 @@ var catchPart, errorPart; o.indent = this.idt(1); errorPart = this.error ? " (" + (this.error.compile(o)) + ") " : ' '; - catchPart = this.recovery ? " catch" + errorPart + "{\n" + (this.recovery.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}" : !(this.ensure || this.recovery) ? ' catch (_e) {}' : undefined; + catchPart = this.recovery ? " catch" + errorPart + "{\n" + (this.recovery.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}" : !(this.ensure || this.recovery) ? ' catch (_e) {}' : void 0; return ("" + this.tab + "try {\n" + (this.attempt.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}" + (catchPart || '')) + (this.ensure ? " finally {\n" + (this.ensure.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}" : ''); }; return Try; @@ -1540,8 +1540,8 @@ For.prototype.compileNode = function(o) { var _ref2, _ref3, _ref4, _ref5, _ref6, body, code, cond, defPart, forPart, guardPart, idt, index, ivar, lvar, name, namePart, pvar, retPart, rvar, scope, sourcePart, step, svar, tail, tvar, varPart, vars; scope = o.scope; - name = !this.pattern && ((_ref2 = this.name) != null ? _ref2.compile(o) : undefined); - index = (_ref3 = this.index) != null ? _ref3.compile(o) : undefined; + name = !this.pattern && ((_ref2 = this.name) != null ? _ref2.compile(o) : void 0); + index = (_ref3 = this.index) != null ? _ref3.compile(o) : void 0; ivar = !index ? scope.freeVariable('i') : index; varPart = guardPart = defPart = retPart = ''; body = Expressions.wrap([this.body]); @@ -1572,7 +1572,7 @@ } else { sourcePart = svar = this.source.compile(o, LEVEL_PAREN); } - namePart = this.pattern ? new Assign(this.name, new Literal("" + svar + "[" + ivar + "]")).compile(o, LEVEL_TOP) : name ? "" + name + " = " + svar + "[" + ivar + "]" : undefined; + namePart = this.pattern ? new Assign(this.name, new Literal("" + svar + "[" + ivar + "]")).compile(o, LEVEL_TOP) : name ? "" + name + " = " + svar + "[" + ivar + "]" : void 0; if (!this.object) { if (0 > pvar && (pvar | 0) === +pvar) { vars = "" + ivar + " = " + svar + ".length - 1"; @@ -1659,7 +1659,7 @@ var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, block, body, code, cond, conditions, expr, i, idt1, idt2; idt1 = this.idt(1); idt2 = o.indent = this.idt(2); - code = this.tab + ("switch (" + (((_ref2 = this.subject) != null ? _ref2.compile(o, LEVEL_PAREN) : undefined) || false) + ") {\n"); + code = this.tab + ("switch (" + (((_ref2 = this.subject) != null ? _ref2.compile(o, LEVEL_PAREN) : void 0) || false) + ") {\n"); for (i = 0, _len = this.cases.length; i < _len; i++) { _ref3 = this.cases[i], conditions = _ref3[0], block = _ref3[1]; _ref4 = flatten([conditions]); @@ -1711,11 +1711,11 @@ If.prototype.children = ['condition', 'body', 'elseBody']; If.prototype.bodyNode = function() { var _ref2; - return (_ref2 = this.body) != null ? _ref2.unwrap() : undefined; + return (_ref2 = this.body) != null ? _ref2.unwrap() : void 0; }; If.prototype.elseBodyNode = function() { var _ref2; - return (_ref2 = this.elseBody) != null ? _ref2.unwrap() : undefined; + return (_ref2 = this.elseBody) != null ? _ref2.unwrap() : void 0; }; If.prototype.addElse = function(elseBody) { if (this.isChain) { @@ -1728,7 +1728,7 @@ }; If.prototype.isStatement = function(o) { var _ref2; - return (o != null ? o.level : undefined) === LEVEL_TOP || this.bodyNode().isStatement(o) || ((_ref2 = this.elseBodyNode()) != null ? _ref2.isStatement(o) : undefined); + return (o != null ? o.level : void 0) === LEVEL_TOP || this.bodyNode().isStatement(o) || ((_ref2 = this.elseBodyNode()) != null ? _ref2.isStatement(o) : void 0); }; If.prototype.compileNode = function(o) { return this.isStatement(o) ? this.compileStatement(o) : this.compileExpression(o); @@ -1767,8 +1767,11 @@ })) : "{\n" + (this.elseBody.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}"); }; If.prototype.compileExpression = function(o) { - var _ref2, code; - code = this.condition.compile(o, LEVEL_COND) + ' ? ' + this.bodyNode().compile(o, LEVEL_LIST) + ' : ' + ((_ref2 = this.elseBodyNode()) != null ? _ref2.compile(o, LEVEL_LIST) : undefined); + var alt, body, code, cond; + cond = this.condition.compile(o, LEVEL_COND); + body = this.bodyNode().compile(o, LEVEL_LIST); + alt = this.elseBodyNode() ? this.elseBodyNode().compile(o, LEVEL_LIST) : 'void 0'; + code = "" + cond + " ? " + body + " : " + alt; return o.level >= LEVEL_COND ? "(" + code + ")" : code; }; If.prototype.unfoldSoak = function() { diff --git a/lib/parser.js b/lib/parser.js index 47bbc0dc..a2e53bc8 100755 --- a/lib/parser.js +++ b/lib/parser.js @@ -81,7 +81,7 @@ case 35:this.$ = new yy.Literal($$[$0-1+1-1]); break; case 36:this.$ = new yy.Literal($$[$0-1+1-1]); break; -case 37:this.$ = new yy.Literal($$[$0-1+1-1]); +case 37:this.$ = new yy.Literal($$[$0-1+1-1] === 'undefined' ? 'void 0' : $$[$0-1+1-1]); break; case 38:this.$ = new yy.Assign($$[$0-3+1-1], $$[$0-3+3-1]); break; diff --git a/lib/repl.js b/lib/repl.js index 0f650143..1e57bb53 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -17,7 +17,7 @@ globals: true, fileName: 'repl' }); - if (val !== undefined) { + if (val !== void 0) { console.log(val); } } catch (err) { diff --git a/lib/rewriter.js b/lib/rewriter.js index b40065ff..e19ebcdc 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -63,15 +63,15 @@ return 1; } before = tokens[i - 2], prev = tokens[i - 1], post = tokens[i + 1], after = tokens[i + 2]; - if ((after != null ? after[0] : undefined) === 'INDENT') { + if ((after != null ? after[0] : void 0) === 'INDENT') { tokens.splice(i + 2, 1); - if ((before != null ? before[0] : undefined) === 'OUTDENT' && (post != null ? post[0] : undefined) === 'TERMINATOR') { + if ((before != null ? before[0] : void 0) === 'OUTDENT' && (post != null ? post[0] : void 0) === 'TERMINATOR') { tokens.splice(i - 2, 1); } else { tokens.splice(i, 0, after); } } else if (prev && ((_ref = prev[0]) !== 'TERMINATOR' && _ref !== 'INDENT' && _ref !== 'OUTDENT')) { - if ((post != null ? post[0] : undefined) === 'TERMINATOR' && (after != null ? after[0] : undefined) === 'OUTDENT') { + if ((post != null ? post[0] : void 0) === 'TERMINATOR' && (after != null ? after[0] : void 0) === 'OUTDENT') { tokens.splice.apply(tokens, [i + 2, 0].concat(tokens.splice(i, 2))); if (tokens[i + 2][0] !== 'TERMINATOR') { tokens.splice(i + 2, 0, ['TERMINATOR', '\n', prev[2]]); @@ -92,7 +92,7 @@ break; } } - return i ? this.tokens.splice(0, i) : undefined; + return i ? this.tokens.splice(0, i) : void 0; }; exports.Rewriter.prototype.removeMidExpressionNewlines = function() { return this.scanTokens(function(token, i, tokens) { @@ -147,7 +147,7 @@ } _ref = this.tokens, one = _ref[i + 1], two = _ref[i + 2], three = _ref[i + 3]; tag = token[0]; - return (tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : undefined) === ':' || (one != null ? one[0] : undefined) === '@' && (three != null ? three[0] : undefined) === ':' || (one != null ? one[0] : undefined) === '(') || tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT' && _ref2 !== '('); + return (tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '(') || tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT' && _ref2 !== '('); }; action = function(token, i) { return this.tokens.splice(i, 0, ['}', '}', token[2]]); @@ -162,7 +162,7 @@ start = stack.pop(); return 1; } - if (!(tag === ':' && ((ago2 = this.tag(i - 2)) === ':' || (ago1 = this.tag(i - 1)) === ')' && this.tag(start[1] - 1) === ':' || ((_ref2 = stack[stack.length - 1]) != null ? _ref2[0] : undefined) !== '{'))) { + if (!(tag === ':' && ((ago2 = this.tag(i - 2)) === ':' || (ago1 = this.tag(i - 1)) === ')' && this.tag(start[1] - 1) === ':' || ((_ref2 = stack[stack.length - 1]) != null ? _ref2[0] : void 0) !== '{'))) { return 1; } stack.push(['{']); @@ -200,7 +200,7 @@ if (prev && !prev.spaced && tag === '?') { token.call = true; } - if (!(callObject || (prev != null ? prev.spaced : undefined) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) { + if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) { return 1; } tokens.splice(i, 0, ['CALL_START', '(', token[2]]); @@ -277,7 +277,7 @@ } original = token; this.detectEnd(i + 1, condition, function(token, i) { - return token[0] !== 'INDENT' ? original[0] = 'POST_' + original[0] : undefined; + return token[0] !== 'INDENT' ? original[0] = 'POST_' + original[0] : void 0; }); return 1; }); @@ -363,7 +363,7 @@ }; exports.Rewriter.prototype.tag = function(i) { var _ref; - return (_ref = this.tokens[i]) != null ? _ref[0] : undefined; + return (_ref = this.tokens[i]) != null ? _ref[0] : void 0; }; BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']]; INVERSES = {}; diff --git a/lib/scope.js b/lib/scope.js index 6be6a0e1..f2bb27a8 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -71,10 +71,10 @@ Scope.prototype.check = function(name, options) { var _ref2, immediate; immediate = !!this.type(name); - if (immediate || (options != null ? options.immediate : undefined)) { + if (immediate || (options != null ? options.immediate : void 0)) { return immediate; } - return !!((_ref2 = this.parent) != null ? _ref2.check(name) : undefined); + return !!((_ref2 = this.parent) != null ? _ref2.check(name) : void 0); }; Scope.prototype.temporary = function(name, index) { return name.length > 1 ? '_' + name + (index > 1 ? index : '') : '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a'); diff --git a/src/grammar.coffee b/src/grammar.coffee index 2ca9d16a..574d94bb 100644 --- a/src/grammar.coffee +++ b/src/grammar.coffee @@ -131,7 +131,8 @@ grammar = o 'AlphaNumeric' o 'JS', -> new Literal $1 o 'REGEX', -> new Literal $1 - o 'BOOL', -> new Literal $1 + o 'BOOL', -> + new Literal if $1 is 'undefined' then 'void 0' else $1 ] # Assignment of a variable, property, or index to a value. @@ -486,24 +487,20 @@ grammar = # if-related rules are broken up along these lines in order to avoid # ambiguity. IfBlock: [ - o 'IF Expression Block', -> new If $2, $3 - o 'UNLESS Expression Block', -> new If $2, $3, invert: true - o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5 - o 'IfBlock ELSE Block', -> $1.addElse $3 + o 'IF Expression Block', -> new If $2, $3 + o 'UNLESS Expression Block', -> new If $2, $3, invert: true + o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5 + o 'IfBlock ELSE Block', -> $1.addElse $3 ] # The full complement of *if* expressions, including postfix one-liner # *if* and *unless*. If: [ o 'IfBlock' - o 'Statement POST_IF Expression', -> - new If $3, Expressions.wrap([$1]), statement: true - o 'Expression POST_IF Expression', -> - new If $3, Expressions.wrap([$1]), statement: true - o 'Statement POST_UNLESS Expression', -> - new If $3, Expressions.wrap([$1]), statement: true, invert: true - o 'Expression POST_UNLESS Expression', -> - new If $3, Expressions.wrap([$1]), statement: true, invert: true + o 'Statement POST_IF Expression', -> new If $3, Expressions.wrap([$1]), statement: true + o 'Expression POST_IF Expression', -> new If $3, Expressions.wrap([$1]), statement: true + o 'Statement POST_UNLESS Expression', -> new If $3, Expressions.wrap([$1]), statement: true, invert: true + o 'Expression POST_UNLESS Expression', -> new If $3, Expressions.wrap([$1]), statement: true, invert: true ] # Arithmetic and logical operators, working on one or more operands. diff --git a/src/lexer.coffee b/src/lexer.coffee index 2c75e04a..ab7a6e05 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -116,10 +116,10 @@ exports.Lexer = class Lexer @identifierError id unless forcedIdentifier id = COFFEE_ALIASES[id] if COFFEE_ALIASES.hasOwnProperty id - tag = if id is '!' then 'UNARY' - else if id in ['==', '!='] then 'COMPARE' - else if id in ['&&', '||'] then 'LOGIC' - else if id in ['true', 'false', 'null'] then 'BOOL' + tag = if id is '!' then 'UNARY' + else if id in ['==', '!='] then 'COMPARE' + else if id in ['&&', '||'] then 'LOGIC' + else if id in ['true', 'false', 'null', 'undefined'] then 'BOOL' else tag @token tag, id @token ':', ':' if colon @@ -497,7 +497,7 @@ JS_KEYWORDS = [ ] # CoffeeScript-only keywords. -COFFEE_KEYWORDS = ['then', 'unless', 'until', 'loop', 'of', 'by', 'when'] +COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'] COFFEE_KEYWORDS.push op for all op of COFFEE_ALIASES = and : '&&' or : '||' @@ -599,7 +599,7 @@ MATH = ['*', '/', '%'] RELATION = ['IN', 'OF', 'INSTANCEOF'] # Boolean tokens. -BOOL = ['TRUE', 'FALSE', 'NULL'] +BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED'] # Tokens which a regular expression will never immediately follow, but which # a division operator might. diff --git a/src/nodes.coffee b/src/nodes.coffee index 3a625bb6..0ded8aa0 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1453,9 +1453,10 @@ exports.If = class If extends Base # Compile the If as a conditional operator. compileExpression: (o) -> - code = @condition .compile(o, LEVEL_COND) + ' ? ' + - @bodyNode().compile(o, LEVEL_LIST) + ' : ' + - @elseBodyNode()?.compile o, LEVEL_LIST + cond = @condition.compile o, LEVEL_COND + body = @bodyNode().compile o, LEVEL_LIST + alt = if @elseBodyNode() then @elseBodyNode().compile(o, LEVEL_LIST) else 'void 0' + code = "#{cond} ? #{body} : #{alt}" if o.level >= LEVEL_COND then "(#{code})" else code unfoldSoak: -> @soak and this