implement and test part of the language; exit using quit
This commit is contained in:
parent
ca315724ff
commit
0f924355ca
7 changed files with 582 additions and 8 deletions
29
lib/core.rb
Normal file
29
lib/core.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
require_relative './language/general'
|
||||||
|
require_relative './language/operations'
|
||||||
|
require_relative './language/stack'
|
||||||
|
|
||||||
|
module Rpn
|
||||||
|
module Core
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def stack_extract( stack, needs )
|
||||||
|
raise 'Not enough elements' if stack.size < needs.size
|
||||||
|
|
||||||
|
args = []
|
||||||
|
needs.each do |need|
|
||||||
|
elt = stack.pop
|
||||||
|
|
||||||
|
raise "Type Error, needed #{need} got #{elt[:type]}" if need != :any && !need.include?( elt[:type] )
|
||||||
|
|
||||||
|
args << elt
|
||||||
|
end
|
||||||
|
|
||||||
|
[stack, args.reverse]
|
||||||
|
end
|
||||||
|
|
||||||
|
def __todo( stack )
|
||||||
|
puts '__NOT IMPLEMENTED__'
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,15 +4,174 @@ module Rpn
|
||||||
class Dictionary
|
class Dictionary
|
||||||
def initialize
|
def initialize
|
||||||
@parser = Parser.new
|
@parser = Parser.new
|
||||||
@words = {
|
@words = {}
|
||||||
'+' => proc { |stack|
|
|
||||||
stack + @parser.parse_input( (stack.pop[:value] + stack.pop[:value]).to_s )
|
# GENERAL
|
||||||
},
|
add( 'nop', proc { |stack| Rpn::Core::General.nop( stack ) } )
|
||||||
}
|
add( 'help', proc { |stack| Rpn::Core.__todo( stack ) } ) # this help message
|
||||||
|
add( 'quit', proc { |stack| Rpn::Core.__todo( stack ) } ) # quit software
|
||||||
|
add( 'version', proc { |stack| Rpn::Core.__todo( stack ) } ) # show rpn version
|
||||||
|
add( 'uname', proc { |stack| Rpn::Core.__todo( stack ) } ) # show rpn complete identification string
|
||||||
|
add( 'history', proc { |stack| Rpn::Core.__todo( stack ) } ) # see commands history
|
||||||
|
|
||||||
|
# USUAL OPERATIONS ON REALS AND COMPLEXES
|
||||||
|
add( '+', proc { |stack| Rpn::Core::Operations.add( stack ) } )
|
||||||
|
add( '-', proc { |stack| Rpn::Core::Operations.subtract( stack ) } )
|
||||||
|
add( 'chs', proc { |stack| Rpn::Core::Operations.negate( stack ) } )
|
||||||
|
add( '*', proc { |stack| Rpn::Core::Operations.multiply( stack ) } )
|
||||||
|
add( '/', proc { |stack| Rpn::Core::Operations.divide( stack ) } )
|
||||||
|
add( 'inv', proc { |stack| Rpn::Core::Operations.inverse( stack ) } )
|
||||||
|
add( '^', proc { |stack| Rpn::Core::Operations.power( stack ) } )
|
||||||
|
add( 'sqrt', proc { |stack| Rpn::Core.__todo( stack ) } ) # rpn_square root
|
||||||
|
add( 'sq', proc { |stack| Rpn::Core.__todo( stack ) } ) # rpn_square
|
||||||
|
add( 'abs', proc { |stack| Rpn::Core.__todo( stack ) } ) # absolute value
|
||||||
|
add( 'dec', proc { |stack| Rpn::Core.__todo( stack ) } ) # decimal representation
|
||||||
|
add( 'hex', proc { |stack| Rpn::Core.__todo( stack ) } ) # hexadecimal representation
|
||||||
|
add( 'bin', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary representation
|
||||||
|
add( 'base', proc { |stack| Rpn::Core.__todo( stack ) } ) # arbitrary base representation
|
||||||
|
add( 'sign', proc { |stack| Rpn::Core.__todo( stack ) } ) # 1 if number at stack level 1 is > 0, 0 if == 0, -1 if <= 0
|
||||||
|
|
||||||
|
# OPERATIONS ON REALS
|
||||||
|
add( '%', proc { |stack| Rpn::Core.__todo( stack ) } ) # percent
|
||||||
|
add( '%CH', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse percent
|
||||||
|
add( 'mod', proc { |stack| Rpn::Core.__todo( stack ) } ) # modulo
|
||||||
|
add( 'fact', proc { |stack| Rpn::Core.__todo( stack ) } ) # n! for integer n or Gamma(x+1) for fractional x
|
||||||
|
add( 'mant', proc { |stack| Rpn::Core.__todo( stack ) } ) # mantissa of a real number
|
||||||
|
add( 'xpon', proc { |stack| Rpn::Core.__todo( stack ) } ) # exponant of a real number
|
||||||
|
add( 'floor', proc { |stack| Rpn::Core.__todo( stack ) } ) # largest number <=
|
||||||
|
add( 'ceil', proc { |stack| Rpn::Core.__todo( stack ) } ) # smallest number >=
|
||||||
|
add( 'ip', proc { |stack| Rpn::Core.__todo( stack ) } ) # integer part
|
||||||
|
add( 'fp', proc { |stack| Rpn::Core.__todo( stack ) } ) # fractional part
|
||||||
|
add( 'min', proc { |stack| Rpn::Core.__todo( stack ) } ) # min of 2 real numbers
|
||||||
|
add( 'max', proc { |stack| Rpn::Core.__todo( stack ) } ) # max of 2 real numbers
|
||||||
|
|
||||||
|
# OPERATIONS ON COMPLEXES
|
||||||
|
add( 're', proc { |stack| Rpn::Core.__todo( stack ) } ) # complex real part
|
||||||
|
add( 'im', proc { |stack| Rpn::Core.__todo( stack ) } ) # complex imaginary part
|
||||||
|
add( 'conj', proc { |stack| Rpn::Core.__todo( stack ) } ) # complex conjugate
|
||||||
|
add( 'arg', proc { |stack| Rpn::Core.__todo( stack ) } ) # complex argument in radians
|
||||||
|
add( 'c->r', proc { |stack| Rpn::Core.__todo( stack ) } ) # transform a complex in 2 reals
|
||||||
|
add( 'r->c', proc { |stack| Rpn::Core.__todo( stack ) } ) # transform 2 reals in a complex
|
||||||
|
add( 'p->r', proc { |stack| Rpn::Core.__todo( stack ) } ) # cartesian to polar
|
||||||
|
add( 'r->p', proc { |stack| Rpn::Core.__todo( stack ) } ) # polar to cartesian
|
||||||
|
|
||||||
|
# MODE
|
||||||
|
add( 'std', proc { |stack| Rpn::Core.__todo( stack ) } ) # standard floating numbers representation. ex: std
|
||||||
|
add( 'fix', proc { |stack| Rpn::Core.__todo( stack ) } ) # fixed point representation. ex: 6 fix
|
||||||
|
add( 'sci', proc { |stack| Rpn::Core.__todo( stack ) } ) # scientific floating point representation. ex: 20 sci
|
||||||
|
add( 'prec', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float precision in bits. ex: 256 prec
|
||||||
|
add( 'round', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float rounding mode. ex: ["nearest", "toward zero", "toward +inf", "toward -inf", "away from zero"] round
|
||||||
|
add( 'default', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float representation and precision to default
|
||||||
|
add( 'type', proc { |stack| Rpn::Core.__todo( stack ) } ) # show type of stack first entry
|
||||||
|
|
||||||
|
# TEST
|
||||||
|
add( '>', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator >
|
||||||
|
add( '>=', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator >=
|
||||||
|
add( '<', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator <
|
||||||
|
add( '<=', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator <=
|
||||||
|
add( '!=', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator != (different)
|
||||||
|
add( '==', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator == (equal)
|
||||||
|
add( 'and', proc { |stack| Rpn::Core.__todo( stack ) } ) # boolean operator and
|
||||||
|
add( 'or', proc { |stack| Rpn::Core.__todo( stack ) } ) # boolean operator or
|
||||||
|
add( 'xor', proc { |stack| Rpn::Core.__todo( stack ) } ) # boolean operator xor
|
||||||
|
add( 'not', proc { |stack| Rpn::Core.__todo( stack ) } ) # boolean operator not
|
||||||
|
add( 'same', proc { |stack| Rpn::Core.__todo( stack ) } ) # boolean operator same (equal)
|
||||||
|
|
||||||
|
# STACK
|
||||||
|
add( 'swap', proc { |stack| Rpn::Core::Stack.swap( stack ) } )
|
||||||
|
add( 'drop', proc { |stack| Rpn::Core::Stack.drop( stack ) } )
|
||||||
|
add( 'drop2', proc { |stack| Rpn::Core::Stack.drop2( stack ) } )
|
||||||
|
add( 'dropn', proc { |stack| Rpn::Core::Stack.dropn( stack ) } )
|
||||||
|
add( 'del', proc { |stack| Rpn::Core::Stack.del( stack ) } )
|
||||||
|
add( 'rot', proc { |stack| Rpn::Core::Stack.rot( stack ) } )
|
||||||
|
add( 'dup', proc { |stack| Rpn::Core::Stack.dup( stack ) } )
|
||||||
|
add( 'dup2', proc { |stack| Rpn::Core::Stack.dup2( stack ) } )
|
||||||
|
add( 'dupn', proc { |stack| Rpn::Core::Stack.dupn( stack ) } )
|
||||||
|
add( 'pick', proc { |stack| Rpn::Core::Stack.pick( stack ) } )
|
||||||
|
add( 'depth', proc { |stack| Rpn::Core::Stack.depth( stack ) } )
|
||||||
|
add( 'roll', proc { |stack| Rpn::Core::Stack.roll( stack ) } )
|
||||||
|
add( 'rolld', proc { |stack| Rpn::Core::Stack.rolld( stack ) } )
|
||||||
|
add( 'over', proc { |stack| Rpn::Core::Stack.over( stack ) } )
|
||||||
|
|
||||||
|
# STRING
|
||||||
|
add( '->str', proc { |stack| Rpn::Core.__todo( stack ) } ) # convert an object into a string
|
||||||
|
add( 'str->', proc { |stack| Rpn::Core.__todo( stack ) } ) # convert a string into an object
|
||||||
|
add( 'chr', proc { |stack| Rpn::Core.__todo( stack ) } ) # convert ASCII character code in stack level 1 into a string
|
||||||
|
add( 'num', proc { |stack| Rpn::Core.__todo( stack ) } ) # return ASCII code of the first character of the string in stack level 1 as a real number
|
||||||
|
add( 'size', proc { |stack| Rpn::Core.__todo( stack ) } ) # return the length of the string
|
||||||
|
add( 'pos', proc { |stack| Rpn::Core.__todo( stack ) } ) # seach for the string in level 1 within the string in level 2
|
||||||
|
add( 'sub', proc { |stack| Rpn::Core.__todo( stack ) } ) # return a substring of the string in level 3
|
||||||
|
|
||||||
|
# BRANCH
|
||||||
|
add( 'if', proc { |stack| Rpn::Core.__todo( stack ) } ) # if <test-instruction> then <true-instructions> else <false-instructions> end
|
||||||
|
add( 'then', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with if
|
||||||
|
add( 'else', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with if
|
||||||
|
add( 'end', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with various branch instructions
|
||||||
|
add( 'start', proc { |stack| Rpn::Core.__todo( stack ) } ) # <start> <end> start <instructions> next|<step> step
|
||||||
|
add( 'for', proc { |stack| Rpn::Core.__todo( stack ) } ) # <start> <end> for <variable> <instructions> next|<step> step
|
||||||
|
add( 'next', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with start and for
|
||||||
|
add( 'step', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with start and for
|
||||||
|
add( 'ift', proc { |stack| Rpn::Core.__todo( stack ) } ) # similar to if-then-end, <test-instruction> <true-instruction> ift
|
||||||
|
add( 'ifte', proc { |stack| Rpn::Core.__todo( stack ) } ) # similar to if-then-else-end, <test-instruction> <true-instruction> <false-instruction> ifte
|
||||||
|
add( 'do', proc { |stack| Rpn::Core.__todo( stack ) } ) # do <instructions> until <condition> end
|
||||||
|
add( 'until', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with do
|
||||||
|
add( 'while', proc { |stack| Rpn::Core.__todo( stack ) } ) # while <test-instruction> repeat <loop-instructions> end
|
||||||
|
add( 'repeat', proc { |stack| Rpn::Core.__todo( stack ) } ) # used with while
|
||||||
|
|
||||||
|
# STORE
|
||||||
|
add( 'sto', proc { |stack| Rpn::Core.__todo( stack ) } ) # store a variable. ex: 1 'name' sto
|
||||||
|
add( 'rcl', proc { |stack| Rpn::Core.__todo( stack ) } ) # recall a variable. ex: 'name' rcl
|
||||||
|
add( 'purge', proc { |stack| Rpn::Core.__todo( stack ) } ) # delete a variable. ex: 'name' purge
|
||||||
|
add( 'vars', proc { |stack| Rpn::Core.__todo( stack ) } ) # list all variables
|
||||||
|
add( 'clusr', proc { |stack| Rpn::Core.__todo( stack ) } ) # erase all variables
|
||||||
|
add( 'edit', proc { |stack| Rpn::Core.__todo( stack ) } ) # edit a variable content
|
||||||
|
add( 'sto+', proc { |stack| Rpn::Core.__todo( stack ) } ) # add to a stored variable. ex: 1 'name' sto+ 'name' 2 sto+
|
||||||
|
add( 'sto-', proc { |stack| Rpn::Core.__todo( stack ) } ) # substract to a stored variable. ex: 1 'name' sto- 'name' 2 sto-
|
||||||
|
add( 'sto*', proc { |stack| Rpn::Core.__todo( stack ) } ) # multiply a stored variable. ex: 3 'name' sto* 'name' 2 sto*
|
||||||
|
add( 'sto/', proc { |stack| Rpn::Core.__todo( stack ) } ) # divide a stored variable. ex: 3 'name' sto/ 'name' 2 sto/
|
||||||
|
add( 'sneg', proc { |stack| Rpn::Core.__todo( stack ) } ) # negate a variable. ex: 'name' sneg
|
||||||
|
add( 'sinv', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse a variable. ex: 1 'name' sinv
|
||||||
|
|
||||||
|
# PROGRAM
|
||||||
|
add( 'eval', proc { |stack| Rpn::Core.__todo( stack ) } ) # evaluate (run) a program, or recall a variable. ex: 'my_prog' eval
|
||||||
|
add( '->', proc { |stack| Rpn::Core.__todo( stack ) } ) # load program local variables. ex: << -> n m << 0 n m for i i + next >> >>
|
||||||
|
|
||||||
|
# TRIG ON REALS AND COMPLEXES
|
||||||
|
add( 'pi', proc { |stack| Rpn::Core.__todo( stack ) } ) # pi constant
|
||||||
|
add( 'sin', proc { |stack| Rpn::Core.__todo( stack ) } ) # sinus
|
||||||
|
add( 'asin', proc { |stack| Rpn::Core.__todo( stack ) } ) # arg sinus
|
||||||
|
add( 'cos', proc { |stack| Rpn::Core.__todo( stack ) } ) # cosinus
|
||||||
|
add( 'acos', proc { |stack| Rpn::Core.__todo( stack ) } ) # arg cosinus
|
||||||
|
add( 'tan', proc { |stack| Rpn::Core.__todo( stack ) } ) # tangent
|
||||||
|
add( 'atan', proc { |stack| Rpn::Core.__todo( stack ) } ) # arg tangent
|
||||||
|
add( 'd->r', proc { |stack| Rpn::Core.__todo( stack ) } ) # convert degrees to radians
|
||||||
|
add( 'r->d', proc { |stack| Rpn::Core.__todo( stack ) } ) # convert radians to degrees
|
||||||
|
|
||||||
|
# LOGS ON REALS AND COMPLEXES
|
||||||
|
add( 'e', proc { |stack| Rpn::Core.__todo( stack ) } ) # Euler constant
|
||||||
|
add( 'ln', proc { |stack| Rpn::Core.__todo( stack ) } ) # logarithm base e
|
||||||
|
add( 'lnp1', proc { |stack| Rpn::Core.__todo( stack ) } ) # ln(1+x) which is useful when x is close to 0
|
||||||
|
add( 'exp', proc { |stack| Rpn::Core.__todo( stack ) } ) # exponential
|
||||||
|
add( 'expm', proc { |stack| Rpn::Core.__todo( stack ) } ) # exp(x)-1 which is useful when x is close to 0
|
||||||
|
add( 'log10', proc { |stack| Rpn::Core.__todo( stack ) } ) # logarithm base 10
|
||||||
|
add( 'alog10', proc { |stack| Rpn::Core.__todo( stack ) } ) # exponential base 10
|
||||||
|
add( 'log2', proc { |stack| Rpn::Core.__todo( stack ) } ) # logarithm base 2
|
||||||
|
add( 'alog2', proc { |stack| Rpn::Core.__todo( stack ) } ) # exponential base 2
|
||||||
|
add( 'sinh', proc { |stack| Rpn::Core.__todo( stack ) } ) # hyperbolic sine
|
||||||
|
add( 'asinh', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse hyperbolic sine
|
||||||
|
add( 'cosh', proc { |stack| Rpn::Core.__todo( stack ) } ) # hyperbolic cosine
|
||||||
|
add( 'acosh', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse hyperbolic cosine
|
||||||
|
add( 'tanh', proc { |stack| Rpn::Core.__todo( stack ) } ) # hyperbolic tangent
|
||||||
|
add( 'atanh', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse hyperbolic tangent
|
||||||
|
|
||||||
|
# TIME AND DATE
|
||||||
|
add( 'time', proc { |stack| Rpn::Core.__todo( stack ) } ) # time in local format
|
||||||
|
add( 'date', proc { |stack| Rpn::Core.__todo( stack ) } ) # date in local format
|
||||||
|
add( 'ticks', proc { |stack| Rpn::Core.__todo( stack ) } ) # system tick in µs
|
||||||
end
|
end
|
||||||
|
|
||||||
def add( word )
|
def add( name, implementation )
|
||||||
@words[ word[:name] ] = word[:value]
|
@words[ name ] = implementation
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup( name )
|
def lookup( name )
|
||||||
|
|
12
lib/language/general.rb
Normal file
12
lib/language/general.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module Rpn
|
||||||
|
module Core
|
||||||
|
module General
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# no operation
|
||||||
|
def nop( stack )
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
93
lib/language/operations.rb
Normal file
93
lib/language/operations.rb
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
module Rpn
|
||||||
|
module Core
|
||||||
|
module Operations
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# addition
|
||||||
|
def add( stack )
|
||||||
|
addable = %i[numeric string name]
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [addable, addable] )
|
||||||
|
|
||||||
|
result = { type: case args[0][:type]
|
||||||
|
when :name
|
||||||
|
:name
|
||||||
|
when :string
|
||||||
|
:string
|
||||||
|
when :numeric
|
||||||
|
if args[1][:type] == :numeric
|
||||||
|
:numeric
|
||||||
|
else
|
||||||
|
:string
|
||||||
|
end
|
||||||
|
end }
|
||||||
|
|
||||||
|
args.each do |elt|
|
||||||
|
elt[:value] = elt[:value][1..-2] unless elt[:type] == :numeric
|
||||||
|
end
|
||||||
|
|
||||||
|
result[:value] = case result[:type]
|
||||||
|
when :name
|
||||||
|
"'#{args[0][:value]}#{args[1][:value]}'"
|
||||||
|
when :string
|
||||||
|
"\"#{args[0][:value]}#{args[1][:value]}\""
|
||||||
|
when :numeric
|
||||||
|
args[0][:value] + args[1][:value]
|
||||||
|
end
|
||||||
|
|
||||||
|
stack << result
|
||||||
|
end
|
||||||
|
|
||||||
|
# substraction
|
||||||
|
def subtract( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric], %i[numeric]] )
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: args[0][:value] - args[1][:value] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# negation
|
||||||
|
def negate( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: args[0][:value] * -1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
# multiplication
|
||||||
|
def multiply( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric], %i[numeric]] )
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: args[0][:value] * args[1][:value] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# division
|
||||||
|
def divide( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric], %i[numeric]] )
|
||||||
|
|
||||||
|
raise 'Division by 0' if args[0][:value].zero?
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: args[0][:value] / args[1][:value] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# inverse
|
||||||
|
def inverse( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
|
||||||
|
raise 'Division by 0' if args[0][:value].zero?
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: 1.0 / args[0][:value] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# power
|
||||||
|
def power( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric], %i[numeric]] )
|
||||||
|
|
||||||
|
stack << { type: :numeric,
|
||||||
|
value: args[0][:value]**args[1][:value] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
122
lib/language/stack.rb
Normal file
122
lib/language/stack.rb
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
module Rpn
|
||||||
|
module Core
|
||||||
|
module Stack
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# swap 2 first stack entries
|
||||||
|
def swap( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any any] )
|
||||||
|
|
||||||
|
stack << args[1] << args[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
# drop first stack entry
|
||||||
|
def drop( stack )
|
||||||
|
dropn( stack << { type: :numeric, value: 1 } )
|
||||||
|
end
|
||||||
|
|
||||||
|
# drop 2 first stack entries
|
||||||
|
def drop2( stack )
|
||||||
|
dropn( stack << { type: :numeric, value: 2 } )
|
||||||
|
end
|
||||||
|
|
||||||
|
# drop n first stack entries
|
||||||
|
def dropn( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
stack, _args = Rpn::Core.stack_extract( stack, %i[any] * args[0][:value] )
|
||||||
|
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# drop all stack entries
|
||||||
|
def del( _stack )
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# rotate 3 first stack entries
|
||||||
|
def rot( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any any any] )
|
||||||
|
|
||||||
|
stack << args[1] << args[2] << args[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
# duplicate first stack entry
|
||||||
|
def dup( stack )
|
||||||
|
dupn( stack << { type: :numeric, value: 1 } )
|
||||||
|
end
|
||||||
|
|
||||||
|
# duplicate 2 first stack entries
|
||||||
|
def dup2( stack )
|
||||||
|
dupn( stack << { type: :numeric, value: 2 } )
|
||||||
|
end
|
||||||
|
|
||||||
|
# duplicate n first stack entries
|
||||||
|
def dupn( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
n = args[0][:value]
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any] * args[0][:value] )
|
||||||
|
|
||||||
|
2.times do
|
||||||
|
n.times.each do |i|
|
||||||
|
stack << args[ i ]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# push a copy of the given stack level onto the stack
|
||||||
|
def pick( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
n = args[0][:value]
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any] * args[0][:value] )
|
||||||
|
|
||||||
|
n.times.each do |i|
|
||||||
|
stack << args[ i ]
|
||||||
|
end
|
||||||
|
stack << args[0]
|
||||||
|
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# give stack depth
|
||||||
|
def depth( stack )
|
||||||
|
stack << { type: :numeric, value: stack.size }
|
||||||
|
end
|
||||||
|
|
||||||
|
# move a stack entry to the top of the stack
|
||||||
|
def roll( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
n = args[0][:value]
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any] * args[0][:value] )
|
||||||
|
|
||||||
|
(1..(n - 1)).each do |i|
|
||||||
|
stack << args[ i ]
|
||||||
|
end
|
||||||
|
stack << args[0]
|
||||||
|
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# move the element on top of the stack to a higher stack position
|
||||||
|
def rolld( stack )
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] )
|
||||||
|
n = args[0][:value]
|
||||||
|
stack, args = Rpn::Core.stack_extract( stack, %i[any] * args[0][:value] )
|
||||||
|
|
||||||
|
stack << args[n - 1]
|
||||||
|
|
||||||
|
(0..(n - 2)).each do |i|
|
||||||
|
stack << args[ i ]
|
||||||
|
end
|
||||||
|
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
# push a copy of the element in stack level 2 onto the stack
|
||||||
|
def over( stack )
|
||||||
|
pick( stack << { type: :numeric, value: 2 } )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
3
repl.rb
3
repl.rb
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
require 'readline'
|
require 'readline'
|
||||||
|
|
||||||
|
require './lib/core'
|
||||||
require './lib/dictionary'
|
require './lib/dictionary'
|
||||||
require './lib/parser'
|
require './lib/parser'
|
||||||
require './lib/runner'
|
require './lib/runner'
|
||||||
|
@ -29,7 +30,7 @@ module Rpn
|
||||||
|
|
||||||
loop do
|
loop do
|
||||||
input = Readline.readline( ' ', true )
|
input = Readline.readline( ' ', true )
|
||||||
break if input.nil? || input == 'exit'
|
break if input.nil? || input == 'quit'
|
||||||
|
|
||||||
# Remove blank lines from history
|
# Remove blank lines from history
|
||||||
Readline::HISTORY.pop if input.empty?
|
Readline::HISTORY.pop if input.empty?
|
||||||
|
|
158
spec/language_stack_spec.rb
Normal file
158
spec/language_stack_spec.rb
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
# coding: utf-8
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test/unit'
|
||||||
|
|
||||||
|
require_relative '../lib/core'
|
||||||
|
|
||||||
|
class TestParser < Test::Unit::TestCase
|
||||||
|
def test_swap
|
||||||
|
stack = Rpn::Core::Stack.swap [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 2, type: :numeric },
|
||||||
|
{ value: 1, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_drop
|
||||||
|
stack = Rpn::Core::Stack.drop [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_drop2
|
||||||
|
stack = Rpn::Core::Stack.drop2 [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dropn
|
||||||
|
stack = Rpn::Core::Stack.dropn [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_del
|
||||||
|
stack = Rpn::Core::Stack.del [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_rot
|
||||||
|
stack = Rpn::Core::Stack.rot [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }]
|
||||||
|
assert_equal [{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 1, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dup
|
||||||
|
stack = Rpn::Core::Stack.dup [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dup2
|
||||||
|
stack = Rpn::Core::Stack.dup2 [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dupn
|
||||||
|
stack = Rpn::Core::Stack.dupn [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_pick
|
||||||
|
stack = Rpn::Core::Stack.pick [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_depth
|
||||||
|
stack = Rpn::Core::Stack.depth []
|
||||||
|
assert_equal [{ value: 0, type: :numeric }],
|
||||||
|
stack
|
||||||
|
|
||||||
|
stack = Rpn::Core::Stack.depth [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_roll
|
||||||
|
stack = Rpn::Core::Stack.roll [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_rolld
|
||||||
|
stack = Rpn::Core::Stack.rolld [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_over
|
||||||
|
stack = Rpn::Core::Stack.over [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric }]
|
||||||
|
assert_equal [{ value: 1, type: :numeric },
|
||||||
|
{ value: 2, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric },
|
||||||
|
{ value: 4, type: :numeric },
|
||||||
|
{ value: 3, type: :numeric }],
|
||||||
|
stack
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue