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:
Torsten Ruger 2016-03-07 11:55:28 +02:00
parent d7b210d63a
commit 229f5896c6
22 changed files with 126 additions and 118 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 )

View File

@ -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 )

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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