first, totally broken branch of significant whitespace -- it can handle examples/whitespace.cs though

This commit is contained in:
Jeremy Ashkenas
2009-12-24 13:48:46 -08:00
parent 39ceca477d
commit aafd3cd6ea
4 changed files with 72 additions and 18 deletions

12
examples/whitespace.cs Normal file
View File

@@ -0,0 +1,12 @@
# square: x => x * x
square: x =>
x * x
elements.each(el =>
el.click(event =>
el.show()
)
)
a: 5

8
examples/whitespace.js Normal file
View File

@@ -0,0 +1,8 @@
(function(){
// square: x => x * x
var square = function(x) {
return x * x;
};
var a = 5;
})();

View File

@@ -15,6 +15,7 @@ token SUPER
token NEWLINE token NEWLINE
token COMMENT token COMMENT
token JS token JS
token INDENT OUTDENT
# Declare order of operations. # Declare order of operations.
prechigh prechigh
@@ -32,7 +33,8 @@ prechigh
right THROW FOR IN WHILE NEW right THROW FOR IN WHILE NEW
left UNLESS IF ELSE left UNLESS IF ELSE
left ":" '||:' '&&:' left ":" '||:' '&&:'
right RETURN right RETURN INDENT
left OUTDENT
preclow preclow
# We expect 4 shift/reduce errors for optional syntax. # We expect 4 shift/reduce errors for optional syntax.
@@ -84,6 +86,11 @@ rule
| Comment | Comment
; ;
Block:
Expression Terminator { result = Expressions.new([val[0]]) }
| INDENT Expressions OUTDENT { result = val[1] }
;
# All tokens that can terminate an expression. # All tokens that can terminate an expression.
Terminator: Terminator:
"\n" "\n"
@@ -191,14 +198,14 @@ rule
# Function definition. # Function definition.
Code: Code:
ParamList "=>" CodeBody "." { result = CodeNode.new(val[0], val[2]) } ParamList "=>" CodeBody { result = CodeNode.new(val[0], val[2]) }
| "=>" CodeBody "." { result = CodeNode.new([], val[1]) } | "=>" CodeBody { result = CodeNode.new([], val[1]) }
; ;
# The body of a function. # The body of a function.
CodeBody: CodeBody:
/* nothing */ { result = Expressions.new([]) } /* nothing */ { result = Expressions.new([]) }
| Expressions { result = val[0] } | Block { result = val[0] }
; ;
# The parameters to a function definition. # The parameters to a function definition.
@@ -279,15 +286,15 @@ rule
# Try/catch/finally exception handling blocks. # Try/catch/finally exception handling blocks.
Try: Try:
TRY Expressions Catch "." { result = TryNode.new(val[1], val[2][0], val[2][1]) } TRY Expressions Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
| TRY Expressions Catch | TRY Expressions Catch
FINALLY Expressions "." { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) } FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
; ;
# A catch clause. # A catch clause.
Catch: Catch:
/* nothing */ { result = [nil, nil] } /* nothing */ { result = [nil, nil] }
| CATCH IDENTIFIER Expressions { result = [val[1], val[2]] } | CATCH IDENTIFIER Block { result = [val[1], val[2]] }
; ;
# Throw an exception. # Throw an exception.
@@ -302,32 +309,31 @@ rule
# The while loop. (there is no do..while). # The while loop. (there is no do..while).
While: While:
WHILE Expression Then WHILE Expression Then Block { result = WhileNode.new(val[1], val[3]) }
Expressions "." { result = WhileNode.new(val[1], val[3]) }
; ;
# Array comprehensions, including guard and current index. # Array comprehensions, including guard and current index.
For: For:
Expression FOR IDENTIFIER Expression FOR IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[4], val[2], nil) } IN PureExpression { result = ForNode.new(val[0], val[4], val[2], nil) }
| Expression FOR | Expression FOR
IDENTIFIER "," IDENTIFIER IDENTIFIER "," IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[6], val[2], nil, val[4]) } IN PureExpression { result = ForNode.new(val[0], val[6], val[2], nil, val[4]) }
| Expression FOR IDENTIFIER | Expression FOR IDENTIFIER
IN PureExpression IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[4], val[2], val[6]) } IF Expression { result = ForNode.new(val[0], val[4], val[2], val[6]) }
| Expression FOR | Expression FOR
IDENTIFIER "," IDENTIFIER IDENTIFIER "," IDENTIFIER
IN PureExpression IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[6], val[2], val[8], val[4]) } IF Expression { result = ForNode.new(val[0], val[6], val[2], val[8], val[4]) }
; ;
# Switch/When blocks. # Switch/When blocks.
Switch: Switch:
SWITCH Expression Then SWITCH Expression Then
Whens "." { result = val[3].rewrite_condition(val[1]) } Whens { result = val[3].rewrite_condition(val[1]) }
| SWITCH Expression Then | SWITCH Expression Then
Whens ELSE Expressions "." { result = val[3].rewrite_condition(val[1]).add_else(val[5]) } Whens ELSE Block { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
; ;
# The inner list of whens. # The inner list of whens.
@@ -338,7 +344,7 @@ rule
# An individual when. # An individual when.
When: When:
WHEN Expression Then Expressions { result = IfNode.new(val[1], val[3]) } WHEN Expression Then Block { result = IfNode.new(val[1], val[3]) }
; ;
# All of the following nutso if-else destructuring is to make the # All of the following nutso if-else destructuring is to make the
@@ -358,8 +364,8 @@ rule
# Terminating else bodies are strictly optional. # Terminating else bodies are strictly optional.
ElseBody ElseBody
"." { result = nil } /* nothing */ { result = nil }
| ELSE Expressions "." { result = val[1] } | ELSE Block { result = val[1] }
; ;
# All the alternatives for ending an if-else block. # All the alternatives for ending an if-else block.

View File

@@ -28,6 +28,7 @@ module CoffeeScript
COMMENT = /\A((#[^\n]*\s*)+)/m COMMENT = /\A((#[^\n]*\s*)+)/m
CODE = /\A(=>)/ CODE = /\A(=>)/
REGEX = /\A(\/(.*?)[^\\]\/[imgy]{0,4})/ REGEX = /\A(\/(.*?)[^\\]\/[imgy]{0,4})/
INDENT = /\A\n( *)/
# Token cleaning regexes. # Token cleaning regexes.
JS_CLEANER = /(\A`|`\Z)/ JS_CLEANER = /(\A`|`\Z)/
@@ -45,6 +46,8 @@ module CoffeeScript
@code = code.chomp # Cleanup code by remove extra line breaks @code = code.chomp # Cleanup code by remove extra line breaks
@i = 0 # Current character position we're parsing @i = 0 # Current character position we're parsing
@line = 1 # The current line. @line = 1 # The current line.
@indent = 0 # The current indent level.
@indents = [] # The stack of all indent levels we are currently within.
@tokens = [] # Collection of all parsed tokens in the form [:TOKEN_TYPE, value] @tokens = [] # Collection of all parsed tokens in the form [:TOKEN_TYPE, value]
while @i < @code.length while @i < @code.length
@chunk = @code[@i..-1] @chunk = @code[@i..-1]
@@ -62,6 +65,7 @@ module CoffeeScript
return if js_token return if js_token
return if regex_token return if regex_token
return if comment_token return if comment_token
return if indent_token
return if whitespace_token return if whitespace_token
return literal_token return literal_token
end end
@@ -118,6 +122,23 @@ module CoffeeScript
@i += comment.length @i += comment.length
end end
def indent_token
return false unless indent = @chunk[INDENT, 1]
size = indent.size
return literal_token if size == @indent
if size > @indent
tag = :INDENT
@indent = size
@indents << @indent
else
tag = :OUTDENT
@indents.pop
@indent = @indents.first || 0
end
@i += (size + 1)
token(tag, size)
end
# Matches and consumes non-meaningful whitespace. # Matches and consumes non-meaningful whitespace.
def whitespace_token def whitespace_token
return false unless whitespace = @chunk[WHITESPACE, 1] return false unless whitespace = @chunk[WHITESPACE, 1]
@@ -182,6 +203,13 @@ module CoffeeScript
@tokens.pop if last_value == "\n" @tokens.pop if last_value == "\n"
end end
# Close up all remaining open blocks.
def close_indentation
while indent = @indents.pop
token(:OUTDENT, @indents.first || 0)
end
end
end end
end end