[WIP] almost there

This commit is contained in:
Gwenhael Le Moine 2022-02-26 18:53:39 +01:00
parent 02f322744b
commit fcb809d797
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
35 changed files with 609 additions and 996 deletions

View file

@ -45,7 +45,7 @@ class RplRepl
stack_size = @interpreter.stack.size
@interpreter.stack.each_with_index do |elt, i|
puts "#{stack_size - i}: #{@interpreter.stringify( elt )}"
puts "#{stack_size - i}: #{elt}"
end
end
end
@ -85,4 +85,4 @@ end
RplRepl.new( interpreter ).run if options[:run_REPL]
# last print resulting stack on exit (formatted so that it can be fed back later to interpreter)
pp interpreter.stack.map { |elt| interpreter.stringify( elt ) }.join(' ')
pp interpreter.export_stack

View file

@ -1,12 +1,12 @@
# frozen_string_literal: true
require 'rpl/types'
require 'rpl/interpreter'
require 'rpl/types'
require 'rpl/words'
class Rpl < Interpreter
include Types
def initialize( stack = [], dictionary = Dictionary.new )
super

View file

@ -5,9 +5,11 @@ require 'bigdecimal/math'
require 'bigdecimal/util'
require 'rpl/dictionary'
require 'rpl/types'
class Interpreter
include BigMath
include Types
attr_reader :stack,
:dictionary,
@ -16,171 +18,34 @@ class Interpreter
attr_accessor :precision
def initialize( stack = [], dictionary = Rpl::Lang::Dictionary.new )
@version = 0.1
@precision = default_precision
@version = 0.6
@dictionary = dictionary
@stack = stack
end
def default_precision
12
end
def parse( input )
is_numeric = lambda do |elt|
!Float(elt).nil?
rescue ArgumentError
begin
!Integer(elt).nil?
rescue ArgumentError
false
end
end
unless input.index("\n").nil?
input = input.split("\n")
.map do |line|
comment_begin_index = line.index('#')
if comment_begin_index.nil?
line
elsif comment_begin_index.zero?
''
else
line[0..(comment_begin_index - 1)]
end
end
.join(' ')
end
splitted_input = input.split(' ')
# 2-passes:
# 1. regroup strings and programs
opened_programs = 0
closed_programs = 0
opened_lists = 0
closed_lists = 0
string_delimiters = 0
name_delimiters = 0
regrouping = false
regrouped_input = []
splitted_input.each do |elt|
if elt[0] == '«'
opened_programs += 1
elt.gsub!( '«', '« ') if elt.length > 1 && elt[1] != ' '
elsif elt[0] == '{'
opened_lists += 1
elt.gsub!( '{', '{ ') if elt.length > 1 && elt[1] != ' '
elsif elt[0] == '"' && elt.length > 1
string_delimiters += 1
elsif elt[0] == "'" && elt.length > 1
name_delimiters += 1
end
elt = "#{regrouped_input.pop} #{elt}".strip if regrouping
regrouped_input << elt
case elt[-1]
when '»'
closed_programs += 1
elt.gsub!( '»', ' »') if elt.length > 1 && elt[-2] != ' '
when '}'
closed_lists += 1
elt.gsub!( '}', ' }') if elt.length > 1 && elt[-2] != ' '
when '"'
string_delimiters += 1
when "'"
name_delimiters += 1
end
regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs ) || (opened_lists > closed_lists )
end
# 2. parse
# TODO: parse ∞, <NaN> as numerics
parsed_tree = []
regrouped_input.each do |elt|
parsed_entry = { value: elt }
parsed_entry[:type] = case elt[0]
when '«'
:program
when '{'
:list
when '"'
:string
when "'"
:name
else
if is_numeric.call( elt )
:numeric
else
:word
end
end
if %I[string name].include?( parsed_entry[:type] )
parsed_entry[:value] = parsed_entry[:value][1..-2]
elsif %I[program].include?( parsed_entry[:type] )
parsed_entry[:value] = parsed_entry[:value][2..-3]
elsif %I[list].include?( parsed_entry[:type] )
parsed_entry[:value] = parse( parsed_entry[:value][2..-3] )
elsif %I[numeric].include?( parsed_entry[:type] )
underscore_position = parsed_entry[:value].index('_')
if parsed_entry[:value][0] == '0' && ( %w[b o x].include?( parsed_entry[:value][1] ) || !underscore_position.nil? )
if parsed_entry[:value][1] == 'x'
parsed_entry[:base] = 16
elsif parsed_entry[:value][1] == 'b'
parsed_entry[:base] = 2
elsif parsed_entry[:value][1] == 'o'
parsed_entry[:base] = 8
parsed_entry[:value] = parsed_entry[:value][2..-1]
elsif !underscore_position.nil?
parsed_entry[:base] = parsed_entry[:value][1..(underscore_position - 1)].to_i
parsed_entry[:value] = parsed_entry[:value][(underscore_position + 1)..-1]
end
else
parsed_entry[:base] = 10
end
parsed_entry[:value] = parsed_entry[:value].to_i( parsed_entry[:base] ) unless parsed_entry[:base] == 10
parsed_entry[:value] = BigDecimal( parsed_entry[:value], @precision )
end
parsed_tree << parsed_entry
end
parsed_tree
end
def run( input )
@dictionary.add_local_vars_layer
parse( input.to_s ).each do |elt|
case elt[:type]
when :word
break if %w[break quit exit].include?( elt[:value] )
Parser.parse( input.to_s ).each do |elt|
if elt.instance_of?( RplName )
break if %w[break quit exit].include?( elt.value )
command = @dictionary.lookup( elt[:value] )
if elt.not_to_evaluate
@stack << elt
else
command = @dictionary.lookup( elt.value )
if command.nil?
# if there isn't a command by that name then it's a name
elt[:type] = :name
# elt[:type] = :name
@stack << elt
elsif command.is_a?( Proc )
command.call
else
run( command[:value] )
run( command.value )
end
end
else
@stack << elt
@ -199,7 +64,7 @@ class Interpreter
needs.each_with_index do |need, index|
stack_index = (index + 1) * -1
raise ArgumentError, "Type Error, needed #{need} got #{@stack[stack_index]}" unless need == :any || need.include?( @stack[stack_index][:type] )
raise ArgumentError, "Type Error, needed #{need} got #{@stack[stack_index]}" unless need == :any || need.include?( @stack[stack_index].class )
end
args = []
@ -210,52 +75,7 @@ class Interpreter
args
end
def stringify( elt )
case elt[:type]
when :numeric
prefix = case elt[:base]
when 2
'0b'
when 8
'0o'
when 10
''
when 16
'0x'
else
"0#{elt[:base]}_"
end
if elt[:value].infinite?
suffix = elt[:value].infinite?.positive? ? '∞' : '-∞'
elsif elt[:value].nan?
suffix = '<NaN>'
else
suffix = if elt[:value].to_i == elt[:value]
elt[:value].to_i
else
elt[:value].to_s('F')
end
suffix = elt[:value].to_s( elt[:base] ) unless elt[:base] == 10
end
"#{prefix}#{suffix}"
when :list
"{ #{elt[:value].map { |e| stringify( e ) }.join(' ')} }"
when :program
"« #{elt[:value]} »"
when :string
"\"#{elt[:value]}\""
when :name
"'#{elt[:value]}'"
else
elt[:value]
end
end
def infer_resulting_base( numerics )
10 if numerics.length.zero?
numerics.last[:base]
def export_stack
@stack.map(&:to_s).join(' ')
end
end

View file

@ -23,5 +23,10 @@ module Types
%w[TrueClass FalseClass].include?( value.class.to_s )
end
def ==( other )
other.class == RplBoolean and
other.value == value
end
end
end

View file

@ -9,8 +9,12 @@ module Types
def initialize( value )
raise RplTypeError unless self.class.can_parse?( value )
@value = if value.instance_of?( Array )
value
else
# we systematicalyl trim enclosing { }
@value = Parser.parse( value[2..-3] )
Parser.parse( value[2..-3] )
end
end
def to_s
@ -18,7 +22,13 @@ module Types
end
def self.can_parse?( value )
value.instance_of?( Array ) or
value[0..1] == '{ ' && value[-2..-1] == ' }'
end
def ==( other )
other.class == RplList and
other.value == value
end
end
end

View file

