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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a15136e1a9999fb8cba9a433f0e72d6bd237b8de8d0c948477824944853da2d
4
- data.tar.gz: 98a7b20d9225d717865eaa8fa461fa660e970a281f9de464725fd3923c0dee9f
3
+ metadata.gz: af513b1ba26657cd202191bd69f97e6018c0bdc42ca5b518bc2f1de4800d9cf1
4
+ data.tar.gz: 73f15cd6414c7908118c0f78e5f18f6407f5e6f947fa470110f5d7777f3ebc83
5
5
  SHA512:
6
- metadata.gz: 998e06a88b8d4bfb4cd1529c01a5ea347b3f1874c5c11ddbe9952942db999cb25bba645c5f49bfdb40ff7f17a217af035561ad58fcb06dd8c4383d8906362947
7
- data.tar.gz: b9a5b0703efca344bdd1f1f0b972ddabad86746cbd6745eb71c2a82ef61fc4a30370bd7052dfe703e1a44055926f17c9a57005ff5b353057dc462078c148ea80
6
+ metadata.gz: 88094db69266fed8e2a9ff8f435406ebb623e815dfb85de6087a95ad3352da6dfd53162a3f457773a62247199a38b4ffdede637741d8dcc2e29707c6a5070ff4
7
+ data.tar.gz: 3568647377cc4049c042aee130e6d409e1d4cfa6d556ba3179696bbab7d71a70744ce2e50cb5104dfd564b4bbb417154a22ae08c4e52aba4f797e11cecf67a22
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (0.9.0)
4
+ code-ruby (0.9.3)
5
5
  activesupport (~> 7)
6
6
  bigdecimal (~> 3)
