rpl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|