@ -25,8 +25,14 @@ module Types
def self.can_parse?( value )
( value.length > 2 and value[0] == "'" and value[-1] == "'" ) or
( value != "''" and !value.match?(/^[0-9']+$/) and
# it's not any other type
[RplBoolean, RplList, RplProgram, RplString, RplNumeric].reduce( true ) { |memo, type_class| memo && !type_class.can_parse?( value ) }
[RplBoolean, RplList, RplProgram, RplString, RplNumeric].reduce( true ) { |memo, type_class| memo && !type_class.can_parse?( value ) } )
end
def ==( other )
other.class == RplName and
other.value == value
end
end
end

View file

@ -7,11 +7,23 @@ module Types
attr_accessor :value,
:base
@@precision = 12 # rubocop:disable Style/ClassVars
def self.default_precision
@@precision = 12 # rubocop:disable Style/ClassVars
end
def self.precision
@@precision
end
def self.precision=( precision )
@@precision = precision.to_i # rubocop:disable Style/ClassVars
end
def initialize( value, base = 10 )
raise RplTypeError unless self.class.can_parse?( value )
@@precision = 12 # rubocop:disable Style/ClassVars
@base = base
case value
@ -53,12 +65,6 @@ module Types
end
end
def self.precision=( precision )
raise RplTypeError unless precision.is_a? RplNumeric
@@precision = precision.value.to_i # rubocop:disable Style/ClassVars
end
def to_s
prefix = case @base
when 2
@ -105,11 +111,5 @@ module Types
other.base == base and
other.value == value
end
def self.infer_resulting_base( numerics )
10 if numerics.length.zero?
numerics.last.base
end
end
end

View file

@ -18,5 +18,10 @@ module Types
def self.can_parse?( value )
value.length > 4 && value[0..1] == '« ' && value[-2..-1] == ' »' && !value[2..-3].strip.empty?
end
def ==( other )
other.class == RplProgram and
other.value == value
end
end
end

View file

@ -18,5 +18,10 @@ module Types
def self.can_parse?( value )
value[0] == '"' && value[-1] == '"'
end
def ==( other )
other.class == RplString and
other.value == value
end
end
end

View file

@ -3,36 +3,35 @@
module RplLang
module Words
module Branch
include Types
def populate_dictionary
super
@dictionary.add_word( ['ift'],
'Branch',
'( t pt -- … ) eval pt or not based on the value of boolean t',
proc do
run( '« nop » ifte' )
end )
RplProgram.new( '« « nop » ifte »' ) )
@dictionary.add_word( ['ifte'],
'Branch',
'( t pt pf -- … ) eval pt or pf based on the value of boolean t',
proc do
args = stack_extract( [:any, :any, %i[boolean]] )
args = stack_extract( [:any, :any, [RplBoolean]] )
run( args[ args[2][:value] ? 1 : 0 ][:value] )
run( args[ args[2].value ? 1 : 0 ].value )
end )
@dictionary.add_word( ['times'],
'Branch',
'( p n -- … ) eval p n times while pushing counter on stack before',
proc do
args = stack_extract( [%i[numeric], :any] )
args = stack_extract( [[RplNumeric], :any] )
args[0][:value].to_i.times do |i|
counter = { value: BigDecimal( i, @precision ), type: :numeric, base: 10 }
@stack << counter
args[0].value.to_i.times do |counter|
@stack << RplNumeric.new( counter )
run( args[1][:value] )
run( args[1].value )
end
end )
@ -40,13 +39,12 @@ module RplLang
'Branch',
'( p n1 n2 -- … ) eval p looping from n1 to n2 while pushing counter on stack before',
proc do
args = stack_extract( [%i[numeric], %i[numeric], :any] )
args = stack_extract( [[RplNumeric], [RplNumeric], :any] )
((args[1][:value].to_i)..(args[0][:value].to_i)).each do |i|
counter = { value: BigDecimal( i, @precision ), type: :numeric, base: 10 }
@stack << counter
((args[1].value.to_i)..(args[0].value.to_i)).each do |counter|
@stack << RplNumeric.new( counter )
run( args[2][:value] )
run( args[2].value )
end
end )
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module FileSystem
include Types
def populate_dictionary
super
@ -10,29 +12,26 @@ module RplLang
'Filesystem',
'( filename -- content ) read file and put content on stack as string',
proc do
args = stack_extract( [%i[string]] )
args = stack_extract( [[RplString]] )
path = File.absolute_path( args[0][:value] )
path = File.absolute_path( args[0].value )
@stack << { type: :string,
value: File.read( path ) }
@stack << RplString.new( "\"#{File.read( path )}\"" )
end )
@dictionary.add_word( ['feval'],
'Filesystem',
'( filename -- … ) read and run file',
proc do
run( 'fread eval' )
end )
RplProgram.new( '« fread eval »' ) )
@dictionary.add_word( ['fwrite'],
'Filesystem',
'( content filename -- ) write content into filename',
proc do
args = stack_extract( [%i[string], :any] )
args = stack_extract( [[RplString], :any] )
File.write( File.absolute_path( args[0][:value] ),
args[1][:value] )
File.write( File.absolute_path( args[0].value ),
args[1].value )
end )
end
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module General
include Types
def populate_dictionary
super
@ -15,12 +17,11 @@ module RplLang
'General',
'( w -- s ) pop help string of the given word',
proc do
args = stack_extract( [%i[name]] )
args = stack_extract( [[RplName]] )
word = @dictionary.words[ args[0][:value] ]
word = @dictionary.words[ args[0].value ]
@stack << { type: :string,
value: "#{args[0][:value]}: #{word.nil? ? 'not a core word' : word[:help]}" }
@stack << RplString.new( "\"#{args[0].value}: #{word.nil? ? 'not a core word' : word[:help]}\"" )
end )
@dictionary.add_word( ['words'],
@ -45,14 +46,14 @@ module RplLang
'General',
'( -- n ) Pop the interpreter\'s version number',
proc do
@stack += parse( @version.to_s )
@stack += Parser.parse( @version.to_s )
end )
@dictionary.add_word( ['uname'],
'General',
'( -- s ) Pop the interpreter\'s complete indentification string',
proc do
@stack += parse( "\"Rpl Interpreter version #{@version}\"" )
@stack += Parser.parse( "\"Rpl Interpreter version #{@version}\"" )
end )
@dictionary.add_word( ['history'],

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Logarithm
include Types
def populate_dictionary
super
@ -10,9 +12,7 @@ module RplLang
'Logs on reals and complexes',
'( … -- ℇ ) push ℇ',
proc do
@stack << { type: :numeric,
base: 10,
value: BigMath.E( @precision ) }
@stack << RplNumeric.new( BigMath.E( RplNumeric.precision ) )
end )
# @dictionary.add_word( ['ln'],

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Mode
include Types
def populate_dictionary
super
@ -10,15 +12,15 @@ module RplLang
'Mode',
'( a -- ) set precision to a',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@precision = args[0][:value]
RplNumeric.precision = args[0].value
end )
@dictionary.add_word( ['default'],
'Mode',
'( -- ) set default precision',
proc do
@precision = default_precision
RplNumeric.default_precision
end )
@dictionary.add_word( ['type'],
'Mode',
@ -26,8 +28,7 @@ module RplLang
proc do
args = stack_extract( [:any] )
@stack << { type: :string,
value: args[0][:type].to_s }
@stack << RplString.new( "\"#{args[0].class.to_s[10..-1]}\"" )
end )
# @dictionary.add_word( ['std'],

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Operations
include Types
def populate_dictionary
super
@ -11,69 +13,70 @@ module RplLang
'Usual operations on reals and complexes',
'( a b -- c ) addition',
proc do
addable = %i[numeric string name list]
addable = [RplNumeric, RplString, RplName, RplList]
args = stack_extract( [addable, addable] )
# | + | 1 numeric | 1 string | 1 name | 1 list |
# | + | 1 numeric | 1 string | 1 name |v1 list |
# |-----------+-----------+----------+--------+--------|
# | 0 numeric | numeric | string | name | list |
# | 0 string | string | string | string | list |
# | 0 name | string | string | name | list |
# | 0 list | list | list | list | list |
# | 0 numeric | numeric | string | name |vlist |
# |v0 string |vstring |vstring |vstring |vlist |
# |v0 name |vstring |vstring |vname |vlist |
# |v0 list |vlist |vlist |vlist |vlist |
result = { type: case args[0][:type]
when :numeric
args[1][:type]
args.reverse!
when :string
case args[1][:type]
when :list
:list
result = if args[0].instance_of?( RplList )
if args[1].instance_of?( RplList )
args[0].value.concat( args[1].value )
else
:string
args[0].value << args[1]
end
args[0]
when :name
case args[1][:type]
when :name
:name
when :list
:list
elsif args[1].instance_of?( RplList )
if args[0].instance_of?( RplList )
args[0].value.concat( args[1].value )
args[0]
else
:string
args[1].value.unshift( args[0] )
args[1]
end
when :list
:list
elsif args[0].instance_of?( RplString )
args[0].value = if args[1].instance_of?( RplString ) ||
args[1].instance_of?( RplName )
"#{args[0].value}#{args[1].value}"
else
args[0][:type]
end }
if result[:type] == :list
args.each do |elt|
next unless elt[:type] != :list
elt_copy = Marshal.load(Marshal.dump( elt ))
elt[:type] = :list
elt[:value] = [elt_copy]
end
"#{args[0].value}#{args[1]}"
end
args[0]
value_to_string = lambda do |e|
if e[:type] == :numeric
stringify( e )
elsif args[0].instance_of?( RplName )
if args[1].instance_of?( RplName )
args[0].value = "#{args[0].value}#{args[1].value}"
args[0]
else
e[:value].to_s
end
end
result[:value] = if %i[name string].include?( result[:type] )
"#{value_to_string.call( args[1] )}#{value_to_string.call( args[0] )}"
if args[1].instance_of?( RplString )
RplString.new( "\"#{args[0].value}#{args[1].value}\"" )
elsif args[1].instance_of?( RplNumeric )
args[0].value = "#{args[0].value}#{args[1]}"
args[0]
else
args[1][:value] + args[0][:value]
RplString.new( "\"#{args[0]}#{args[1]}\"" )
end
end
result[:base] = args[0][:base] if result[:type] == :numeric
elsif args[0].instance_of?( RplNumeric )
if args[1].instance_of?( RplNumeric )
args[0].value += args[1].value
args[0]
elsif args[1].instance_of?( RplString ) ||
args[1].instance_of?( RplName )
args[1].value = "#{args[0]}#{args[1].value}"
args[1]
end
end
@stack << result
end )
@ -82,11 +85,11 @@ module RplLang
'Usual operations on reals and complexes',
'( a b -- c ) subtraction',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[1][:value] - args[0][:value] }
args[1].value = args[1].value - args[0].value
@stack << args[1]
end )
@dictionary.add_word( ['chs'],
@ -100,22 +103,22 @@ module RplLang
'Usual operations on reals and complexes',
'( a b -- c ) multiplication',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[1][:value] * args[0][:value] }
args[1].value = args[1].value * args[0].value
@stack << args[1]
end )
@dictionary.add_word( ['÷', '/'],
'Usual operations on reals and complexes',
'( a b -- c ) division',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[1][:value] / args[0][:value] }
args[1].value = args[1].value / args[0].value
@stack << args[1]
end )
@dictionary.add_word( ['inv'],
@ -129,22 +132,22 @@ module RplLang
'Usual operations on reals and complexes',
'( a b -- c ) a to the power of b',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[1][:value]**args[0][:value] }
args[1].value = args[1].value**args[0].value
@stack << args[1]
end )
@dictionary.add_word( ['√', 'sqrt'],
'Usual operations on reals and complexes',
'( a -- b ) square root',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: BigMath.sqrt( BigDecimal( args[0][:value], precision ), precision ) }
args[0].value = BigMath.sqrt( args[0].value, RplNumeric.precision )
@stack << args[0]
end )
@dictionary.add_word( ['²', 'sq'],
@ -158,11 +161,11 @@ module RplLang
'Usual operations on reals and complexes',
'( a -- b ) absolute value',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value].abs }
args[0].value = args[0].value.abs
@stack << args[0]
end )
@dictionary.add_word( ['dec'],
@ -190,9 +193,9 @@ module RplLang
'Usual operations on reals and complexes',
'( a b -- a ) set numeric\'s base to b',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
args[1][:base] = args[0][:value]
args[1].base = args[0].value
@stack << args[1]
end )
@ -201,18 +204,16 @@ module RplLang
'Usual operations on reals and complexes',
'( a -- b ) sign of element',
proc do
args = stack_extract( [%i[numeric]] )
value = if args[0][:value].positive?
args = stack_extract( [[RplNumeric]] )
args[0].value = if args[0].value.positive?
1
elsif args[0][:value].negative?
elsif args[0].value.negative?
-1
else
0
end
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: value }
@stack << args[0]
end )
# Operations on reals
@ -220,106 +221,104 @@ module RplLang
'Operations on reals',
'( a b -- c ) b% of a',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value] * ( args[1][:value] / 100.0 ) }
args[1].value = args[0].value * ( args[1].value / 100.0 )
@stack << args[1]
end )
@dictionary.add_word( ['%CH'],
'Operations on reals',
'( a b -- c ) b is c% of a',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: 100.0 * ( args[0][:value] / args[1][:value] ) }
args[1].value = 100.0 * ( args[0].value / args[1].value )
@stack << args[1]
end )
@dictionary.add_word( ['mod'],
'Operations on reals',
'( a b -- c ) modulo',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[1][:value] % args[0][:value] }
args[1].value = args[1].value % args[0].value
@stack << args[1]
end )
@dictionary.add_word( ['!', 'fact'],
'Operations on reals',
'( a -- b ) factorial',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: Math.gamma( args[0][:value] ) }
args[0].value = Math.gamma( args[0].value )
@stack << args[0]
end )
@dictionary.add_word( ['floor'],
'Operations on reals',
'( a -- b ) highest integer under a',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value].floor }
args[0].value = args[0].value.floor
@stack << args[0]
end )
@dictionary.add_word( ['ceil'],
'Operations on reals',
'( a -- b ) highest integer over a',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value].ceil }
args[0].value = args[0].value.ceil
@stack << args[0]
end )
@dictionary.add_word( ['min'],
'Operations on reals',
'( a b -- a/b ) leave lowest of a or b',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << ( args[0][:value] < args[1][:value] ? args[0] : args[1] )
@stack << ( args[0].value < args[1].value ? args[0] : args[1] )
end )
@dictionary.add_word( ['max'],
'Operations on reals',
'( a b -- a/b ) leave highest of a or b',
proc do
args = stack_extract( [%i[numeric], %i[numeric]] )
args = stack_extract( [[RplNumeric], [RplNumeric]] )
@stack << ( args[0][:value] > args[1][:value] ? args[0] : args[1] )
@stack << ( args[0].value > args[1].value ? args[0] : args[1] )
end )
@dictionary.add_word( ['mant'],
'Operations on reals',
'mantissa of a real number',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: BigDecimal( args[0][:value].to_s.split('e').first.to_f.abs, @precision ) }
@stack << RplNumeric.new( args[0].value.to_s.split('e').first.to_f.abs )
end )
@dictionary.add_word( ['xpon'],
'Operations on reals',
'exponant of a real number',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value].exponent }
args[0].value = args[0].value.exponent
@stack << args[0]
end )
@dictionary.add_word( ['ip'],
@ -333,11 +332,11 @@ module RplLang
'Operations on reals',
'( n -- f ) fractional part',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: args[0][:value].frac }
args[0].value = args[0].value.frac
@stack << args[0]
end )
# Operations on complexes

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Program
include Types
def populate_dictionary
super
@ -12,10 +14,10 @@ module RplLang
proc do
args = stack_extract( [:any] )
if %i[list numeric boolean].include?( args[0][:type] )
if [RplList, RplNumeric, RplBoolean].include?( args[0].class )
@stack << args[0] # these types evaluate to themselves
else
run( args[0][:value].to_s )
run( args[0].value.to_s )
end
end )
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Stack
include Types
def populate_dictionary
super
@ -18,9 +20,7 @@ module RplLang
@dictionary.add_word( ['drop'],
'Stack',
'( a -- ) drop first stack element',
proc do
run( '1 dropn' )
end )
RplProgram.new( '« 1 dropn »' ) )
@dictionary.add_word( ['drop2'],
'Stack',
@ -33,9 +33,9 @@ module RplLang
'Stack',
'( a b … n -- ) drop first n stack elements',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
_args = stack_extract( %i[any] * args[0][:value] )
_args = stack_extract( %i[any] * args[0].value )
end )
@dictionary.add_word( ['del'],
@ -57,24 +57,20 @@ module RplLang
@dictionary.add_word( ['dup'],
'Stack',
'( a -- a a ) duplicate first stack element',
proc do
run( '1 dupn' )
end )
RplProgram.new( '« 1 dupn »' ) )
@dictionary.add_word( ['dup2'],
'Stack',
'( a b -- a b a b ) duplicate first two stack elements',
proc do
run( '2 dupn' )
end )
RplProgram.new( '« 2 dupn »' ) )
@dictionary.add_word( ['dupn'],
'Stack',
'( a b … n -- a b … a b … ) duplicate first n stack elements',
proc do
args = stack_extract( [%i[numeric]] )
n = args[0][:value].to_i
args = stack_extract( %i[any] * args[0][:value] )
args = stack_extract( [[RplNumeric]] )
n = args[0].value.to_i
args = stack_extract( %i[any] * args[0].value )
args.reverse!
@ -89,9 +85,9 @@ module RplLang
'Stack',
'( … b … n -- … b … b ) push a copy of the given stack level onto the stack',
proc do
args = stack_extract( [%i[numeric]] )
n = args[0][:value].to_i
args = stack_extract( %i[any] * args[0][:value] )
args = stack_extract( [[RplNumeric]] )
n = args[0].value.to_i
args = stack_extract( %i[any] * args[0].value )
args.reverse!
@ -106,16 +102,16 @@ module RplLang
'Stack',
'( … -- … n ) push stack depth onto the stack',
proc do
@stack << { type: :numeric, base: 10, value: BigDecimal( stack.size ) }
@stack << RplNumeric.new( stack.size )
end )
@dictionary.add_word( ['roll'],
'Stack',
'( … a -- a … ) move a stack element to the top of the stack',
proc do
args = stack_extract( [%i[numeric]] )
n = args[0][:value]
args = stack_extract( %i[any] * args[0][:value] )
args = stack_extract( [[RplNumeric]] )
n = args[0].value
args = stack_extract( %i[any] * args[0].value )
args.reverse!
@ -130,9 +126,9 @@ module RplLang
'Stack',
'( a … -- … a ) move the element on top of the stack to a higher stack position',
proc do
args = stack_extract( [%i[numeric]] )
n = args[0][:value]
args = stack_extract( %i[any] * args[0][:value] )
args = stack_extract( [[RplNumeric]] )
n = args[0].value
args = stack_extract( %i[any] * args[0].value )
args.reverse!
@ -146,9 +142,7 @@ module RplLang
@dictionary.add_word( ['over'],
'Stack',
'( a b -- a b a ) push a copy of the element in stack level 2 onto the stack',
proc do
run( '2 pick' )
end )
RplProgram.new( '« 2 pick »' ) )
end
end
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Store
include Types
def populate_dictionary
super
@ -10,9 +12,9 @@ module RplLang
'Store',
'( content name -- ) store to variable',
proc do
args = stack_extract( [%i[name], :any] )
args = stack_extract( [[RplName], :any] )
@dictionary.add_var( args[0][:value],
@dictionary.add_var( args[0].value,
args[1] )
end )
@ -20,9 +22,9 @@ module RplLang
'Store',
'( name -- … ) push content of variable name onto stack',
proc do
args = stack_extract( [%i[name]] )
args = stack_extract( [[RplName]] )
content = @dictionary.lookup( args[0][:value] )
content = @dictionary.lookup( args[0].value )
@stack << content unless content.nil?
end )
@ -31,17 +33,16 @@ module RplLang
'Store',
'( name -- ) delete variable',
proc do
args = stack_extract( [%i[name]] )
args = stack_extract( [[RplName]] )
@dictionary.remove_var( args[0][:value] )
@dictionary.remove_var( args[0].value )
end )
@dictionary.add_word( ['vars'],
'Store',
'( -- […] ) list variables',
proc do
@stack << { type: :list,
value: (@dictionary.vars.keys + @dictionary.local_vars_layers.reduce([]) { |memo, layer| memo + layer.keys }).map { |name| { type: :name, value: name } } }
@stack << RplList.new( (@dictionary.vars.keys + @dictionary.local_vars_layers.reduce([]) { |memo, layer| memo + layer.keys }).map { |name| RplName.new( name ) } )
end )
@dictionary.add_word( ['clusr'],
@ -54,68 +55,52 @@ module RplLang
@dictionary.add_word( ['sto+'],
'Store',
'( a n -- ) add content to variable\'s value',
proc do
run( '
dup type "name" ==
RplProgram.new( '« dup type "Name" ==
« swap »
ift
over rcl + swap sto' )
end )
over rcl + swap sto »' ) )
@dictionary.add_word( ['sto-'],
'Store',
'( a n -- ) subtract content to variable\'s value',
proc do
run( '
dup type "name" ==
RplProgram.new( '« dup type "Name" ==
« swap »
ift
over rcl swap - swap sto' )
end )
over rcl swap - swap sto »' ) )
@dictionary.add_word( ['sto×', 'sto*'],
'Store',
'( a n -- ) multiply content of variable\'s value',
proc do
run( '
dup type "name" ==
RplProgram.new( '« dup type "Name" ==
« swap »
ift
over rcl * swap sto' )
end )
over rcl * swap sto »' ) )
@dictionary.add_word( ['sto÷', 'sto/'],
'Store',
'( a n -- ) divide content of variable\'s value',
proc do
run( '
dup type "name" ==
RplProgram.new( '« dup type "Name" ==
« swap »
ift
over rcl swap / swap sto' )
end )
over rcl swap / swap sto »' ) )
@dictionary.add_word( ['sneg'],
'Store',
'( a n -- ) negate content of variable\'s value',
proc do
run( 'dup rcl chs swap sto' )
end )
RplProgram.new( '« dup rcl chs swap sto »' ) )
@dictionary.add_word( ['sinv'],
'Store',
'( a n -- ) invert content of variable\'s value',
proc do
run( 'dup rcl inv swap sto' )
end )
RplProgram.new( '« dup rcl inv swap sto »' ) )
@dictionary.add_word( ['↴', 'lsto'],
'Program',
'( content name -- ) store to local variable',
proc do
args = stack_extract( [%i[name], :any] )
args = stack_extract( [[RplName], :any] )
@dictionary.add_local_var( args[0][:value],
@dictionary.add_local_var( args[0].value,
args[1] )
end )
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module StringAndList
include Types
def populate_dictionary
super
@ -12,37 +14,35 @@ module RplLang
proc do
args = stack_extract( [:any] )
@stack << { type: :string,
value: stringify( args[0] ) }
@stack << RplString.new( "\"#{args[0]}\"" )
end )
@dictionary.add_word( ['str→', 'str->'],
'String',
'( s -- a ) convert string to element',
proc do
args = stack_extract( [%i[string]] )
args = stack_extract( [[RplString]] )
@stack += parse( args[0][:value] )
@stack += Parser.parse( args[0].value )
end )
@dictionary.add_word( ['→list', '->list'],
'Lists',
'( … x -- […] ) pack x stacks levels into a list',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( %i[any] * args[0][:value] )
args = stack_extract( [[RplNumeric]] )
args = stack_extract( %i[any] * args[0].value )
@stack << { type: :list,
value: args.reverse }
@stack << RplList.new( args.reverse )
end )
@dictionary.add_word( ['list→', 'list->'],
'Lists',
'( […] -- … ) unpack list on stack',
proc do
args = stack_extract( [%i[list]] )
args = stack_extract( [[RplList]] )
args[0][:value].each do |elt|
args[0].value.each do |elt|
@stack << elt
end
end )
@ -51,83 +51,66 @@ module RplLang
'String',
'( n -- c ) convert ASCII character code in stack level 1 into a string',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :string,
value: args[0][:value].to_i.chr }
@stack << RplString.new( "\"#{args[0].value.to_i.chr}\"" )
end )
@dictionary.add_word( ['num'],
'String',
'( s -- n ) return ASCII code of the first character of the string in stack level 1 as a real number',
proc do
args = stack_extract( [%i[string]] )
args = stack_extract( [[RplString]] )
@stack << { type: :numeric,
base: 10,
value: BigDecimal( args[0][:value].ord ) }
@stack << RplNumeric.new( args[0].value.ord )
end )
@dictionary.add_word( ['size'],
'String',
'( s -- n ) return the length of the string or list',
proc do
args = stack_extract( [%i[string list]] )
args = stack_extract( [[RplString, RplList]] )
@stack << { type: :numeric,
base: 10,
value: BigDecimal( args[0][:value].length ) }
@stack << RplNumeric.new( args[0].value.length )
end )
@dictionary.add_word( ['pos'],
'String',
'( s s -- n ) search for the string in level 1 within the string in level 2',
proc do
args = stack_extract( [%i[string], %i[string]] )
args = stack_extract( [[RplString], [RplString]] )
@stack << { type: :numeric,
base: 10,
value: BigDecimal( args[1][:value].index( args[0][:value] ) ) }
@stack << RplNumeric.new( args[1].value.index( args[0].value ) )
end )
@dictionary.add_word( ['sub'],
'String',
'( s n n -- s ) return a substring of the string in level 3',
proc do
args = stack_extract( [%i[numeric], %i[numeric], %i[string]] )
args = stack_extract( [[RplNumeric], [RplNumeric], [RplString]] )
@stack << { type: :string,
value: args[2][:value][ (args[1][:value] - 1)..(args[0][:value] - 1) ] }
@stack << RplString.new( "\"#{args[2].value[ (args[1].value - 1)..(args[0].value - 1) ]}\"" )
end )
@dictionary.add_word( ['rev'],
'String',
'( s -- s ) reverse string or list',
proc do
args = stack_extract( [%i[string list]] )
args = stack_extract( [[RplString, RplList]] )
result = args[0]
args[0].value.reverse!
case args[0][:type]
when :string
result = { type: :string,
value: args[0][:value].reverse }
when :list
result[:value].reverse!
end
@stack << result
@stack << args[0]
end )
@dictionary.add_word( ['split'],
'String',
'( s c -- … ) split string s on character c',
proc do
args = stack_extract( [%i[string], %i[string]] )
args = stack_extract( [[RplString], [RplString]] )
args[1][:value].split( args[0][:value] ).each do |elt|
@stack << { type: :string,
value: elt }
args[1].value.split( args[0].value ).each do |elt|
@stack << RplString.new( "\"#{elt}\"" )
end
end )
@ -135,14 +118,14 @@ module RplLang
'Lists',
'( […] prg -- … ) run prg on each element of a list',
proc do
args = stack_extract( [%i[program], %i[list]] )
args = stack_extract( [[RplProgram], [RplList]] )
args[1][:value].each do |elt|
args[1].value.each do |elt|
@stack << elt
run( args[0][:value] )
run( args[0].value )
end
run( "#{args[1][:value].length} →list" )
run( "#{args[1].value.length} →list" )
end )
end
end

View file

@ -3,6 +3,8 @@
module RplLang
module Words
module Test
include Types
def populate_dictionary
super
@ -12,8 +14,7 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] > args[0][:value] }
@stack << RplBoolean.new( args[1].value > args[0].value )
end )
@dictionary.add_word( ['≥', '>='],
@ -22,8 +23,7 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] >= args[0][:value] }
@stack << RplBoolean.new( args[1].value >= args[0].value )
end )
@dictionary.add_word( ['<'],
@ -32,8 +32,7 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] < args[0][:value] }
@stack << RplBoolean.new( args[1].value < args[0].value )
end )
@dictionary.add_word( ['≤', '<='],
@ -42,8 +41,7 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] <= args[0][:value] }
@stack << RplBoolean.new( args[1].value <= args[0].value )
end )
@dictionary.add_word( ['≠', '!='],
@ -52,8 +50,7 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] != args[0][:value] }
@stack << RplBoolean.new( args[1].value != args[0].value )
end )
@dictionary.add_word( ['==', 'same'],
@ -62,64 +59,57 @@ module RplLang
proc do
args = stack_extract( %i[any any] )
@stack << { type: :boolean,
value: args[1][:value] == args[0][:value] }
@stack << RplBoolean.new( args[1].value == args[0].value )
end )
@dictionary.add_word( ['and'],
'Test',
'( a b -- t ) boolean and',
proc do
args = stack_extract( [%i[boolean], %i[boolean]] )
args = stack_extract( [[RplBoolean], [RplBoolean]] )
@stack << { type: :boolean,
value: args[1][:value] && args[0][:value] }
@stack << RplBoolean.new( args[1].value && args[0].value )
end )
@dictionary.add_word( ['or'],
'Test',
'( a b -- t ) boolean or',
proc do
args = stack_extract( [%i[boolean], %i[boolean]] )
args = stack_extract( [[RplBoolean], [RplBoolean]] )
@stack << { type: :boolean,
value: args[1][:value] || args[0][:value] }
@stack << RplBoolean.new( args[1].value || args[0].value )
end )
@dictionary.add_word( ['xor'],
'Test',
'( a b -- t ) boolean xor',
proc do
args = stack_extract( [%i[boolean], %i[boolean]] )
args = stack_extract( [[RplBoolean], [RplBoolean]] )
@stack << { type: :boolean,
value: args[1][:value] ^ args[0][:value] }
@stack << RplBoolean.new( args[1].value ^ args[0].value )
end )
@dictionary.add_word( ['not'],
'Test',
'( a -- t ) invert boolean value',
proc do
args = stack_extract( [%i[boolean]] )
args = stack_extract( [[RplBoolean]] )
@stack << { type: :boolean,
value: !args[0][:value] }
@stack << RplBoolean.new(!args[0].value )
end )
@dictionary.add_word( ['true'],
'Test',
'( -- t ) push true onto stack',
proc do
@stack << { type: :boolean,
value: true }
@stack << RplBoolean.new( true )
end )
@dictionary.add_word( ['false'],
'Test',
'( -- t ) push false onto stack',
proc do
@stack << { type: :boolean,
value: false }
@stack << RplBoolean.new( false )
end )
end
end

