diff --git a/ruby/Gemfile b/ruby/Gemfile index e76dde0..ccdc25d 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -7,3 +7,4 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem "minitest" gem "pry" gem "rake" +gem "typeprof" diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index 9900ff7..416e677 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -8,6 +8,9 @@ GEM coderay (~> 1.1) method_source (~> 1.0) rake (13.0.6) + rbs (2.6.0) + typeprof (0.21.3) + rbs (>= 1.8.1) PLATFORMS arm64-darwin-21 @@ -16,6 +19,7 @@ DEPENDENCIES minitest pry rake + typeprof BUNDLED WITH 2.3.18 diff --git a/ruby/lib/lox/ast_printer.rb b/ruby/lib/lox/ast_printer.rb index b79776b..a073805 100644 --- a/ruby/lib/lox/ast_printer.rb +++ b/ruby/lib/lox/ast_printer.rb @@ -22,6 +22,10 @@ module Lox parenthesize("assign #{expr.name.lexeme}", expr.value) end + def visit_block(expr) + parenthesize("block", *expr.stmts) + end + private def parenthesize(name, *exprs) diff --git a/ruby/lib/lox/interpreter.rb b/ruby/lib/lox/interpreter.rb index b712ffe..3725811 100644 --- a/ruby/lib/lox/interpreter.rb +++ b/ruby/lib/lox/interpreter.rb @@ -11,11 +11,28 @@ module Lox # we're going to do it in the runner instead. def interpret(stmts) stmts.each do |stmt| - evaluate(stmt) + execute(stmt) end end - def evaluate(stmt) = stmt.accept(self) + def evaluate(expr) = expr.accept(self) + def execute(stmt) = stmt.accept(self) + + def visit_block(stmt) + execute_block(stmt.stmts, Environment.new(@env)) + nil + end + + def execute_block(stmts, env) + prev_env = @env + @env = env + + stmts.each do |stmt| + execute(stmt) + end + ensure + @env = prev_env + end def visit_expr(expr) evaluate(expr.expr) diff --git a/ruby/lib/lox/parser.rb b/ruby/lib/lox/parser.rb index 197cdef..bfece21 100644 --- a/ruby/lib/lox/parser.rb +++ b/ruby/lib/lox/parser.rb @@ -38,6 +38,7 @@ module Lox def statement return print if match?(:PRINT) + return Stmt::Block.new(block) if match?(:LEFT_BRACE) expression_stmt end @@ -54,6 +55,15 @@ module Lox Stmt::Expr.new(value) end + def block + statements = [] + until check?(:RIGHT_BRACE) || eot? + statements << declaration + end + consume!(:RIGHT_BRACE, "Expect '}' after block.") + statements + end + def assignment expr = equality diff --git a/ruby/lib/lox/stmt.rb b/ruby/lib/lox/stmt.rb index 9f5d6c2..ba05b17 100644 --- a/ruby/lib/lox/stmt.rb +++ b/ruby/lib/lox/stmt.rb @@ -2,6 +2,10 @@ require_relative "visitable" module Lox module Stmt + Block = Struct.new(:stmts) do + include Visitable + end + Expr = Struct.new(:expr) do include Visitable end diff --git a/ruby/test/lox/test_interpreter.rb b/ruby/test/lox/test_interpreter.rb index fde7786..10605cf 100644 --- a/ruby/test/lox/test_interpreter.rb +++ b/ruby/test/lox/test_interpreter.rb @@ -116,6 +116,40 @@ class TestInterpreter < Lox::Test SRC end + def test_block + assert_interpreted <<~EXPECTED.chomp, <<~SRC + inner a + outer b + global c + outer a + outer b + global c + global a + global b + global c + EXPECTED + var a = "global a"; + var b = "global b"; + var c = "global c"; + { + var a = "outer a"; + var b = "outer b"; + { + var a = "inner a"; + print a; + print b; + print c; + } + print a; + print b; + print c; + } + print a; + print b; + print c; + SRC + end + private def assert_interpreted(expected, src) diff --git a/ruby/test/lox/test_parser.rb b/ruby/test/lox/test_parser.rb index 67b80ae..e5ad65c 100644 --- a/ruby/test/lox/test_parser.rb +++ b/ruby/test/lox/test_parser.rb @@ -60,6 +60,15 @@ class TestParser < Lox::Test assert_parsed "(print (assign foo 42.0))", :declaration, "print foo = 42.0;" end + def test_block + assert_parsed "(block (var foo bar) (print (var foo)))", :statement, <<~SRC + { + var foo = "bar"; + print foo; + } + SRC + end + private def assert_parsed(expected, name, src)