implement store.* (STO, RCL, PURGE, ...)

This commit is contained in:
Gwenhael Le Moine 2021-12-08 12:46:57 +01:00
parent 169e5249db
commit 8816e6ce32
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
6 changed files with 303 additions and 30 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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]}'" }

View file

@ -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

View file

@ -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