View file

@ -5,6 +5,8 @@ require 'date'
module RplLang
module Words
module TimeAndDate
include Types
def populate_dictionary
super
@ -12,15 +14,13 @@ module RplLang
'Time and date',
'( -- t ) push current time',
proc do
@stack << { type: :string,
value: Time.now.to_s }
@stack << RplString.new( "\"#{Time.now}\"" )
end )
@dictionary.add_word( ['date'],
'Time and date',
'( -- d ) push current date',
proc do
@stack << { type: :string,
value: Date.today.to_s }
@stack << RplString.new( "\"#{Date.today}\"" )
end )
@dictionary.add_word( ['ticks'],
'Time and date',
@ -28,9 +28,7 @@ module RplLang
proc do
ticks_since_epoch = Time.utc( 1, 1, 1 ).to_i * 10_000_000
now = Time.now
@stack << { type: :numeric,
base: 10,
value: now.to_i * 10_000_000 + now.nsec / 100 - ticks_since_epoch }
@stack << RplNumeric.new( now.to_i * 10_000_000 + now.nsec / 100 - ticks_since_epoch )
end )
end
end

View file

@ -5,6 +5,8 @@
module RplLang
module Words
module Trig
include Types
def populate_dictionary
super
@ -12,49 +14,38 @@ module RplLang
'Trig on reals and complexes',
'( … -- 𝛑 ) push 𝛑',
proc do
@stack << { type: :numeric,
base: 10,
value: BigMath.PI( precision ) }
@stack << RplNumeric.new( BigMath.PI( RplNumeric.precision ) )
end )
@dictionary.add_word( ['sin'],
'Trig on reals and complexes',
'( n -- m ) compute sinus of n',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: BigMath.sin( BigDecimal( args[0][:value], precision ), precision ) }
@stack << RplNumeric.new( BigMath.sin( BigDecimal( args[0].value, RplNumeric.precision ), RplNumeric.precision ) )
end )
@dictionary.add_word( ['asin'],
'Trig on reals and complexes',
'( n -- m ) compute arg-sinus of n',
proc do
run( '
dup abs 1 ==
RplProgram.new( '« dup abs 1 ==
« 𝛑 2 / * »
« dup sq 1 swap - sqrt / atan »
ifte' )
end )
ifte »' ) )
@dictionary.add_word( ['cos'],
'Trig on reals and complexes',
'( n -- m ) compute cosinus of n',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: BigMath.cos( BigDecimal( args[0][:value], precision ), precision ) }
@stack << RplNumeric.new( BigMath.cos( BigDecimal( args[0].value, RplNumeric.precision ), RplNumeric.precision ) )
end )
@dictionary.add_word( ['acos'],
'Trig on reals and complexes',
'( n -- m ) compute arg-cosinus of n',
proc do
run( '
dup 0 ==
RplProgram.new( '« dup 0 ==
« drop 𝛑 2 / »
«
dup sq 1 swap - sqrt / atan
@ -62,38 +53,31 @@ module RplLang
« 𝛑 + »
ift
»
ifte' )
end )
ifte »' ) )
@dictionary.add_word( ['tan'],
'Trig on reals and complexes',
'( n -- m ) compute tangent of n',
proc do
run( 'dup sin swap cos /' )
end )
RplProgram.new( '« dup sin swap cos / »' ) )
@dictionary.add_word( ['atan'],
'Trig on reals and complexes',
'( n -- m ) compute arc-tangent of n',
proc do
args = stack_extract( [%i[numeric]] )
args = stack_extract( [[RplNumeric]] )
@stack << RplNumeric.new( BigMath.atan( BigDecimal( args[0].value, RplNumeric.precision ), RplNumeric.precision ) )
end )
@stack << { type: :numeric,
base: infer_resulting_base( args ),
value: BigMath.atan( BigDecimal( args[0][:value], precision ), precision ) }
end)
@dictionary.add_word( ['d→r', 'd->r'],
'Trig on reals and complexes',
'( n -- m ) convert degree to radian',
proc do
run( '180 / 𝛑 *' )
end )
RplProgram.new( '« 180 / 𝛑 * »' ) )
@dictionary.add_word( ['r→d', 'r->d'],
'Trig on reals and complexes',
'( n -- m ) convert radian to degree',
proc do
run( '𝛑 180 / /' )
end)
RplProgram.new( '« 𝛑 180 / / »' ) )
end
end
end

