implement store.* (STO, RCL, PURGE, ...)
This commit is contained in:
parent
169e5249db
commit
8816e6ce32
6 changed files with 303 additions and 30 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]}'" }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue