diff --git a/lib/core/program.rb b/lib/core/program.rb index 5860140..1c253be 100644 --- a/lib/core/program.rb +++ b/lib/core/program.rb @@ -15,7 +15,6 @@ module Rpl stack, dictionary = Rpl::Lang::Runner.new.run_input( parsed_input, stack, dictionary ) - # TODO: check that STO actually updates dictionary [stack, dictionary] end diff --git a/lib/core/store.rb b/lib/core/store.rb index a3775c5..23caeb5 100644 --- a/lib/core/store.rb +++ b/lib/core/store.rb @@ -5,15 +5,113 @@ module Rpl module Core module_function - # evaluate (run) a program, or recall a variable. ex: 'my_prog' eval + # store a variable. ex: 1 'name' sto def sto( stack, dictionary ) - stack, args = Rpl::Lang::Core.stack_extract( stack, [:any, %i[name]] ) + stack, args = Rpl::Lang::Core.stack_extract( stack, [%i[name], :any] ) - # TODO - dictionary.add( args[1][:value], proc { |stk| Rpl::Lang::Core.eval( stk << args[0][:value], dictionary ) } ) + dictionary.add_var( args[0][:value][1..-2], # removing surrounding ' + 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]] ) + + word = dictionary[ args[0][:value][1..-2] ] + + stack, dictionary = word.call( stack, dictionary, true ) unless word.nil? + + [stack, dictionary] + end + + # delete a variable. ex: 'name' purge + def purge( stack, dictionary ) + stack, args = Rpl::Lang::Core.stack_extract( stack, [%i[name]] ) + + dictionary.remove_var( args[0][:value][1..-2] ) + + [stack, dictionary] + end + + # list all variables + def vars( stack, dictionary ) + # stack << { type: :list, + # value: dictionary.vars.keys.map { |name| { value: name, type: :name } } } + stack << { type: :list, + value: dictionary.vars.keys.map { |name| "'#{name}'" } } + + [stack, dictionary] + end + + # erase all variables + def clusr( stack, dictionary ) + dictionary.remove_all_vars + + [stack, dictionary] + end + + def edit( stack, dictionary ) + # TODO + [stack, dictionary] + end + + # add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+ + def sto_add( stack, dictionary ) + stack << { value: '« dup type "name" == « swap » ift over rcl + swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end + + # substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto- + def sto_subtract( stack, dictionary ) + stack << { value: '« dup type "name" == « swap » ift over rcl swap - swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end + + # multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto* + def sto_multiply( stack, dictionary ) + stack << { value: '« dup type "name" == « swap » ift over rcl * swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end + + # divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/ + def sto_divide( stack, dictionary ) + stack << { value: '« dup type "name" == « swap » ift over rcl swap / swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end + + # negate a variable. ex: 'name' sneg + def sto_negate( stack, dictionary ) + stack << { value: '« dup rcl -1 * swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end + + # inverse a variable. ex: 1 'name' sinv + def sto_inverse( stack, dictionary ) + stack << { value: '« dup rcl 1 swap / swap sto »', + type: :program } + + Rpl::Lang::Core.eval( stack, dictionary ) + end end end end diff --git a/lib/dictionary.rb b/lib/dictionary.rb index 89c7c2f..9fe4ff9 100644 --- a/lib/dictionary.rb +++ b/lib/dictionary.rb @@ -3,9 +3,12 @@ module Rpl module Lang class Dictionary + attr_reader :vars + def initialize @parser = Parser.new @words = {} + @vars = {} # GENERAL add( 'nop', proc { |stack, dictionary| Rpl::Lang::Core.nop( stack, dictionary ) } ) @@ -133,19 +136,19 @@ module Rpl add( 'repeat', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # used with while # STORE - add( 'sto', proc { |stack, dictionary| Rpl::Lang::Core.sto( stack, dictionary ) } ) # store a variable. ex: 1 'name' sto + add( 'sto', proc { |stack, dictionary| Rpl::Lang::Core.sto( stack, dictionary ) } ) add( '▶', proc { |stack, dictionary| Rpl::Lang::Core.sto( stack, dictionary ) } ) # alias - add( 'rcl', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # recall a variable. ex: 'name' rcl - add( 'purge', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # delete a variable. ex: 'name' purge - add( 'vars', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # list all variables - add( 'clusr', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # erase all variables + add( 'rcl', proc { |stack, dictionary| Rpl::Lang::Core.rcl( stack, dictionary ) } ) + add( 'purge', proc { |stack, dictionary| Rpl::Lang::Core.purge( stack, dictionary ) } ) + add( 'vars', proc { |stack, dictionary| Rpl::Lang::Core.vars( stack, dictionary ) } ) + add( 'clusr', proc { |stack, dictionary| Rpl::Lang::Core.clusr( stack, dictionary ) } ) add( 'edit', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # edit a variable content - add( 'sto+', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+ - add( 'sto-', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto- - add( 'sto*', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto* - add( 'sto/', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/ - add( 'sneg', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # negate a variable. ex: 'name' sneg - add( 'sinv', proc { |stack, dictionary| Rpl::Lang::Core.__todo( stack, dictionary ) } ) # inverse a variable. ex: 1 'name' sinv + add( 'sto+', proc { |stack, dictionary| Rpl::Lang::Core.sto_add( stack, dictionary ) } ) + add( 'sto-', proc { |stack, dictionary| Rpl::Lang::Core.sto_subtract( stack, dictionary ) } ) + add( 'sto*', proc { |stack, dictionary| Rpl::Lang::Core.sto_multiply( stack, dictionary ) } ) + add( 'sto/', proc { |stack, dictionary| Rpl::Lang::Core.sto_divide( stack, dictionary ) } ) + add( 'sneg', proc { |stack, dictionary| Rpl::Lang::Core.sto_negate( stack, dictionary ) } ) + add( 'sinv', proc { |stack, dictionary| Rpl::Lang::Core.sto_inverse( stack, dictionary ) } ) # PROGRAM add( 'eval', proc { |stack, dictionary| Rpl::Lang::Core.eval( stack, dictionary ) } ) @@ -194,11 +197,24 @@ module Rpl @words[ name ] = implementation end - def lookup( name ) - @words[ name ] if @words.include?( name ) + def add_var( name, implementation ) + @vars[ name ] = implementation end - # TODO: alias + def remove_var( name ) + @vars.delete( name ) + end + + def remove_all_vars + @vars = {} + end + + def []( name ) + word = @words[ name ] + word ||= @vars[ name ] + + word + end end end end diff --git a/lib/runner.rb b/lib/runner.rb index d020b84..7f064e8 100644 --- a/lib/runner.rb +++ b/lib/runner.rb @@ -9,7 +9,7 @@ module Rpl input.each do |elt| case elt[:type] when :word - command = dictionary.lookup( elt[:value] ) + command = dictionary[ elt[:value] ] if command.nil? stack << { type: :name, value: "'#{elt[:value]}'" } diff --git a/spec/language_program_spec.rb b/spec/language_program_spec.rb index 4e9b239..a1b0dda 100644 --- a/spec/language_program_spec.rb +++ b/spec/language_program_spec.rb @@ -26,11 +26,4 @@ class TestLanguageProgram < Test::Unit::TestCase { value: 4, type: :numeric, base: 10 }], stack end - - def test_sto - stack, _dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup »', type: :program }, - { value: "'quatre'", type: :name }], Rpl::Lang::Dictionary.new ) - - assert_equal [], stack - end end diff --git a/spec/language_store_spec.rb b/spec/language_store_spec.rb index b3fc65e..446cf13 100644 --- a/spec/language_store_spec.rb +++ b/spec/language_store_spec.rb @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true require 'test/unit' @@ -7,10 +6,178 @@ require_relative '../language' class TestLanguageProgram < Test::Unit::TestCase def test_sto - stack, _dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup »', type: :program }, + stack, dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup * »', type: :program }, + { value: "'quatre'", type: :name }], + Rpl::Lang::Dictionary.new ) + assert_equal [], stack + + stack, _dictionary = Rpl::Lang::Core.eval( [{ value: 'quatre', type: :word }], + dictionary ) + assert_equal [{ value: 4, type: :numeric, base: 10 }], + stack + end + + def test_rcl + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup * »', type: :program }, { value: "'quatre'", type: :name }], Rpl::Lang::Dictionary.new ) - assert_equal [], stack + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'quatre'", type: :name }], + dictionary ) + assert_equal [{ value: '« 2 dup * »', type: :program }], + stack + end + + def test_purge + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup * »', type: :program }, + { value: "'quatre'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.purge( [{ value: "'quatre'", type: :name }], + dictionary ) + assert_equal nil, + dictionary['quatre'] + end + + def test_vars + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup * »', type: :program }, + { value: "'quatre'", type: :name }], + Rpl::Lang::Dictionary.new ) + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 1, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + + stack, _dictionary = Rpl::Lang::Core.vars( [], + dictionary ) + assert_equal [{ value: ["'quatre'", "'un'"], type: :list }], + stack + end + + def test_clusr + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: '« 2 dup * »', type: :program }, + { value: "'quatre'", type: :name }], + Rpl::Lang::Dictionary.new ) + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 1, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + + _stack, dictionary = Rpl::Lang::Core.clusr( [], + dictionary ) + assert_equal( {}, + dictionary.vars ) + end + + def test_sto_add + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 1, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_add( [{ value: "'un'", type: :name }, + { value: 3, type: :numeric, base: 10 }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 4, type: :numeric, base: 10 }], + stack + + _stack, dictionary = Rpl::Lang::Core.sto_add( [{ value: 3, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 7, type: :numeric, base: 10 }], + stack + end + + def test_sto_subtract + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 1, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_subtract( [{ value: "'un'", type: :name }, + { value: 3, type: :numeric, base: 10 }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: -2, type: :numeric, base: 10 }], + stack + + _stack, dictionary = Rpl::Lang::Core.sto_subtract( [{ value: 3, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: -5, type: :numeric, base: 10 }], + stack + end + + def test_sto_multiply + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 2, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_multiply( [{ value: "'un'", type: :name }, + { value: 3, type: :numeric, base: 10 }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 6, type: :numeric, base: 10 }], + stack + + _stack, dictionary = Rpl::Lang::Core.sto_multiply( [{ value: 3, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 18, type: :numeric, base: 10 }], + stack + end + + def test_sto_divide + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 2, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_divide( [{ value: "'un'", type: :name }, + { value: 4.0, type: :numeric, base: 10 }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 0.5, type: :numeric, base: 10 }], + stack + + _stack, dictionary = Rpl::Lang::Core.sto_divide( [{ value: 5, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 0.1, type: :numeric, base: 10 }], + stack + end + + def test_sto_negate + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 2, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_negate( [{ value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: -2, type: :numeric, base: 10 }], + stack + end + + def test_sto_inverse + _stack, dictionary = Rpl::Lang::Core.sto( [{ value: 2, type: :numeric, base: 10 }, + { value: "'un'", type: :name }], + Rpl::Lang::Dictionary.new ) + + _stack, dictionary = Rpl::Lang::Core.sto_inverse( [{ value: "'un'", type: :name }], + dictionary ) + stack, _dictionary = Rpl::Lang::Core.rcl( [{ value: "'un'", type: :name }], + dictionary ) + assert_equal [{ value: 0.5, type: :numeric, base: 10 }], + stack end end