From 92a9372dccc3f5bf30e5b804d5520edda3c36153 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Mon, 28 Apr 2014 21:21:12 +0300 Subject: [PATCH] moved nodes to parser (one more layer) and fixed tests. also adds assignment --- lib/crystal.rb | 1 - lib/parser/basic_types.rb | 3 +++ lib/parser/composed.rb | 6 +++-- lib/{vm => parser}/nodes.rb | 51 ++++++++----------------------------- lib/parser/transform.rb | 19 ++++++++------ test/test_nodes.rb | 32 +++++++++++------------ test/test_parser.rb | 8 ++++++ test/test_transform.rb | 38 ++++++++++++++------------- 8 files changed, 74 insertions(+), 84 deletions(-) rename lib/{vm => parser}/nodes.rb (56%) diff --git a/lib/crystal.rb b/lib/crystal.rb index 43033691..e1d5af56 100644 --- a/lib/crystal.rb +++ b/lib/crystal.rb @@ -4,5 +4,4 @@ require "asm/program" require "elf/object_writer" require 'parser/composed' require 'parser/transform' -require 'vm/nodes' diff --git a/lib/parser/basic_types.rb b/lib/parser/basic_types.rb index 167e835a..b1844d6b 100644 --- a/lib/parser/basic_types.rb +++ b/lib/parser/basic_types.rb @@ -6,9 +6,12 @@ module Parser include Parslet rule(:space) { match('\s').repeat(1) } rule(:space?) { space.maybe } + rule(:eol) { (str("\n") >> space?) | any.absent? } + rule(:double_quote){ str('"') } rule(:minus) { str('-') } rule(:plus) { str('+') } + rule(:equal_sign) { str('=') >> space?} rule(:sign) { plus | minus } rule(:dot) { str('.') } rule(:digit) { match('[0-9]') } diff --git a/lib/parser/composed.rb b/lib/parser/composed.rb index 14fe8506..4766fcda 100644 --- a/lib/parser/composed.rb +++ b/lib/parser/composed.rb @@ -25,6 +25,8 @@ module Parser rule(:function_call) { name.as(:function_call) >> argument_list } + rule(:assignment) { name.as(:asignee) >> equal_sign >> expression.as(:asigned) >> eol } + rule(:expression) { conditional | function_call | integer | name } def delimited_expressions( delimit ) @@ -36,7 +38,7 @@ module Parser delimited_expressions(keyword_else).as(:if_true) >> delimited_expressions(keyword_end).as(:if_false) } - + rule(:expressions_else) { delimited_expressions(keyword_else) } rule(:expressions_end) { delimited_expressions(keyword_end) } @@ -51,6 +53,6 @@ module Parser ((name.as(:parmeter) >> (comma >> name.as(:parmeter)).repeat(0)).maybe).as(:parmeter_list) >> right_parenthesis } - rule(:root){ function_definition | expression } + rule(:root){ function_definition | expression | assignment } end end diff --git a/lib/vm/nodes.rb b/lib/parser/nodes.rb similarity index 56% rename from lib/vm/nodes.rb rename to lib/parser/nodes.rb index 8bbdaa41..478cf995 100644 --- a/lib/vm/nodes.rb +++ b/lib/parser/nodes.rb @@ -1,7 +1,6 @@ -module Vm - +# ast classes +module Parser class Expression - # evey Expression has a eval function that returns a value def eval raise "abstract" end @@ -24,9 +23,6 @@ module Vm def == other compare other , [:value] end - def eval(context, builder) - builder.mov "r0" , value - end end class NameExpression < Expression @@ -37,12 +33,6 @@ module Vm def == other compare other , [:name] end - def eval(context, builder) - param_names = context[:params] || [] - position = param_names.index(name) - raise "Unknown parameter #{name}" unless position - builder.iload position - end end class FuncallExpression < Expression @@ -53,11 +43,6 @@ module Vm def == other compare other , [:name , :args] end - def eval(context, builder) - args.each { |a| a.eval(context, builder) } - types = [builder.int] * (args.length + 1) - builder.invokestatic builder.class_builder, name, types - end end class ConditionalExpression < Expression @@ -68,21 +53,17 @@ module Vm def == other compare other , [:cond, :if_true, :if_false] end - def eval(context, builder) - cond.eval context, builder - - builder.ifeq :else - - if_true.eval context, builder - builder.goto :endif - - builder.label :else - if_false.eval context, builder - - builder.label :endif - end end + class AssignmentExpression < Expression + attr_reader :assignee, :assigned + def initialize assignee, assigned + @assignee, @assigned = assignee, assigned + end + def == other + compare other , [:assignee, :assigned] + end + end class FunctionExpression < Expression attr_reader :name, :params, :block def initialize name, params, block @@ -91,15 +72,5 @@ module Vm def == other compare other , [:name, :params, :block] end - def eval(context, builder) - param_names = [params].flatten.map(&:name) - context[:params] = param_names - types = [builder.int] * (param_names.count + 1) - - builder.public_static_method(self.name, [], *types) do |method| - self.block.eval(context, method) - method.ireturn - end - end end end diff --git a/lib/parser/transform.rb b/lib/parser/transform.rb index 39d557dd..030e9303 100644 --- a/lib/parser/transform.rb +++ b/lib/parser/transform.rb @@ -1,10 +1,10 @@ require 'parslet' -require 'vm/nodes' +require_relative 'nodes' module Parser class Transform < Parslet::Transform - rule(:integer => simple(:value)) { Vm::IntegerExpression.new(value.to_i) } - rule(:name => simple(:name)) { Vm::NameExpression.new(name.to_s) } + rule(:integer => simple(:value)) { IntegerExpression.new(value.to_i) } + rule(:name => simple(:name)) { NameExpression.new(name.to_s) } rule(:argument => simple(:argument)) { argument } rule(:argument_list => sequence(:argument_list)) { argument_list } @@ -12,17 +12,17 @@ module Parser # need TWO transform rules, for one/many arguments (see the[] wrapping in the first) rule(:function_call => simple(:function_call), :argument_list => simple(:argument)) do - Vm::FuncallExpression.new(function_call.name, [argument]) + FuncallExpression.new(function_call.name, [argument]) end rule( :function_call => simple(:function_call), :argument_list => sequence(:argument_list)) do - Vm::FuncallExpression.new(function_call.name, argument_list) + FuncallExpression.new(function_call.name, argument_list) end rule(:conditional => simple(:conditional), :if_true => {:expressions => sequence(:if_true)}, :if_false => {:expressions => sequence(:if_false)}) do - Vm::ConditionalExpression.new(conditional, if_true, if_false) + ConditionalExpression.new(conditional, if_true, if_false) end rule(:parmeter => simple(:parmeter)) { parmeter } @@ -32,15 +32,18 @@ module Parser rule(:function_definition => simple(:function_definition), :parmeter_list => simple(:parmeter), :expressions => sequence(:expressions)) do - Vm::FunctionExpression.new(function_definition.name, [parmeter], expressions) + FunctionExpression.new(function_definition.name, [parmeter], expressions) end rule(:function_definition => simple(:function_definition), :parmeter_list => sequence(:parmeter_list), :expressions => sequence(:expressions)) do - Vm::FunctionExpression.new(function_definition.name, parmeter_list, expressions) + FunctionExpression.new(function_definition.name, parmeter_list, expressions) end + rule(:asignee => simple(:left) , :asigned => simple(:right) ) do + AssignmentExpression.new(left.name, right ) + end #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 diff --git a/test/test_nodes.rb b/test/test_nodes.rb index fe43a422..3ce62343 100644 --- a/test/test_nodes.rb +++ b/test/test_nodes.rb @@ -22,37 +22,37 @@ class TestNodes < MiniTest::Test def test_number @input = '42 ' - @expected = Vm::IntegerExpression.new(42) + @expected = Parser::IntegerExpression.new(42) @parser = @parser.integer check end def test_name @input = 'foo ' - @expected = Vm::NameExpression.new('foo') + @expected = Parser::NameExpression.new('foo') @parser = @parser.name check end def test_one_argument @input = '(42)' - @expected = { :argument_list => Vm::IntegerExpression.new(42) } + @expected = { :argument_list => Parser::IntegerExpression.new(42) } @parser = @parser.argument_list check end def test_argument_list @input = '(42, foo)' - @expected = [Vm::IntegerExpression.new(42), - Vm::NameExpression.new('foo')] + @expected = [Parser::IntegerExpression.new(42), + Parser::NameExpression.new('foo')] @parser = @parser.argument_list check end def test_function_call @input = 'baz(42, foo)' - @expected = Vm::FuncallExpression.new 'baz', [Vm::IntegerExpression.new(42), - Vm::NameExpression.new('foo')] + @expected = Parser::FuncallExpression.new 'baz', [Parser::IntegerExpression.new(42), + Parser::NameExpression.new('foo')] @parser = @parser.function_call check @@ -64,7 +64,7 @@ class TestNodes < MiniTest::Test 5 else HERE - @expected = {:expressions=>[ Vm::IntegerExpression.new(4), Vm::IntegerExpression.new(5)]} + @expected = {:expressions=>[ Parser::IntegerExpression.new(4), Parser::IntegerExpression.new(5)]} @parser = @parser.expressions_else check @@ -77,8 +77,8 @@ name call(4,6) end HERE - @expected = {:expressions=> [Vm::IntegerExpression.new(5), Vm::NameExpression.new("name"), - Vm::FuncallExpression.new("call", [Vm::IntegerExpression.new(4), Vm::IntegerExpression.new(6) ]) ] } + @expected = {:expressions=> [Parser::IntegerExpression.new(5), Parser::NameExpression.new("name"), + Parser::FuncallExpression.new("call", [Parser::IntegerExpression.new(4), Parser::IntegerExpression.new(6) ]) ] } @parser = @parser.expressions_end check end @@ -91,9 +91,9 @@ else 667 end HERE - @expected = Vm::ConditionalExpression.new( Vm::IntegerExpression.new(0), - [Vm::IntegerExpression.new(42)], - [Vm::IntegerExpression.new(667)]) + @expected = Parser::ConditionalExpression.new( Parser::IntegerExpression.new(0), + [Parser::IntegerExpression.new(42)], + [Parser::IntegerExpression.new(667)]) @parser = @parser.conditional check end @@ -104,9 +104,9 @@ def foo(x) 5 end HERE - @expected = Vm::FunctionExpression.new('foo', - [Vm::NameExpression.new('x')], - [Vm::IntegerExpression.new(5)]) + @expected = Parser::FunctionExpression.new('foo', + [Parser::NameExpression.new('x')], + [Parser::IntegerExpression.new(5)]) @parser = @parser.function_definition check end diff --git a/test/test_parser.rb b/test/test_parser.rb index 65829692..2deaec8a 100644 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -105,4 +105,12 @@ HERE @parser = @parser.function_definition check end + + def test_assignment + @input = "a = 5" + @expected = { :asignee => { :name=>"a" } , :asigned => { :integer => "5" } } + @parser = @parser.assignment + check + end + end diff --git a/test/test_transform.rb b/test/test_transform.rb index f86843f4..e3739d70 100644 --- a/test/test_transform.rb +++ b/test/test_transform.rb @@ -1,7 +1,5 @@ require_relative 'helper' -include Vm - class TransformTest < MiniTest::Test def setup @@ -14,29 +12,29 @@ class TransformTest < MiniTest::Test end def test_number @input = {:integer => '42'} - @expected = Vm::IntegerExpression.new(42) + @expected = Parser::IntegerExpression.new(42) check_equals assert_equal 42 , @expected.value end def test_name @input = {:name => 'foo'} - @expected = Vm::NameExpression.new('foo') + @expected = Parser::NameExpression.new('foo') check_equals end def test_argument_list @input = {:argument_list => [{:argument => {:integer => '42'}}, {:argument => {:name => 'foo'}}]} - @expected = [Vm::IntegerExpression.new(42), - Vm::NameExpression.new('foo')] + @expected = [Parser::IntegerExpression.new(42), + Parser::NameExpression.new('foo')] check_equals end def test_single_argument @input = {:function_call => {:name => 'foo'}, :argument_list => {:argument => {:integer => '42'} } } - @expected = Vm::FuncallExpression.new 'foo', [Vm::IntegerExpression.new(42)] + @expected = Parser::FuncallExpression.new 'foo', [Parser::IntegerExpression.new(42)] check_equals end @@ -45,8 +43,8 @@ class TransformTest < MiniTest::Test @input = {:function_call => {:name => 'baz'}, :argument_list => [{:argument => {:integer => '42'}}, {:argument => {:name => 'foo'}}]} - @expected = Vm::FuncallExpression.new 'baz', [Vm::IntegerExpression.new(42), - Vm::NameExpression.new('foo')] + @expected = Parser::FuncallExpression.new 'baz', [Parser::IntegerExpression.new(42), + Parser::NameExpression.new('foo')] check_equals end @@ -55,20 +53,20 @@ class TransformTest < MiniTest::Test @input = { :conditional => { :integer => "0"}, :if_true => { :expressions => [ { :integer => "42" } ] } , :if_false => { :expressions => [ { :integer => "667" } ] } } - @expected = Vm::ConditionalExpression.new( Vm::IntegerExpression.new(0), - [Vm::IntegerExpression.new(42)], - [Vm::IntegerExpression.new(667)]) + @expected = Parser::ConditionalExpression.new( Parser::IntegerExpression.new(0), + [Parser::IntegerExpression.new(42)], + [Parser::IntegerExpression.new(667)]) check_equals end def test_parmeter @input = {:parmeter => { :name => "foo"}} - @expected = Vm::NameExpression.new('foo') + @expected = Parser::NameExpression.new('foo') check_equals end def test_parmeter_list @input = {:parmeter_list => [{:parmeter => { :name => "foo"}}]} - @expected = [Vm::NameExpression.new('foo')] + @expected = [Parser::NameExpression.new('foo')] check_equals end @@ -76,9 +74,15 @@ class TransformTest < MiniTest::Test @input = {:function_definition => {:name => 'foo'}, :parmeter_list => {:parmeter => {:name => 'x'}}, :expressions => [{:integer => '5'}]} - @expected = Vm::FunctionExpression.new('foo', - [Vm::NameExpression.new('x')], - [Vm::IntegerExpression.new(5)]) + @expected = Parser::FunctionExpression.new('foo', + [Parser::NameExpression.new('x')], + [Parser::IntegerExpression.new(5)]) + check_equals + end + + def test_assignment + @input = { :asignee => { :name=>"a" } , :asigned => { :integer => "5" } } + @expected = Parser::AssignmentExpression.new("a", Parser::IntegerExpression.new(5) ) check_equals end end