soml-parser/lib/parser/transform.rb

131 lines
5.9 KiB
Ruby

#include is private in 1.9, who'd have known without travis
Parslet::Context.send :include , AST::Sexp
Parslet::Context.class_eval do
def type_sym t
if( t.is_a? AST::Node )
t = t.children.first
else
t = t.to_sym
t = :Integer if t == :int
end
t
end
end
module Parser
class Transform < Parslet::Transform
rule(:string => sequence(:chars)) { s(:string , chars.join) }
rule(:esc => simple(:esc)) { '\\' + esc }
rule(char: simple(:char)) { char }
rule(:true => simple(:true)) { s(:true) }
rule(:false => simple(:false)) { s(:false) }
rule(:nil => simple(:nil)) { s(:nil) }
rule(:integer => simple(:value)) { s(:int ,value.to_i) }
rule(:name => simple(:name)) { s(:name , name.to_sym) }
# local variables
rule(:type => simple(:type), :name => simple(:name)) {
s(:field_def , type_sym(type) , s(:name , name.to_sym)) }
rule(:type => simple(:type), :name => simple(:name) , :value => simple(:value)) {
s(:field_def , type_sym(type) , s(:name , name.to_sym) , value ) }
# class field
rule(:field => simple(:field) , :type => simple(:type), :name => simple(:name)) {
s(:class_field , type_sym(type) , name.to_sym) }
rule(:field => simple(:field) , :type => simple(:type), :name => simple(:name) , :value => simple(:value)) {
s(:class_field , type_sym(type) , name.to_sym , value ) }
rule(:l_value => simple(:l_value) , :assign => simple(:assign) , :r_value => simple(:r_value)) {
s(:assignment , l_value , r_value)
}
rule( :left => simple(:left) , :operator => simple(:operator) ,
:right => simple(:right)) { s(:operator_value , operator.to_sym , left , right )}
rule(:class_name => simple(:class_name)) { s(:class_name,class_name.to_s.to_sym) }
rule(:array_constant => sequence(:array_constant) ) { s(:array , *array_constant) }
rule(:array_element => simple(:array_element)) { array_element }
rule(:hash_constant => sequence(:hash_constant) ) { s(:hash , *hash_constant) }
rule(:hash_key => simple(:hash_key) , :hash_value => simple(:hash_value)) { s(:assoc , hash_key , hash_value) }
rule(:hash_pair => simple(:hash_pair) ) { hash_pair }
rule(:argument => simple(:argument)) { argument }
rule(:argument_list => sequence(:argument_list)) { argument_list }
#Two rules for calls, simple and qualified. Keeps the rules simpler
rule( :call_site => simple(:call_site),
:argument_list => sequence(:argument_list)) do
s(:call , call_site, s(:arguments , *argument_list) )
end
rule( :receiver => simple(:receiver) , :call_site => simple(:call_site),
:argument_list => sequence(:argument_list)) do
s(:call , call_site, s(:arguments , *argument_list) , s(:receiver , receiver))
end
rule( :receiver => simple(:receiver) , :field => simple(:field) ) do
s(:field_access , s(:receiver , receiver) , s(:field , field) )
end
rule(:condition => simple(:condition) ,
:conditional => simple(:conditional),
:body => {:statements => sequence(:body) , :end => simple(:e) }) do
s(:while_statement,condition.to_s.to_sym, s(:conditional , conditional), s(:statements , *body))
end
rule(:condition => simple(:condition), :conditional => simple(:conditional),
:true_statements => {:statements => sequence(:true_statements) , :else => simple(:else) },
:false_statements => {:statements => sequence(:false_statements) , :end => simple(:e) }) do
s(:if_statement, condition.to_s.to_sym,
s(:condition, conditional),
s(:true_statements, *true_statements),
s(:false_statements , *false_statements))
end
rule(:condition => simple(:condition), :conditional => simple(:conditional),
:true_statements => {:statements => sequence(:true_statements) , :end => simple(:e) }) do
s(:if_statement, condition.to_s.to_sym, s(:condition, conditional),
s(:true_statements, *true_statements), s(:false_statements , nil) )
end
rule(:return => simple(:return) , :return_statement => simple(:return_statement))do
s(:return , return_statement)
end
rule(:parameter => simple(:parameter)) { s(:parameter , parameter.children[0] , parameter.children[1].children[0]) }
# Also two rules for function definitions, unqualified and qualified
rule(:type => simple(:type) ,
:function_name => simple(:function_name),
:parameter_list => sequence(:parameter_list),
:statements => sequence(:statements) , :end => simple(:e)) do
s(:function, type_sym(type) , function_name, s(:parameters , *parameter_list ),
s(:statements , *statements))
end
rule(:type => simple(:type) ,
:receiver=> simple(:receiver),
:function_name => simple(:function_name),
:parameter_list => sequence(:parameter_list),
:statements => sequence(:statements) , :end => simple(:e)) do
s(:function, type_sym(type) , function_name, s(:parameters , *parameter_list ),
s(:statements , *statements) , s(:receiver , *receiver))
end
rule( :class_name => simple(:class_name) , :derived_name => simple(:derived_name) , :class_statements => sequence(:class_statements) , :end=>"end") do
s(:class , class_name.to_s.to_sym ,
s(:derives, derived_name ? derived_name.to_a.first.to_sym : nil) ,
s(:statements, *class_statements) )
end
rule(:statement_list => sequence(:statement_list)) {
s(:statements , *statement_list)
}
#shortcut to get the ast tree for a given string
# optional second arguement specifies a rule that will be parsed (mainly for testing)
def self.ast string , rule = :root
syntax = Parser.new.send(rule).parse(string)
tree = Transform.new.apply(syntax)
tree
end
end
end