code-ruby 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/bin/code +54 -55
- data/lib/code/error.rb +16 -0
- data/lib/code/node/base_10.rb +3 -4
- data/lib/code/node/decimal.rb +3 -4
- data/lib/code/node/while.rb +13 -6
- data/lib/code/node.rb +1 -1
- data/lib/code/object/boolean.rb +1 -5
- data/lib/code/object/dictionary.rb +13 -0
- data/lib/code/object/global.rb +6 -0
- data/lib/code/object/integer.rb +1 -0
- data/lib/code/object/list.rb +30 -0
- data/lib/code/object/range.rb +1 -1
- data/lib/code/object.rb +1 -1
- data/lib/code/parser/bitwise_and.rb +4 -0
- data/lib/code/parser/chained_call.rb +4 -8
- data/lib/code/parser/equal.rb +4 -0
- data/lib/code/parser/equality.rb +4 -0
- data/lib/code/parser/left_operation.rb +5 -1
- data/lib/code/parser/multiplication.rb +4 -0
- data/lib/code/parser/right_operation.rb +5 -1
- data/lib/code/parser/string.rb +1 -1
- data/lib/code/parser/while.rb +10 -2
- data/lib/code/type/repeat.rb +1 -1
- data/lib/code/version.rb +1 -1
- data/spec/code/object/string_spec.rb +4 -2
- data/spec/code_spec.rb +8 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af513b1ba26657cd202191bd69f97e6018c0bdc42ca5b518bc2f1de4800d9cf1
|
4
|
+
data.tar.gz: 73f15cd6414c7908118c0f78e5f18f6407f5e6f947fa470110f5d7777f3ebc83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88094db69266fed8e2a9ff8f435406ebb623e815dfb85de6087a95ad3352da6dfd53162a3f457773a62247199a38b4ffdede637741d8dcc2e29707c6a5070ff4
|
7
|
+
data.tar.gz: 3568647377cc4049c042aee130e6d409e1d4cfa6d556ba3179696bbab7d71a70744ce2e50cb5104dfd564b4bbb417154a22ae08c4e52aba4f797e11cecf67a22
|
data/Gemfile.lock
CHANGED
data/bin/code
CHANGED
@@ -12,65 +12,64 @@ options = {
|
|
12
12
|
parse: false
|
13
13
|
}
|
14
14
|
|
15
|
-
argv =
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
15
|
+
argv =
|
16
|
+
OptionParser
|
17
|
+
.new do |opts|
|
18
|
+
opts.banner = "Usage: code INPUT\n\n"
|
19
|
+
|
20
|
+
opts.on("-v", "--version", "Version of Code") do |_input|
|
21
|
+
puts Code::Version
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on(
|
26
|
+
"-i INPUT",
|
27
|
+
"--input INPUT",
|
28
|
+
"Input in the Code language (String or File)"
|
29
|
+
) do |input|
|
30
|
+
input = File.read(input) if File.exist?(input)
|
31
|
+
|
32
|
+
options[:input] = input
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-p", "--parse", "Parser tree for input") do |parse|
|
36
|
+
options[:parse] = parse
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on(
|
40
|
+
"-t TIMEOUT",
|
41
|
+
"--timeout TIMEOUT",
|
42
|
+
"Set timeout in seconds"
|
43
|
+
) { |timeout| options[:timeout] = timeout.to_f }
|
44
|
+
|
45
|
+
opts.on("--profile", "Profile Ruby code") do |_timeout|
|
46
|
+
require "ruby-prof"
|
47
|
+
options[:profile] = true
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on(
|
51
|
+
"--profiler TYPE",
|
52
|
+
"Profiler output type (text (default) or html)"
|
53
|
+
) do |profiler|
|
54
|
+
require "ruby-prof"
|
55
|
+
options[:profile] = true
|
56
|
+
options[:profiler] = profiler
|
57
|
+
end
|
47
58
|
end
|
48
|
-
|
49
|
-
opts.on(
|
50
|
-
"--profiler TYPE",
|
51
|
-
"Profiler output type (text (default) or html)"
|
52
|
-
) do |profiler|
|
53
|
-
require "ruby-prof"
|
54
|
-
options[:profile] = true
|
55
|
-
options[:profiler] = profiler
|
56
|
-
end
|
57
|
-
end
|
58
|
-
.parse!
|
59
|
+
.parse!
|
59
60
|
|
60
61
|
options[:input] = argv.join(" ") if options[:input].empty?
|
61
62
|
|
62
|
-
if options[:input].empty?
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
HELP
|
73
|
-
end
|
63
|
+
abort <<~HELP if options[:input].empty?
|
64
|
+
Usage: code INPUT
|
65
|
+
|
66
|
+
-v, --version Version of Code
|
67
|
+
-i, --input INPUT Input in the Code language (String or File)
|
68
|
+
-p, --parse Parser tree for input
|
69
|
+
-t, --timeout TIMEOUT Set timeout in seconds
|
70
|
+
--profile Profile Ruby code
|
71
|
+
--profiler TYPE Profiler output type (text (default) or html)
|
72
|
+
HELP
|
74
73
|
|
75
74
|
RubyProf.start if options[:profile]
|
76
75
|
|
data/lib/code/error.rb
CHANGED
@@ -25,5 +25,21 @@ class Code
|
|
25
25
|
|
26
26
|
class KeyNotFound < Error
|
27
27
|
end
|
28
|
+
|
29
|
+
class Break < Error
|
30
|
+
attr_reader :value
|
31
|
+
|
32
|
+
def initialize(value = nil)
|
33
|
+
@value = value || Object::Nothing.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Next < Error
|
38
|
+
attr_reader :value
|
39
|
+
|
40
|
+
def initialize(value = nil)
|
41
|
+
@value = value || Object::Nothing.new
|
42
|
+
end
|
43
|
+
end
|
28
44
|
end
|
29
45
|
end
|
data/lib/code/node/base_10.rb
CHANGED
@@ -6,10 +6,9 @@ class Code
|
|
6
6
|
def initialize(parsed)
|
7
7
|
@whole = parsed.delete(:whole)
|
8
8
|
|
9
|
-
|
10
|
-
Node::Statement.new(parsed.delete(:exponent))
|
11
|
-
|
12
|
-
)
|
9
|
+
if parsed.key?(:exponent)
|
10
|
+
@exponent = Node::Statement.new(parsed.delete(:exponent))
|
11
|
+
end
|
13
12
|
|
14
13
|
super(parsed)
|
15
14
|
end
|
data/lib/code/node/decimal.rb
CHANGED
@@ -6,10 +6,9 @@ class Code
|
|
6
6
|
def initialize(parsed)
|
7
7
|
@decimal = parsed.delete(:decimal)
|
8
8
|
|
9
|
-
|
10
|
-
Node::Statement.new(parsed.delete(:exponent))
|
11
|
-
|
12
|
-
)
|
9
|
+
if parsed.key?(:exponent)
|
10
|
+
@exponent = Node::Statement.new(parsed.delete(:exponent))
|
11
|
+
end
|
13
12
|
|
14
13
|
super(parsed)
|
15
14
|
end
|
data/lib/code/node/while.rb
CHANGED
@@ -5,32 +5,39 @@ class Code
|
|
5
5
|
class While < Node
|
6
6
|
WHILE_KEYWORD = "while"
|
7
7
|
UNTIL_KEYWORD = "until"
|
8
|
+
LOOP_KEYWORD = "loop"
|
8
9
|
|
9
10
|
def initialize(parsed)
|
10
11
|
@operator = parsed.delete(:operator)
|
11
|
-
@statement =
|
12
|
-
@body =
|
12
|
+
@statement = Statement.new(parsed.delete(:statement)) if parsed[:statement]
|
13
|
+
@body = Code.new(parsed.delete(:body))
|
13
14
|
super(parsed)
|
14
15
|
end
|
15
16
|
|
16
17
|
def evaluate(**args)
|
17
|
-
|
18
|
-
|
18
|
+
case @operator
|
19
|
+
when WHILE_KEYWORD
|
20
|
+
last = Object::Nothing.new
|
19
21
|
|
20
22
|
last = @body.evaluate(**args) while @statement.evaluate(
|
21
23
|
**args
|
22
24
|
).truthy?
|
23
25
|
|
24
26
|
last
|
25
|
-
|
26
|
-
last =
|
27
|
+
when UNTIL_KEYWORD
|
28
|
+
last = Object::Nothing.new
|
27
29
|
|
28
30
|
last = @body.evaluate(**args) while @statement.evaluate(**args).falsy?
|
29
31
|
|
30
32
|
last
|
33
|
+
when LOOP_KEYWORD
|
34
|
+
loop { @body.evaluate(**args) }
|
35
|
+
raise NotImplementedError
|
31
36
|
else
|
32
37
|
raise NotImplementedError, @operator
|
33
38
|
end
|
39
|
+
rescue Error::Break => e
|
40
|
+
e.value || Object::Nothing.new
|
34
41
|
end
|
35
42
|
end
|
36
43
|
end
|
data/lib/code/node.rb
CHANGED
data/lib/code/object/boolean.rb
CHANGED
@@ -470,6 +470,8 @@ class Code
|
|
470
470
|
],
|
471
471
|
**globals
|
472
472
|
).falsy?
|
473
|
+
rescue Error::Next => e
|
474
|
+
(e.value || Nothing.new).falsy?
|
473
475
|
end
|
474
476
|
end
|
475
477
|
|
@@ -486,6 +488,8 @@ class Code
|
|
486
488
|
else
|
487
489
|
raw.key(value) || Nothing.new
|
488
490
|
end
|
491
|
+
rescue Error::Next => e
|
492
|
+
e.value || Nothing.new
|
489
493
|
end
|
490
494
|
|
491
495
|
def code_keys
|
@@ -522,6 +526,9 @@ class Code
|
|
522
526
|
else
|
523
527
|
new_value.tap { index += 1 }
|
524
528
|
end
|
529
|
+
rescue Error::Next => e
|
530
|
+
index += 1
|
531
|
+
e.value || Nothing.new
|
525
532
|
end
|
526
533
|
)
|
527
534
|
end
|
@@ -548,6 +555,8 @@ class Code
|
|
548
555
|
],
|
549
556
|
**globals
|
550
557
|
).truthy?
|
558
|
+
rescue Error::Next => e
|
559
|
+
(e.value || Nothing.new).truthy?
|
551
560
|
end
|
552
561
|
end
|
553
562
|
|
@@ -569,6 +578,8 @@ class Code
|
|
569
578
|
],
|
570
579
|
**globals
|
571
580
|
).truthy?
|
581
|
+
rescue Error::Next => e
|
582
|
+
(e.value || Nothing.new).truthy?
|
572
583
|
end
|
573
584
|
)
|
574
585
|
end
|
@@ -626,6 +637,8 @@ class Code
|
|
626
637
|
],
|
627
638
|
**globals
|
628
639
|
)
|
640
|
+
rescue Error::Next => e
|
641
|
+
e.value || Nothing.new
|
629
642
|
end
|
630
643
|
)
|
631
644
|
end
|
data/lib/code/object/global.rb
CHANGED
@@ -19,6 +19,12 @@ class Code
|
|
19
19
|
when "Boolean"
|
20
20
|
sig(args) { Object.maybe }
|
21
21
|
value ? value.code_to_boolean : Class.new(Boolean)
|
22
|
+
when "break"
|
23
|
+
sig(args) { Object.maybe }
|
24
|
+
raise Error::Break, value || Nothing.new
|
25
|
+
when "next"
|
26
|
+
sig(args) { Object.maybe }
|
27
|
+
raise Error::Next, value || Nothing.new
|
22
28
|
when "Class"
|
23
29
|
sig(args) { Object.maybe }
|
24
30
|
value ? value.code_to_class : Class.new(Class)
|
data/lib/code/object/integer.rb
CHANGED
data/lib/code/object/list.rb
CHANGED
@@ -69,6 +69,9 @@ class Code
|
|
69
69
|
when "select"
|
70
70
|
sig(args) { Function }
|
71
71
|
code_select(value, **globals)
|
72
|
+
when "select!"
|
73
|
+
sig(args) { Function }
|
74
|
+
code_select!(value, **globals)
|
72
75
|
when "size"
|
73
76
|
sig(args)
|
74
77
|
code_size
|
@@ -87,6 +90,8 @@ class Code
|
|
87
90
|
Boolean.new(
|
88
91
|
raw.any? do |element|
|
89
92
|
argument.call(arguments: [Argument.new(element)], **globals).truthy?
|
93
|
+
rescue Error::Next => e
|
94
|
+
e.value || Nothing.new
|
90
95
|
end
|
91
96
|
)
|
92
97
|
end
|
@@ -99,12 +104,16 @@ class Code
|
|
99
104
|
def code_detect(argument, **globals)
|
100
105
|
raw.detect do |element|
|
101
106
|
argument.call(arguments: [Argument.new(element)], **globals).truthy?
|
107
|
+
rescue Error::Next => e
|
108
|
+
e.value || Nothing.new
|
102
109
|
end || Nothing.new
|
103
110
|
end
|
104
111
|
|
105
112
|
def code_each(argument, **globals)
|
106
113
|
raw.each do |element|
|
107
114
|
argument.call(arguments: [Argument.new(element)], **globals)
|
115
|
+
rescue Error::Next => e
|
116
|
+
e.value || Nothing.new
|
108
117
|
end
|
109
118
|
self
|
110
119
|
end
|
@@ -146,6 +155,8 @@ class Code
|
|
146
155
|
List.new(
|
147
156
|
raw.map do |element|
|
148
157
|
argument.call(arguments: [Argument.new(element)], **globals)
|
158
|
+
rescue Error::Next => e
|
159
|
+
e.value || Nothing.new
|
149
160
|
end
|
150
161
|
)
|
151
162
|
end
|
@@ -157,6 +168,8 @@ class Code
|
|
157
168
|
def code_max_by(argument, **globals)
|
158
169
|
raw.max_by do |element|
|
159
170
|
argument.call(arguments: [Argument.new(element)], **globals)
|
171
|
+
rescue Error::Next => e
|
172
|
+
e.value || Nothing.new
|
160
173
|
end || Nothing.new
|
161
174
|
end
|
162
175
|
|
@@ -164,6 +177,8 @@ class Code
|
|
164
177
|
Boolean.new(
|
165
178
|
raw.none? do |element|
|
166
179
|
argument.call(arguments: [Argument.new(element)], **globals).truthy?
|
180
|
+
rescue Error::Next => e
|
181
|
+
(e.value || Nothing.new).truthy?
|
167
182
|
end
|
168
183
|
)
|
169
184
|
end
|
@@ -174,6 +189,8 @@ class Code
|
|
174
189
|
arguments: [Argument.new(acc), Argument.new(element)],
|
175
190
|
**globals
|
176
191
|
)
|
192
|
+
rescue Error::Next => e
|
193
|
+
e.value || Nothing.new
|
177
194
|
end || Nothing.new
|
178
195
|
end
|
179
196
|
|
@@ -185,10 +202,23 @@ class Code
|
|
185
202
|
List.new(
|
186
203
|
raw.select do |element|
|
187
204
|
argument.call(arguments: [Argument.new(element)], **globals).truthy?
|
205
|
+
rescue Error::Next => e
|
206
|
+
(e.value || Nothing.new).truthy?
|
188
207
|
end
|
189
208
|
)
|
190
209
|
end
|
191
210
|
|
211
|
+
def code_select!(argument, **globals)
|
212
|
+
raw.select! do |element|
|
213
|
+
argument.call(arguments: [Argument.new(element)], **globals).truthy?
|
214
|
+
rescue Error::Next => e
|
215
|
+
p e.value
|
216
|
+
(e.value || Nothing.new).truthy?
|
217
|
+
end
|
218
|
+
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
192
222
|
def code_sort
|
193
223
|
List.new(raw.sort)
|
194
224
|
end
|
data/lib/code/object/range.rb
CHANGED
data/lib/code/object.rb
CHANGED
@@ -7,14 +7,6 @@ class Code
|
|
7
7
|
SquareBracket
|
8
8
|
end
|
9
9
|
|
10
|
-
def whitespace
|
11
|
-
Whitespace
|
12
|
-
end
|
13
|
-
|
14
|
-
def whitespace?
|
15
|
-
whitespace.maybe
|
16
|
-
end
|
17
|
-
|
18
10
|
def dot
|
19
11
|
str(".")
|
20
12
|
end
|
@@ -27,6 +19,10 @@ class Code
|
|
27
19
|
str(":")
|
28
20
|
end
|
29
21
|
|
22
|
+
def right_statement
|
23
|
+
Rescue
|
24
|
+
end
|
25
|
+
|
30
26
|
def operator
|
31
27
|
dot | (colon << colon) | (ampersand << dot)
|
32
28
|
end
|
data/lib/code/parser/equal.rb
CHANGED
data/lib/code/parser/equality.rb
CHANGED
@@ -19,11 +19,15 @@ class Code
|
|
19
19
|
raise NotImplementedError
|
20
20
|
end
|
21
21
|
|
22
|
+
def right_statement
|
23
|
+
Statement
|
24
|
+
end
|
25
|
+
|
22
26
|
def root
|
23
27
|
(
|
24
28
|
statement.aka(:first) << (
|
25
29
|
whitespace? << operator.aka(:operator) << whitespace? <<
|
26
|
-
|
30
|
+
right_statement.aka(:statement)
|
27
31
|
).repeat(1).aka(:others).maybe
|
28
32
|
)
|
29
33
|
.aka(:left_operation)
|
@@ -19,11 +19,15 @@ class Code
|
|
19
19
|
raise NotImplementedError
|
20
20
|
end
|
21
21
|
|
22
|
+
def right_statement
|
23
|
+
Statement
|
24
|
+
end
|
25
|
+
|
22
26
|
def root
|
23
27
|
(
|
24
28
|
statement.aka(:left) << (
|
25
29
|
whitespace? << operator.aka(:operator) << whitespace? <<
|
26
|
-
|
30
|
+
right_statement.aka(:right)
|
27
31
|
).maybe
|
28
32
|
)
|
29
33
|
.aka(:right_operation)
|
data/lib/code/parser/string.rb
CHANGED
data/lib/code/parser/while.rb
CHANGED
@@ -27,10 +27,18 @@ class Code
|
|
27
27
|
str("end")
|
28
28
|
end
|
29
29
|
|
30
|
+
def loop_keyword
|
31
|
+
str("loop")
|
32
|
+
end
|
33
|
+
|
30
34
|
def root
|
31
35
|
(
|
32
|
-
(
|
33
|
-
|
36
|
+
(
|
37
|
+
(
|
38
|
+
(while_keyword | until_keyword).aka(:operator) << whitespace <<
|
39
|
+
statement.aka(:statement)
|
40
|
+
) | (loop_keyword.aka(:operator) << whitespace)
|
41
|
+
) << code.aka(:body) << end_keyword.maybe
|
34
42
|
).aka(:while) | statement
|
35
43
|
end
|
36
44
|
end
|
data/lib/code/type/repeat.rb
CHANGED
data/lib/code/version.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
RSpec.describe Code::Object::String do
|
4
6
|
[
|
5
7
|
%w[:a a],
|
6
8
|
%w[:a_b_c_0123 a_b_c_0123],
|
7
|
-
%w[
|
8
|
-
%w[
|
9
|
+
%w[:Hello Hello],
|
10
|
+
%w[:Hello Hello],
|
9
11
|
['"Hello\nWorld"', "Hello\nWorld"],
|
10
12
|
["'Hello\\nWorld'", "Hello\nWorld"]
|
11
13
|
].each do |input, expected|
|
data/spec/code_spec.rb
CHANGED
@@ -141,14 +141,20 @@ RSpec.describe Code do
|
|
141
141
|
['"Hello {1}"', '"Hello 1"'],
|
142
142
|
['user = {} user.name = "Dorian" user.name', ":Dorian"],
|
143
143
|
['user = {} user[:name] = "Dorian" user[:name]', ":Dorian"],
|
144
|
-
['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}']
|
144
|
+
['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}'],
|
145
|
+
["a = 0 loop a += 1 break end a", "1"],
|
146
|
+
["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
|
147
|
+
["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
|
148
|
+
["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"]
|
145
149
|
].each do |input, expected|
|
146
150
|
it "#{input} == #{expected}" do
|
147
151
|
expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
|
148
152
|
end
|
149
153
|
|
150
154
|
it "#{input} converts to json like #{expected}" do
|
151
|
-
expect(Code.evaluate(input).to_json).to eq(
|
155
|
+
expect(Code.evaluate(input).to_json).to eq(
|
156
|
+
Code.evaluate(expected).to_json
|
157
|
+
)
|
152
158
|
end
|
153
159
|
end
|
154
160
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dorian Marié
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|