update to use new ast
soml was updated to have a typed ast layer to make programatic creation easier this brings LOTS of syntax change with it, that does not really mean anything at all All tests pass again so back to the same
This commit is contained in:
parent
d7b210d63a
commit
229f5896c6
@ -12,7 +12,7 @@ GIT
|
||||
|
||||
GIT
|
||||
remote: git://github.com/salama/soml-parser.git
|
||||
revision: 4a9b492dd9fb1d7cbc6f114aee226ff227753568
|
||||
revision: 5c03db709fa3a326288a57a65643bd9ddf905bec
|
||||
specs:
|
||||
soml-parser (0.5.0)
|
||||
ast (~> 2.1.0)
|
||||
|
@ -18,10 +18,10 @@ module Register
|
||||
def div10 context
|
||||
s = "div_10"
|
||||
compiler = Soml::Compiler.new.create_method(:Integer,:div10 ).init_method
|
||||
me = compiler.process( s(:name , :self) )
|
||||
tmp = compiler.process( s(:name , :self) )
|
||||
q = compiler.process( s(:name , :self) )
|
||||
const = compiler.process( s(:int , 1) )
|
||||
me = compiler.process( Soml::NameExpression.new( :self) )
|
||||
tmp = compiler.process( Soml::NameExpression.new( :self) )
|
||||
q = compiler.process( Soml::NameExpression.new( :self) )
|
||||
const = compiler.process( Soml::IntegerExpression.new(1) )
|
||||
# int tmp = self >> 1
|
||||
compiler.add_code Register.op( s , ">>" , tmp , const)
|
||||
# int q = self >> 2
|
||||
|
@ -13,7 +13,7 @@ module Register
|
||||
compiler = Soml::Compiler.new.create_method(:Object , :get_internal_word , {:index => :Integer}).init_method
|
||||
source = "get_internal_word"
|
||||
#Load self by "calling" on_name
|
||||
me = compiler.process( s(:name , :self) )
|
||||
me = compiler.process( Soml::NameExpression.new( :self) )
|
||||
# Load the argument
|
||||
index = compiler.use_reg :Integer
|
||||
compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index )
|
||||
@ -31,7 +31,7 @@ module Register
|
||||
{:index => :Integer, :value => :Object} ).init_method
|
||||
source = "set_internal_word"
|
||||
#Load self by "calling" on_name
|
||||
me = compiler.process( s(:name , :self) )
|
||||
me = compiler.process( Soml::NameExpression.new( :self) )
|
||||
# Load the index
|
||||
index = compiler.use_reg :Integer
|
||||
compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index )
|
||||
|
@ -13,14 +13,14 @@ module Register
|
||||
Kernel.emit_syscall( compiler , :putstring )
|
||||
compiler.method
|
||||
end
|
||||
|
||||
|
||||
# self[index] basically. Index is the first arg > 0
|
||||
# return (and word sized int) is stored in return_value
|
||||
def get_internal_byte context
|
||||
compiler = Soml::Compiler.new.create_method(:Word , :get_internal_byte , {:index => :Integer }).init_method
|
||||
source = "get_internal_word"
|
||||
#Load self by "calling" on_name
|
||||
me = compiler.process( s(:name , :self) )
|
||||
me = compiler.process( Soml::NameExpression.new( :self) )
|
||||
# Load the argument
|
||||
index = compiler.use_reg :Integer
|
||||
compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index )
|
||||
@ -39,7 +39,7 @@ module Register
|
||||
{:index => :Integer, :value => :Integer } ).init_method
|
||||
source = "set_internal_word"
|
||||
#Load self by "calling" on_name
|
||||
me = compiler.process( s(:name , :self) )
|
||||
me = compiler.process( Soml::NameExpression.new( :self) )
|
||||
# Load the index
|
||||
index = compiler.use_reg :Integer
|
||||
compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index )
|
||||
|
@ -19,7 +19,7 @@ module Register
|
||||
@source = source
|
||||
@next = nekst
|
||||
return unless source
|
||||
raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(AST::Node)
|
||||
raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(Soml::Code)
|
||||
end
|
||||
attr_reader :source
|
||||
|
||||
|
@ -32,10 +32,11 @@ module Soml
|
||||
# Helper function to create a new compiler and compie the statement(s)
|
||||
def self.compile statement
|
||||
compiler = Compiler.new
|
||||
compiler.process statement
|
||||
code = Soml.ast_to_code statement
|
||||
compiler.process code
|
||||
end
|
||||
|
||||
class Compiler < AST::Processor
|
||||
class Compiler
|
||||
|
||||
def initialize( method = nil )
|
||||
@regs = []
|
||||
@ -46,8 +47,33 @@ module Soml
|
||||
end
|
||||
attr_reader :clazz , :method
|
||||
|
||||
def handler_missing node
|
||||
raise "No handler on_#{node.type}(node)"
|
||||
|
||||
# Dispatches `code` according to it's class name, for class NameExpression
|
||||
# a method named `on_NameExpression` is invoked with one argument, the `code`
|
||||
#
|
||||
# @param [Soml::Code, nil] code
|
||||
def process(code)
|
||||
name = code.class.name.split("::").last
|
||||
# Invoke a specific handler
|
||||
on_handler = :"on_#{name}"
|
||||
if respond_to? on_handler
|
||||
return send on_handler, code
|
||||
else
|
||||
raise "No handler on_#{name}(code) #{code.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# {#process}es each code from `codes` and returns an array of
|
||||
# results.
|
||||
#
|
||||
def process_all(codes)
|
||||
codes.to_a.map do |code|
|
||||
process code
|
||||
end
|
||||
end
|
||||
|
||||
def on_Statements(codes)
|
||||
process_all codes.statements
|
||||
end
|
||||
|
||||
# create the method, do some checks and set it as the current method to be added to
|
||||
@ -150,7 +176,8 @@ module Soml
|
||||
|
||||
def self.load_parfait
|
||||
each_parfait do |parts|
|
||||
self.new.process( parts )
|
||||
code = Soml.ast_to_code parts
|
||||
self.new.process( code )
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_assignment statement
|
||||
def on_Assignment statement
|
||||
reset_regs # statements reset registers, ie have all at their disposal
|
||||
#puts statement.inspect
|
||||
name , value = *statement
|
||||
name = no_space name.to_a.first
|
||||
v = process(value)
|
||||
# name , value = *statement
|
||||
name_s = no_space statement.name
|
||||
v = process(statement.value)
|
||||
raise "Not register #{v}" unless v.is_a?(Register::RegisterValue)
|
||||
code = nil
|
||||
if( index = @method.has_arg(name))
|
||||
if( index = @method.has_arg(name_s.name))
|
||||
# TODO, check type @method.arguments[index].type
|
||||
code = Register.set_slot(statement , v , :message , Parfait::Message.get_indexed(index) )
|
||||
else # or a local so it is in the frame
|
||||
index = @method.has_local( name )
|
||||
index = @method.has_local( name_s.name )
|
||||
if(index)
|
||||
# TODO, check type @method.locals[index].type
|
||||
frame = use_reg(:Frame)
|
||||
|
@ -10,41 +10,41 @@ module Soml
|
||||
|
||||
# But in the future (in the one that holds great things) we optimize those unneccesay moves away
|
||||
|
||||
def on_int expression
|
||||
int = expression.first
|
||||
def on_IntegerExpression expression
|
||||
int = expression.value
|
||||
reg = use_reg :Integer , int
|
||||
add_code Register::LoadConstant.new( expression, int , reg )
|
||||
return reg
|
||||
end
|
||||
|
||||
def on_true expression
|
||||
def on_TrueExpression expression
|
||||
reg = use_reg :Boolean
|
||||
add_code Register::LoadConstant.new( expression, true , reg )
|
||||
return reg
|
||||
end
|
||||
|
||||
def on_false expression
|
||||
def on_FalseExpression expression
|
||||
reg = use_reg :Boolean
|
||||
add_code Register::LoadConstant.new( expression, false , reg )
|
||||
return reg
|
||||
end
|
||||
|
||||
def on_nil expression
|
||||
def on_NilExpression expression
|
||||
reg = use_reg :NilClass
|
||||
add_code Register::LoadConstant.new( expression, nil , reg )
|
||||
return reg
|
||||
end
|
||||
|
||||
def on_string expression
|
||||
value = Parfait.new_word expression.first.to_sym
|
||||
def on_StringExpression expression
|
||||
value = Parfait.new_word expression.value.to_sym
|
||||
reg = use_reg :Word
|
||||
Register.machine.constants << value
|
||||
add_code Register::LoadConstant.new( expression, value , reg )
|
||||
return reg
|
||||
end
|
||||
|
||||
def on_class_name expression
|
||||
name = expression.first
|
||||
def on_ClassExpression expression
|
||||
name = expression.value
|
||||
clazz = Parfait::Space.object_space.get_class_by_name! name
|
||||
raise "No such class #{name}" unless clazz
|
||||
reg = use_reg :MetaClass , clazz
|
||||
|
@ -1,16 +1,16 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_call statement
|
||||
def on_CallSite statement
|
||||
#puts statement
|
||||
name_s , arguments , receiver = *statement
|
||||
# name_s , arguments , receiver = *statement
|
||||
raise "not inside method " unless @method
|
||||
reset_regs
|
||||
#move the new message (that we need to populate to make a call) to std register
|
||||
new_message = Register.resolve_to_register(:new_message)
|
||||
add_code Register.get_slot(statement, :message , :next_message , new_message )
|
||||
if receiver
|
||||
me = process( receiver.first )
|
||||
if statement.receiver
|
||||
me = process( statement.receiver )
|
||||
else
|
||||
me = use_reg @method.for_class.name
|
||||
add_code Register.get_slot(statement, :message , :receiver , me )
|
||||
@ -24,8 +24,8 @@ module Soml
|
||||
# move our receiver there
|
||||
add_code Register.set_slot( statement , me , :new_message , :receiver)
|
||||
|
||||
set_message_details(name_s , arguments)
|
||||
set_arguments(arguments)
|
||||
set_message_details(statement , statement.arguments)
|
||||
set_arguments(statement.arguments)
|
||||
ret = use_reg( :Integer ) #TODO real return type
|
||||
do_call(clazz , statement)
|
||||
# the effect of the method is that the NewMessage Return slot will be filled, return it
|
||||
@ -36,7 +36,7 @@ module Soml
|
||||
|
||||
private
|
||||
def do_call clazz , statement
|
||||
name = statement.first.first
|
||||
name = statement.name
|
||||
#puts "clazz #{clazz.name}"
|
||||
raise "No such class" unless clazz
|
||||
method = clazz.resolve_method(name)
|
||||
@ -45,15 +45,15 @@ module Soml
|
||||
Register.issue_call( self , method )
|
||||
end
|
||||
def set_message_details name_s , arguments
|
||||
name = name_s.to_a.first
|
||||
name = name_s.name
|
||||
# load method name and set to new message (for exceptions/debug)
|
||||
name_tmp = use_reg(:Word)
|
||||
add_code Register::LoadConstant.new(name_s, name , name_tmp)
|
||||
add_code Register.set_slot( name_s , name_tmp , :new_message , :name)
|
||||
# next arguments. first length then args
|
||||
len_tmp = use_reg(:Integer , arguments.to_a.length )
|
||||
add_code Register::LoadConstant.new(arguments, arguments.to_a.length , len_tmp)
|
||||
add_code Register.set_slot( arguments , len_tmp , :new_message , :indexed_length)
|
||||
add_code Register::LoadConstant.new(name_s, arguments.to_a.length , len_tmp)
|
||||
add_code Register.set_slot( name_s , len_tmp , :new_message , :indexed_length)
|
||||
end
|
||||
def set_arguments arguments
|
||||
# reset tmp regs for each and load result into new_message
|
||||
|
@ -1,19 +1,16 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_class_field statement
|
||||
def on_ClassField statement
|
||||
#puts statement.inspect
|
||||
type , name , value = *statement
|
||||
#type , name , value = *statement
|
||||
|
||||
for_class = @clazz
|
||||
raise "no class" unless for_class
|
||||
index = for_class.instance_type.variable_index(name)
|
||||
index = for_class.instance_type.variable_index(statement.name)
|
||||
#raise "class field already defined:#{name} for class #{for_class.name}" if index
|
||||
#puts "Define field #{name} on class #{for_class.name}"
|
||||
index = for_class.instance_type.add_instance_variable( name , type )
|
||||
|
||||
# not sure how to run class code yet. later
|
||||
raise "value #{value}" if value
|
||||
index = for_class.instance_type.add_instance_variable( statement.name , statement.type )
|
||||
|
||||
return nil # statements don't reurn values, only expressions
|
||||
end
|
||||
|
@ -1,13 +1,12 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_class statement
|
||||
def on_ClassStatement statement
|
||||
#puts statement.inspect
|
||||
name , derives , statements = *statement
|
||||
raise "classes dont yet play babushka, get coding #{name}" if @clazz
|
||||
@clazz = Parfait::Space.object_space.get_class_by_name! name
|
||||
@clazz = Parfait::Space.object_space.get_class_by_name! statement.name
|
||||
#puts "Compiling class #{@clazz.name.inspect}"
|
||||
statement_value = process_all(statements).last
|
||||
statement_value = process(statement.statements).last
|
||||
@clazz = nil
|
||||
return statement_value
|
||||
end
|
||||
|
@ -1,13 +1,13 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_field_access statement
|
||||
receiver_ast , field_ast = *statement
|
||||
receiver = process(receiver_ast)
|
||||
def on_FieldAccess statement
|
||||
# receiver_ast , field_ast = *statement
|
||||
receiver = process(statement.receiver)
|
||||
|
||||
clazz = Register.machine.space.get_class_by_name receiver.type
|
||||
|
||||
field_name = field_ast.first_from(:name)
|
||||
field_name = statement.field.name
|
||||
|
||||
|
||||
index = clazz.instance_type.variable_index(field_name)
|
||||
|
@ -2,14 +2,14 @@ module Soml
|
||||
Compiler.class_eval do
|
||||
include AST::Sexp
|
||||
|
||||
def on_field_def statement
|
||||
def on_FieldDef statement
|
||||
reset_regs # field_def is a statement, no return and all regs
|
||||
#puts statement.inspect
|
||||
type , name , value = *statement
|
||||
name_s = no_space( name.first )
|
||||
@method.ensure_local( name_s, type ) unless( @method.has_arg(name_s))
|
||||
# type , name , value = *statement
|
||||
name_s = no_space( statement.name.value )
|
||||
@method.ensure_local( name_s, statement.type ) unless( @method.has_arg(name_s))
|
||||
# if there is a value assigned, process it as am assignemnt statement (kind of call on_assign)
|
||||
process( s(:assignment , name , value ) ) if value
|
||||
process( Soml::Assignment.new(statement.name , statement.value ) ) if statement.value
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
@ -1,24 +1,23 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_function statement
|
||||
def on_FunctionStatement statement
|
||||
#puts statement.inspect
|
||||
return_type , name , parameters, kids , receiver = *statement
|
||||
name = name.to_a.first
|
||||
# return_type , name , parameters, kids , receiver = *statement
|
||||
name = statement.name
|
||||
raise "Already in method #{@method}" if @method
|
||||
|
||||
args = parameters.to_a.collect do |p|
|
||||
raise "error, argument must be a identifier, not #{p}" unless p.type == :parameter
|
||||
Parfait::Variable.new( *p)
|
||||
args = statement.parameters.collect do |p|
|
||||
Parfait::Variable.new( *p )
|
||||
end
|
||||
|
||||
class_method = nil
|
||||
if(receiver )
|
||||
if( receiver.first == :self) #class method
|
||||
if(statement.receiver )
|
||||
if( statement.receiver.first == :self) #class method
|
||||
class_method = @clazz
|
||||
@clazz = @clazz.meta
|
||||
else
|
||||
raise "Not covered #{receiver}"
|
||||
raise "Not covered #{statement.receiver}"
|
||||
end
|
||||
end
|
||||
|
||||
@ -33,9 +32,8 @@ module Soml
|
||||
@method.source = statement
|
||||
#puts "compile method #{@method.name}"
|
||||
|
||||
kids.to_a.each do |ex|
|
||||
process(ex)
|
||||
end
|
||||
process(statement.statements)
|
||||
|
||||
@clazz = class_method if class_method
|
||||
@method = nil
|
||||
# function definition is a statement, does not return any value
|
||||
|
@ -4,27 +4,27 @@ module Soml
|
||||
# an if evaluates the condition and jumps to the true block if true
|
||||
# so the else block is automatically after that.
|
||||
# But then the else needs to jump over the true block unconditionally.
|
||||
def on_if_statement statement
|
||||
branch_type , condition , if_true , if_false = *statement
|
||||
condition = condition.first
|
||||
def on_IfStatement statement
|
||||
# branch_type , condition , if_true , if_false = *statement
|
||||
# condition = condition.first
|
||||
|
||||
reset_regs
|
||||
process(condition)
|
||||
process(statement.condition)
|
||||
|
||||
branch_class = Object.const_get "Register::Is#{branch_type.capitalize}"
|
||||
true_block = Register::Label.new(if_true, "if_true")
|
||||
add_code branch_class.new( condition , true_block )
|
||||
branch_class = Object.const_get "Register::Is#{statement.branch_type.capitalize}"
|
||||
true_block = Register::Label.new(statement, "if_true")
|
||||
add_code branch_class.new( statement.condition , true_block )
|
||||
|
||||
# compile the false block
|
||||
reset_regs
|
||||
process_all(if_false) if if_false
|
||||
process(statement.if_false) if statement.if_false.statements
|
||||
merge = Register::Label.new(statement , "if_merge")
|
||||
add_code Register::Branch.new(if_false, merge )
|
||||
add_code Register::Branch.new(statement.if_false, merge )
|
||||
|
||||
# compile the true block
|
||||
add_code true_block
|
||||
reset_regs
|
||||
process_all(if_true)
|
||||
process(statement.if_true)
|
||||
|
||||
#puts "compiled if: end"
|
||||
add_code merge
|
||||
|
@ -2,11 +2,11 @@ module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
# attr_reader :name
|
||||
# compiling name needs to check if it's a variable and if so resolve it
|
||||
# otherwise it's a method without args and a send is issued.
|
||||
# compiling name needs to check if it's a local variable
|
||||
# or an argument
|
||||
# whichever way this goes the result is stored in the return slot (as all compiles)
|
||||
def on_name statement
|
||||
name = statement.to_a.first
|
||||
def on_NameExpression statement
|
||||
name = statement.name
|
||||
if( name == :self)
|
||||
ret = use_reg @clazz.name
|
||||
add_code Register.get_slot(statement , :message , :receiver , ret )
|
||||
|
@ -1,17 +1,17 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_operator_value statement
|
||||
def on_OperatorExpression statement
|
||||
#puts "operator #{statement.inspect}"
|
||||
operator , left_e , right_e = *statement
|
||||
# operator , left_e , right_e = *statement
|
||||
# left and right must be expressions. Expressions return a register when compiled
|
||||
left_reg = process(left_e)
|
||||
right_reg = process(right_e)
|
||||
left_reg = process(statement.left_expression)
|
||||
right_reg = process(statement.right_expression)
|
||||
raise "Not register #{left_reg}" unless left_reg.is_a?(Register::RegisterValue)
|
||||
raise "Not register #{right_reg}" unless right_reg.is_a?(Register::RegisterValue)
|
||||
#puts "left #{left_reg}"
|
||||
#puts "right #{right_reg}"
|
||||
add_code Register::OperatorInstruction.new(statement,operator,left_reg,right_reg)
|
||||
add_code Register::OperatorInstruction.new(statement,statement.operator,left_reg,right_reg)
|
||||
return left_reg # though this has wrong value attached
|
||||
end
|
||||
end
|
||||
|
@ -1,8 +1,8 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_return statement
|
||||
reg = process(statement.first)
|
||||
def on_ReturnStatement statement
|
||||
reg = process(statement.return_value)
|
||||
add_code Register.set_slot( statement, reg , :message , :return_value)
|
||||
nil # statements don't return
|
||||
end
|
||||
|
@ -1,27 +1,26 @@
|
||||
module Soml
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_while_statement statement
|
||||
def on_WhileStatement statement
|
||||
#puts statement.inspect
|
||||
branch_type , condition , statements = *statement
|
||||
condition = condition.first
|
||||
#branch_type , condition , statements = *statement
|
||||
|
||||
condition_label = Register::Label.new(statement , "condition_label")
|
||||
condition_label = Register::Label.new(statement.condition , "condition_label")
|
||||
# unconditionally branch to the condition upon entering the loop
|
||||
add_code Register::Branch.new(statement,condition_label)
|
||||
add_code Register::Branch.new(statement.condition,condition_label)
|
||||
|
||||
add_code start = Register::Label.new(statement , "while_start" )
|
||||
reset_regs
|
||||
process_all(statements)
|
||||
process(statement.statements)
|
||||
|
||||
# This is where the loop starts, though in subsequent iterations it's in the middle
|
||||
add_code condition_label
|
||||
reset_regs
|
||||
process(condition)
|
||||
process(statement.condition)
|
||||
|
||||
branch_class = Object.const_get "Register::Is#{branch_type.capitalize}"
|
||||
branch_class = Object.const_get "Register::Is#{statement.branch_type.capitalize}"
|
||||
# this is where the while ends and both branches meet
|
||||
add_code branch_class.new( condition , start )
|
||||
add_code branch_class.new( statement.condition , start )
|
||||
|
||||
nil # statements don't return anything
|
||||
end
|
||||
|
@ -21,10 +21,11 @@ module ExpressionHelper
|
||||
parser = parser.send @root
|
||||
syntax = parser.parse_with_debug(@string_input, reporter: Parslet::ErrorReporter::Deepest.new)
|
||||
parts = Parser::Transform.new.apply(syntax)
|
||||
codes = Soml.ast_to_code parts
|
||||
#puts parts.inspect
|
||||
compiler = Soml::Compiler.new
|
||||
set_main(compiler)
|
||||
produced = compiler.process( parts )
|
||||
produced = compiler.process( codes )
|
||||
assert @output , "No output given"
|
||||
assert_equal produced.class, @output , "Wrong class"
|
||||
produced
|
||||
|
@ -30,7 +30,7 @@ class TestBasic < MiniTest::Test
|
||||
def test_var
|
||||
@string_input = 'int foo '
|
||||
@root = :field_def
|
||||
@output = AST::Node
|
||||
@output = NilClass
|
||||
check
|
||||
end
|
||||
|
||||
|
@ -40,19 +40,6 @@ HERE
|
||||
check
|
||||
end
|
||||
|
||||
def test_class_field_value
|
||||
@string_input = <<HERE
|
||||
class Space
|
||||
field int boo1 = 1
|
||||
int main()
|
||||
return 1
|
||||
end
|
||||
end
|
||||
HERE
|
||||
@expect = [Label, LoadConstant,SetSlot,Label,FunctionReturn]
|
||||
assert_raises{check}
|
||||
end
|
||||
|
||||
def test_class_field
|
||||
@string_input = <<HERE
|
||||
class Space
|
||||
|
Loading…
Reference in New Issue
Block a user