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

324 lines
10 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
2009-12-15 22:30:27 -05:00
token SWITCH CASE DEFAULT
token NEWLINE
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 '!'
left '*' '/' '%'
left '+' '-'
left '<=' '<' '>' '>='
right '==' '!=' IS AINT
left '&&' '||' AND OR
2009-12-15 08:53:21 -05:00
left ':'
right '-=' '+=' '/=' '*=' '||=' '&&='
2009-12-15 08:53:21 -05:00
nonassoc IF
left UNLESS
right RETURN THROW FOR WHILE
2009-12-16 20:19:52 -05:00
nonassoc "."
2009-12-13 17:07:16 -05:00
preclow
2009-12-16 20:19:52 -05:00
# We expect 8 shift/reduce errors for optional syntax.
# There used to be 252 -- greatly improved.
2009-12-16 20:19:52 -05:00
expect 8
2009-12-13 17:07:16 -05:00
rule
# All parsing will end in this rule, being the trunk of the AST.
Root:
/* nothing */ { result = Nodes.new([]) }
| Terminator { result = Nodes.new([]) }
| Expressions { result = val[0] }
2009-12-13 17:07:16 -05:00
;
# Any list of expressions or method body, seperated by line breaks.
Expressions:
Expression { result = Nodes.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
Expression:
Literal
2009-12-14 10:00:31 -05:00
| Value
2009-12-13 17:07:16 -05:00
| Call
| Assign
| Code
| Operation
| 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
2009-12-13 17:07:16 -05:00
;
# All tokens that can terminate an expression
Terminator:
"\n"
| ";"
;
# All tokens that can serve to begin the second block
Then:
THEN
| Terminator
;
2009-12-13 17:07:16 -05:00
# All hard-coded values
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
;
# Assign to a variable
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) }
2009-12-13 17:07:16 -05:00
;
2009-12-13 18:37:29 -05:00
# A Return statement.
Return:
RETURN Expression { result = ReturnNode.new(val[1]) }
;
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]) }
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]) }
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-13 17:07:16 -05:00
;
# Method definition
Code:
2009-12-13 23:25:00 -05:00
ParamList "=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }
2009-12-13 23:59:12 -05:00
| "=>" Expressions "." { result = CodeNode.new([], val[1]) }
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
;
2009-12-14 10:00:31 -05:00
Value:
IDENTIFIER { result = ValueNode.new(val) }
| Array { result = ValueNode.new(val) }
| Object { result = ValueNode.new(val) }
| Parenthetical { result = ValueNode.new(val) }
| Value Accessor { result = val[0] << val[1] }
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
2009-12-13 20:29:44 -05:00
;
Accessor:
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
| Index { result = val[0] }
;
Index:
2009-12-15 11:36:24 -05:00
"[" Expression "]" { result = IndexNode.new(val[1]) }
2009-12-13 17:07:16 -05:00
;
Object:
ObjectStart ObjectEnd { result = ObjectNode.new([]) }
| ObjectStart AssignList ObjectEnd { result = ObjectNode.new(val[1]) }
;
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] }
;
# A method call.
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-13 23:45:18 -05:00
;
Invocation:
2009-12-15 09:11:27 -05:00
Value ParenStart ArgList ParenEnd { result = CallNode.new(val[0], val[2]) }
2009-12-13 17:07:16 -05:00
;
# An Array.
Array:
2009-12-15 09:11:27 -05:00
ArrayStart ArgList ArrayEnd { result = ArrayNode.new(val[1]) }
2009-12-13 17:07:16 -05:00
;
# A list of arguments to a method call.
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
;
If:
IF Expression
Then Expressions "." { result = IfNode.new(val[1], val[3]) }
2009-12-13 17:07:16 -05:00
| IF Expression
Then Expressions
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[5]) }
2009-12-15 08:53:21 -05:00
| Expression IF Expression { result = IfNode.new(val[2], Nodes.new([val[0]])) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Nodes.new([val[0]]), nil, :invert) }
2009-12-13 17:07:16 -05:00
;
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]) }
;
Throw:
THROW Expression { result = ThrowNode.new(val[1]) }
2009-12-14 10:00:31 -05:00
;
2009-12-13 23:25:00 -05:00
Parenthetical:
2009-12-15 09:11:27 -05:00
ParenStart Expressions ParenEnd { result = ParentheticalNode.new(val[1]) }
2009-12-13 23:25:00 -05:00
;
While:
WHILE Expression Then
Expressions "." { result = WhileNode.new(val[1], val[3]) }
;
For:
Expression FOR IDENTIFIER
IN Expression "." { result = ForNode.new(val[0], val[4], val[2]) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN Expression "." { result = ForNode.new(val[0], val[6], val[2], val[4]) }
| Expression FOR IDENTIFIER
IN Expression
IF Expression "." { result = ForNode.new(IfNode.new(val[6], Nodes.new([val[0]])), val[4], val[2]) }
| Expression FOR
IDENTIFIER "," IDENTIFIER
IN Expression
IF Expression "." { result = ForNode.new(IfNode.new(val[8], Nodes.new([val[0]])), val[6], val[2], val[4]) }
;
2009-12-15 22:30:27 -05:00
Switch:
SWITCH Expression Then
Cases "." { result = val[3].rewrite_condition(val[1]) }
| SWITCH Expression Then
Cases DEFAULT Expressions "." { result = val[3].rewrite_condition(val[1]).add_default(val[5]) }
;
Cases:
Case { result = val[0] }
| Cases Case { result = val[0] << val[1] }
;
Case:
CASE Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
;
2009-12-15 09:11:27 -05:00
ObjectStart:
"{" { result = nil }
| "{" "\n" { result = nil }
;
ObjectEnd:
"}" { result = nil }
| "\n" "}" { result = nil }
;
ParenStart:
"(" { result = nil }
| "(" "\n" { result = nil }
;
ParenEnd:
")" { result = nil }
| "\n" ")" { result = nil }
;
ArrayStart:
"[" { result = nil }
| "[" "\n" { result = nil }
;
ArrayEnd:
"]" { result = nil }
| "\n" "]" { result = nil }
;
2009-12-13 17:07:16 -05:00
end
---- header
require "lexer"
require "nodes"
---- inner
def parse(code, show_tokens=false)
# @yydebug = true
@tokens = Lexer.new.tokenize(code)
puts @tokens.inspect if show_tokens
do_parse
end
def next_token
@tokens.shift
end