rpl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/rpl +51 -0
- data/lib/rpl/core/branch.rb +55 -0
- data/lib/rpl/core/filesystem.rb +40 -0
- data/lib/rpl/core/general.rb +57 -0
- data/lib/rpl/core/list.rb +33 -0
- data/lib/rpl/core/logarithm.rb +118 -0
- data/lib/rpl/core/mode.rb +44 -0
- data/lib/rpl/core/operations.rb +387 -0
- data/lib/rpl/core/program.rb +20 -0
- data/lib/rpl/core/stack.rb +155 -0
- data/lib/rpl/core/store.rb +124 -0
- data/lib/rpl/core/string.rb +114 -0
- data/lib/rpl/core/test.rb +127 -0
- data/lib/rpl/core/time-date.rb +38 -0
- data/lib/rpl/core/trig.rb +100 -0
- data/lib/rpl/dictionary.rb +75 -0
- data/lib/rpl/interpreter.rb +217 -0
- data/lib/rpl.rb +43 -0
- metadata +61 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RplLang
|
4
|
+
module Core
|
5
|
+
module String
|
6
|
+
def populate_dictionary
|
7
|
+
super
|
8
|
+
|
9
|
+
@dictionary.add_word( ['→str', '->str'],
|
10
|
+
'String',
|
11
|
+
'( a -- s ) convert element to string',
|
12
|
+
proc do
|
13
|
+
args = stack_extract( [:any] )
|
14
|
+
|
15
|
+
@stack << { type: :string,
|
16
|
+
value: stringify( args[0] ) }
|
17
|
+
end )
|
18
|
+
|
19
|
+
@dictionary.add_word( ['str→', 'str->'],
|
20
|
+
'String',
|
21
|
+
'( s -- a ) convert string to element',
|
22
|
+
proc do
|
23
|
+
args = stack_extract( [%i[string]] )
|
24
|
+
|
25
|
+
@stack += parse( args[0][:value] )
|
26
|
+
end )
|
27
|
+
|
28
|
+
@dictionary.add_word( ['chr'],
|
29
|
+
'String',
|
30
|
+
'( n -- c ) convert ASCII character code in stack level 1 into a string',
|
31
|
+
proc do
|
32
|
+
args = stack_extract( [%i[numeric]] )
|
33
|
+
|
34
|
+
@stack << { type: :string,
|
35
|
+
value: args[0][:value].to_i.chr }
|
36
|
+
end )
|
37
|
+
|
38
|
+
@dictionary.add_word( ['num'],
|
39
|
+
'String',
|
40
|
+
'( s -- n ) return ASCII code of the first character of the string in stack level 1 as a real number',
|
41
|
+
proc do
|
42
|
+
args = stack_extract( [%i[string]] )
|
43
|
+
|
44
|
+
@stack << { type: :numeric,
|
45
|
+
base: 10,
|
46
|
+
value: args[0][:value].ord }
|
47
|
+
end )
|
48
|
+
|
49
|
+
@dictionary.add_word( ['size'],
|
50
|
+
'String',
|
51
|
+
'( s -- n ) return the length of the string',
|
52
|
+
proc do
|
53
|
+
args = stack_extract( [%i[string]] )
|
54
|
+
|
55
|
+
@stack << { type: :numeric,
|
56
|
+
base: 10,
|
57
|
+
value: args[0][:value].length }
|
58
|
+
end )
|
59
|
+
|
60
|
+
@dictionary.add_word( ['pos'],
|
61
|
+
'String',
|
62
|
+
'( s s -- n ) search for the string in level 1 within the string in level 2',
|
63
|
+
proc do
|
64
|
+
args = stack_extract( [%i[string], %i[string]] )
|
65
|
+
|
66
|
+
@stack << { type: :numeric,
|
67
|
+
base: 10,
|
68
|
+
value: args[1][:value].index( args[0][:value] ) }
|
69
|
+
end )
|
70
|
+
|
71
|
+
@dictionary.add_word( ['sub'],
|
72
|
+
'String',
|
73
|
+
'( s n n -- s ) return a substring of the string in level 3',
|
74
|
+
proc do
|
75
|
+
args = stack_extract( [%i[numeric], %i[numeric], %i[string]] )
|
76
|
+
|
77
|
+
@stack << { type: :string,
|
78
|
+
value: args[2][:value][ (args[1][:value] - 1)..(args[0][:value] - 1) ] }
|
79
|
+
end )
|
80
|
+
|
81
|
+
@dictionary.add_word( ['rev'],
|
82
|
+
'String',
|
83
|
+
'( s -- s ) reverse string',
|
84
|
+
proc do
|
85
|
+
args = stack_extract( [%i[string list]] )
|
86
|
+
|
87
|
+
result = args[0]
|
88
|
+
|
89
|
+
case args[0][:type]
|
90
|
+
when :string
|
91
|
+
result = { type: :string,
|
92
|
+
value: args[0][:value].reverse }
|
93
|
+
when :list
|
94
|
+
result[:value].reverse!
|
95
|
+
end
|
96
|
+
|
97
|
+
@stack << result
|
98
|
+
end )
|
99
|
+
|
100
|
+
@dictionary.add_word( ['split'],
|
101
|
+
'String',
|
102
|
+
'( s c -- … ) split string s on character c',
|
103
|
+
proc do
|
104
|
+
args = stack_extract( [%i[string], %i[string]] )
|
105
|
+
|
106
|
+
args[1][:value].split( args[0][:value] ).each do |elt|
|
107
|
+
@stack << { type: :string,
|
108
|
+
value: elt }
|
109
|
+
end
|
110
|
+
end )
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RplLang
|
4
|
+
module Core
|
5
|
+
module Test
|
6
|
+
def populate_dictionary
|
7
|
+
super
|
8
|
+
|
9
|
+
@dictionary.add_word( ['>'],
|
10
|
+
'Test',
|
11
|
+
'( a b -- t ) is a greater than b?',
|
12
|
+
proc do
|
13
|
+
args = stack_extract( %i[any any] )
|
14
|
+
|
15
|
+
@stack << { type: :boolean,
|
16
|
+
value: args[1][:value] > args[0][:value] }
|
17
|
+
end )
|
18
|
+
|
19
|
+
@dictionary.add_word( ['≥', '>='],
|
20
|
+
'Test',
|
21
|
+
'( a b -- t ) is a greater than or equal to b?',
|
22
|
+
proc do
|
23
|
+
args = stack_extract( %i[any any] )
|
24
|
+
|
25
|
+
@stack << { type: :boolean,
|
26
|
+
value: args[1][:value] >= args[0][:value] }
|
27
|
+
end )
|
28
|
+
|
29
|
+
@dictionary.add_word( ['<'],
|
30
|
+
'Test',
|
31
|
+
'( a b -- t ) is a less than b?',
|
32
|
+
proc do
|
33
|
+
args = stack_extract( %i[any any] )
|
34
|
+
|
35
|
+
@stack << { type: :boolean,
|
36
|
+
value: args[1][:value] < args[0][:value] }
|
37
|
+
end )
|
38
|
+
|
39
|
+
@dictionary.add_word( ['≤', '<='],
|
40
|
+
'Test',
|
41
|
+
'( a b -- t ) is a less than or equal to b?',
|
42
|
+
proc do
|
43
|
+
args = stack_extract( %i[any any] )
|
44
|
+
|
45
|
+
@stack << { type: :boolean,
|
46
|
+
value: args[1][:value] <= args[0][:value] }
|
47
|
+
end )
|
48
|
+
|
49
|
+
@dictionary.add_word( ['≠', '!='],
|
50
|
+
'Test',
|
51
|
+
'( a b -- t ) is a not equal to b',
|
52
|
+
proc do
|
53
|
+
args = stack_extract( %i[any any] )
|
54
|
+
|
55
|
+
@stack << { type: :boolean,
|
56
|
+
value: args[1][:value] != args[0][:value] }
|
57
|
+
end )
|
58
|
+
|
59
|
+
@dictionary.add_word( ['==', 'same'],
|
60
|
+
'Test',
|
61
|
+
'( a b -- t ) is a equal to b',
|
62
|
+
proc do
|
63
|
+
args = stack_extract( %i[any any] )
|
64
|
+
|
65
|
+
@stack << { type: :boolean,
|
66
|
+
value: args[1][:value] == args[0][:value] }
|
67
|
+
end )
|
68
|
+
|
69
|
+
@dictionary.add_word( ['and'],
|
70
|
+
'Test',
|
71
|
+
'( a b -- t ) boolean and',
|
72
|
+
proc do
|
73
|
+
args = stack_extract( [%i[boolean], %i[boolean]] )
|
74
|
+
|
75
|
+
@stack << { type: :boolean,
|
76
|
+
value: args[1][:value] && args[0][:value] }
|
77
|
+
end )
|
78
|
+
|
79
|
+
@dictionary.add_word( ['or'],
|
80
|
+
'Test',
|
81
|
+
'( a b -- t ) boolean or',
|
82
|
+
proc do
|
83
|
+
args = stack_extract( [%i[boolean], %i[boolean]] )
|
84
|
+
|
85
|
+
@stack << { type: :boolean,
|
86
|
+
value: args[1][:value] || args[0][:value] }
|
87
|
+
end )
|
88
|
+
|
89
|
+
@dictionary.add_word( ['xor'],
|
90
|
+
'Test',
|
91
|
+
'( a b -- t ) boolean xor',
|
92
|
+
proc do
|
93
|
+
args = stack_extract( [%i[boolean], %i[boolean]] )
|
94
|
+
|
95
|
+
@stack << { type: :boolean,
|
96
|
+
value: args[1][:value] ^ args[0][:value] }
|
97
|
+
end )
|
98
|
+
|
99
|
+
@dictionary.add_word( ['not'],
|
100
|
+
'Test',
|
101
|
+
'( a -- t ) invert boolean value',
|
102
|
+
proc do
|
103
|
+
args = stack_extract( [%i[boolean]] )
|
104
|
+
|
105
|
+
@stack << { type: :boolean,
|
106
|
+
value: !args[0][:value] }
|
107
|
+
end )
|
108
|
+
|
109
|
+
@dictionary.add_word( ['true'],
|
110
|
+
'Test',
|
111
|
+
'( -- t ) push true onto stack',
|
112
|
+
proc do
|
113
|
+
@stack << { type: :boolean,
|
114
|
+
value: true }
|
115
|
+
end )
|
116
|
+
|
117
|
+
@dictionary.add_word( ['false'],
|
118
|
+
'Test',
|
119
|
+
'( -- t ) push false onto stack',
|
120
|
+
proc do
|
121
|
+
@stack << { type: :boolean,
|
122
|
+
value: false }
|
123
|
+
end )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module RplLang
|
6
|
+
module Core
|
7
|
+
module TimeAndDate
|
8
|
+
def populate_dictionary
|
9
|
+
super
|
10
|
+
|
11
|
+
@dictionary.add_word( ['time'],
|
12
|
+
'Time and date',
|
13
|
+
'( -- t ) push current time',
|
14
|
+
proc do
|
15
|
+
@stack << { type: :string,
|
16
|
+
value: Time.now.to_s }
|
17
|
+
end )
|
18
|
+
@dictionary.add_word( ['date'],
|
19
|
+
'Time and date',
|
20
|
+
'( -- d ) push current date',
|
21
|
+
proc do
|
22
|
+
@stack << { type: :string,
|
23
|
+
value: Date.today.to_s }
|
24
|
+
end )
|
25
|
+
@dictionary.add_word( ['ticks'],
|
26
|
+
'Time and date',
|
27
|
+
'( -- t ) push datetime as ticks',
|
28
|
+
proc do
|
29
|
+
ticks_since_epoch = Time.utc( 1, 1, 1 ).to_i * 10_000_000
|
30
|
+
now = Time.now
|
31
|
+
@stack << { type: :numeric,
|
32
|
+
base: 10,
|
33
|
+
value: now.to_i * 10_000_000 + now.nsec / 100 - ticks_since_epoch }
|
34
|
+
end )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# https://rosettacode.org/wiki/Trigonometric_functions#Ruby
|
4
|
+
|
5
|
+
module RplLang
|
6
|
+
module Core
|
7
|
+
module Trig
|
8
|
+
def populate_dictionary
|
9
|
+
super
|
10
|
+
|
11
|
+
@dictionary.add_word( ['𝛑', 'pi'],
|
12
|
+
'Trig on reals and complexes',
|
13
|
+
'( … -- 𝛑 ) push 𝛑',
|
14
|
+
proc do
|
15
|
+
@stack << { type: :numeric,
|
16
|
+
base: 10,
|
17
|
+
value: BigMath.PI( precision ) }
|
18
|
+
end )
|
19
|
+
|
20
|
+
@dictionary.add_word( ['sin'],
|
21
|
+
'Trig on reals and complexes',
|
22
|
+
'( n -- m ) compute sinus of n',
|
23
|
+
proc do
|
24
|
+
args = stack_extract( [%i[numeric]] )
|
25
|
+
|
26
|
+
@stack << { type: :numeric,
|
27
|
+
base: infer_resulting_base( args ),
|
28
|
+
value: BigMath.sin( BigDecimal( args[0][:value], precision ), precision ) }
|
29
|
+
end )
|
30
|
+
|
31
|
+
@dictionary.add_word( ['asin'],
|
32
|
+
'Trig on reals and complexes',
|
33
|
+
'( n -- m ) compute arg-sinus of n',
|
34
|
+
proc do
|
35
|
+
run( '
|
36
|
+
dup abs 1 ==
|
37
|
+
« 𝛑 2 / * »
|
38
|
+
« dup sq 1 swap - sqrt / atan »
|
39
|
+
ifte' )
|
40
|
+
end )
|
41
|
+
|
42
|
+
@dictionary.add_word( ['cos'],
|
43
|
+
'Trig on reals and complexes',
|
44
|
+
'( n -- m ) compute cosinus of n',
|
45
|
+
proc do
|
46
|
+
args = stack_extract( [%i[numeric]] )
|
47
|
+
|
48
|
+
@stack << { type: :numeric,
|
49
|
+
base: infer_resulting_base( args ),
|
50
|
+
value: BigMath.cos( BigDecimal( args[0][:value], precision ), precision ) }
|
51
|
+
end )
|
52
|
+
@dictionary.add_word( ['acos'],
|
53
|
+
'Trig on reals and complexes',
|
54
|
+
'( n -- m ) compute arg-cosinus of n',
|
55
|
+
proc do
|
56
|
+
run( '
|
57
|
+
dup 0 ==
|
58
|
+
« drop 𝛑 2 / »
|
59
|
+
«
|
60
|
+
dup sq 1 swap - sqrt / atan
|
61
|
+
dup 0 <
|
62
|
+
« 𝛑 + »
|
63
|
+
ift
|
64
|
+
»
|
65
|
+
ifte' )
|
66
|
+
end )
|
67
|
+
|
68
|
+
@dictionary.add_word( ['tan'],
|
69
|
+
'Trig on reals and complexes',
|
70
|
+
'( n -- m ) compute tangent of n',
|
71
|
+
proc do
|
72
|
+
run( 'dup sin swap cos /' )
|
73
|
+
end )
|
74
|
+
|
75
|
+
@dictionary.add_word( ['atan'],
|
76
|
+
'Trig on reals and complexes',
|
77
|
+
'( n -- m ) compute arc-tangent of n',
|
78
|
+
proc do
|
79
|
+
args = stack_extract( [%i[numeric]] )
|
80
|
+
|
81
|
+
@stack << { type: :numeric,
|
82
|
+
base: infer_resulting_base( args ),
|
83
|
+
value: BigMath.atan( BigDecimal( args[0][:value], precision ), precision ) }
|
84
|
+
end)
|
85
|
+
@dictionary.add_word( ['d→r', 'd->r'],
|
86
|
+
'Trig on reals and complexes',
|
87
|
+
'( n -- m ) convert degree to radian',
|
88
|
+
proc do
|
89
|
+
run( '180 / 𝛑 *' )
|
90
|
+
end )
|
91
|
+
@dictionary.add_word( ['r→d', 'r->d'],
|
92
|
+
'Trig on reals and complexes',
|
93
|
+
'( n -- m ) convert radian to degree',
|
94
|
+
proc do
|
95
|
+
run( '𝛑 180 / /' )
|
96
|
+
end)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Dictionary
|
4
|
+
attr_reader :words,
|
5
|
+
:vars,
|
6
|
+
:local_vars_layers
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@words = {}
|
10
|
+
@vars = {}
|
11
|
+
@local_vars_layers = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_word( names, category, help, implementation )
|
15
|
+
names.each do |name|
|
16
|
+
@words[ name ] = { category: category,
|
17
|
+
help: help,
|
18
|
+
implementation: implementation }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_var( name, implementation )
|
23
|
+
@vars[ name ] = implementation
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_vars( names )
|
27
|
+
names.each do |name|
|
28
|
+
@vars.delete( name )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_var( name )
|
33
|
+
remove_vars( [name] )
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_all_vars
|
37
|
+
@vars = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_local_vars_layer
|
41
|
+
@local_vars_layers << {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_local_var( name, implementation )
|
45
|
+
@local_vars_layers.last[ name ] = implementation
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove_local_vars( names )
|
49
|
+
names.each do |name|
|
50
|
+
@local_vars_layers.last.delete( name )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_local_var( name )
|
55
|
+
remove_local_vars( [name] )
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_local_vars_layer
|
59
|
+
@local_vars_layers.pop
|
60
|
+
end
|
61
|
+
|
62
|
+
def lookup( name )
|
63
|
+
# look in local variables from the deepest layer up
|
64
|
+
local_vars_layer = @local_vars_layers.reverse.find { |layer| layer[ name ] }
|
65
|
+
word = local_vars_layer.nil? ? nil : local_vars_layer[ name ]
|
66
|
+
|
67
|
+
# otherwise look in (global) variables
|
68
|
+
word ||= @vars[ name ]
|
69
|
+
|
70
|
+
# or is it a core word
|
71
|
+
word ||= @words[ name ].nil? ? nil : @words[ name ][:implementation]
|
72
|
+
|
73
|
+
word
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal/math'
|
4
|
+
|
5
|
+
require 'rpl/dictionary'
|
6
|
+
|
7
|
+
class Interpreter
|
8
|
+
include BigMath
|
9
|
+
|
10
|
+
attr_reader :stack,
|
11
|
+
:dictionary,
|
12
|
+
:version
|
13
|
+
|
14
|
+
attr_accessor :precision
|
15
|
+
|
16
|
+
def initialize( stack = [], dictionary = Rpl::Lang::Dictionary.new )
|
17
|
+
@version = 0.1
|
18
|
+
|
19
|
+
@precision = default_precision
|
20
|
+
|
21
|
+
@dictionary = dictionary
|
22
|
+
@stack = stack
|
23
|
+
|
24
|
+
populate_dictionary if @dictionary.words.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_precision
|
28
|
+
12
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse( input )
|
32
|
+
is_numeric = lambda do |elt|
|
33
|
+
begin
|
34
|
+
!Float(elt).nil?
|
35
|
+
rescue ArgumentError
|
36
|
+
begin
|
37
|
+
!Integer(elt).nil?
|
38
|
+
rescue ArgumentError
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
splitted_input = input.split(' ')
|
45
|
+
|
46
|
+
# 2-passes:
|
47
|
+
# 1. regroup strings and programs
|
48
|
+
opened_programs = 0
|
49
|
+
closed_programs = 0
|
50
|
+
string_delimiters = 0
|
51
|
+
name_delimiters = 0
|
52
|
+
regrouping = false
|
53
|
+
|
54
|
+
regrouped_input = []
|
55
|
+
splitted_input.each do |elt|
|
56
|
+
if elt[0] == '«'
|
57
|
+
opened_programs += 1
|
58
|
+
elt.gsub!( '«', '« ') if elt.length > 1 && elt[1] != ' '
|
59
|
+
end
|
60
|
+
string_delimiters += 1 if elt[0] == '"' && elt.length > 1
|
61
|
+
name_delimiters += 1 if elt[0] == "'" && elt.length > 1
|
62
|
+
|
63
|
+
elt = "#{regrouped_input.pop} #{elt}".strip if regrouping
|
64
|
+
|
65
|
+
regrouped_input << elt
|
66
|
+
|
67
|
+
if elt[-1] == '»'
|
68
|
+
closed_programs += 1
|
69
|
+
elt.gsub!( '»', ' »') if elt.length > 1 && elt[-2] != ' '
|
70
|
+
end
|
71
|
+
string_delimiters += 1 if elt[-1] == '"'
|
72
|
+
name_delimiters += 1 if elt[-1] == "'"
|
73
|
+
|
74
|
+
regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs )
|
75
|
+
end
|
76
|
+
|
77
|
+
# 2. parse
|
78
|
+
# TODO: parse ∞, <NaN> as numerics
|
79
|
+
parsed_tree = []
|
80
|
+
regrouped_input.each do |elt|
|
81
|
+
parsed_entry = { value: elt }
|
82
|
+
|
83
|
+
parsed_entry[:type] = case elt[0]
|
84
|
+
when '«'
|
85
|
+
:program
|
86
|
+
when '"'
|
87
|
+
:string
|
88
|
+
when "'"
|
89
|
+
:name # TODO: check for forbidden space
|
90
|
+
else
|
91
|
+
if is_numeric.call( elt )
|
92
|
+
:numeric
|
93
|
+
else
|
94
|
+
:word
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if %I[string name].include?( parsed_entry[:type] )
|
99
|
+
parsed_entry[:value] = parsed_entry[:value][1..-2]
|
100
|
+
elsif parsed_entry[:type] == :program
|
101
|
+
parsed_entry[:value] = parsed_entry[:value][2..-3]
|
102
|
+
elsif parsed_entry[:type] == :numeric
|
103
|
+
parsed_entry[:base] = 10 # TODO: parse others possible bases 0x...
|
104
|
+
|
105
|
+
begin
|
106
|
+
parsed_entry[:value] = Float( parsed_entry[:value] )
|
107
|
+
parsed_entry[:value] = parsed_entry[:value].to_i if (parsed_entry[:value] % 1).zero? && elt.index('.').nil?
|
108
|
+
rescue ArgumentError
|
109
|
+
parsed_entry[:value] = Integer( parsed_entry[:value] )
|
110
|
+
end
|
111
|
+
|
112
|
+
parsed_entry[:value] = BigDecimal( parsed_entry[:value], @precision )
|
113
|
+
end
|
114
|
+
|
115
|
+
parsed_tree << parsed_entry
|
116
|
+
end
|
117
|
+
|
118
|
+
parsed_tree
|
119
|
+
end
|
120
|
+
|
121
|
+
def run( input )
|
122
|
+
@dictionary.add_local_vars_layer
|
123
|
+
|
124
|
+
parse( input.to_s ).each do |elt|
|
125
|
+
case elt[:type]
|
126
|
+
when :word
|
127
|
+
break if %w[break quit exit].include?( elt[:value] )
|
128
|
+
|
129
|
+
command = @dictionary.lookup( elt[:value] )
|
130
|
+
|
131
|
+
if command.nil?
|
132
|
+
# if there isn't a command by that name then it's a name
|
133
|
+
elt[:type] = :name
|
134
|
+
|
135
|
+
@stack << elt
|
136
|
+
elsif command.is_a?( Proc )
|
137
|
+
command.call
|
138
|
+
else
|
139
|
+
run( command[:value] )
|
140
|
+
end
|
141
|
+
else
|
142
|
+
@stack << elt
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
@dictionary.remove_local_vars_layer
|
147
|
+
|
148
|
+
# superfluous but feels nice
|
149
|
+
@stack
|
150
|
+
end
|
151
|
+
|
152
|
+
def stack_extract( needs )
|
153
|
+
raise ArgumentError, 'Not enough elements' if @stack.size < needs.size
|
154
|
+
|
155
|
+
needs.each_with_index do |need, index|
|
156
|
+
stack_index = (index + 1) * -1
|
157
|
+
|
158
|
+
raise ArgumentError, "Type Error, needed #{need} got #{@stack[stack_index]}" unless need == :any || need.include?( @stack[stack_index][:type] )
|
159
|
+
end
|
160
|
+
|
161
|
+
args = []
|
162
|
+
needs.size.times do
|
163
|
+
args << @stack.pop
|
164
|
+
end
|
165
|
+
|
166
|
+
args
|
167
|
+
end
|
168
|
+
|
169
|
+
def stringify( elt )
|
170
|
+
case elt[:type]
|
171
|
+
when :numeric
|
172
|
+
prefix = case elt[:base]
|
173
|
+
when 2
|
174
|
+
'0b'
|
175
|
+
when 8
|
176
|
+
'0o'
|
177
|
+
when 10
|
178
|
+
''
|
179
|
+
when 16
|
180
|
+
'0x'
|
181
|
+
else
|
182
|
+
"0#{elt[:base]}_"
|
183
|
+
end
|
184
|
+
|
185
|
+
if elt[:value].infinite?
|
186
|
+
suffix = elt[:value].infinite?.positive? ? '∞' : '-∞'
|
187
|
+
elsif elt[:value].nan?
|
188
|
+
suffix = '<NaN>'
|
189
|
+
else
|
190
|
+
suffix = if elt[:value].to_i == elt[:value]
|
191
|
+
elt[:value].to_i
|
192
|
+
else
|
193
|
+
elt[:value].to_s('F')
|
194
|
+
end
|
195
|
+
suffix = elt[:value].to_s( elt[:base] ) unless elt[:base] == 10
|
196
|
+
end
|
197
|
+
|
198
|
+
"#{prefix}#{suffix}"
|
199
|
+
when :list
|
200
|
+
"[#{elt[:value].map { |e| stringify( e ) }.join(', ')}]"
|
201
|
+
when :program
|
202
|
+
"« #{elt[:value]} »"
|
203
|
+
when :string
|
204
|
+
"\"#{elt[:value]}\""
|
205
|
+
when :name
|
206
|
+
"'#{elt[:value]}'"
|
207
|
+
else
|
208
|
+
elt[:value]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def infer_resulting_base( numerics )
|
213
|
+
10 if numerics.length.zero?
|
214
|
+
|
215
|
+
numerics.last[:base]
|
216
|
+
end
|
217
|
+
end
|