ruby/test/racc/assets/huia.y

319 строки
12 KiB
Plaintext

# Copyright (c) 2014 James Harton
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class Huia::Parser
token
IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING
EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE
FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING
DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE
RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT
GTE LTE AT
prechigh
left EXPO
left BANG TILDE
left ASTERISK FWD_SLASH PERCENT
left PLUS MINUS
right EQUAL
preclow
rule
statements: statement
| statements statement { return scope }
statement: expr eol { return scope.append val[0] }
| expr { return scope.append val[0] }
| eol { return scope }
eol: NL | EOF
nlq: NL |
expr: literal
| grouped_expr
| binary_op
| unary_op
| method_call
| constant
| variable
| array
| hash
| return
return: return_expr
| return_nil
return_expr: RETURN expr { return n(:Return, val[1]) }
return_nil: RETURN { return n(:Return, n(:Nil)) }
array: empty_array
| array_list
empty_array: BOX { return n :Array }
array_list: LSQUARE array_items RSQUARE { return val[1] }
array_items: expr { return n :Array, [val[0]] }
| array_items COMMA expr { val[0].append(val[2]); return val[0] }
hash: empty_hash
| hash_list
empty_hash: FACES { return n :Hash }
hash_list: LFACE hash_items RFACE { return val[1] }
hash_items: hash_item { return n :Hash, val[0] }
| hash_items COMMA hash_item { val[0].append(val[2]); return val[0] }
hash_item: expr COLON expr { return n :HashItem, val[0], val[2] }
constant: CONSTANT { return constant val[0] }
indented: indented_w_stmts
| indented_w_expr
| indented_wo_stmts
indented_w_stmts: indent statements outdent { return val[0] }
indented_w_expr: indent expr outdent { return val[0].append(val[1]) }
indented_wo_stmts: indent outdent { return val[0] }
outdent: OUTDENT { return pop_scope }
indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] }
indent_pipe: PIPE { return push_scope }
indent_wo_args: INDENT { return push_scope }
indent: indent_w_args
| indent_wo_args
indent_args: indent_arg
| indent_args COMMA indent_arg
indent_arg: arg_var { return scope.add_argument val[0] }
| arg_var EQUAL expr { return n :Assignment, val[0], val[2] }
arg_var: IDENTIFIER { return n :Variable, val[0] }
method_call: method_call_on_object
| method_call_on_self
| method_call_on_closure
method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] }
| expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) }
method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] }
method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] }
| AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) }
call_signature: call_arguments
| call_simple_name
call_simple_name: CALL { return n :CallSignature, val[0] }
call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] }
call_passed_arg: call_passed_simple
| call_passed_indented
call_passed_simple: expr
| expr NL
call_passed_indented: indented
| indented NL
call_arguments: call_argument { return val[0] }
| call_arguments call_argument { return val[0].concat_signature val[1] }
grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] }
variable: IDENTIFIER { return allocate_local val[0] }
binary_op: assignment
| addition
| subtraction
| multiplication
| division
| exponentiation
| modulo
| equality
| not_equality
| logical_or
| logical_and
| greater_than
| less_than
| greater_or_eq
| less_or_eq
assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] }
addition: expr PLUS expr { return binary val[0], val[2], 'plus:' }
subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' }
multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' }
division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' }
exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' }
modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' }
equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' }
not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' }
logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' }
logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' }
greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' }
less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' }
greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' }
less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' }
unary_op: unary_not
| unary_plus
| unary_minus
| unary_complement
unary_not: BANG expr { return unary val[1], 'unaryNot' }
unary_plus: PLUS expr { return unary val[1], 'unaryPlus' }
unary_minus: MINUS expr { return unary val[1], 'unaryMinus' }
unary_complement: TILDE expr { return unary val[1], 'unaryComplement' }
literal: integer
| float
| string
| nil
| true
| false
| self
float: FLOAT { return n :Float, val[0] }
integer: INTEGER { return n :Integer, val[0] }
nil: NIL { return n :Nil }
true: TRUE { return n :True }
false: FALSE { return n :False }
self: SELF { return n :Self }
string: STRING { return n :String, val[0] }
| interpolated_string
| empty_string
interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] }
interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] }
interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] }
| interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] }
interpolated_string_chunk: chars { return val[0] }
| interpolation { return to_string(val[0]) }
empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' }
chars: CHAR { return n :String, val[0] }
| chars CHAR { val[0].append(val[1]); return val[0] }
end
---- inner
attr_accessor :lexer, :scopes, :state
def initialize lexer
@lexer = lexer
@state = []
@scopes = []
push_scope
end
def ast
@ast ||= do_parse
@scopes.first
end
def on_error t, val, vstack
line = lexer.line
col = lexer.column
message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
start = line - 5 > 0 ? line - 5 : 0
i_size = line.to_s.size
(start..(start + 5)).each do |i|
message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
end
raise SyntaxError, message
end
def next_token
nt = lexer.next_computed_token
# just use a state stack for now, we'll have to do something
# more sophisticated soon.
if nt && nt.first == :state
if nt.last
state.push << nt.last
else
state.pop
end
next_token
else
nt
end
end
def push_scope
new_scope = Huia::AST::Scope.new scope
new_scope.file = lexer.filename
new_scope.line = lexer.line
new_scope.column = lexer.column
scopes.push new_scope
new_scope
end
def pop_scope
scopes.pop
end
def scope
scopes.last
end
def binary left, right, method
node(:MethodCall, left, node(:CallSignature, method, [right]))
end
def unary left, method
node(:MethodCall, left, node(:CallSignature, method))
end
def node type, *args
Huia::AST.const_get(type).new(*args).tap do |n|
n.file = lexer.filename
n.line = lexer.line
n.column = lexer.column
end
end
alias n node
def allocate_local name
node(:Variable, name).tap do |n|
scope.allocate_local n
end
end
def allocate_local_assignment name, value
node(:Assignment, name, value).tap do |n|
scope.allocate_local n
end
end
def this_closure
allocate_local('@')
end
def scope_instance
node(:ScopeInstance, scope)
end
def constant name
return scope_instance if name == 'self'
node(:Constant, name)
end
def to_string expr
node(:MethodCall, expr, node(:CallSignature, 'toString'))
end