update the reader rewrite and reflect name changes
This commit is contained in:
parent
02e9975ad6
commit
4c17ed2e6e
@ -12,9 +12,9 @@ GIT
|
||||
|
||||
GIT
|
||||
remote: git://github.com/salama/salama-reader.git
|
||||
revision: 58b3553251bd23d2b666b25e23c8bc13ad87acdb
|
||||
revision: 6bd5e9b5ee26e4e0157283e9af4389b13d660193
|
||||
specs:
|
||||
salama-reader (0.3.0)
|
||||
salama-reader (0.4.0)
|
||||
ast (~> 2.1.0)
|
||||
parslet (~> 1.7.0)
|
||||
|
||||
|
@ -74,7 +74,7 @@ The full list is on the net and involves mostly just work.
|
||||
Parse Phisol, using Parslet. This has been separated out as it's own gem, [salama-reader](https://github.com/salama/salama-reader).
|
||||
|
||||
Phisol is now fully typed (all variables, arguments and return). Also it has statements, unlike ruby
|
||||
where everything is an expressions. Statements have no value. Otherwise it is quite basic, and
|
||||
where everything is an statements. Statements have no value. Otherwise it is quite basic, and
|
||||
it's main purpose is to have an oo system language to compile to.
|
||||
|
||||
I spent some time on the parse testing framework, so it is safe to fiddle and add.
|
||||
|
2
ToDo.md
2
ToDo.md
@ -10,7 +10,7 @@ Some things that would be nice . . (if you did them :-) )
|
||||
- utf8 support (string improvements generally)
|
||||
- SOF parser
|
||||
- more ruby grammar niceties. At the moment i am keeping it simple, so if there is a way around it
|
||||
i won't implement it. Multi-assignments, all that chique where newline is used as expression demarcation
|
||||
i won't implement it. Multi-assignments, all that chique where newline is used as statement demarcation
|
||||
|
||||
Or the list of things i am not even planning of tackling at the moment
|
||||
|
||||
|
@ -13,35 +13,35 @@ module Phisol
|
||||
# Some compile methods just add code, some may add structure (ie Blocks) while
|
||||
# others instantiate Class and Method objects
|
||||
#
|
||||
# Everything in ruby is an expression, ie returns a value. So the effect of every compile
|
||||
# Everything in ruby is an statement, ie returns a value. So the effect of every compile
|
||||
# is that a value is put into the ReturnSlot of the current Message.
|
||||
# The compile method (so every compile method) returns the value that it deposits.
|
||||
#
|
||||
# The process uses a visitor pattern (from AST::Processor) to dispatch according to the
|
||||
# type the expression. So a s(:if xx) will become an on_if(node) call.
|
||||
# type the statement. So a s(:if xx) will become an on_if(node) call.
|
||||
# This makes the dispatch extensible, ie Expressions may be added by external code,
|
||||
# as long as matching compile methods are supplied too.
|
||||
#
|
||||
def self.compile expression
|
||||
def self.compile statement
|
||||
compiler = Compiler.new
|
||||
compiler.process expression
|
||||
compiler.process statement
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "ast_helper"
|
||||
require_relative "compiler/basic_expressions"
|
||||
require_relative "compiler/callsite_expression"
|
||||
require_relative "compiler/basic_values"
|
||||
require_relative "compiler/call_site"
|
||||
require_relative "compiler/class_field"
|
||||
require_relative "compiler/compound_expressions"
|
||||
require_relative "compiler/expression_list"
|
||||
require_relative "compiler/collections"
|
||||
require_relative "compiler/statement_list"
|
||||
require_relative "compiler/field_def"
|
||||
require_relative "compiler/field_access"
|
||||
require_relative "compiler/function_expression"
|
||||
require_relative "compiler/if_expression"
|
||||
require_relative "compiler/module_expression"
|
||||
require_relative "compiler/function_definition"
|
||||
require_relative "compiler/if_statement"
|
||||
require_relative "compiler/class_statement"
|
||||
require_relative "compiler/name_expression"
|
||||
require_relative "compiler/operator_expressions"
|
||||
require_relative "compiler/return_expression"
|
||||
require_relative "compiler/while_expression"
|
||||
require_relative "compiler/operator_value"
|
||||
require_relative "compiler/return_statement"
|
||||
require_relative "compiler/while_statement"
|
||||
|
@ -3,7 +3,7 @@ module Phisol
|
||||
|
||||
Compiler.class_eval do
|
||||
|
||||
# Constant expressions can by definition be evaluated at compile time.
|
||||
# Constant statements can by definition be evaluated at compile time.
|
||||
# But that does not solve their storage, ie they need to be accessible at runtime from _somewhere_
|
||||
# So we view ConstantExpressions like functions that return the value of the constant.
|
||||
# In other words, their storage is the return slot as it would be for a method
|
||||
@ -11,34 +11,34 @@ module Phisol
|
||||
# The current approach moves the constant into a variable before using it
|
||||
# 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_int statement
|
||||
int = statement.first
|
||||
to = Virtual::Return.new(Virtual::Integer , int)
|
||||
@method.source.add_code Virtual::Set.new( int , to )
|
||||
to
|
||||
end
|
||||
|
||||
def on_true expression
|
||||
def on_true statement
|
||||
to = Virtual::Return.new(Virtual::Reference , true )
|
||||
@method.source.add_code Virtual::Set.new( true , to )
|
||||
to
|
||||
end
|
||||
|
||||
def on_false expression
|
||||
def on_false statement
|
||||
to = Virtual::Return.new(Virtual::Reference , false)
|
||||
@method.source.add_code Virtual::Set.new( false , to )
|
||||
to
|
||||
end
|
||||
|
||||
def on_nil expression
|
||||
def on_nil statement
|
||||
to = Virtual::Return.new(Virtual::Reference , nil)
|
||||
@method.source.add_code Virtual::Set.new( nil , to )
|
||||
to
|
||||
end
|
||||
|
||||
def on_string expression
|
||||
def on_string statement
|
||||
# Clearly a TODO here to implement strings rather than reusing symbols
|
||||
value = expression.first.to_sym
|
||||
value = statement.first.to_sym
|
||||
to = Virtual::Return.new(Virtual::Reference , value)
|
||||
@method.source.constants << value
|
||||
@method.source.add_code Virtual::Set.new( value , to )
|
@ -1,8 +1,8 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_call expression
|
||||
name , arguments , receiver = *expression
|
||||
def on_call statement
|
||||
name , arguments , receiver = *statement
|
||||
name = name.to_a.first
|
||||
raise "not inside method " unless @method
|
||||
if receiver
|
||||
@ -65,7 +65,7 @@ module Phisol
|
||||
end
|
||||
raise "Method not implemented #{me.value}.#{name}" unless method
|
||||
# the effect of the method is that the NewMessage Return slot will be filled, return it
|
||||
# (this is what is moved _inside_ above loop for such expressions that are calls (or constants))
|
||||
# (this is what is moved _inside_ above loop for such statements that are calls (or constants))
|
||||
Virtual::Return.new( method.source.return_type )
|
||||
end
|
||||
end
|
@ -1,9 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_class_field expression
|
||||
#puts expression.inspect
|
||||
type , name , value = *expression
|
||||
def on_class_field statement
|
||||
#puts statement.inspect
|
||||
type , name , value = *statement
|
||||
|
||||
for_class = @clazz
|
||||
raise "no class" unless for_class
|
||||
|
15
lib/phisol/compiler/class_statement.rb
Normal file
15
lib/phisol/compiler/class_statement.rb
Normal file
@ -0,0 +1,15 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_class 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
|
||||
puts "Compiling class #{@clazz.name.inspect}"
|
||||
statement_value = process_all(statements).last
|
||||
@clazz = nil
|
||||
return statement_value
|
||||
end
|
||||
end
|
||||
end
|
@ -2,7 +2,7 @@ module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
# attr_reader :values
|
||||
def on_array expession, context
|
||||
def on_array statement, context
|
||||
end
|
||||
# attr_reader :key , :value
|
||||
def on_association context
|
@ -1,8 +0,0 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
# list - attr_reader :expressions
|
||||
def on_expressions expession
|
||||
process_all( expession.children )
|
||||
end
|
||||
end
|
||||
end
|
@ -1,9 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_field_access expression
|
||||
#puts expression.inspect
|
||||
receiver_ast , field_ast = *expression
|
||||
def on_field_access statement
|
||||
#puts statement.inspect
|
||||
receiver_ast , field_ast = *statement
|
||||
receiver = receiver_ast.first_from(:name)
|
||||
field_name = field_ast.first_from(:name)
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_field_def expression
|
||||
#puts expression.inspect
|
||||
type , name , value = *expression
|
||||
def on_field_def statement
|
||||
#puts statement.inspect
|
||||
type , name , value = *statement
|
||||
|
||||
index = @method.ensure_local( name , type )
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_function expression
|
||||
#puts expression.inspect
|
||||
return_type , name , parameters, kids , receiver = *expression
|
||||
def on_function statement
|
||||
#puts statement.inspect
|
||||
return_type , name , parameters, kids , receiver = *statement
|
||||
name = name.to_a.first
|
||||
args = parameters.to_a.collect do |p|
|
||||
raise "error, argument must be a identifier, not #{p}" unless p.type == :parameter
|
@ -2,8 +2,8 @@ module Phisol
|
||||
Compiler.class_eval do
|
||||
# if - attr_reader :cond, :if_true, :if_false
|
||||
|
||||
def on_if expression
|
||||
condition , if_true , if_false = *expression
|
||||
def on_if statement
|
||||
condition , if_true , if_false = *statement
|
||||
condition = condition.first
|
||||
# to execute the logic as the if states it, the blocks are the other way around
|
||||
# so we can the jump over the else if true ,
|
||||
@ -25,7 +25,7 @@ module Phisol
|
||||
# compile the false block
|
||||
@method.source.current false_block
|
||||
last = process_all(if_false).last if if_false
|
||||
@method.source.add_code Register::AlwaysBranch.new(expression, merge_block )
|
||||
@method.source.add_code Register::AlwaysBranch.new(statement, merge_block )
|
||||
|
||||
#puts "compiled if: end"
|
||||
@method.source.current merge_block
|
@ -1,20 +0,0 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
# module attr_reader :name ,:expressions
|
||||
def on_module expression
|
||||
name , rest = *expression
|
||||
return process_all(rest).last
|
||||
end
|
||||
|
||||
def on_class expression
|
||||
#puts expression.inspect
|
||||
name , derives , expressions = *expression
|
||||
raise "classes dont yet play babushka, get coding #{name}" if @clazz
|
||||
@clazz = Parfait::Space.object_space.get_class_by_name! name
|
||||
puts "Compiling class #{@clazz.name.inspect}"
|
||||
expression_value = process_all(expressions).last
|
||||
@clazz = nil
|
||||
return expression_value
|
||||
end
|
||||
end
|
||||
end
|
@ -5,8 +5,8 @@ module Phisol
|
||||
# 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.
|
||||
# whichever way this goes the result is stored in the return slot (as all compiles)
|
||||
def on_name expression
|
||||
name = expression.to_a.first
|
||||
def on_name statement
|
||||
name = statement.to_a.first
|
||||
return Virtual::Self.new( Virtual::Reference.new(@clazz)) if name == :self
|
||||
# either an argument, so it's stored in message
|
||||
if( index = @method.has_arg(name))
|
||||
|
@ -1,30 +1,30 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_operator expression
|
||||
puts "operator #{expression.inspect}"
|
||||
operator , left_e , right_e = *expression
|
||||
def on_operator statement
|
||||
puts "operator #{statement.inspect}"
|
||||
operator , left_e , right_e = *statement
|
||||
left_slot = process(left_e)
|
||||
right_slot = process(right_e)
|
||||
puts "left #{left_slot}"
|
||||
puts "right #{right_slot}"
|
||||
tmp1 = Register.tmp_reg
|
||||
tmp2 = tmp1.next_reg_use
|
||||
get = Register.get_slot_to(expression , left_slot , tmp1 )
|
||||
get2 = Register.get_slot_to(expression , right_slot , tmp2 )
|
||||
get = Register.get_slot_to(statement , left_slot , tmp1 )
|
||||
get2 = Register.get_slot_to(statement , right_slot , tmp2 )
|
||||
puts "GET #{get}"
|
||||
puts "GET2 #{get2}"
|
||||
@method.source.add_code get
|
||||
@method.source.add_code get2
|
||||
|
||||
@method.source.add_code Register::OperatorInstruction.new(expression,operator, tmp1,tmp2)
|
||||
@method.source.add_code Register::OperatorInstruction.new(statement,operator, tmp1,tmp2)
|
||||
|
||||
Virtual::Return.new(:int )
|
||||
end
|
||||
|
||||
def on_assign expression
|
||||
puts expression.inspect
|
||||
name , value = *expression
|
||||
def on_assign statement
|
||||
puts statement.inspect
|
||||
name , value = *statement
|
||||
name = name.to_a.first
|
||||
v = process(value)
|
||||
index = @method.has_local( name )
|
@ -1,9 +0,0 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
# return attr_reader :expression
|
||||
def on_return expression
|
||||
return process(expression.to_a.first )
|
||||
end
|
||||
end
|
||||
end
|
9
lib/phisol/compiler/return_statement.rb
Normal file
9
lib/phisol/compiler/return_statement.rb
Normal file
@ -0,0 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
# return attr_reader :statement
|
||||
def on_return statement
|
||||
return process(statement.to_a.first )
|
||||
end
|
||||
end
|
||||
end
|
8
lib/phisol/compiler/statement_list.rb
Normal file
8
lib/phisol/compiler/statement_list.rb
Normal file
@ -0,0 +1,8 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_statements statement
|
||||
process_all( statement.children )
|
||||
end
|
||||
end
|
||||
end
|
@ -1,9 +1,9 @@
|
||||
module Phisol
|
||||
Compiler.class_eval do
|
||||
|
||||
def on_while expression
|
||||
#puts expression.inspect
|
||||
condition , expressions = *expression
|
||||
def on_while statement
|
||||
#puts statement.inspect
|
||||
condition , statements = *statement
|
||||
condition = condition.first
|
||||
|
||||
# this is where the while ends and both branches meet
|
||||
@ -16,10 +16,10 @@ module Phisol
|
||||
|
||||
@method.source.add_code Register::IsZeroBranch.new(condition,merge)
|
||||
|
||||
last = process_all(expressions).last
|
||||
last = process_all(statements).last
|
||||
|
||||
# unconditionally branch to the start
|
||||
@method.source.add_code Register::AlwaysBranch.new(expression,start)
|
||||
@method.source.add_code Register::AlwaysBranch.new(statement,start)
|
||||
|
||||
# continue execution / compiling at the merge block
|
||||
@method.source.current merge
|
@ -108,7 +108,7 @@ module Virtual
|
||||
# happened
|
||||
|
||||
# But subsequent statements are still using the original block (self) to add code to
|
||||
# So the while expression creates the extra blocks, adds them and the code and then "moves"
|
||||
# So the while statement creates the extra blocks, adds them and the code and then "moves"
|
||||
# the insertion point along
|
||||
def current block
|
||||
@current = block
|
||||
|
@ -11,11 +11,11 @@ class Object
|
||||
end
|
||||
HERE
|
||||
|
||||
expressions = Virtual.machine.boot.parse_and_compile input
|
||||
if( expressions.first.is_a? Virtual::Self )
|
||||
expressions.first.type.instance_variable_set :@of_class , nil
|
||||
statements = Virtual.machine.boot.parse_and_compile input
|
||||
if( statements.first.is_a? Virtual::Self )
|
||||
statements.first.type.instance_variable_set :@of_class , nil
|
||||
end
|
||||
is = Sof.write(expressions)
|
||||
is = Sof.write(statements)
|
||||
#puts is
|
||||
assert_equal @output , is
|
||||
end
|
||||
|
@ -7,16 +7,16 @@ class CompilerTest < MiniTest::Test
|
||||
Virtual.machine.boot
|
||||
end
|
||||
def check
|
||||
res = Phisol::Compiler.compile( @expression )
|
||||
res = Phisol::Compiler.compile( @statement )
|
||||
assert res.is_a?(Virtual::Slot) , "compiler must compile to slot, not #{res.inspect}"
|
||||
end
|
||||
def test_function_expression
|
||||
@expression = s(:class, :Foo,
|
||||
def test_function_statement
|
||||
@statement = s(:class, :Foo,
|
||||
s(:derives, :Object),
|
||||
s(:expressions,
|
||||
s(:statements,
|
||||
s(:function, :int, s(:name, :foo),
|
||||
s(:parameters, s(:parameter, :ref, :x)),
|
||||
s(:expressions, s(:int, 5)))))
|
||||
s(:statements, s(:int, 5)))))
|
||||
check
|
||||
end
|
||||
end
|
||||
|
@ -6,7 +6,7 @@ class HelloTest < MiniTest::Test
|
||||
machine = Virtual.machine.boot
|
||||
Parfait::Space.object_space.get_class_by_name(:Integer).remove_instance_method :plus
|
||||
#TODO remove this hack: write proper aliases
|
||||
expressions = machine.parse_and_compile @string_input
|
||||
statements = machine.parse_and_compile @string_input
|
||||
output_at = "Register::CallImplementation"
|
||||
#{}"Register::CallImplementation"
|
||||
machine.run_before output_at
|
||||
|
@ -2,14 +2,14 @@ require_relative '../helper'
|
||||
|
||||
# simple tests to check parsing pworks and the first classes come out right.
|
||||
#
|
||||
# build up from small to check larger expressions are correct
|
||||
# build up from small to check larger statements are correct
|
||||
|
||||
module Fragments
|
||||
|
||||
def check
|
||||
expressions = Virtual.machine.boot.parse_and_compile @string_input
|
||||
statements = Virtual.machine.boot.parse_and_compile @string_input
|
||||
@expect.each_with_index do | should , i |
|
||||
exp_i = expressions[i]
|
||||
exp_i = statements[i]
|
||||
assert exp_i.is_a?(Virtual::Slot) , "compiles should return #{should}, not #{exp_i}"
|
||||
assert_equal should , exp_i.class
|
||||
end
|
||||
|
@ -8,11 +8,11 @@ class AddTest < MiniTest::Test
|
||||
Virtual.machine.boot
|
||||
code = s(:class, :Object,
|
||||
s(:derives, nil),
|
||||
s(:expressions,
|
||||
s(:statements,
|
||||
s(:function, :int,
|
||||
s(:name, :main),
|
||||
s(:parameters),
|
||||
s(:expressions,
|
||||
s(:statements,
|
||||
s(:call,
|
||||
s(:name, :plus),
|
||||
s(:arguments , s(:int , 5)),
|
||||
|
@ -53,8 +53,8 @@ HERE
|
||||
puts parts.inspect
|
||||
Phisol::Compiler.compile( parts )
|
||||
|
||||
# expressions = Virtual.machine.boot.parse_and_compile @string_input
|
||||
# Phisol::Compiler.compile( expressions , Virtual.machine.space.get_main )
|
||||
# statements = Virtual.machine.boot.parse_and_compile @string_input
|
||||
# Phisol::Compiler.compile( statements , Virtual.machine.space.get_main )
|
||||
Virtual.machine.run_before "Register::CallImplementation"
|
||||
@interpreter = Interpreter::Interpreter.new
|
||||
@interpreter.start Virtual.machine.init
|
||||
|
@ -7,11 +7,11 @@ class TestPuts < MiniTest::Test
|
||||
Virtual.machine.boot
|
||||
code = s(:class, :Object,
|
||||
s(:derives, nil),
|
||||
s(:expressions,
|
||||
s(:statements,
|
||||
s(:function, :int,
|
||||
s(:name, :main),
|
||||
s(:parameters),
|
||||
s(:expressions,
|
||||
s(:statements,
|
||||
s(:call,
|
||||
s(:name, :putstring),
|
||||
s(:arguments),
|
||||
|
@ -24,7 +24,7 @@ class TestRunner < MiniTest::Test
|
||||
syntax = parser.parse_with_debug(string)
|
||||
assert syntax
|
||||
parts = Parser::Transform.new.apply(syntax)
|
||||
# file is a list of expressions, all but the last must be a function
|
||||
# file is a list of statements, all but the last must be a function
|
||||
# and the last is wrapped as a main
|
||||
parts.each_with_index do |part,index|
|
||||
if index == (parts.length - 1)
|
||||
|
Loading…
Reference in New Issue
Block a user