code-ruby 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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