View file

@ -5,29 +5,24 @@ require 'minitest/autorun'
require 'rpl'
class TestParser < MiniTest::Test
include Types
def test_stack_extract
interpreter = Rpl.new( [{ value: 1, type: :numeric },
{ value: 2, type: :numeric }] )
interpreter = Rpl.new
interpreter.run '1 2'
args = interpreter.stack_extract [:any]
assert_equal [{ value: 1, type: :numeric }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
assert_equal [{ value: 2, type: :numeric }],
assert_equal [RplNumeric.new( 2 )],
args
interpreter = Rpl.new( [{ value: 'test', type: :string },
{ value: 2, type: :numeric }] )
args = interpreter.stack_extract [[:numeric], :any]
interpreter = Rpl.new
interpreter.run '"test" 2'
args = interpreter.stack_extract [[RplNumeric], :any]
assert_equal [],
interpreter.stack
assert_equal [{ value: 2, type: :numeric },
{ value: 'test', type: :string }],
assert_equal [RplNumeric.new( 2 ),
RplString.new( '"test"' )],
args
end
def test_stringify
assert_equal '∞',
Rpl.new.stringify( { value: Float::INFINITY,
base: 10,
type: :numeric } )
end
end

View file

@ -6,16 +6,18 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageBranch < MiniTest::Test
include Types
def test_loop
interpreter = Rpl.new
interpreter.run '« "hello no." swap + » 11 16 loop'
assert_equal [{ value: 'hello no.11', type: :string },
{ value: 'hello no.12', type: :string },
{ value: 'hello no.13', type: :string },
{ value: 'hello no.14', type: :string },
{ value: 'hello no.15', type: :string },
{ value: 'hello no.16', type: :string }],
assert_equal [RplString.new( '"hello no.11"' ),
RplString.new( '"hello no.12"' ),
RplString.new( '"hello no.13"' ),
RplString.new( '"hello no.14"' ),
RplString.new( '"hello no.15"' ),
RplString.new( '"hello no.16"' )],
interpreter.stack
end
@ -23,11 +25,11 @@ class TestLanguageBranch < MiniTest::Test
interpreter = Rpl.new
interpreter.run '« "hello no." swap + » 5 times'
assert_equal [{ value: 'hello no.0', type: :string },
{ value: 'hello no.1', type: :string },
{ value: 'hello no.2', type: :string },
{ value: 'hello no.3', type: :string },
{ value: 'hello no.4', type: :string }],
assert_equal [RplString.new( '"hello no.0"' ),
RplString.new( '"hello no.1"' ),
RplString.new( '"hello no.2"' ),
RplString.new( '"hello no.3"' ),
RplString.new( '"hello no.4"' )],
interpreter.stack
end
@ -35,13 +37,13 @@ class TestLanguageBranch < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true « 2 3 + » « 2 3 - » ifte'
assert_equal [{ value: 5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 5 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false « 2 3 + » « 2 3 - » ifte'
assert_equal [{ value: -1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -1 )],
interpreter.stack
end
@ -49,7 +51,7 @@ class TestLanguageBranch < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true « 2 3 + » ift'
assert_equal [{ value: 5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 5 )],
interpreter.stack
interpreter = Rpl.new

View file

@ -6,24 +6,26 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageFileSystem < MiniTest::Test
include Types
def test_fread
interpreter = Rpl.new
interpreter.run '"./spec/test.rpl" fread'
assert_equal [{:type=>:string, :value=>"1 2 +\n\n« dup dup * * »\n'trrr' sto\n\ntrrr\n"}],
assert_equal [RplString.new( "\"1 2 +\n\n« dup dup * * »\n'trrr' sto\n\ntrrr\n\"" )],
interpreter.stack
interpreter.run 'eval vars'
assert_equal [{ value: 27, base: 10, type: :numeric },
{ value: [{ type: :name, value: 'trrr' }], type: :list }],
assert_equal [RplNumeric.new( 27 ),
RplList.new( [ RplName.new( 'trrr' ) ] )],
interpreter.stack
end
def test_feval
interpreter = Rpl.new
interpreter.run '"spec/test.rpl" feval vars'
assert_equal [{ value: 27, base: 10, type: :numeric },
{ value: [{ type: :name, value: 'trrr' }], type: :list }],
assert_equal [RplNumeric.new( 27 ),
RplList.new( [ RplName.new( 'trrr' ) ] )],
interpreter.stack
end

View file

@ -6,312 +6,309 @@ require 'minitest/autorun'
require 'rpl'
class TesttLanguageOperations < MiniTest::Test
include Types
def test_add
interpreter = Rpl.new
interpreter.run '1 2 +'
assert_equal [{ value: 3, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 3 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 "a" +'
assert_equal [{ value: '1a', type: :string }],
assert_equal [RplString.new( '"1a"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 \'a\' +'
assert_equal [{ value: '1a', type: :string }],
assert_equal [RplName.new( "'1a'" )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 dup dup →list +'
assert_equal [{ value: [{ value: 1, type: :numeric, base: 10 },
{ value: 1, type: :numeric, base: 10 }],
type: :list }],
assert_equal [RplList.new( [RplNumeric.new( 1 ),
RplNumeric.new( 1 )] )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"a" "b" +'
assert_equal [{ value: 'ab', type: :string }],
assert_equal [RplString.new( '"ab"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"a" \'b\' +'
assert_equal [{ value: 'ab', type: :string }],
assert_equal [RplString.new( '"ab"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"a" 1 +'
assert_equal [{ value: 'a1', type: :string }],
assert_equal [RplString.new( '"a1"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"a" 1 dup →list +'
assert_equal [{ value: [{ value: 'a', type: :string },
{ value: 1, type: :numeric, base: 10 }],
type: :list }],
assert_equal [RplList.new( [RplString.new( '"a"' ),
RplNumeric.new( 1 )] )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '\'a\' 1 +'
assert_equal [{ value: 'a1', type: :name }],
assert_equal [RplName.new( 'a1' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '\'a\' "b" +'
assert_equal [{ value: 'ab', type: :string }],
assert_equal [RplString.new( '"ab"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '\'a\' \'b\' +'
assert_equal [{ value: 'ab', type: :name }],
assert_equal [RplName.new( "'ab'" )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '\'a\' 1 dup →list +'
assert_equal [{ value: [{ value: 'a', type: :name },
{ value: 1, type: :numeric, base: 10 }],
type: :list }],
assert_equal [RplList.new( [RplName.new( 'a' ),
RplNumeric.new( 1 )] )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 a "test" 3 →list dup rev +'
assert_equal [{ type: :list,
value: [{ value: 1, type: :numeric, base: 10 },
{ type: :name, value: 'a' },
{ value: 'test', type: :string },
{ value: 'test', type: :string },
{ type: :name, value: 'a' },
{ value: 1, type: :numeric, base: 10 }] }],
assert_equal [RplList.new( [RplNumeric.new( 1 ),
RplName.new( 'a' ),
RplString.new( '"test"' ),
RplString.new( '"test"' ),
RplName.new( 'a' ),
RplNumeric.new( 1 )] )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 a "test" 3 →list 9 +'
assert_equal [{ type: :list,
value: [{ value: 1, type: :numeric, base: 10 },
{ type: :name, value: 'a' },
{ value: 'test', type: :string },
{ value: 9, type: :numeric, base: 10 }] }],
assert_equal [RplList.new( [RplNumeric.new( 1 ),
RplName.new( 'a' ),
RplString.new( '"test"' ),
RplNumeric.new( 9 )] )],
interpreter.stack
end
def test_subtract
interpreter = Rpl.new
interpreter.run '1 2 -'
assert_equal [{ value: -1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '2 1 -'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
def test_negate
interpreter = Rpl.new
interpreter.run '-1 chs'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 chs'
assert_equal [{ value: -1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -1 )],
interpreter.stack
end
def test_multiply
interpreter = Rpl.new
interpreter.run '3 4 *'
assert_equal [{ value: 12, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 12 )],
interpreter.stack
end
def test_divide
interpreter = Rpl.new
interpreter.run '3.0 4 /'
assert_equal [{ value: 0.75, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0.75 )],
interpreter.stack
end
def test_inverse
interpreter = Rpl.new
interpreter.run '4 inv'
assert_equal [{ value: 0.25, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0.25 )],
interpreter.stack
end
def test_power
interpreter = Rpl.new
interpreter.run '3 4 ^'
assert_equal [{ value: 81, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 81 )],
interpreter.stack
end
def test_sqrt
interpreter = Rpl.new
interpreter.run '16 √'
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
end
def test_sq
interpreter = Rpl.new
interpreter.run '4 sq'
assert_equal [{ value: 16, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 16 )],
interpreter.stack
end
def test_abs
interpreter = Rpl.new
interpreter.run '-1 abs'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 abs'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
def test_dec
interpreter = Rpl.new
interpreter.run '0x1 dec'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
def test_hex
interpreter = Rpl.new
interpreter.run '1 hex'
assert_equal [{ value: 1, type: :numeric, base: 16 }],
assert_equal [RplNumeric.new( 1, 16 )],
interpreter.stack
end
def test_bin
interpreter = Rpl.new
interpreter.run '1 bin'
assert_equal [{ value: 1, type: :numeric, base: 2 }],
assert_equal [RplNumeric.new( 1, 2 )],
interpreter.stack
end
def test_base
interpreter = Rpl.new
interpreter.run '1 31 base'
assert_equal [{ value: 1, type: :numeric, base: 31 }],
assert_equal [RplNumeric.new( 1, 31 )],
interpreter.stack
end
def test_sign
interpreter = Rpl.new
interpreter.run '-10 sign'
assert_equal [{ value: -1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '10 sign'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '0 sign'
assert_equal [{ value: 0, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0 )],
interpreter.stack
end
def test_percent
interpreter = Rpl.new
interpreter.run '2 33 %'
assert_equal [{ value: 0.66, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0.66 )],
interpreter.stack
end
def test_inverse_percent
interpreter = Rpl.new
interpreter.run '2 0.66 %CH'
assert_equal [{ value: 33, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 33 )],
interpreter.stack
end
def test_mod
interpreter = Rpl.new
interpreter.run '9 4 mod'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
def test_fact
interpreter = Rpl.new
interpreter.run '5 !'
assert_equal [{ value: 24, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 24 )],
interpreter.stack
end
def test_floor
interpreter = Rpl.new
interpreter.run '5.23 floor'
assert_equal [{ value: 5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 5 )],
interpreter.stack
end
def test_ceil
interpreter = Rpl.new
interpreter.run '5.23 ceil'
assert_equal [{ value: 6, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 6 )],
interpreter.stack
end
def test_min
interpreter = Rpl.new
interpreter.run '1 2 min'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '2 1 min'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
def test_max
interpreter = Rpl.new
interpreter.run '1 2 max'
assert_equal [{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 2 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '2 1 max'
assert_equal [{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 2 )],
interpreter.stack
end
def test_ip
interpreter = Rpl.new
interpreter.run '3.14 ip'
assert_equal [{ value: 3, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 3 )],
interpreter.stack
end
def test_fp
interpreter = Rpl.new
interpreter.run '3.14 fp'
assert_equal [{ value: 0.14, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0.14 )],
interpreter.stack
end
def test_mant
interpreter = Rpl.new
interpreter.run '123.456 mant -123.456 mant 0 mant'
assert_equal [{ type: :numeric, base: 10, value: BigDecimal( 0.123456, interpreter.precision ) },
{ type: :numeric, base: 10, value: BigDecimal( 0.123456, interpreter.precision ) },
{ type: :numeric, base: 10, value: BigDecimal( 0 ) }],
assert_equal [RplNumeric.new( 0.123456 ),
RplNumeric.new( 0.123456 ),
RplNumeric.new( 0 )],
interpreter.stack
end
def test_xpon
interpreter = Rpl.new
interpreter.run '123.456 xpon -123.456 xpon 0 xpon'
assert_equal [{ value: 3, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 0, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 3 ),
RplNumeric.new( 3 ),
RplNumeric.new( 0 )],
interpreter.stack
end
end

View file

@ -5,19 +5,21 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageProgram < MiniTest::Test
include Types
def test_eval
interpreter = Rpl.new
interpreter.run '« 2 dup * dup » eval'
assert_equal [{ value: 4, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 ),
RplNumeric.new( 4 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '4 \'dup\' eval'
assert_equal [{ value: 4, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 ),
RplNumeric.new( 4 )],
interpreter.stack
interpreter = Rpl.new
@ -25,8 +27,8 @@ class TestLanguageProgram < MiniTest::Test
\'dup\'
eval'
assert_equal [{ value: 4, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 ),
RplNumeric.new( 4 )],
interpreter.stack
end
end

View file

@ -6,12 +6,14 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageStack < MiniTest::Test
include Types
def test_swap
interpreter = Rpl.new
interpreter.run '1 2 swap'
assert_equal [{ value: 2, type: :numeric, base: 10 },
{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 2 ),
RplNumeric.new( 1 )],
interpreter.stack
end
@ -19,7 +21,7 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 drop'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
@ -35,7 +37,7 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 4 3 dropn'
assert_equal [{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 )],
interpreter.stack
end
@ -50,9 +52,9 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 rot'
assert_equal [{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 1, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 1 )],
interpreter.stack
end
@ -60,9 +62,9 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 dup'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 2 )],
interpreter.stack
end
@ -70,10 +72,10 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 dup2'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 1 ),
RplNumeric.new( 2 )],
interpreter.stack
end
@ -81,13 +83,13 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 4 3 dupn'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 ),
RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 )],
interpreter.stack
end
@ -95,11 +97,11 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 4 3 pick'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 ),
RplNumeric.new( 2 )],
interpreter.stack
end
@ -107,15 +109,15 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'depth'
assert_equal [{ value: 0, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 2 depth'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 2 )],
interpreter.stack
end
@ -123,10 +125,10 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 4 3 roll'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 ),
RplNumeric.new( 2 )],
interpreter.stack
end
@ -134,10 +136,10 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 4 3 2 rolld'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 )],
interpreter.stack
end
@ -145,11 +147,11 @@ class TestLanguageStack < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 2 3 4 over'
assert_equal [{ value: 1, type: :numeric, base: 10 },
{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: 4, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1 ),
RplNumeric.new( 2 ),
RplNumeric.new( 3 ),
RplNumeric.new( 4 ),
RplNumeric.new( 3 )],
interpreter.stack
end
end

View file

@ -4,14 +4,16 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageProgram < MiniTest::Test
class TestLanguageStore < MiniTest::Test
include Types
def test_sto
interpreter = Rpl.new
interpreter.run '« 2 dup * » \'quatre\' sto'
assert_empty interpreter.stack
interpreter.run 'quatre'
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
end
@ -20,21 +22,21 @@ class TestLanguageProgram < MiniTest::Test
interpreter.run "« 2 'deux' lsto deux dup * » eval 'deux' rcl"
assert_empty interpreter.dictionary.local_vars_layers
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run "« 2 'deux' lsto « 3 'trois' lsto trois drop » eval deux dup * » eval 'deux' rcl 'trois' rcl"
assert_empty interpreter.dictionary.local_vars_layers
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
end
def test_rcl
interpreter = Rpl.new
interpreter.run '« 2 dup * » \'quatre\' sto \'quatre\' rcl'
assert_equal [{ value: '2 dup *', type: :program }],
assert_equal [RplProgram.new( '« 2 dup * »' )],
interpreter.stack
end
@ -47,8 +49,8 @@ class TestLanguageProgram < MiniTest::Test
def test_vars
interpreter = Rpl.new
interpreter.run '« 2 dup * » \'quatre\' sto 1 \'un\' sto vars'
assert_equal [{ value: [{ type: :name, value: 'quatre' },
{ type: :name, value: 'un' }], type: :list }],
assert_equal [RplList.new( [RplName.new( 'quatre' ),
RplName.new( 'un' )] )],
interpreter.stack
end
@ -61,62 +63,62 @@ class TestLanguageProgram < MiniTest::Test
def test_sto_add
interpreter = Rpl.new
interpreter.run '1 \'test\' sto \'test\' 3 sto+ \'test\' rcl'
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 \'test\' sto 3 \'test\' sto+ \'test\' rcl'
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
end
def test_sto_subtract
interpreter = Rpl.new
interpreter.run '1 \'test\' sto \'test\' 3 sto- \'test\' rcl'
assert_equal [{ value: -2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -2 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 \'test\' sto 3 \'test\' sto- \'test\' rcl'
assert_equal [{ value: -2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -2 )],
interpreter.stack
end
def test_sto_multiply
interpreter = Rpl.new
interpreter.run '2 \'test\' sto \'test\' 3 sto* \'test\' rcl'
assert_equal [{ value: 6, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 6 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '2 \'test\' sto 3 \'test\' sto* \'test\' rcl'
assert_equal [{ value: 6, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 6 )],
interpreter.stack
end
def test_sto_divide
interpreter = Rpl.new
interpreter.run '3 \'test\' sto \'test\' 2.0 sto÷ \'test\' rcl'
assert_equal [{ value: 1.5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1.5 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '3 \'test\' sto 2.0 \'test\' sto÷ \'test\' rcl'
assert_equal [{ value: 1.5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 1.5 )],
interpreter.stack
end
def test_sto_negate
interpreter = Rpl.new
interpreter.run '3 \'test\' sto \'test\' sneg \'test\' rcl'
assert_equal [{ value: -3, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( -3 )],
interpreter.stack
end
def test_sto_inverse
interpreter = Rpl.new
interpreter.run '2 \'test\' sto \'test\' sinv \'test\' rcl'
assert_equal [{ value: 0.5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 0.5 )],
interpreter.stack
end
end

View file

@ -6,11 +6,13 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageString < MiniTest::Test
include Types
def test_to_string
interpreter = Rpl.new
interpreter.run '2 →str'
assert_equal [{ value: '2', type: :string }],
assert_equal [RplString.new( '"2"' )],
interpreter.stack
end
@ -18,15 +20,15 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"2" str→'
assert_equal [{ value: 2, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 2 )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"« dup * » \'carré\' sto" str→'
assert_equal [{ value: 'dup *', type: :program },
{ value: 'carré', type: :name },
{ value: 'sto', type: :word }],
assert_equal [RplProgram.new( '« dup * »' ),
RplName.new( 'carré' ),
RplName.new( 'sto' )],
interpreter.stack
end
@ -34,7 +36,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '71 chr'
assert_equal [{ value: 'G', type: :string }],
assert_equal [RplString.new( '"G"' )],
interpreter.stack
end
@ -42,7 +44,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"G" num'
assert_equal [{ value: 71, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 71 )],
interpreter.stack
end
@ -50,7 +52,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"test" size'
assert_equal [{ value: 4, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 4 )],
interpreter.stack
end
@ -58,7 +60,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"test of POS" "of" pos'
assert_equal [{ value: 5, type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 5 )],
interpreter.stack
end
@ -66,7 +68,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"my string to sub" 4 6 sub'
assert_equal [{ value: 'str', type: :string }],
assert_equal [RplString.new( '"str"' )],
interpreter.stack
end
@ -74,7 +76,7 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"my string to sub" rev'
assert_equal [{ value: 'my string to sub'.reverse, type: :string }],
assert_equal [RplString.new( '"my string to sub"'.reverse )],
interpreter.stack
end
@ -82,18 +84,18 @@ class TestLanguageString < MiniTest::Test
interpreter = Rpl.new
interpreter.run '"my string to sub" " " split'
assert_equal [{ value: 'my', type: :string },
{ value: 'string', type: :string },
{ value: 'to', type: :string },
{ value: 'sub', type: :string }],
assert_equal [RplString.new( '"my"' ),
RplString.new( '"string"' ),
RplString.new( '"to"' ),
RplString.new( '"sub"' )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '"my,string,to sub" "," split'
assert_equal [{ value: 'my', type: :string },
{ value: 'string', type: :string },
{ value: 'to sub', type: :string }],
assert_equal [RplString.new( '"my"' ),
RplString.new( '"string"' ),
RplString.new( '"to sub"' )],
interpreter.stack
end
end

View file

@ -6,23 +6,25 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageTest < MiniTest::Test
include Types
def test_greater_than
interpreter = Rpl.new
interpreter.run '0 0.1 >'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '0.1 0 >'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 1 >'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
end
@ -30,19 +32,19 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run '0 0.1 >='
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '0.1 0 ≥'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 1 ≥'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -50,19 +52,19 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run '0 0.1 <'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '0.1 0 <'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 1 <'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
end
@ -70,19 +72,19 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run '0 0.1 <='
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '0.1 0 ≤'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 1 ≤'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -90,13 +92,13 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 1 !='
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 2 ≠'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -104,25 +106,25 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true true and'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false false and'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'true false and'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false true and'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
end
@ -130,25 +132,25 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true true or'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false false or'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'true false or'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false true or'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -156,25 +158,25 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true true xor'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false false xor'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'true false xor'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false true xor'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -182,13 +184,13 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true not'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
interpreter = Rpl.new
interpreter.run 'false not'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -196,13 +198,13 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run '1 1 same'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
interpreter = Rpl.new
interpreter.run '1 2 =='
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
end
@ -210,7 +212,7 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'true'
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
@ -218,7 +220,7 @@ class TestLanguageTest < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'false'
assert_equal [{ value: false, type: :boolean }],
assert_equal [RplBoolean.new( false )],
interpreter.stack
end
end

View file

@ -6,12 +6,14 @@ require 'minitest/autorun'
require 'rpl'
class TestLanguageTimeDate < MiniTest::Test
include Types
def test_time
now = Time.now.to_s
interpreter = Rpl.new
interpreter.run 'time'
assert_equal [{ value: now, type: :string }],
assert_equal [RplString.new( "\"#{now}\"" )],
interpreter.stack
end
@ -20,7 +22,7 @@ class TestLanguageTimeDate < MiniTest::Test
interpreter = Rpl.new
interpreter.run 'date'
assert_equal [{ value: now, type: :string }],
assert_equal [RplString.new( "\"#{now}\"" )],
interpreter.stack
end
@ -29,7 +31,7 @@ class TestLanguageTimeDate < MiniTest::Test
interpreter.run 'ticks'
# TODO: better test, but how?
assert_equal :numeric,
interpreter.stack[0][:type]
assert_equal RplNumeric,
interpreter.stack[0].class
end
end

View file

@ -4,69 +4,71 @@
require 'minitest/autorun'
require 'rpl'
require 'rpl/types'
class TesttLanguageOperations < MiniTest::Test
include Types
def test_pi
interpreter = Rpl.new
interpreter.run 'pi'
assert_equal [{ value: BigMath.PI( interpreter.precision ), type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( BigMath.PI( RplNumeric.precision ) )],
interpreter.stack
end
def test_sin
interpreter = Rpl.new
interpreter.run '3 sin'
assert_equal [{ value: BigMath.sin( BigDecimal( 3 ), interpreter.precision ), type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( BigMath.sin( BigDecimal( 3 ), RplNumeric.precision ) )],
interpreter.stack
end
def test_asin
interpreter = Rpl.new
interpreter.run '1 asin pi 2 / =='
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
def test_cos
interpreter = Rpl.new
interpreter.run '3 cos'
assert_equal [{ value: BigMath.cos( BigDecimal( 3 ), interpreter.precision ), type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( BigMath.cos( BigDecimal( 3 ), RplNumeric.precision ) )],
interpreter.stack
end
def test_acos
interpreter = Rpl.new
interpreter.run '0 acos pi 2 / =='
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
def test_tan
interpreter = Rpl.new
interpreter.run '0 tan 0 =='
assert_equal [{ value: true, type: :boolean }],
assert_equal [RplBoolean.new( true )],
interpreter.stack
end
def test_atan
interpreter = Rpl.new
interpreter.run '1 atan'
assert_equal [{ value: BigMath.atan( BigDecimal( 1 ), interpreter.precision ), type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( BigMath.atan( BigDecimal( 1 ), RplNumeric.precision ) )],
interpreter.stack
end
def test_d2r
interpreter = Rpl.new
interpreter.run '90 d→r'
assert_equal [{ value: BigMath.PI( interpreter.precision ) / 2,
type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( BigMath.PI( RplNumeric.precision ) / 2 )],
interpreter.stack
end
def test_r2d
interpreter = Rpl.new
interpreter.run 'pi r→d'
assert_equal [{ value: BigDecimal( 180 ), type: :numeric, base: 10 }],
assert_equal [RplNumeric.new( 180 )],
interpreter.stack
end
end

View file

@ -1,185 +0,0 @@
# coding: utf-8
# frozen_string_literal: true
require 'minitest/autorun'
require 'rpl'
class TestParser < MiniTest::Test
def test_number
result = Rpl.new.parse( '1' )
assert_equal [{ value: 1, type: :numeric, base: 10 }], result
result = Rpl.new.parse( '0b101' )
assert_equal [{ value: BigDecimal( 5 ), type: :numeric, base: 2 }], result
result = Rpl.new.parse( '0o57' )
assert_equal [{ value: BigDecimal( 47 ), type: :numeric, base: 8 }], result
result = Rpl.new.parse( '03_10' )
assert_equal [{ value: BigDecimal( 3 ), type: :numeric, base: 3 }], result
end
def test_word
result = Rpl.new.parse( 'dup' )
assert_equal [{ value: 'dup', type: :word }], result
end
def test_string
result = Rpl.new.parse( '"test"' )
assert_equal [{ value: 'test', type: :string }], result
result = Rpl.new.parse( '" test"' )
assert_equal [{ value: ' test', type: :string }], result
result = Rpl.new.parse( '"test "' )
assert_equal [{ value: 'test ', type: :string }], result
result = Rpl.new.parse( '" test "' )
assert_equal [{ value: ' test ', type: :string }], result
result = Rpl.new.parse( '" « test » "' )
assert_equal [{ value: ' « test » ', type: :string }], result
end
def test_name
result = Rpl.new.parse( "'test'" )
assert_equal [{ value: 'test', type: :name }], result
end
def test_program
result = Rpl.new.parse( '« test »' )
assert_equal [{ value: 'test', type: :program }], result
result = Rpl.new.parse( '«test »' )
assert_equal [{ value: 'test', type: :program }], result
result = Rpl.new.parse( '« test»' )
assert_equal [{ value: 'test', type: :program }], result
result = Rpl.new.parse( '«test»' )
assert_equal [{ value: 'test', type: :program }], result
result = Rpl.new.parse( '« test test »' )
assert_equal [{ value: 'test test', type: :program }], result
result = Rpl.new.parse( '« test « test » »' )
assert_equal [{ value: 'test « test »', type: :program }], result
result = Rpl.new.parse( '« test "test" test »' )
assert_equal [{ value: 'test "test" test', type: :program }], result
end
def test_list
result = Rpl.new.parse( '{ test }' )
assert_equal [{ value: [{ value: 'test', type: :word }], type: :list }], result
result = Rpl.new.parse( '{test }' )
assert_equal [{ value: [{ value: 'test', type: :word }], type: :list }], result
result = Rpl.new.parse( '{ test}' )
assert_equal [{ value: [{ value: 'test', type: :word }], type: :list }], result
result = Rpl.new.parse( '{test}' )
assert_equal [{ value: [{ value: 'test', type: :word }], type: :list }], result
result = Rpl.new.parse( '{ test test }' )
assert_equal [{ value: [{ value: 'test', type: :word },
{ value: 'test', type: :word }], type: :list }], result
result = Rpl.new.parse( '{ test { test } }' )
assert_equal [{ value: [{ value: 'test', type: :word },
{ value: [{ value: 'test', type: :word }], type: :list }], type: :list }], result
result = Rpl.new.parse( '{ test "test" test }' )
assert_equal [{ value: [{ value: 'test', type: :word },
{ value: 'test', type: :string },
{ value: 'test', type: :word }], type: :list }], result
end
def test_number_number
result = Rpl.new.parse( '2 3' )
assert_equal [{ value: 2, type: :numeric, base: 10 }, { value: 3, type: :numeric, base: 10 }], result
end
def test_number_number_word
result = Rpl.new.parse( '2 3 +' )
assert_equal [{ value: 2, type: :numeric, base: 10 },
{ value: 3, type: :numeric, base: 10 },
{ value: '+', type: :word }], result
end
def test_number_string
result = Rpl.new.parse( '4 "test"' )
assert_equal [{ value: 4, type: :numeric, base: 10 }, { value: 'test', type: :string }], result
end
def test_emptystring
result = Rpl.new.parse( '""' )
assert_equal [{ value: '', type: :string }], result
end
def test_spacestring
result = Rpl.new.parse( '" "' )
assert_equal [{ value: ' ', type: :string }], result
end
def test_string_spacestring
result = Rpl.new.parse( '"test string" " "' )
assert_equal [{ value: 'test string', type: :string },
{ value: ' ', type: :string }], result
end
def test_string_word
result = Rpl.new.parse( '"test string" split' )
assert_equal [{ value: 'test string', type: :string },
{ value: 'split', type: :word }], result
end
def test_spacestring_word
result = Rpl.new.parse( '" " split' )
assert_equal [{ value: ' ', type: :string },
{ value: 'split', type: :word }], result
end
def test_program_name
result = Rpl.new.parse( "« 2 dup * » 'carré' sto" )
assert_equal [{ value: '2 dup *', type: :program },
{ value: 'carré', type: :name },
{ value: 'sto', type: :word }],
result
end
def test_with_multiline
result = Rpl.new.parse( "« 2
dup * »
'carré' sto" )
assert_equal [{ value: '2 dup *', type: :program },
{ value: 'carré', type: :name },
{ value: 'sto', type: :word }],
result
end
def test_with_comments
result = Rpl.new.parse( "« 2 #deux
# on duplique le deux
dup * »
# on va STOcker ce programme dans la variable 'carré'
'carré' sto" )
assert_equal [{ value: '2 dup *', type: :program },
{ value: 'carré', type: :name },
{ value: 'sto', type: :word }],
result
end
end

View file

@ -38,9 +38,12 @@ class TestTypes < MiniTest::Test
assert_equal true, RplName.can_parse?( "'test test'" ) # let's just allow spaces in names
assert_equal true, RplName.can_parse?( 'test' )
assert_equal true, RplName.can_parse?( 'test test' ) # let's just allow spaces in names
assert_equal false, RplName.can_parse?( "'test" )
assert_equal false, RplName.can_parse?( "test'" )
assert_equal true, RplName.can_parse?( "'test" )
assert_equal true, RplName.can_parse?( "test'" )
assert_equal false, RplName.can_parse?( "''" )
assert_equal false, RplName.can_parse?( "'1" )
assert_equal false, RplName.can_parse?( "1'" )
assert_equal false, RplName.can_parse?( '1' )
assert_equal RplName, RplName.new( "'test'" ).class
assert_equal RplName, RplName.new( "'test test'" ).class