7
7
  json (~> 2)
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
- OptionParser
17
- .new do |opts|
18
- opts.banner = "Usage: code [options]"
19
-
20
- opts.on("-v", "--version", "Version of template") 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(
36
- "-c CONTEXT",
37
- "--context CONTEXT",
38
- "Context in the code language (String or File)"
39
- ) do |context|
40
- context = File.read(context) if File.exist?(context)
41
-
42
- options[:context] = context
43
- end
44
-
45
- opts.on("-p", "--parse", "Get parser results for input") do |parse|
46
- options[:parse] = parse
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
- opts.on(
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
- opts.on("--profile", "Profile Ruby code") do |_timeout|
56
- require "ruby-prof"
57
- options[:profile] = true
58
- end
63
+ abort <<~HELP if options[:input].empty?
64
+ Usage: code INPUT
59
65
 
60
- opts.on(
61
- "--profiler TYPE",
62
- "Profiler output type (text (default) or html)"
63
- ) do |profiler|
64
- require "ruby-prof"
65
- options[:profile] = true
66
- options[:profiler] = profiler
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
@@ -6,10 +6,9 @@ class Code
6
6
  def initialize(parsed)
7
7
  @whole = parsed.delete(:whole)
8
8
 
9
- @exponent =
10
- Node::Statement.new(parsed.delete(:exponent)) if parsed.key?(
11
- :exponent
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
@@ -6,10 +6,9 @@ class Code
6
6
  def initialize(parsed)
7
7
  @decimal = parsed.delete(:decimal)
8
8
 
9
- @exponent =
10
- Node::Statement.new(parsed.delete(:exponent)) if parsed.key?(
11
- :exponent
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
@@ -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 = Node::Statement.new(parsed.delete(:statement))
12
- @body = Node::Code.new(parsed.delete(: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
- if @operator == WHILE_KEYWORD
18
- last = ::Code::Object::Nothing.new
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
- elsif @operator == UNTIL_KEYWORD
26
- last = ::Code::Object::Nothing.new
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
@@ -3,7 +3,7 @@
3
3
  class Code
4
4
  class Node
5
5
  def initialize(parsed)
6
- return unless parsed.any?
6
+ return if parsed.nil? || parsed.empty?
7
7
 
8
8
  raise NotImplementedError, "#{self.class.name}: #{parsed.inspect}"
9
9
  end
@@ -6,11 +6,7 @@ class Code
6
6
  attr_reader :raw
7
7
 
8
8
  def initialize(raw)
9
- if raw.is_a?(Object)
10
- @raw = raw.truthy?
11
- else
12
- @raw = !!raw
13
- end
9
+ @raw = (raw.is_a?(Object) ? raw.truthy? : !!raw)
14
10
  end
15
11
 
16
12
  def self.name
@@ -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
@@ -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)
@@ -9,6 +9,7 @@ class Code
9
9
  whole = whole.raw if whole.is_a?(Integer)
10
10
  @raw = whole.to_i
11
11
  return unless exponent
12
+
12
13
  exponent = exponent.raw if exponent.is_a?(Number)
13
14
  @raw *= 10**exponent
14
15
  end
@@ -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
@@ -8,7 +8,7 @@ class Code
8
8
  def initialize(left, right, exclude_end: false)
9
9
  @left = left
10
10
  @right = right
11
- @exclude_end = !!exclude_end
11
+ @exclude_end = !exclude_end.nil?
12
12
  @raw = ::Range.new(left, right, exclude_end)
13
13
  end
14
14
 
data/lib/code/object.rb CHANGED
@@ -176,7 +176,7 @@ class Code
176
176
  other == self
177
177
  end
178
178
  end
179
- alias_method :eql?, :==
179
+ alias eql? ==
180
180
 
181
181
  def call(**args)
182
182
  operator = args.fetch(:operator, nil)
@@ -7,6 +7,10 @@ class Code
7
7
  Shift
8
8
  end
9
9
 
10
+ def whitespace?
11
+ whitespace
12
+ end
13
+
10
14
  def ampersand
11
15
  str("&")
12
16
  end
@@ -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
@@ -55,6 +55,10 @@ class Code
55
55
  str(".")
56
56
  end
57
57
 
58
+ def right_statement
59
+ OrKeyword
60
+ end
61
+
58
62
  def operator
59
63
  equal | (plus << equal) | (minus << equal) | (asterisk << equal) |
60
64
  (slash << equal) | (percent << equal) |
@@ -27,6 +27,10 @@ class Code
27
27
  str("~")
28
28
  end
29
29
 
30
+ def right_statement
31
+ Greater
32
+ end
33
+
30
34
  def operator
31
35
  (equal << equal << equal) | (equal << equal) |
32
36
  (lesser << equal << greater) | (exclamation_point << equal) |
@@ -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
- statement.aka(:statement)
30
+ right_statement.aka(:statement)
27
31
  ).repeat(1).aka(:others).maybe
28
32
  )
29
33
  .aka(:left_operation)
@@ -27,6 +27,10 @@ class Code
27
27
  str("÷")
28
28
  end
29
29
 
30
+ def right_statement
31
+ Negation
32
+ end
33
+
30
34
  def operator
31
35
  asterisk | slash | percent | multiplication_sign | division_sign
32
36
  end
@@ -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
- self.class.aka(:right)
30
+ right_statement.aka(:right)
27
31
  ).maybe
28
32
  )
29
33
  .aka(:right_operation)
@@ -20,7 +20,7 @@ class Code
20
20
  end
21
21
 
22
22
  def backslash
23
- str("\\")
23
+ str('\\')
24
24
  end
25
25
 
26
26
  def opening_curly_bracket
@@ -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
- (while_keyword | until_keyword).aka(:operator) << whitespace <<
33
- statement.aka(:statement) << code.aka(:body) << end_keyword.maybe
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
@@ -21,7 +21,7 @@ class Code
21
21
 
22
22
  def max_arguments
23
23
  max_arguments = max_arguments_of(clazz)
24
- (max_arguments.nil? || maximum.nil?) ? nil : maximum * max_arguments
24
+ max_arguments.nil? || maximum.nil? ? nil : maximum * max_arguments
25
25
  end
26
26
 
27
27
  def name
data/lib/code/version.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative "../code"
4
4
 
5
- Code::Version = Gem::Version.new("0.9.2")
5
+ Code::Version = Gem::Version.new("0.10.0")
@@ -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["Hello" Hello],
8
- %w['Hello' Hello],
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(Code.evaluate(expected).to_json)
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.9.2
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-27 00:00:00.000000000 Z
11
+ date: 2024-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport