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:
|
2009-12-13 17:23:10 -05:00
|
|
|
/* 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:
|
2009-12-13 17:23:10 -05:00
|
|
|
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:
|
2009-12-13 17:23:10 -05:00
|
|
|
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:
|
2009-12-13 17:23:10 -05:00
|
|
|
Variable ":" Expression { result = AssignNode.new(val[0], val[2]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
# Assignment within an object literal.
|
|
|
|
AssignObj:
|
2009-12-13 17:23:10 -05:00
|
|
|
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:
|
2009-12-13 17:23:10 -05:00
|
|
|
'!' 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
|
|
|
|
|
|
|
|
2009-12-13 17:23:10 -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
|
|
|
|
2009-12-13 17:23:10 -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
|
|
|
|
2009-12-13 17:23:10 -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
|
|
|
|
2009-12-13 17:23:10 -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
|
|
|
|
2009-12-13 17:23:10 -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?
|
|
|
|
|
2009-12-13 17:23:10 -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
|
|
|
# Add ||= &&=
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
|
|
# Method definition
|
|
|
|
Code:
|
2009-12-13 17:23:10 -05:00
|
|
|
"=>" Expressions "." { result = CodeNode.new([], val[1]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
| ParamList
|
2009-12-13 17:23:10 -05:00
|
|
|
"=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
ParamList:
|
2009-12-13 17:23:10 -05:00
|
|
|
/* nothing */ { result = [] }
|
|
|
|
| IDENTIFIER { result = val }
|
|
|
|
| ParamList "," IDENTIFIER { result = val[0] << val[2] }
|
2009-12-13 17:07:16 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
Variable:
|
2009-12-13 17:23:10 -05:00
|
|
|
IDENTIFIER { result = VariableNode.new(val) }
|
|
|
|
| Variable PROPERTY_ACCESS
|
|
|
|
IDENTIFIER { result = val[0] << val[2] }
|
2009-12-13 17:07:16 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
Object:
|
2009-12-13 17:23:10 -05:00
|
|
|
"{" "}" { result = ObjectNode.new([]) }
|
|
|
|
| "{" AssignList "}" { result = ObjectNode.new(val[1]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
| "{" Terminator AssignList
|
2009-12-13 17:23:10 -05:00
|
|
|
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:
|
2009-12-13 17:23:10 -05:00
|
|
|
/* nothing */ { result = [] }
|
|
|
|
| Expression { result = val }
|
|
|
|
| ArgList "," Expression { result = val[0] << val[2] }
|
2009-12-13 17:07:16 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
If:
|
|
|
|
IF Expression
|
2009-12-13 17:23:10 -05:00
|
|
|
THEN Expression "." { result = TernaryNode.new(val[1], val[3]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
| IF Expression Terminator
|
2009-12-13 17:23:10 -05:00
|
|
|
Expressions "." { result = IfNode.new(val[1], val[3]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
| IF Expression
|
|
|
|
THEN Expression
|
2009-12-13 17:23:10 -05:00
|
|
|
ELSE Expression "." { result = TernaryNode.new(val[1], val[3], val[5]) }
|
2009-12-13 17:07:16 -05:00
|
|
|
| IF Expression Terminator
|
|
|
|
Expressions Terminator
|
2009-12-13 17:23:10 -05:00
|
|
|
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
|