code-ruby 0.9.2 → 0.10.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/Gemfile.lock +1 -1
- data/bin/code +54 -52
- 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
@@ -9,64 +9,67 @@ options = {
|
|
9
9
|
profile: false,
|
10
10
|
profiler: "text",
|
11
11
|
input: "",
|
12
|
-
context: "",
|
13
12
|
parse: false
|
14
13
|
}
|
15
14
|
|
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
|
59
|
+
.parse!
|
48
60
|
|
49
|
-
|
50
|
-
"-t TIMEOUT",
|
51
|
-
"--timeout TIMEOUT",
|
52
|
-
"Set timeout in seconds"
|
53
|
-
) { |timeout| options[:timeout] = timeout.to_f }
|
61
|
+
options[:input] = argv.join(" ") if options[:input].empty?
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
options[:profile] = true
|
58
|
-
end
|
63
|
+
abort <<~HELP if options[:input].empty?
|
64
|
+
Usage: code INPUT
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
69
|
-
.parse!
|
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
|
70
73
|
|
71
74
|
RubyProf.start if options[:profile]
|
72
75
|
|
@@ -75,7 +78,6 @@ if options[:parse]
|
|
75
78
|
else
|
76
79
|
print Code.evaluate(
|
77
80
|
options[:input],
|
78
|
-
options[:context],
|
79
81
|
output: $stdout,
|
80
82
|
error: $stderr,
|
81
83
|
timeout: options[:timeout]
|
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
|