1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00
jashkenas--coffeescript/lib/coffee_script/grammar.y

399 lines
13 KiB
Text
Raw Normal View History

2009-12-13 17:07:16 -05:00
class Parser
# Declare tokens produced by the lexer
2009-12-15 08:53:21 -05:00
token IF ELSE THEN UNLESS
token NUMBER STRING REGEX
2009-12-13 17:07:16 -05:00
token TRUE FALSE NULL
token IDENTIFIER PROPERTY_ACCESS
token CODE PARAM NEW RETURN
2009-12-14 10:00:31 -05:00
token TRY CATCH FINALLY THROW
2009-12-14 23:11:28 -05:00
token BREAK CONTINUE
token FOR IN WHILE
token SWITCH CASE
2009-12-21 11:41:45 -05:00
token EXTENDS SUPER
2009-12-17 21:21:07 -05:00
token DELETE
token NEWLINE
token COMMENT
2009-12-15 09:11:27 -05:00
token JS
2009-12-13 17:07:16 -05:00
2009-12-16 20:19:52 -05:00
# Declare order of operations.
2009-12-13 17:07:16 -05:00
prechigh
nonassoc UMINUS NOT '!' '~'
2009-12-13 17:07:16 -05:00
left '*' '/' '%'
left '+' '-'
left '<<' '>>' '>>>'
left '&' '|' '^'
2009-12-13 17:07:16 -05:00
left '<=' '<' '>' '>='
right '==' '!=' IS AINT
left '&&' '||' AND OR
right '-=' '+=' '/=' '*=' '||=' '&&='
2009-12-17 21:21:07 -05:00
right DELETE
left "."
2009-12-21 11:41:45 -05:00
right THROW FOR IN WHILE NEW
left UNLESS IF ELSE EXTENDS
left ":"
right RETURN
2009-12-13 17:07:16 -05:00
preclow
# We expect 4 shift/reduce errors for optional syntax.
# There used to be 252 -- greatly improved.
expect 4
2009-12-13 17:07:16 -05:00
rule
# All parsing will end in this rule, being the trunk of the AST.
Root:
2009-12-17 22:58:40 -05:00
/* nothing */ { result = Expressions.new([]) }
| Terminator { result = Expressions.new([]) }
| Expressions { result = val[0] }
2009-12-13 17:07:16 -05:00
;
# Any list of expressions or method body, seperated by line breaks or semis.
2009-12-13 17:07:16 -05:00
Expressions:
2009-12-17 22:58:40 -05:00
Expression { result = Expressions.new(val) }
| Expressions Terminator Expression { result = val[0] << val[2] }
2009-12-13 23:25:31 -05:00
| Expressions Terminator { result = val[0] }
| Terminator Expressions { result = val[1] }
2009-12-13 17:07:16 -05:00
;
# All types of expressions in our language.
2009-12-13 17:07:16 -05:00
Expression:
PureExpression
| Statement
;
# The parts that are natural JavaScript expressions.
PureExpression:
2009-12-13 17:07:16 -05:00
Literal
2009-12-14 10:00:31 -05:00
| Value
2009-12-13 17:07:16 -05:00
| Call
| Code
| Operation
2009-12-21 11:41:45 -05:00
| Extend
;
# We have to take extra care to convert these statements into expressions.
Statement:
Assign
2009-12-13 17:07:16 -05:00
| If
2009-12-14 10:00:31 -05:00
| Try
| Throw
2009-12-13 18:37:29 -05:00
| Return
| While
| For
2009-12-15 22:30:27 -05:00
| Switch
| Comment
2009-12-13 17:07:16 -05:00
;
# All tokens that can terminate an expression.
2009-12-13 17:07:16 -05:00
Terminator:
"\n"
| ";"
;
# All tokens that can serve to begin the second block of a multi-part expression.
Then:
THEN
| Terminator
;
# All hard-coded values.
2009-12-13 17:07:16 -05:00
Literal:
NUMBER { result = LiteralNode.new(val[0]) }
| STRING { result = LiteralNode.new(val[0]) }
2009-12-15 09:11:27 -05:00
| JS { result = LiteralNode.new(val[0]) }
2009-12-13 18:37:29 -05:00
| REGEX { result = LiteralNode.new(val[0]) }
| TRUE { result = LiteralNode.new(true) }
| FALSE { result = LiteralNode.new(false) }
| NULL { result = LiteralNode.new(nil) }
2009-12-14 23:11:28 -05:00
| BREAK { result = LiteralNode.new(val[0]) }
| CONTINUE { result = LiteralNode.new(val[0]) }
2009-12-13 17:07:16 -05:00
;
# Assignment to a variable.
2009-12-13 17:07:16 -05:00
Assign:
Value ":" Expression { result = AssignNode.new(val[0], val[2]) }
2009-12-13 17:07:16 -05:00
;
# Assignment within an object literal.
AssignObj:
IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
| Comment { result = val[0] }
2009-12-13 17:07:16 -05:00
;
# A return statement.
2009-12-13 18:37:29 -05:00
Return:
RETURN Expression { result = ReturnNode.new(val[1]) }
;
# A comment.
Comment:
COMMENT { result = CommentNode.new(val[0]) }
;
2009-12-13 17:07:16 -05:00
# Arithmetic and logical operators
# For Ruby's Operator precedence, see:
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
Operation:
'!' Expression { result = OpNode.new(val[0], val[1]) }
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
| NOT Expression { result = OpNode.new(val[0], val[1]) }
| '~' Expression { result = OpNode.new(val[0], val[1]) }
2009-12-13 17:07:16 -05:00
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 17:07:16 -05:00
| Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 17:07:16 -05:00
| Expression '<<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '>>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '|' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '^' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 17:07:16 -05:00
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression AINT Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 17:07:16 -05:00
| Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 17:07:16 -05:00
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-13 20:29:44 -05:00
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
2009-12-17 21:21:07 -05:00
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
2009-12-13 17:07:16 -05:00
;
# Function definition.
2009-12-13 17:07:16 -05:00
Code:
2009-12-17 09:07:42 -05:00
ParamList "=>" CodeBody "." { result = CodeNode.new(val[0], val[2]) }
| "=>" CodeBody "." { result = CodeNode.new([], val[1]) }
2009-12-13 17:07:16 -05:00
;
# The body of a function.
2009-12-17 09:07:42 -05:00
CodeBody:
2009-12-17 22:58:40 -05:00
/* nothing */ { result = Expressions.new([]) }
2009-12-17 09:07:42 -05:00
| Expressions { result = val[0] }
;
# The parameters to a function definition.
2009-12-13 17:07:16 -05:00
ParamList:
2009-12-13 23:59:12 -05:00
PARAM { result = val }
2009-12-13 20:29:44 -05:00
| ParamList "," PARAM { result = val[0] << val[2] }
2009-12-13 17:07:16 -05:00
;
# Expressions that can be treated as values.
2009-12-14 10:00:31 -05:00
Value:
IDENTIFIER { result = ValueNode.new(val[0]) }
| Array { result = ValueNode.new(val[0]) }
| Object { result = ValueNode.new(val[0]) }
| Parenthetical { result = ValueNode.new(val[0]) }
2009-12-14 10:00:31 -05:00
| Value Accessor { result = val[0] << val[1] }
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
2009-12-13 20:29:44 -05:00
;
# Accessing into an object or array, through dot or index notation.
2009-12-13 20:29:44 -05:00
Accessor:
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
| Index { result = val[0] }
2009-12-16 21:43:26 -05:00
| Slice { result = val[0] }
2009-12-13 20:29:44 -05:00
;
# Indexing into an object or array.
2009-12-13 20:29:44 -05:00
Index:
2009-12-15 11:36:24 -05:00
"[" Expression "]" { result = IndexNode.new(val[1]) }
2009-12-13 17:07:16 -05:00
;
# Array slice literal.
2009-12-16 21:43:26 -05:00
Slice:
"[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) }
;
# An object literal.
2009-12-13 17:07:16 -05:00
Object:
"{" AssignList "}" { result = ObjectNode.new(val[1]) }
;
# Assignment within an object literal (comma or newline separated).
2009-12-13 17:07:16 -05:00
AssignList:
/* nothing */ { result = []}
| AssignObj { result = val }
| AssignList "," AssignObj { result = val[0] << val[2] }
| AssignList Terminator AssignObj { result = val[0] << val[2] }
;
# All flavors of function call (instantiation, super, and regular).
2009-12-13 17:07:16 -05:00
Call:
2009-12-13 23:59:12 -05:00
Invocation { result = val[0] }
2009-12-14 10:00:31 -05:00
| NEW Invocation { result = val[1].new_instance }
2009-12-17 09:07:42 -05:00
| Super { result = val[0] }
2009-12-13 23:45:18 -05:00
;
# A generic function invocation.
2009-12-13 23:45:18 -05:00
Invocation:
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
2009-12-13 17:07:16 -05:00
;
# Calling super.
2009-12-17 09:07:42 -05:00
Super:
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
;
2009-12-21 11:41:45 -05:00
# Extending a class.
Extend:
IDENTIFIER EXTENDS Expression { result = ExtendNode.new(val[0], val[2]) }
;
# The array literal.
2009-12-13 17:07:16 -05:00
Array:
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
2009-12-13 17:07:16 -05:00
;
# A list of arguments to a method call, or as the contents of an array.
2009-12-13 17:07:16 -05:00
ArgList:
/* nothing */ { result = [] }
| Expression { result = val }
| ArgList "," Expression { result = val[0] << val[2] }
2009-12-13 20:29:44 -05:00
| ArgList Terminator Expression { result = val[0] << val[2] }
2009-12-13 17:07:16 -05:00
;
2009-12-17 22:22:35 -05:00
# Try/catch/finally exception handling blocks.
2009-12-14 10:00:31 -05:00
Try:
TRY Expressions CATCH IDENTIFIER
Expressions "." { result = TryNode.new(val[1], val[3], val[4]) }
| TRY Expressions FINALLY
Expressions "." { result = TryNode.new(val[1], nil, nil, val[3]) }
| TRY Expressions CATCH IDENTIFIER
Expressions
FINALLY Expressions "." { result = TryNode.new(val[1], val[3], val[4], val[6]) }
;
2009-12-17 22:22:35 -05:00
# Throw an exception.
Throw:
THROW Expression { result = ThrowNode.new(val[1]) }
2009-12-14 10:00:31 -05:00
;
2009-12-17 22:22:35 -05:00
# Parenthetical expressions.
2009-12-13 23:25:00 -05:00
Parenthetical:
"(" Expressions ")" { result = ParentheticalNode.new(val[1]) }
2009-12-13 23:25:00 -05:00
;
2009-12-17 22:22:35 -05:00
# The while loop. (there is no do..while).
While:
WHILE Expression Then
Expressions "." { result = WhileNode.new(val[1], val[3]) }
;
2009-12-17 22:22:35 -05:00
# Array comprehensions, including guard and current index.
For:
Expression FOR IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[4], val[2], nil) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN PureExpression "." { result = ForNode.new(val[0], val[6], val[2], nil, val[4]) }
| Expression FOR IDENTIFIER
IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[4], val[2], val[6]) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN PureExpression
IF Expression "." { result = ForNode.new(val[0], val[6], val[2], val[8], val[4]) }
;
2009-12-17 22:22:35 -05:00
# Switch/Case blocks.
2009-12-15 22:30:27 -05:00
Switch:
SWITCH Expression Then
Cases "." { result = val[3].rewrite_condition(val[1]) }
| SWITCH Expression Then
2009-12-17 22:58:40 -05:00
Cases ELSE Expressions "." { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
2009-12-15 22:30:27 -05:00
;
2009-12-17 22:22:35 -05:00
# The inner list of cases.
2009-12-15 22:30:27 -05:00
Cases:
Case { result = val[0] }
| Cases Case { result = val[0] << val[1] }
;
2009-12-17 22:22:35 -05:00
# An individual case.
2009-12-15 22:30:27 -05:00
Case:
CASE Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
;
# All of the following nutso if-else destructuring is to make the
# grammar expand unambiguously.
# An elsif portion of an if-else block.
ElsIf:
ELSE IF Expression
Then Expressions { result = IfNode.new(val[2], val[4]) }
;
# Multiple elsifs can be chained together.
ElsIfs:
ElsIf { result = val[0] }
| ElsIfs ElsIf { result = val[0].add_else(val[1]) }
;
# Terminating else bodies are strictly optional.
ElseBody
"." { result = nil }
| ELSE Expressions "." { result = val[1] }
;
# All the alternatives for ending an if-else block.
IfEnd:
ElseBody { result = val[0] }
| ElsIfs ElseBody { result = val[0].add_else(val[1]) }
;
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
If:
IF Expression
Then Expressions IfEnd { result = IfNode.new(val[1], val[3], val[4]) }
| Expression IF Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true}) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, {:statement => true, :invert => true}) }
;
2009-12-13 17:07:16 -05:00
end
---- header
module CoffeeScript
2009-12-13 17:07:16 -05:00
---- inner
2009-12-17 22:22:35 -05:00
# Lex and parse a CoffeeScript.
2009-12-17 10:04:43 -05:00
def parse(code)
2009-12-17 22:22:35 -05:00
# Uncomment the following line to enable grammar debugging, in combination
# with the -g flag in the Rake build task.
2009-12-13 17:07:16 -05:00
# @yydebug = true
@tokens = Lexer.new.tokenize(code)
do_parse
end
2009-12-17 22:22:35 -05:00
# Retrieve the next token from the list.
2009-12-13 17:07:16 -05:00
def next_token
@tokens.shift
2009-12-17 10:04:43 -05:00
end
2009-12-17 22:22:35 -05:00
# Raise a custom error class that knows about line numbers.
2009-12-17 10:04:43 -05:00
def on_error(error_token_id, error_value, value_stack)
raise ParseError.new(token_to_str(error_token_id), error_value, value_stack)
end
---- footer
end