diff --git a/language.rb b/language.rb index 0124808..0a5d2e2 100644 --- a/language.rb +++ b/language.rb @@ -167,6 +167,8 @@ module Rpl @dictionary.add( 'sneg', proc { |stack, dictionary| Rpl::Lang::Core.sto_negate( stack, dictionary ) } ) @dictionary.add( 'sinv', proc { |stack, dictionary| Rpl::Lang::Core.sto_inverse( stack, dictionary ) } ) # @dictionary.add( 'edit', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # edit a variable content + @dictionary.add( 'lsto', proc { |stack, dictionary| Rpl::Lang::Core.lsto( stack, dictionary ) } ) # store to local variable + @dictionary.add( '↴', proc { |stack, dictionary| Rpl::Lang::Core.lsto( stack, dictionary ) } ) # alias # PROGRAM @dictionary.add( 'eval', proc { |stack, dictionary| Rpl::Lang::Core.eval( stack, dictionary ) } ) diff --git a/lib/core/store.rb b/lib/core/store.rb index d341727..8fbb988 100644 --- a/lib/core/store.rb +++ b/lib/core/store.rb @@ -23,6 +23,24 @@ module Rpl [stack, dictionary] end + # store a local variable + def lsto( stack, dictionary ) + stack, args = Rpl::Lang::Core.stack_extract( stack, [%i[name], :any] ) + + dictionary.add_local_var( args[0][:value], + proc { |stk, dict, rcl_only = false| + stk << args[1] + + if rcl_only + [stk, dict] + else + Rpl::Lang::Core.eval( stk, dict ) + end + } ) + + [stack, dictionary] + end + # recall a variable. ex: 'name' rcl def rcl( stack, dictionary ) stack, args = Rpl::Lang::Core.stack_extract( stack, [%i[name]] ) @@ -46,7 +64,7 @@ module Rpl # list all variables def vars( stack, dictionary ) stack << { type: :list, - value: dictionary.vars.keys } + value: dictionary.vars.keys + dictionary.local_vars_layers.reduce([]) { |memo, layer| memo + layer.keys } } [stack, dictionary] end diff --git a/lib/dictionary.rb b/lib/dictionary.rb index 735fb05..db10dd0 100644 --- a/lib/dictionary.rb +++ b/lib/dictionary.rb @@ -3,12 +3,63 @@ module Rpl module Lang class Dictionary - attr_reader :vars + attr_reader :vars, + :local_vars_layers + + def add( name, implementation ) + @words[ name ] = implementation + end + + def add_var( name, implementation ) + @vars[ name ] = implementation + end + + def remove_vars( names ) + names.each { |name| @vars.delete( name ) } + end + + def remove_var( name ) + remove_vars( [name] ) + end + + def remove_all_vars + @vars = {} + end + + def add_local_vars_layer + @local_vars_layers << {} + end + + def add_local_var( name, implementation ) + @local_vars_layers.last[ name ] = implementation + end + + def remove_local_vars( names ) + names.each { |name| @local_vars_layers.last.delete( name ) } + end + + def remove_local_var( name ) + remove_local_vars( [name] ) + end + + def remove_local_vars_layer + @local_vars_layers.pop + end + + def lookup( name ) + local_var = @local_vars_layers.reverse.find { |layer| layer[ name ] } + word = local_var.nil? ? nil : local_var[name] + word ||= @vars[ name ] + word ||= @words[ name ] + + word + end def initialize @parser = Parser.new @words = {} @vars = {} + @local_vars_layers = [] # GENERAL add( 'nop', proc { |stack, dictionary| Rpl::Lang::Core.nop( stack, dictionary ) } ) @@ -212,29 +263,6 @@ module Rpl # add( 'fload', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # ( filename -- … ) « FREAD EVAL » # add( 'fwrite', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # ( content filename mode -- ) write content into filename using mode (w, a, …) end - - def add( name, implementation ) - @words[ name ] = implementation - end - - def add_var( name, implementation ) - @vars[ name ] = implementation - end - - def remove_var( name ) - @vars.delete( name ) - end - - def remove_all_vars - @vars = {} - end - - def lookup( name ) - word = @words[ name ] - word ||= @vars[ name ] - - word - end end end end diff --git a/lib/runner.rb b/lib/runner.rb index 4af9dbc..331caab 100644 --- a/lib/runner.rb +++ b/lib/runner.rb @@ -6,6 +6,8 @@ module Rpl def initialize; end def run_input( input, stack, dictionary ) + dictionary.add_local_vars_layer + input.each do |elt| case elt[:type] when :word @@ -24,6 +26,8 @@ module Rpl end end + dictionary.remove_local_vars_layer + [stack, dictionary] end end diff --git a/spec/language_store_spec.rb b/spec/language_store_spec.rb index 15d4259..42d40e7 100644 --- a/spec/language_store_spec.rb +++ b/spec/language_store_spec.rb @@ -15,6 +15,22 @@ class TestLanguageProgram < Test::Unit::TestCase lang.stack end + def test_lsto + lang = Rpl::Language.new + lang.run "« 2 'deux' lsto deux dup * » eval 'deux' rcl" + + assert_empty lang.dictionary.local_vars_layers + assert_equal [{ value: 4, type: :numeric, base: 10 }], + lang.stack + + lang = Rpl::Language.new + lang.run "« 2 'deux' lsto « 3 'trois' lsto trois drop » eval deux dup * » eval 'deux' rcl 'trois' rcl" + + assert_empty lang.dictionary.local_vars_layers + assert_equal [{ value: 4, type: :numeric, base: 10 }], + lang.stack + end + def test_rcl lang = Rpl::Language.new lang.run '« 2 dup * » \'quatre\' sto \'quatre\' rcl'