mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-14 01:07:55 -05:00
Using an experimental version of new optional-brace object literals.
This commit is contained in:
@@ -236,6 +236,8 @@
|
||||
return [$1];
|
||||
}), o("ClassBody TERMINATOR ClassAssign", function() {
|
||||
return $1.concat($3);
|
||||
}), o("{ ClassBody }", function() {
|
||||
return $2;
|
||||
})
|
||||
],
|
||||
Call: [
|
||||
|
||||
244
lib/parser.js
244
lib/parser.js
File diff suppressed because one or more lines are too long
@@ -19,6 +19,7 @@
|
||||
this.removeMidExpressionNewlines();
|
||||
this.closeOpenCallsAndIndexes();
|
||||
this.addImplicitIndentation();
|
||||
this.addImplicitBraces();
|
||||
this.addImplicitParentheses();
|
||||
this.ensureBalance(BALANCED_PAIRS);
|
||||
this.rewriteClosingParens();
|
||||
@@ -115,6 +116,56 @@
|
||||
}
|
||||
})(this));
|
||||
};
|
||||
Rewriter.prototype.addImplicitBraces = function() {
|
||||
var closeBrackets, stack;
|
||||
stack = [0];
|
||||
closeBrackets = (function(_this) {
|
||||
return function(i) {
|
||||
var _c, len, size, tmp;
|
||||
len = stack.length - 1;
|
||||
_c = stack[len];
|
||||
for (tmp = 0; (0 <= _c ? tmp < _c : tmp > _c); (0 <= _c ? tmp += 1 : tmp -= 1)) {
|
||||
_this.tokens.splice(i, 0, ['}', '}', _this.tokens[i][2]]);
|
||||
}
|
||||
size = stack[len] + 1;
|
||||
stack[len] = 0;
|
||||
return size;
|
||||
}
|
||||
})(this);
|
||||
return this.scanTokens((function(_this) {
|
||||
return function(prev, token, post, i) {
|
||||
var after, before, idx, len, open, size, tag;
|
||||
tag = token[0];
|
||||
len = stack.length - 1;
|
||||
before = _this.tokens[i - 2];
|
||||
after = _this.tokens[i + 2];
|
||||
open = stack[len] > 0;
|
||||
if (include(EXPRESSION_START, tag)) {
|
||||
stack.push(tag === '{' ? 1 : 0);
|
||||
if (tag === '{' && post && post[0] === 'INDENT') {
|
||||
return 2;
|
||||
}
|
||||
} else if (include(EXPRESSION_END, tag)) {
|
||||
if (tag === 'OUTDENT' && post && post[0] === '}') {
|
||||
return 1;
|
||||
}
|
||||
stack[len - 1] += stack.pop();
|
||||
if (tag === '}') {
|
||||
stack[len - 1] -= 1;
|
||||
}
|
||||
} else if (tag === ':' && !open) {
|
||||
idx = before && before[0] === '@' ? i - 2 : i - 1;
|
||||
_this.tokens.splice(idx, 0, ['{', '{', token[2]]);
|
||||
stack[stack.length - 1] += 1;
|
||||
return 2;
|
||||
} else if (tag === 'TERMINATOR' && !((after && after[0] === ':') || (post && post[0] === '@' && _this.tokens[i + 3] && _this.tokens[i + 3][0] === ':'))) {
|
||||
size = closeBrackets(i);
|
||||
return size;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
})(this));
|
||||
};
|
||||
Rewriter.prototype.addImplicitParentheses = function() {
|
||||
var closeCalls, stack;
|
||||
stack = [0];
|
||||
|
||||
@@ -48,7 +48,7 @@ exports.run = ->
|
||||
path.exists 'Cakefile', (exists) ->
|
||||
throw new Error("Cakefile not found in ${process.cwd()}") unless exists
|
||||
args = process.argv[2...process.argv.length]
|
||||
CoffeeScript.run fs.readFileSync('Cakefile').toString(), {source: 'Cakefile'}
|
||||
CoffeeScript.run fs.readFileSync('Cakefile').toString(), source: 'Cakefile'
|
||||
oparse = new optparse.OptionParser switches
|
||||
return printTasks() unless args.length
|
||||
options = oparse.parse(args)
|
||||
|
||||
@@ -287,6 +287,7 @@ grammar = {
|
||||
o "", -> []
|
||||
o "ClassAssign", -> [$1]
|
||||
o "ClassBody TERMINATOR ClassAssign", -> $1.concat $3
|
||||
o "{ ClassBody }", -> $2
|
||||
]
|
||||
|
||||
# The three flavors of function call: normal, object instantiation with `new`,
|
||||
|
||||
@@ -131,7 +131,7 @@ exports.Lexer = class Lexer
|
||||
return false unless match = @chunk.match(HEREDOC)
|
||||
quote = match[1].substr 0, 1
|
||||
doc = @sanitizeHeredoc match[2] or match[4], {quote}
|
||||
@interpolateString "$quote$doc$quote", {heredoc: yes}
|
||||
@interpolateString "$quote$doc$quote", heredoc: yes
|
||||
@line += count match[1], "\n"
|
||||
@i += match[1].length
|
||||
true
|
||||
@@ -142,7 +142,7 @@ exports.Lexer = class Lexer
|
||||
@line += count match[1], "\n"
|
||||
@i += match[1].length
|
||||
if match[2]
|
||||
comment = @sanitizeHeredoc match[2], {herecomment: true}
|
||||
comment = @sanitizeHeredoc match[2], herecomment: true
|
||||
@token 'HERECOMMENT', comment.split MULTILINER
|
||||
@token 'TERMINATOR', '\n'
|
||||
true
|
||||
@@ -169,7 +169,7 @@ exports.Lexer = class Lexer
|
||||
str = regex.substring(1).split('/')[0]
|
||||
str = str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
|
||||
@tokens = @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
|
||||
@interpolateString "\"$str\"", {escapeQuotes: yes}
|
||||
@interpolateString "\"$str\"", escapeQuotes: yes
|
||||
@tokens.splice @tokens.length, 0, [',', ','], ['STRING', "\"$flags\""] if flags
|
||||
@tokens.splice @tokens.length, 0, [')', ')'], [')', ')']
|
||||
else
|
||||
@@ -405,7 +405,7 @@ exports.Lexer = class Lexer
|
||||
inner = expr.substring(2, expr.length - 1)
|
||||
if inner.length
|
||||
inner = inner.replace new RegExp('\\\\' + quote, 'g'), quote if options.heredoc
|
||||
nested = lexer.tokenize "($inner)", {line: @line}
|
||||
nested = lexer.tokenize "($inner)", line: @line
|
||||
(tok[0] = ')') for tok, idx in nested when tok[0] is 'CALL_END'
|
||||
nested.pop()
|
||||
tokens.push ['TOKENS', nested]
|
||||
|
||||
@@ -224,7 +224,7 @@ exports.Expressions = class Expressions extends BaseNode
|
||||
# statement, ask the statement to do so.
|
||||
compileExpression: (node, o) ->
|
||||
@tab = o.indent
|
||||
compiledNode = node.compile merge o, {top: true}
|
||||
compiledNode = node.compile merge o, top: true
|
||||
if node.isStatement() then compiledNode else "${@idt()}$compiledNode;"
|
||||
|
||||
# Wrap up the given nodes as an **Expressions**, unless it already happens
|
||||
@@ -542,9 +542,9 @@ exports.RangeNode = class RangeNode extends BaseNode
|
||||
# Compiles the range's source variables -- where it starts and where it ends.
|
||||
# But only if they need to be cached to avoid double evaluation.
|
||||
compileVariables: (o) ->
|
||||
o = merge(o, {top: true})
|
||||
[@from, @fromVar] = @from.compileReference o, {precompile: yes}
|
||||
[@to, @toVar] = @to.compileReference o, {precompile: yes}
|
||||
o = merge(o, top: true)
|
||||
[@from, @fromVar] = @from.compileReference o, precompile: yes
|
||||
[@to, @toVar] = @to.compileReference o, precompile: yes
|
||||
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
|
||||
parts = []
|
||||
parts.push @from if @from isnt @fromVar
|
||||
@@ -579,7 +579,7 @@ exports.RangeNode = class RangeNode extends BaseNode
|
||||
# When used as a value, expand the range into the equivalent array.
|
||||
compileArray: (o) ->
|
||||
idt = @idt 1
|
||||
vars = @compileVariables(merge(o, {indent: idt}))
|
||||
vars = @compileVariables merge o, indent: idt
|
||||
result = o.scope.freeVariable()
|
||||
i = o.scope.freeVariable()
|
||||
pre = "\n${idt}${result} = []; ${vars}"
|
||||
@@ -832,7 +832,7 @@ exports.AssignNode = class AssignNode extends BaseNode
|
||||
# Compile the assignment from an array splice literal, using JavaScript's
|
||||
# `Array#splice` method.
|
||||
compileSplice: (o) ->
|
||||
name = @variable.compile merge o, {onlyFirst: true}
|
||||
name = @variable.compile merge o, onlyFirst: true
|
||||
l = @variable.properties.length
|
||||
range = @variable.properties[l - 1].range
|
||||
plus = if range.exclusive then '' else ' + 1'
|
||||
@@ -1006,7 +1006,7 @@ exports.WhileNode = class WhileNode extends BaseNode
|
||||
pre = "$set${@tab}while ($cond)"
|
||||
@body = Expressions.wrap([new IfNode(@guard, @body)]) if @guard
|
||||
if @returns
|
||||
post = '\n' + new ReturnNode(literal(rvar)).compile(merge(o, {indent: @idt()}))
|
||||
post = '\n' + new ReturnNode(literal(rvar)).compile(merge(o, indent: @idt()))
|
||||
else
|
||||
post = ''
|
||||
"$pre {\n${ @body.compile(o) }\n$@tab}$post"
|
||||
@@ -1107,7 +1107,7 @@ exports.InNode = class InNode extends BaseNode
|
||||
@array instanceof ValueNode and @array.isArray()
|
||||
|
||||
compileNode: (o) ->
|
||||
[@obj1, @obj2] = @object.compileReference o, {precompile: yes}
|
||||
[@obj1, @obj2] = @object.compileReference o, precompile: yes
|
||||
if @isArray() then @compileOrTest(o) else @compileLoopTest(o)
|
||||
|
||||
compileOrTest: (o) ->
|
||||
@@ -1116,7 +1116,7 @@ exports.InNode = class InNode extends BaseNode
|
||||
"(${tests.join(' || ')})"
|
||||
|
||||
compileLoopTest: (o) ->
|
||||
[@arr1, @arr2] = @array.compileReference o, {precompile: yes}
|
||||
[@arr1, @arr2] = @array.compileReference o, precompile: yes
|
||||
[i, l] = [o.scope.freeVariable(), o.scope.freeVariable()]
|
||||
prefix = if @obj1 isnt @obj2 then @obj1 + '; ' else ''
|
||||
"!!(function(){ ${prefix}for (var $i=0, $l=${@arr1}.length; $i<$l; $i++) if (${@arr2}[$i] === $@obj2) return true; }).call(this)"
|
||||
@@ -1289,7 +1289,7 @@ exports.ForNode = class ForNode extends BaseNode
|
||||
body = Expressions.wrap([@body])
|
||||
if range
|
||||
sourcePart = source.compileVariables(o)
|
||||
forPart = source.compile merge o, {index: ivar, step: @step}
|
||||
forPart = source.compile merge o, index: ivar, step: @step
|
||||
else
|
||||
svar = scope.freeVariable()
|
||||
sourcePart = "$svar = ${ @source.compile(o) };"
|
||||
|
||||
@@ -23,7 +23,7 @@ exports.OptionParser = class OptionParser
|
||||
# many option parsers that allow you to attach callback actions for every
|
||||
# flag. Instead, you're responsible for interpreting the options object.
|
||||
parse: (args) ->
|
||||
options = {arguments: []}
|
||||
options = arguments: []
|
||||
args = normalizeArguments args
|
||||
for arg, i in args
|
||||
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
|
||||
|
||||
@@ -13,16 +13,14 @@ readline = require 'readline'
|
||||
stdio = process.openStdin()
|
||||
|
||||
# Quick alias for quitting the REPL.
|
||||
helpers.extend global, {
|
||||
quit: -> process.exit(0)
|
||||
}
|
||||
helpers.extend global, quit: -> process.exit(0)
|
||||
|
||||
# The main REPL function. **run** is called every time a line of code is entered.
|
||||
# Attempt to evaluate the command. If there's an exception, print it out instead
|
||||
# of exiting.
|
||||
run = (buffer) ->
|
||||
try
|
||||
val = CoffeeScript.run buffer.toString(), {noWrap: true, globals: true, source: 'repl'}
|
||||
val = CoffeeScript.run buffer.toString(), noWrap: true, globals: true, source: 'repl'
|
||||
puts inspect val if val isnt undefined
|
||||
catch err
|
||||
puts err.stack or err.toString()
|
||||
|
||||
@@ -31,6 +31,7 @@ exports.Rewriter = class Rewriter
|
||||
@removeMidExpressionNewlines()
|
||||
@closeOpenCallsAndIndexes()
|
||||
@addImplicitIndentation()
|
||||
@addImplicitBraces()
|
||||
@addImplicitParentheses()
|
||||
@ensureBalance BALANCED_PAIRS
|
||||
@rewriteClosingParens()
|
||||
@@ -110,6 +111,41 @@ exports.Rewriter = class Rewriter
|
||||
brackets[brackets.length - 1] -= 1
|
||||
return 1
|
||||
|
||||
# Object literals may be written with implicit braces, for simple cases.
|
||||
# Insert the missing braces here, so that the parser doesn't have to.
|
||||
addImplicitBraces: ->
|
||||
stack = [0]
|
||||
closeBrackets = (i) =>
|
||||
len = stack.length - 1
|
||||
for tmp in [0...stack[len]]
|
||||
@tokens.splice(i, 0, ['}', '}', @tokens[i][2]])
|
||||
size = stack[len] + 1
|
||||
stack[len] = 0
|
||||
size
|
||||
@scanTokens (prev, token, post, i) =>
|
||||
tag = token[0]
|
||||
len = stack.length - 1
|
||||
before = @tokens[i - 2]
|
||||
after = @tokens[i + 2]
|
||||
open = stack[len] > 0
|
||||
if include EXPRESSION_START, tag
|
||||
stack.push(if tag is '{' then 1 else 0)
|
||||
return 2 if tag is '{' and post and post[0] is 'INDENT'
|
||||
else if include EXPRESSION_END, tag
|
||||
return 1 if tag is 'OUTDENT' and post and post[0] is '}'
|
||||
stack[len - 1] += stack.pop()
|
||||
stack[len - 1] -= 1 if tag is '}'
|
||||
else if tag is ':' and not open
|
||||
idx = if before and before[0] is '@' then i - 2 else i - 1
|
||||
@tokens.splice idx, 0, ['{', '{', token[2]]
|
||||
stack[stack.length - 1] += 1
|
||||
return 2
|
||||
else if tag is 'TERMINATOR' and
|
||||
not ((after and after[0] is ':') or (post and post[0] is '@' and @tokens[i + 3] and @tokens[i + 3][0] is ':'))
|
||||
size = closeBrackets(i)
|
||||
return size
|
||||
return 1
|
||||
|
||||
# Methods may be optionally called without parentheses, for simple cases.
|
||||
# Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
# deal with them.
|
||||
|
||||
@@ -61,7 +61,7 @@ exports.Scope = class Scope
|
||||
# Ensure that an assignment is made at the top of this scope
|
||||
# (or at the top-level scope, if requested).
|
||||
assign: (name, value) ->
|
||||
@variables[name] = {value: value, assigned: true}
|
||||
@variables[name] = value: value, assigned: true
|
||||
|
||||
# Does this scope reference any variables that need to be declared in the
|
||||
# given function body?
|
||||
|
||||
@@ -53,11 +53,6 @@ trailingComma = [
|
||||
trailingComma = {k1: "v1", k2: 4, k3: (-> true),}
|
||||
ok trailingComma.k3() and (trailingComma.k2 is 4) and (trailingComma.k1 is "v1")
|
||||
|
||||
multiline = {a: 15,
|
||||
b: 26}
|
||||
|
||||
ok multiline.b is 26
|
||||
|
||||
|
||||
money$ = 'dollars'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user