rpl 0.3.0 → 0.6.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 +4 -4
- data/bin/rpl +11 -9
- data/lib/rpl/interpreter.rb +27 -177
- data/lib/rpl/parser.rb +87 -0
- data/lib/rpl/types/boolean.rb +32 -0
- data/lib/rpl/types/list.rb +34 -0
- data/lib/rpl/types/name.rb +36 -0
- data/lib/rpl/types/numeric.rb +115 -0
- data/lib/rpl/types/program.rb +27 -0
- data/lib/rpl/types/string.rb +27 -0
- data/lib/rpl/types.rb +8 -0
- data/lib/rpl/{core → words}/branch.rb +14 -16
- data/lib/rpl/{core → words}/filesystem.rb +10 -11
- data/lib/rpl/{core → words}/general.rb +36 -7
- data/lib/rpl/{core → words}/logarithm.rb +4 -4
- data/lib/rpl/{core → words}/mode.rb +7 -6
- data/lib/rpl/{core → words}/operations.rb +144 -134
- data/lib/rpl/{core → words}/program.rb +5 -3
- data/lib/rpl/{core → words}/stack.rb +22 -28
- data/lib/rpl/{core → words}/store.rb +22 -37
- data/lib/rpl/words/string-list.rb +133 -0
- data/lib/rpl/{core → words}/test.rb +19 -29
- data/lib/rpl/{core → words}/time-date.rb +6 -8
- data/lib/rpl/{core → words}/trig.rb +20 -36
- data/lib/rpl/words.rb +15 -0
- data/lib/rpl.rb +17 -29
- metadata +24 -16
- data/lib/rpl/core/list.rb +0 -33
- data/lib/rpl/core/string.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473327f669ed4e0484a32f534efa52bcbfbb8521ebcad7fa4d38de35f6576972
|
4
|
+
data.tar.gz: cd6d921d8f40edcd95a32d4af529d7435962c65f621fa7903cbee4a1fabcf07f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4db7ebbce26e2eb92cc4baaa0a35954f3de5e8129b28c348bd78d420109300ec988dc4d8ed27464e7455ba8103c6141ed084d6c63526651b982c712a52cd9413
|
7
|
+
data.tar.gz: fbc27ef9008a261401d02bb69a86c4eff00861df9018d2af4a029aa69fbf21475436a67aa8066d23c4eb6ac01455944e2a567c4f18b36f121059b06c351ef903
|
data/bin/rpl
CHANGED
@@ -10,6 +10,7 @@ require 'rpl'
|
|
10
10
|
class RplRepl
|
11
11
|
def initialize( interpreter )
|
12
12
|
interpreter ||= Rpl.new
|
13
|
+
|
13
14
|
@interpreter = interpreter
|
14
15
|
end
|
15
16
|
|
@@ -33,28 +34,22 @@ class RplRepl
|
|
33
34
|
begin
|
34
35
|
@interpreter.run( input )
|
35
36
|
rescue ArgumentError => e
|
36
|
-
|
37
|
+
pp e
|
37
38
|
end
|
38
39
|
|
39
40
|
print_stack
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
def format_element( elt )
|
44
|
-
@interpreter.stringify( elt )
|
45
|
-
end
|
46
|
-
|
47
44
|
def print_stack
|
48
45
|
stack_size = @interpreter.stack.size
|
49
46
|
|
50
47
|
@interpreter.stack.each_with_index do |elt, i|
|
51
|
-
puts "#{stack_size - i}: #{
|
48
|
+
puts "#{stack_size - i}: #{elt}"
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
|
-
interpreter = Rpl.new
|
57
|
-
|
58
53
|
options = { run_REPL: ARGV.empty?,
|
59
54
|
files: [],
|
60
55
|
programs: [] }
|
@@ -73,14 +68,21 @@ OptionParser.new do |opts|
|
|
73
68
|
end
|
74
69
|
end.parse!
|
75
70
|
|
71
|
+
# Instantiate interpreter
|
72
|
+
interpreter = Rpl.new
|
73
|
+
|
74
|
+
# first run provided files if any
|
76
75
|
options[:files].each do |filename|
|
77
76
|
interpreter.run "\"#{filename}\" feval"
|
78
77
|
end
|
79
78
|
|
79
|
+
# second run provided code if any
|
80
80
|
options[:programs].each do |program|
|
81
81
|
interpreter.run program
|
82
82
|
end
|
83
83
|
|
84
|
+
# third launch REPL if (explicitely or implicitely) asked
|
84
85
|
RplRepl.new( interpreter ).run if options[:run_REPL]
|
85
86
|
|
86
|
-
|
87
|
+
# last print resulting stack on exit (formatted so that it can be fed back later to interpreter)
|
88
|
+
pp interpreter.export_stack
|
data/lib/rpl/interpreter.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bigdecimal'
|
3
4
|
require 'bigdecimal/math'
|
5
|
+
require 'bigdecimal/util'
|
4
6
|
|
5
7
|
require 'rpl/dictionary'
|
8
|
+
require 'rpl/types'
|
6
9
|
|
7
10
|
class Interpreter
|
8
11
|
include BigMath
|
12
|
+
include Types
|
9
13
|
|
10
14
|
attr_reader :stack,
|
11
15
|
:dictionary,
|
@@ -14,146 +18,37 @@ class Interpreter
|
|
14
18
|
attr_accessor :precision
|
15
19
|
|
16
20
|
def initialize( stack = [], dictionary = Rpl::Lang::Dictionary.new )
|
17
|
-
@version = 0.
|
18
|
-
|
19
|
-
@precision = default_precision
|
21
|
+
@version = 0.6
|
20
22
|
|
21
23
|
@dictionary = dictionary
|
22
24
|
@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
|
-
opened_lists = 0
|
51
|
-
closed_lists = 0
|
52
|
-
string_delimiters = 0
|
53
|
-
name_delimiters = 0
|
54
|
-
regrouping = false
|
55
|
-
|
56
|
-
regrouped_input = []
|
57
|
-
splitted_input.each do |elt|
|
58
|
-
if elt[0] == '«'
|
59
|
-
opened_programs += 1
|
60
|
-
elt.gsub!( '«', '« ') if elt.length > 1 && elt[1] != ' '
|
61
|
-
elsif elt[0] == '{'
|
62
|
-
opened_lists += 1
|
63
|
-
elt.gsub!( '{', '{ ') if elt.length > 1 && elt[1] != ' '
|
64
|
-
elsif elt[0] == '"' && elt.length > 1
|
65
|
-
string_delimiters += 1
|
66
|
-
elsif elt[0] == "'" && elt.length > 1
|
67
|
-
name_delimiters += 1
|
68
|
-
end
|
69
|
-
|
70
|
-
elt = "#{regrouped_input.pop} #{elt}".strip if regrouping
|
71
|
-
|
72
|
-
regrouped_input << elt
|
73
|
-
|
74
|
-
case elt[-1]
|
75
|
-
when '»'
|
76
|
-
closed_programs += 1
|
77
|
-
elt.gsub!( '»', ' »') if elt.length > 1 && elt[-2] != ' '
|
78
|
-
when '}'
|
79
|
-
closed_lists += 1
|
80
|
-
elt.gsub!( '}', ' }') if elt.length > 1 && elt[-2] != ' '
|
81
|
-
when '"'
|
82
|
-
string_delimiters += 1
|
83
|
-
when "'"
|
84
|
-
name_delimiters += 1
|
85
|
-
end
|
86
|
-
|
87
|
-
regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs ) || (opened_lists > closed_lists )
|
88
|
-
end
|
89
|
-
|
90
|
-
# 2. parse
|
91
|
-
# TODO: parse ∞, <NaN> as numerics
|
92
|
-
parsed_tree = []
|
93
|
-
regrouped_input.each do |elt|
|
94
|
-
parsed_entry = { value: elt }
|
95
|
-
|
96
|
-
parsed_entry[:type] = case elt[0]
|
97
|
-
when '«'
|
98
|
-
:program
|
99
|
-
when '{'
|
100
|
-
:list
|
101
|
-
when '"'
|
102
|
-
:string
|
103
|
-
when "'"
|
104
|
-
:name # TODO: check for forbidden space
|
105
|
-
else
|
106
|
-
if is_numeric.call( elt )
|
107
|
-
:numeric
|
108
|
-
else
|
109
|
-
:word
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
if %I[string name].include?( parsed_entry[:type] )
|
114
|
-
parsed_entry[:value] = parsed_entry[:value][1..-2]
|
115
|
-
elsif parsed_entry[:type] == :program
|
116
|
-
parsed_entry[:value] = parsed_entry[:value][2..-3]
|
117
|
-
elsif parsed_entry[:type] == :list
|
118
|
-
parsed_entry[:value] = parse( parsed_entry[:value][2..-3] )
|
119
|
-
elsif parsed_entry[:type] == :numeric
|
120
|
-
parsed_entry[:base] = 10 # TODO: parse others possible bases 0x...
|
121
|
-
|
122
|
-
begin
|
123
|
-
parsed_entry[:value] = Float( parsed_entry[:value] )
|
124
|
-
parsed_entry[:value] = parsed_entry[:value].to_i if (parsed_entry[:value] % 1).zero? && elt.index('.').nil?
|
125
|
-
rescue ArgumentError
|
126
|
-
parsed_entry[:value] = Integer( parsed_entry[:value] )
|
127
|
-
end
|
128
|
-
|
129
|
-
parsed_entry[:value] = BigDecimal( parsed_entry[:value], @precision )
|
130
|
-
end
|
131
|
-
|
132
|
-
parsed_tree << parsed_entry
|
133
|
-
end
|
134
|
-
|
135
|
-
parsed_tree
|
136
25
|
end
|
137
26
|
|
138
27
|
def run( input )
|
139
28
|
@dictionary.add_local_vars_layer
|
140
29
|
|
141
|
-
parse( input.to_s ).each do |elt|
|
142
|
-
|
143
|
-
|
144
|
-
break if %w[break quit exit].include?( elt[:value] )
|
145
|
-
|
146
|
-
command = @dictionary.lookup( elt[:value] )
|
147
|
-
|
148
|
-
if command.nil?
|
149
|
-
# if there isn't a command by that name then it's a name
|
150
|
-
elt[:type] = :name
|
30
|
+
Parser.parse( input.to_s ).each do |elt|
|
31
|
+
if elt.instance_of?( RplName )
|
32
|
+
break if %w[break quit exit].include?( elt.value )
|
151
33
|
|
34
|
+
if elt.not_to_evaluate
|
152
35
|
@stack << elt
|
153
|
-
elsif command.is_a?( Proc )
|
154
|
-
command.call
|
155
36
|
else
|
156
|
-
|
37
|
+
command = @dictionary.lookup( elt.value )
|
38
|
+
if command.nil?
|
39
|
+
# if there isn't a command by that name then it's a name
|
40
|
+
# elt[:type] = :name
|
41
|
+
|
42
|
+
@stack << elt
|
43
|
+
elsif command.is_a?( Proc )
|
44
|
+
command.call
|
45
|
+
else
|
46
|
+
if command.instance_of?( RplProgram )
|
47
|
+
run( command.value )
|
48
|
+
else
|
49
|
+
@stack << command
|
50
|
+
end
|
51
|
+
end
|
157
52
|
end
|
158
53
|
else
|
159
54
|
@stack << elt
|
@@ -172,7 +67,7 @@ class Interpreter
|
|
172
67
|
needs.each_with_index do |need, index|
|
173
68
|
stack_index = (index + 1) * -1
|
174
69
|
|
175
|
-
raise ArgumentError, "Type Error, needed #{need} got #{@stack[stack_index]}" unless need == :any || need.include?( @stack[stack_index]
|
70
|
+
raise ArgumentError, "Type Error, needed #{need} got #{@stack[stack_index]}" unless need == :any || need.include?( @stack[stack_index].class )
|
176
71
|
end
|
177
72
|
|
178
73
|
args = []
|
@@ -183,52 +78,7 @@ class Interpreter
|
|
183
78
|
args
|
184
79
|
end
|
185
80
|
|
186
|
-
def
|
187
|
-
|
188
|
-
when :numeric
|
189
|
-
prefix = case elt[:base]
|
190
|
-
when 2
|
191
|
-
'0b'
|
192
|
-
when 8
|
193
|
-
'0o'
|
194
|
-
when 10
|
195
|
-
''
|
196
|
-
when 16
|
197
|
-
'0x'
|
198
|
-
else
|
199
|
-
"0#{elt[:base]}_"
|
200
|
-
end
|
201
|
-
|
202
|
-
if elt[:value].infinite?
|
203
|
-
suffix = elt[:value].infinite?.positive? ? '∞' : '-∞'
|
204
|
-
elsif elt[:value].nan?
|
205
|
-
suffix = '<NaN>'
|
206
|
-
else
|
207
|
-
suffix = if elt[:value].to_i == elt[:value]
|
208
|
-
elt[:value].to_i
|
209
|
-
else
|
210
|
-
elt[:value].to_s('F')
|
211
|
-
end
|
212
|
-
suffix = elt[:value].to_s( elt[:base] ) unless elt[:base] == 10
|
213
|
-
end
|
214
|
-
|
215
|
-
"#{prefix}#{suffix}"
|
216
|
-
when :list
|
217
|
-
"{ #{elt[:value].map { |e| stringify( e ) }.join(' ')} }"
|
218
|
-
when :program
|
219
|
-
"« #{elt[:value]} »"
|
220
|
-
when :string
|
221
|
-
"\"#{elt[:value]}\""
|
222
|
-
when :name
|
223
|
-
"'#{elt[:value]}'"
|
224
|
-
else
|
225
|
-
elt[:value]
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
def infer_resulting_base( numerics )
|
230
|
-
10 if numerics.length.zero?
|
231
|
-
|
232
|
-
numerics.last[:base]
|
81
|
+
def export_stack
|
82
|
+
@stack.map(&:to_s).join(' ')
|
233
83
|
end
|
234
84
|
end
|
data/lib/rpl/parser.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Parser
|
4
|
+
include Types
|
5
|
+
|
6
|
+
def self.parse( input )
|
7
|
+
unless input.index("\n").nil?
|
8
|
+
input = input.split("\n")
|
9
|
+
.map do |line|
|
10
|
+
comment_begin_index = line.index('#')
|
11
|
+
|
12
|
+
case comment_begin_index
|
13
|
+
when nil
|
14
|
+
line
|
15
|
+
when 0
|
16
|
+
''
|
17
|
+
else
|
18
|
+
line[0..(comment_begin_index - 1)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
.join(' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
splitted_input = input.split(' ')
|
25
|
+
|
26
|
+
# 2-passes:
|
27
|
+
# 1. regroup strings and programs
|
28
|
+
opened_programs = 0
|
29
|
+
closed_programs = 0
|
30
|
+
opened_lists = 0
|
31
|
+
closed_lists = 0
|
32
|
+
string_delimiters = 0
|
33
|
+
name_delimiters = 0
|
34
|
+
regrouping = false
|
35
|
+
|
36
|
+
regrouped_input = []
|
37
|
+
splitted_input.each do |elt|
|
38
|
+
if elt[0] == '«'
|
39
|
+
opened_programs += 1
|
40
|
+
elt.gsub!( '«', '« ') if elt.length > 1 && elt[1] != ' '
|
41
|
+
elsif elt[0] == '{'
|
42
|
+
opened_lists += 1
|
43
|
+
elt.gsub!( '{', '{ ') if elt.length > 1 && elt[1] != ' '
|
44
|
+
elsif elt[0] == '"' && elt.length > 1
|
45
|
+
string_delimiters += 1
|
46
|
+
elsif elt[0] == "'" && elt.length > 1
|
47
|
+
name_delimiters += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
elt = "#{regrouped_input.pop} #{elt}".strip if regrouping
|
51
|
+
|
52
|
+
regrouped_input << elt
|
53
|
+
|
54
|
+
case elt[-1]
|
55
|
+
when '»'
|
56
|
+
closed_programs += 1
|
57
|
+
elt.gsub!( '»', ' »') if elt.length > 1 && elt[-2] != ' '
|
58
|
+
when '}'
|
59
|
+
closed_lists += 1
|
60
|
+
elt.gsub!( '}', ' }') if elt.length > 1 && elt[-2] != ' '
|
61
|
+
when '"'
|
62
|
+
string_delimiters += 1
|
63
|
+
when "'"
|
64
|
+
name_delimiters += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs ) || (opened_lists > closed_lists )
|
68
|
+
end
|
69
|
+
|
70
|
+
# 2. parse
|
71
|
+
regrouped_input.map do |element|
|
72
|
+
if RplBoolean.can_parse?( element )
|
73
|
+
RplBoolean.new( element )
|
74
|
+
elsif RplNumeric.can_parse?( element )
|
75
|
+
RplNumeric.new( element )
|
76
|
+
elsif RplList.can_parse?( element )
|
77
|
+
RplList.new( element )
|
78
|
+
elsif RplString.can_parse?( element )
|
79
|
+
RplString.new( element )
|
80
|
+
elsif RplProgram.can_parse?( element )
|
81
|
+
RplProgram.new( element )
|
82
|
+
elsif RplName.can_parse?( element )
|
83
|
+
RplName.new( element )
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Types
|
4
|
+
class RplBoolean
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize( value )
|
8
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
9
|
+
|
10
|
+
@value = if value.is_a?( String )
|
11
|
+
value.downcase == 'true'
|
12
|
+
else
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@value.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.can_parse?( value )
|
22
|
+
return %w[true false].include?( value.downcase ) if value.is_a?( String )
|
23
|
+
|
24
|
+
%w[TrueClass FalseClass].include?( value.class.to_s )
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==( other )
|
28
|
+
other.class == RplBoolean and
|
29
|
+
other.value == value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rpl/parser'
|
4
|
+
|
5
|
+
module Types
|
6
|
+
class RplList
|
7
|
+
attr_accessor :value
|
8
|
+
|
9
|
+
def initialize( value )
|
10
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
11
|
+
|
12
|
+
@value = if value.instance_of?( Array )
|
13
|
+
value
|
14
|
+
else
|
15
|
+
# we systematicalyl trim enclosing { }
|
16
|
+
Parser.parse( value[2..-3] )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"{ #{@value.map(&:to_s).join(' ')} }"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.can_parse?( value )
|
25
|
+
value.instance_of?( Array ) or
|
26
|
+
value[0..1] == '{ ' && value[-2..-1] == ' }'
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==( other )
|
30
|
+
other.class == RplList and
|
31
|
+
other.value == value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Types
|
4
|
+
class RplName
|
5
|
+
attr_accessor :value,
|
6
|
+
:not_to_evaluate
|
7
|
+
|
8
|
+
def initialize( value )
|
9
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
10
|
+
|
11
|
+
# we systematicalyl trim enclosing '
|
12
|
+
@not_to_evaluate = value[0] == "'"
|
13
|
+
@value = if value[0] == "'" && value[-1] == "'"
|
14
|
+
value[1..-2]
|
15
|
+
else
|
16
|
+
value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"'#{@value}'"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.can_parse?( value )
|
25
|
+
( value.length > 2 and value[0] == "'" and value[-1] == "'" ) or
|
26
|
+
( value != "''" and !value.match?(/^[0-9']+$/) and
|
27
|
+
# it's not any other type
|
28
|
+
[RplBoolean, RplList, RplProgram, RplString, RplNumeric].reduce( true ) { |memo, type_class| memo && !type_class.can_parse?( value ) } )
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==( other )
|
32
|
+
other.class == RplName and
|
33
|
+
other.value == value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
module Types
|
6
|
+
class RplNumeric
|
7
|
+
attr_accessor :value,
|
8
|
+
:base
|
9
|
+
|
10
|
+
@@precision = 12 # rubocop:disable Style/ClassVars
|
11
|
+
|
12
|
+
def self.default_precision
|
13
|
+
@@precision = 12 # rubocop:disable Style/ClassVars
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.precision
|
17
|
+
@@precision
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.precision=( precision )
|
21
|
+
@@precision = precision.to_i # rubocop:disable Style/ClassVars
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize( value, base = 10 )
|
25
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
26
|
+
|
27
|
+
@base = base
|
28
|
+
|
29
|
+
case value
|
30
|
+
when RplNumeric
|
31
|
+
@value = value.value
|
32
|
+
@base = value.base
|
33
|
+
when BigDecimal
|
34
|
+
@value = value
|
35
|
+
when Integer
|
36
|
+
@value = BigDecimal( value, @@precision )
|
37
|
+
when Float
|
38
|
+
@value = BigDecimal( value, @@precision )
|
39
|
+
when String
|
40
|
+
case value
|
41
|
+
when '∞'
|
42
|
+
@value = BigDecimal('+Infinity')
|
43
|
+
when '-∞'
|
44
|
+
@value = BigDecimal('-Infinity')
|
45
|
+
else
|
46
|
+
underscore_position = value.index('_')
|
47
|
+
|
48
|
+
if value[0] == '0' && ( %w[b o x].include?( value[1] ) || !underscore_position.nil? )
|
49
|
+
if value[1] == 'x'
|
50
|
+
@base = 16
|
51
|
+
elsif value[1] == 'b'
|
52
|
+
@base = 2
|
53
|
+
elsif value[1] == 'o'
|
54
|
+
@base = 8
|
55
|
+
value = value[2..-1]
|
56
|
+
elsif !underscore_position.nil?
|
57
|
+
@base = value[1..(underscore_position - 1)].to_i
|
58
|
+
value = value[(underscore_position + 1)..-1]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
value = value.to_i( @base ) unless @base == 10
|
63
|
+
@value = BigDecimal( value, @@precision )
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
prefix = case @base
|
70
|
+
when 2
|
71
|
+
'0b'
|
72
|
+
when 8
|
73
|
+
'0o'
|
74
|
+
when 10
|
75
|
+
''
|
76
|
+
when 16
|
77
|
+
'0x'
|
78
|
+
else
|
79
|
+
"0#{@base}_"
|
80
|
+
end
|
81
|
+
|
82
|
+
if @value.infinite?
|
83
|
+
suffix = @value.infinite?.positive? ? '∞' : '-∞'
|
84
|
+
elsif @value.nan?
|
85
|
+
suffix = '<NaN>'
|
86
|
+
else
|
87
|
+
suffix = if @value.to_i == @value
|
88
|
+
@value.to_i
|
89
|
+
else
|
90
|
+
@value.to_s('F')
|
91
|
+
end
|
92
|
+
suffix = @value.to_s( @base ) unless @base == 10
|
93
|
+
end
|
94
|
+
|
95
|
+
"#{prefix}#{suffix}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.can_parse?( value )
|
99
|
+
[RplNumeric, BigDecimal, Integer, Float].include?( value.class ) or
|
100
|
+
( value.is_a?( String ) and ( value.match?(/^-?[0-9]*\.?[0-9]+$/) or
|
101
|
+
value.match?(/^-?∞$/) or
|
102
|
+
value.match?(/^0b[0-1]+$/) or
|
103
|
+
value.match?(/^0o[0-7]+$/) or
|
104
|
+
value.match?(/^0x[0-9a-f]+$/) or
|
105
|
+
( value.match?(/^0[0-9]+_[0-9a-z]+$/) and
|
106
|
+
value.split('_').first.to_i <= 36 ) ) )
|
107
|
+
end
|
108
|
+
|
109
|
+
def ==( other )
|
110
|
+
other.class == RplNumeric and
|
111
|
+
other.base == base and
|
112
|
+
other.value == value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Types
|
4
|
+
class RplProgram
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize( value )
|
8
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
9
|
+
|
10
|
+
# we systematicalyl trim enclosing « »
|
11
|
+
@value = value[2..-3] # TODO: parse each element ?
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"« #{@value} »"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.can_parse?( value )
|
19
|
+
value.length > 4 && value[0..1] == '« ' && value[-2..-1] == ' »' && !value[2..-3].strip.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==( other )
|
23
|
+
other.class == RplProgram and
|
24
|
+
other.value == value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Types
|
4
|
+
class RplString
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize( value )
|
8
|
+
raise RplTypeError unless self.class.can_parse?( value )
|
9
|
+
|
10
|
+
# we systematicalyl trim enclosing "
|
11
|
+
@value = value[1..-2]
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"\"#{@value}\""
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.can_parse?( value )
|
19
|
+
value[0] == '"' && value[-1] == '"'
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==( other )
|
23
|
+
other.class == RplString and
|
24
|
+
other.value == value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|