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

207 lines
6.7 KiB
Text
Raw Normal View History

2009-12-13 17:07:16 -05:00
class Parser
# Declare tokens produced by the lexer
token IF ELSE THEN
token NEWLINE
token NUMBER
token STRING
token TRUE FALSE NULL
token IDENTIFIER PROPERTY_ACCESS
token CODE
prechigh
nonassoc UMINUS NOT '!'
left '*' '/' '%'
left '+' '-'
left '<=' '<' '>' '>='
right '==' '!=' IS AINT
left '&&' '||' AND OR
right '-=' '+=' '/=' '*='
preclow
rule
# All rules are declared in this format:
#
# RuleName:
# OtherRule TOKEN AnotherRule { code to run when this matches }
# | OtherRule { ... }
# ;
#
# In the code section (inside the {...} on the right):
# - Assign to "result" the value returned by the rule.
# - Use val[index of expression] to reference expressions on the left.
# All parsing will end in this rule, being the trunk of the AST.
Root:
/* nothing */ { 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] }
| Expressions Terminator { result = Nodes.new([val[0]]) }
| Terminator Expressions { result = Nodes.new([val[1]]) }
2009-12-13 17:07:16 -05:00
;
# All types of expressions in our language
Expression:
Literal
| Variable
| Call
| Assign
| Object
| Code
| Operation
| Array
| If
;
# All tokens that can terminate an expression
Terminator:
"\n"
| ";"
;
# All hard-coded values
Literal:
NUMBER { result = LiteralNode.new(val[0]) }
| STRING { result = LiteralNode.new(val[0]) }
| TRUE { result = LiteralNode.new(true) }
| FALSE { result = LiteralNode.new(false) }
| NULL { result = LiteralNode.new(nil) }
2009-12-13 17:07:16 -05:00
;
# Assign to a variable
Assign:
Variable ":" 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
;
# 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
# Add ternary?
| 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
# Add ||= &&=
;
# Method definition
Code:
"=>" Expressions "." { result = CodeNode.new([], val[1]) }
2009-12-13 17:07:16 -05:00
| ParamList
"=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }
2009-12-13 17:07:16 -05:00
;
ParamList:
/* nothing */ { result = [] }
| IDENTIFIER { result = val }
| ParamList "," IDENTIFIER { result = val[0] << val[2] }
2009-12-13 17:07:16 -05:00
;
Variable:
IDENTIFIER { result = VariableNode.new(val) }
| Variable PROPERTY_ACCESS
IDENTIFIER { result = val[0] << val[2] }
2009-12-13 17:07:16 -05:00
;
Object:
"{" "}" { result = ObjectNode.new([]) }
| "{" AssignList "}" { result = ObjectNode.new(val[1]) }
2009-12-13 17:07:16 -05:00
| "{" Terminator AssignList
Terminator "}" { result = ObjectNode.new(val[2]) }
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:
Variable "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
;
# An Array.
Array:
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
;
# A list of arguments to a method call.
ArgList:
/* nothing */ { result = [] }
| Expression { result = val }
| ArgList "," Expression { result = val[0] << val[2] }
2009-12-13 17:07:16 -05:00
;
If:
IF Expression
THEN Expression "." { result = TernaryNode.new(val[1], val[3]) }
2009-12-13 17:07:16 -05:00
| IF Expression Terminator
Expressions "." { result = IfNode.new(val[1], val[3]) }
2009-12-13 17:07:16 -05:00
| IF Expression
THEN Expression
ELSE Expression "." { result = TernaryNode.new(val[1], val[3], val[5]) }
2009-12-13 17:07:16 -05:00
| IF Expression Terminator
Expressions Terminator
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[6]) }
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