code-ruby 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/.github/workflows/rspec.yml +14 -0
- data/.gitignore +2 -0
- data/.prettierrc +3 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +70 -0
- data/LICENSE +7 -0
- data/README.md +103 -0
- data/TODO.md +1 -0
- data/bin/template +39 -0
- data/code-ruby.gemspec +22 -0
- data/docs/euler/1.template +14 -0
- data/docs/euler/2.template +16 -0
- data/docs/euler/3.template +16 -0
- data/docs/euler/4.template +11 -0
- data/docs/euler/5.template +14 -0
- data/docs/precedence.template +94 -0
- data/lib/code/error.rb +15 -0
- data/lib/code/node/base_10_decimal.rb +32 -0
- data/lib/code/node/base_10_integer.rb +32 -0
- data/lib/code/node/base_10_number.rb +19 -0
- data/lib/code/node/base_16_number.rb +19 -0
- data/lib/code/node/base_2_number.rb +19 -0
- data/lib/code/node/base_8_number.rb +19 -0
- data/lib/code/node/block.rb +17 -0
- data/lib/code/node/boolean.rb +22 -0
- data/lib/code/node/call.rb +52 -0
- data/lib/code/node/call_argument.rb +37 -0
- data/lib/code/node/chained_call.rb +38 -0
- data/lib/code/node/code.rb +16 -0
- data/lib/code/node/defined.rb +19 -0
- data/lib/code/node/dictionnary.rb +22 -0
- data/lib/code/node/dictionnary_key_value.rb +23 -0
- data/lib/code/node/equal.rb +36 -0
- data/lib/code/node/function.rb +17 -0
- data/lib/code/node/function_argument.rb +45 -0
- data/lib/code/node/group.rb +13 -0
- data/lib/code/node/if.rb +55 -0
- data/lib/code/node/if_modifier.rb +48 -0
- data/lib/code/node/keyword_call_argument.rb +30 -0
- data/lib/code/node/keyword_function_argument.rb +33 -0
- data/lib/code/node/list.rb +19 -0
- data/lib/code/node/name.rb +50 -0
- data/lib/code/node/negation.rb +33 -0
- data/lib/code/node/not_keyword.rb +13 -0
- data/lib/code/node/nothing.rb +12 -0
- data/lib/code/node/number.rb +23 -0
- data/lib/code/node/operation.rb +33 -0
- data/lib/code/node/or_keyword.rb +34 -0
- data/lib/code/node/power.rb +16 -0
- data/lib/code/node/range.rb +31 -0
- data/lib/code/node/regular_call_argument.rb +34 -0
- data/lib/code/node/regular_function_argument.rb +36 -0
- data/lib/code/node/rescue.rb +16 -0
- data/lib/code/node/statement.rb +81 -0
- data/lib/code/node/string.rb +17 -0
- data/lib/code/node/ternary.rb +26 -0
- data/lib/code/node/unary_minus.rb +22 -0
- data/lib/code/node/while.rb +42 -0
- data/lib/code/node.rb +14 -0
- data/lib/code/object/argument.rb +41 -0
- data/lib/code/object/boolean.rb +27 -0
- data/lib/code/object/decimal.rb +54 -0
- data/lib/code/object/dictionnary.rb +55 -0
- data/lib/code/object/function.rb +64 -0
- data/lib/code/object/integer.rb +116 -0
- data/lib/code/object/list.rb +217 -0
- data/lib/code/object/nothing.rb +23 -0
- data/lib/code/object/number.rb +6 -0
- data/lib/code/object/range.rb +158 -0
- data/lib/code/object/string.rb +68 -0
- data/lib/code/object.rb +130 -0
- data/lib/code/parser/addition.rb +29 -0
- data/lib/code/parser/and_operator.rb +28 -0
- data/lib/code/parser/bitwise_and.rb +28 -0
- data/lib/code/parser/bitwise_or.rb +29 -0
- data/lib/code/parser/boolean.rb +14 -0
- data/lib/code/parser/call.rb +90 -0
- data/lib/code/parser/code.rb +19 -0
- data/lib/code/parser/defined.rb +20 -0
- data/lib/code/parser/dictionnary.rb +41 -0
- data/lib/code/parser/equal.rb +42 -0
- data/lib/code/parser/equality.rb +36 -0
- data/lib/code/parser/function.rb +57 -0
- data/lib/code/parser/greater_than.rb +33 -0
- data/lib/code/parser/group.rb +17 -0
- data/lib/code/parser/if.rb +33 -0
- data/lib/code/parser/if_modifier.rb +28 -0
- data/lib/code/parser/list.rb +29 -0
- data/lib/code/parser/multiplication.rb +30 -0
- data/lib/code/parser/name.rb +89 -0
- data/lib/code/parser/negation.rb +19 -0
- data/lib/code/parser/not_keyword.rb +21 -0
- data/lib/code/parser/nothing.rb +17 -0
- data/lib/code/parser/number.rb +98 -0
- data/lib/code/parser/or_keyword.rb +29 -0
- data/lib/code/parser/or_operator.rb +28 -0
- data/lib/code/parser/power.rb +25 -0
- data/lib/code/parser/range.rb +25 -0
- data/lib/code/parser/rescue.rb +23 -0
- data/lib/code/parser/shift.rb +31 -0
- data/lib/code/parser/statement.rb +8 -0
- data/lib/code/parser/string.rb +72 -0
- data/lib/code/parser/ternary.rb +25 -0
- data/lib/code/parser/unary_minus.rb +13 -0
- data/lib/code/parser/while.rb +25 -0
- data/lib/code/parser.rb +4 -0
- data/lib/code-ruby.rb +11 -0
- data/lib/code.rb +29 -0
- data/lib/template/node/code_part.rb +13 -0
- data/lib/template/node/part.rb +19 -0
- data/lib/template/node/template.rb +15 -0
- data/lib/template/node/text_part.rb +13 -0
- data/lib/template/node.rb +4 -0
- data/lib/template/parser/template.rb +30 -0
- data/lib/template/parser.rb +4 -0
- data/lib/template/version.rb +3 -0
- data/lib/template-ruby.rb +11 -0
- data/lib/template.rb +34 -0
- data/spec/call_spec.rb +22 -0
- data/spec/code/error/type_error_spec.rb +65 -0
- data/spec/code/parser/boolean_spec.rb +18 -0
- data/spec/code/parser/call_spec.rb +66 -0
- data/spec/code/parser/dictionnary_spec.rb +46 -0
- data/spec/code/parser/function_spec.rb +32 -0
- data/spec/code/parser/list_spec.rb +29 -0
- data/spec/code/parser/name_spec.rb +15 -0
- data/spec/code/parser/nothing_spec.rb +19 -0
- data/spec/code/parser/number_spec.rb +117 -0
- data/spec/code/parser/string_spec.rb +30 -0
- data/spec/code_spec.rb +108 -0
- data/spec/function_spec.rb +26 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/template/parser/template_spec.rb +19 -0
- data/spec/template_spec.rb +27 -0
- data/template-ruby.gemspec +24 -0
- metadata +266 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
class Template
|
2
|
+
class Parser
|
3
|
+
class Template < Parslet::Parser
|
4
|
+
rule(:code) { ::Code::Parser::Code.new }
|
5
|
+
|
6
|
+
rule(:left_curly_bracket) { str("{") }
|
7
|
+
rule(:right_curly_bracket) { str("}") }
|
8
|
+
rule(:escape) { str("\\") }
|
9
|
+
|
10
|
+
rule(:text_part) do
|
11
|
+
(
|
12
|
+
(escape.ignore >> left_curly_bracket) |
|
13
|
+
(left_curly_bracket.absent? >> any)
|
14
|
+
).repeat(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
rule(:code_part) do
|
18
|
+
left_curly_bracket.ignore >> code >>
|
19
|
+
(right_curly_bracket.ignore | any.absent?)
|
20
|
+
end
|
21
|
+
|
22
|
+
rule(:template) do
|
23
|
+
(text_part.as(:text) | code_part.as(:code)).repeat(1) |
|
24
|
+
str("").as(:text).repeat(1, 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
root(:template)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "parslet"
|
2
|
+
require "zeitwerk"
|
3
|
+
require "bigdecimal"
|
4
|
+
require "active_support"
|
5
|
+
require "active_support/core_ext/object/blank"
|
6
|
+
require "stringio"
|
7
|
+
require "timeout"
|
8
|
+
|
9
|
+
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
10
|
+
loader.ignore(__FILE__)
|
11
|
+
loader.setup
|
data/lib/template.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class Template
|
2
|
+
def initialize(input, io: StringIO.new, timeout: 10)
|
3
|
+
@input = input
|
4
|
+
@parsed =
|
5
|
+
Timeout.timeout(timeout) do
|
6
|
+
::Template::Parser::Template.new.parse(@input)
|
7
|
+
end
|
8
|
+
@io = io
|
9
|
+
@timeout = timeout
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.render(input, context = "", io: StringIO.new, timeout: 10)
|
13
|
+
new(input, io: io, timeout: timeout).render(context)
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(context = "")
|
17
|
+
Timeout.timeout(@timeout) do
|
18
|
+
if context.present?
|
19
|
+
context = ::Code.evaluate(context, timeout: @timeout)
|
20
|
+
else
|
21
|
+
context = ::Code::Object::Dictionnary.new
|
22
|
+
end
|
23
|
+
|
24
|
+
::Template::Node::Template.new(@parsed).evaluate(
|
25
|
+
context: context,
|
26
|
+
io: @io,
|
27
|
+
)
|
28
|
+
|
29
|
+
@io.is_a?(StringIO) ? @io.string : nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require_relative "template/version"
|
data/spec/call_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code do
|
4
|
+
subject { described_class.evaluate(input).to_s }
|
5
|
+
|
6
|
+
[
|
7
|
+
%w[(1..2).any?(&:even?) true],
|
8
|
+
%w[(1..1).any?(&:even?) false],
|
9
|
+
%w[(3..3).any?(&:even?) false],
|
10
|
+
%w[(2..5).any?(&:even?) true],
|
11
|
+
["(2..5).any? { |n| n.even? }", "true"],
|
12
|
+
["(2..5).select { |i| i.even? }.any? { |n| n.even? }", "true"],
|
13
|
+
].each do |(input, expected)|
|
14
|
+
context input.inspect do
|
15
|
+
let(:input) { input }
|
16
|
+
|
17
|
+
it "succeeds" do
|
18
|
+
expect(subject).to eq(expected)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe ::Code::Error::TypeError do
|
4
|
+
describe "Integer" do
|
5
|
+
context "creating an integer with a non-number exponent" do
|
6
|
+
it "raises" do
|
7
|
+
expect { ::Code::Object::Integer.new(1, exponent: "2") }.to raise_error(
|
8
|
+
described_class,
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
[
|
14
|
+
'1 / ""',
|
15
|
+
'1 ** ""',
|
16
|
+
'1 % ""',
|
17
|
+
'1 + ""',
|
18
|
+
'1 - ""',
|
19
|
+
'1 << ""',
|
20
|
+
'1 >> ""',
|
21
|
+
'1 & ""',
|
22
|
+
'1 | ""',
|
23
|
+
'1 ^ ""',
|
24
|
+
'1 > ""',
|
25
|
+
'1 >= ""',
|
26
|
+
'1 < ""',
|
27
|
+
'1 <= ""',
|
28
|
+
].each do |input|
|
29
|
+
context input.inspect do
|
30
|
+
it "raises" do
|
31
|
+
expect { ::Code.evaluate(input) }.to raise_error(described_class)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Decimal" do
|
38
|
+
context "creating an integer with a non-number exponent" do
|
39
|
+
it "raises" do
|
40
|
+
expect { ::Code::Object::Decimal.new(1, exponent: "2") }.to raise_error(
|
41
|
+
described_class,
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
[
|
47
|
+
'1.0 / ""',
|
48
|
+
'1.0 ** ""',
|
49
|
+
'1.0 * ""',
|
50
|
+
'1.0 % ""',
|
51
|
+
'1.0 + ""',
|
52
|
+
'1.0 - ""',
|
53
|
+
'1.0 > ""',
|
54
|
+
'1.0 >= ""',
|
55
|
+
'1.0 < ""',
|
56
|
+
'1.0 <= ""',
|
57
|
+
].each do |input|
|
58
|
+
context input.inspect do
|
59
|
+
it "raises" do
|
60
|
+
expect { ::Code.evaluate(input) }.to raise_error(described_class)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Boolean do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
["true", { boolean: "true" }],
|
8
|
+
["false", { boolean: "false" }],
|
9
|
+
].each do |(input, expected)|
|
10
|
+
context input.inspect do
|
11
|
+
let(:input) { input }
|
12
|
+
|
13
|
+
it "succeeds" do
|
14
|
+
expect(subject).to eq(expected)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Call do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
[
|
8
|
+
"user.first_name",
|
9
|
+
{ call: { left: { name: "user" }, right: [{ name: "first_name" }] } },
|
10
|
+
],
|
11
|
+
[
|
12
|
+
"3.times",
|
13
|
+
{
|
14
|
+
call: {
|
15
|
+
left: {
|
16
|
+
number: {
|
17
|
+
base_10: {
|
18
|
+
integer: {
|
19
|
+
whole: "3",
|
20
|
+
},
|
21
|
+
},
|
22
|
+
},
|
23
|
+
},
|
24
|
+
right: [{ name: "times" }],
|
25
|
+
},
|
26
|
+
},
|
27
|
+
],
|
28
|
+
].each do |(input, expected)|
|
29
|
+
context input.inspect do
|
30
|
+
let(:input) { input }
|
31
|
+
|
32
|
+
it "succeeds" do
|
33
|
+
expect(subject).to eq(expected)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
[
|
39
|
+
"User.first",
|
40
|
+
"User.first(10)",
|
41
|
+
'User.find_by(name: "Dorian")',
|
42
|
+
"User.update_all(**attributes)",
|
43
|
+
"User.each(&block)",
|
44
|
+
"user.update(*args)",
|
45
|
+
"sort([1, 2, 3], :asc)",
|
46
|
+
"render()",
|
47
|
+
"render(item)",
|
48
|
+
"Renderer.render(item)",
|
49
|
+
"render(item) { 'Hello' }",
|
50
|
+
"render(item) { |item| item * 2 }",
|
51
|
+
"render(item) { |item1, item2| item1 + item2 }",
|
52
|
+
"render(item) do 'Hello' end",
|
53
|
+
"render(item) do |item| item * 2 end",
|
54
|
+
"render(item) do |item1, item2| item1 + item2 end",
|
55
|
+
"(1..2).any?(&:even?)",
|
56
|
+
"(2..5).select { |i| i.even? }.any? { |n| n.even? }",
|
57
|
+
].each do |input|
|
58
|
+
context input.inspect do
|
59
|
+
let(:input) { input }
|
60
|
+
|
61
|
+
it "succeeds" do
|
62
|
+
expect { subject }.to_not raise_error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Dictionnary do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
[
|
8
|
+
'{name: "Dorian"}',
|
9
|
+
{
|
10
|
+
dictionnary: [{ key: { name: "name" }, value: [{ string: "Dorian" }] }],
|
11
|
+
},
|
12
|
+
],
|
13
|
+
[
|
14
|
+
'{a: true, "b": false}',
|
15
|
+
{
|
16
|
+
dictionnary: [
|
17
|
+
{ key: { name: "a" }, value: [{ boolean: "true" }] },
|
18
|
+
{ key: { string: "b" }, value: [{ boolean: "false" }] },
|
19
|
+
],
|
20
|
+
},
|
21
|
+
],
|
22
|
+
[
|
23
|
+
"{ true => 1, false => 2}",
|
24
|
+
{
|
25
|
+
dictionnary: [
|
26
|
+
{
|
27
|
+
key: [{ boolean: "true" }],
|
28
|
+
value: [{ number: { base_10: { integer: { whole: "1" } } } }],
|
29
|
+
},
|
30
|
+
{
|
31
|
+
key: [{ boolean: "false" }],
|
32
|
+
value: [{ number: { base_10: { integer: { whole: "2" } } } }],
|
33
|
+
},
|
34
|
+
],
|
35
|
+
},
|
36
|
+
],
|
37
|
+
].each do |(input, expected)|
|
38
|
+
context input.inspect do
|
39
|
+
let(:input) { input }
|
40
|
+
|
41
|
+
it "succeeds" do
|
42
|
+
expect(subject).to eq(expected)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Function do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
"() => {}",
|
8
|
+
'() => { "Hello" }',
|
9
|
+
"(a) => { }",
|
10
|
+
"(a = 1)=> {}",
|
11
|
+
"(a, b) =>{}",
|
12
|
+
"(a, b = 2, c = b)=>{}",
|
13
|
+
"(a:)=>{}",
|
14
|
+
"(a:, b:)=>{}",
|
15
|
+
"(a, b:, c)=>{}",
|
16
|
+
"(a:, b: 1)=>{}",
|
17
|
+
"(a, b: 1, c)=>{}",
|
18
|
+
"(*args)=>{}",
|
19
|
+
"(*args, **kargs)=>{}",
|
20
|
+
"(&block)=>{}",
|
21
|
+
"(a = b = 1 + 1 b)=>{}",
|
22
|
+
"(a: b = 1 + 1 b)=>{}",
|
23
|
+
].each do |input|
|
24
|
+
context input.inspect do
|
25
|
+
let(:input) { input }
|
26
|
+
|
27
|
+
it "succeeds" do
|
28
|
+
expect { subject }.to_not raise_error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::List do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
[
|
8
|
+
"[true, false]",
|
9
|
+
{
|
10
|
+
list: [
|
11
|
+
{ code: [{ boolean: "true" }] },
|
12
|
+
{ code: [{ boolean: "false" }] },
|
13
|
+
],
|
14
|
+
},
|
15
|
+
],
|
16
|
+
[
|
17
|
+
"[nothing, a]",
|
18
|
+
{ list: [{ code: [{ nothing: "nothing" }] }, { code: [{ name: "a" }] }] },
|
19
|
+
],
|
20
|
+
].each do |(input, expected)|
|
21
|
+
context input.inspect do
|
22
|
+
let(:input) { input }
|
23
|
+
|
24
|
+
it "succeeds" do
|
25
|
+
expect(subject).to eq(expected)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Name do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
%w[user dorian User RSpec describe?].each do |(input, expected)|
|
7
|
+
context input.inspect do
|
8
|
+
let(:input) { input }
|
9
|
+
|
10
|
+
it "succeeds" do
|
11
|
+
expect(subject).to eq({ name: input })
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Nothing do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
["nothing", { nothing: "nothing" }],
|
8
|
+
["null", { nothing: "null" }],
|
9
|
+
["nil", { nothing: "nil" }],
|
10
|
+
].each do |(input, expected)|
|
11
|
+
context input.inspect do
|
12
|
+
let(:input) { input }
|
13
|
+
|
14
|
+
it "succeeds" do
|
15
|
+
expect(subject).to eq(expected)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::Number do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
["1", { number: { base_10: { integer: { whole: "1" } } } }],
|
8
|
+
["3", { number: { base_10: { integer: { whole: "3" } } } }],
|
9
|
+
["1.0", { number: { base_10: { decimal: { whole: "1", decimal: "0" } } } }],
|
10
|
+
[
|
11
|
+
"-1.0",
|
12
|
+
{
|
13
|
+
number: {
|
14
|
+
base_10: {
|
15
|
+
decimal: {
|
16
|
+
sign: "-",
|
17
|
+
whole: "1",
|
18
|
+
decimal: "0",
|
19
|
+
},
|
20
|
+
},
|
21
|
+
},
|
22
|
+
},
|
23
|
+
],
|
24
|
+
[
|
25
|
+
"+1.0",
|
26
|
+
{
|
27
|
+
number: {
|
28
|
+
base_10: {
|
29
|
+
decimal: {
|
30
|
+
sign: "+",
|
31
|
+
whole: "1",
|
32
|
+
decimal: "0",
|
33
|
+
},
|
34
|
+
},
|
35
|
+
},
|
36
|
+
},
|
37
|
+
],
|
38
|
+
["0", { number: { base_10: { integer: { whole: "0" } } } }],
|
39
|
+
["+0", { number: { base_10: { integer: { sign: "+", whole: "0" } } } }],
|
40
|
+
["-0", { number: { base_10: { integer: { sign: "-", whole: "0" } } } }],
|
41
|
+
["0b010", { number: { base_2: "010" } }],
|
42
|
+
["0o01234567", { number: { base_8: "01234567" } }],
|
43
|
+
[
|
44
|
+
"0x0123456789aAbBcCdDeEfF",
|
45
|
+
{ number: { base_16: "0123456789aAbBcCdDeEfF" } },
|
46
|
+
],
|
47
|
+
[
|
48
|
+
"10e20",
|
49
|
+
{
|
50
|
+
number: {
|
51
|
+
base_10: {
|
52
|
+
integer: {
|
53
|
+
whole: "10",
|
54
|
+
exponent: {
|
55
|
+
integer: {
|
56
|
+
whole: "20",
|
57
|
+
},
|
58
|
+
},
|
59
|
+
},
|
60
|
+
},
|
61
|
+
},
|
62
|
+
},
|
63
|
+
],
|
64
|
+
[
|
65
|
+
"10.34e23.45",
|
66
|
+
{
|
67
|
+
number: {
|
68
|
+
base_10: {
|
69
|
+
decimal: {
|
70
|
+
whole: "10",
|
71
|
+
decimal: "34",
|
72
|
+
exponent: {
|
73
|
+
decimal: {
|
74
|
+
whole: "23",
|
75
|
+
decimal: "45",
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
},
|
80
|
+
},
|
81
|
+
},
|
82
|
+
],
|
83
|
+
[
|
84
|
+
"+10e-20e1.0",
|
85
|
+
{
|
86
|
+
number: {
|
87
|
+
base_10: {
|
88
|
+
integer: {
|
89
|
+
sign: "+",
|
90
|
+
whole: "10",
|
91
|
+
exponent: {
|
92
|
+
integer: {
|
93
|
+
sign: "-",
|
94
|
+
whole: "20",
|
95
|
+
exponent: {
|
96
|
+
decimal: {
|
97
|
+
whole: "1",
|
98
|
+
decimal: "0",
|
99
|
+
},
|
100
|
+
},
|
101
|
+
},
|
102
|
+
},
|
103
|
+
},
|
104
|
+
},
|
105
|
+
},
|
106
|
+
},
|
107
|
+
],
|
108
|
+
].each do |(input, expected)|
|
109
|
+
context input.inspect do
|
110
|
+
let(:input) { input }
|
111
|
+
|
112
|
+
it "succeeds" do
|
113
|
+
expect(subject).to eq(expected)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code::Parser::String do
|
4
|
+
subject { described_class.new.parse(input) }
|
5
|
+
|
6
|
+
[
|
7
|
+
["'hello'", { string: "hello" }],
|
8
|
+
['"hello"', { string: "hello" }],
|
9
|
+
["''", { string: "" }],
|
10
|
+
['""', { string: "" }],
|
11
|
+
["'\\''", { string: "'" }],
|
12
|
+
['"\\t"', { string: "\\t" }],
|
13
|
+
["'\\r'", { string: "\\r" }],
|
14
|
+
['"\\b\\f\\n\\r\\t"', { string: "\\b\\f\\n\\r\\t" }],
|
15
|
+
['"\\uABCG"', { string: "uABCG" }],
|
16
|
+
[
|
17
|
+
"'\\u0123\\u4567\\u89aA\\ubBcC\\UdDeE\\ufFfF'",
|
18
|
+
{ string: "\\u0123\\u4567\\u89aA\\ubBcC\\UdDeE\\ufFfF" },
|
19
|
+
],
|
20
|
+
[":asc", { string: "asc" }],
|
21
|
+
].each do |(input, expected)|
|
22
|
+
context input.inspect do
|
23
|
+
let(:input) { input }
|
24
|
+
|
25
|
+
it "succeeds" do
|
26
|
+
expect(subject).to eq(expected)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/code_spec.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code do
|
4
|
+
subject { described_class.evaluate(input).to_s }
|
5
|
+
|
6
|
+
[
|
7
|
+
["nothing", ""],
|
8
|
+
["null", ""],
|
9
|
+
["nil", ""],
|
10
|
+
%w[true true],
|
11
|
+
%w[false false],
|
12
|
+
%w[1 1],
|
13
|
+
%w[1.2 1.2],
|
14
|
+
%w[0b10 2],
|
15
|
+
%w[0o10 8],
|
16
|
+
%w[0x10 16],
|
17
|
+
%w[1e2 100],
|
18
|
+
%w[1.2e2.2 190.1871830953336182242521644],
|
19
|
+
%w[1e1e1 10000000000],
|
20
|
+
%w['hello' hello],
|
21
|
+
%w["hello" hello],
|
22
|
+
["[true, 1, nothing]", "[true, 1, nothing]"],
|
23
|
+
['{a: 1, "b": 2}', '{"a" => 1, "b" => 2}'],
|
24
|
+
%w[!true false],
|
25
|
+
%w[!!true true],
|
26
|
+
%w[!!nothing false],
|
27
|
+
%w[!!1 true],
|
28
|
+
%w[+1 1],
|
29
|
+
%w[++++1 1],
|
30
|
+
["++++nothing", ""],
|
31
|
+
%w[+{} {}],
|
32
|
+
["2 ** 2", "4"],
|
33
|
+
["2 ** 2 ** 3", "256"],
|
34
|
+
%w[-2 -2],
|
35
|
+
%w[--2 2],
|
36
|
+
["2 * 3", "6"],
|
37
|
+
["1 / 2", "0.5"],
|
38
|
+
["1 / 2 / 2", "0.25"],
|
39
|
+
["12 % 10", "2"],
|
40
|
+
["8 / -2 ** 3", "-1.0"],
|
41
|
+
["1 + 2 * 3 + 4", "11"],
|
42
|
+
["1e1.1 * 2", "25.1785082358833442084790822"],
|
43
|
+
["1 / 3 * 3", "0.999999999999999999999999999999999999"],
|
44
|
+
['3 * "hello"', "hellohellohello"],
|
45
|
+
['"Go" + "od"', "Good"],
|
46
|
+
["1 << 2", "4"],
|
47
|
+
["4 >> 2", "1"],
|
48
|
+
["2 & 1", "0"],
|
49
|
+
["2 | 1", "3"],
|
50
|
+
["5 ^ 6", "3"],
|
51
|
+
["5 > 6", "false"],
|
52
|
+
["5 > 5", "false"],
|
53
|
+
["5 > 4", "true"],
|
54
|
+
["2 > 1 == 3 > 2", "true"],
|
55
|
+
["true && false", "false"],
|
56
|
+
["true || false", "true"],
|
57
|
+
%w[1..3 1..3],
|
58
|
+
['1 > 3 ? "Impossible" : "Sounds about right"', "Sounds about right"],
|
59
|
+
['1 < 3 ? "OK"', "OK"],
|
60
|
+
['1 < "" rescue "oops"', "oops"],
|
61
|
+
['"fine" rescue "oops"', "fine"],
|
62
|
+
["a = 1", "1"],
|
63
|
+
["a = 1 a * 2", "2"],
|
64
|
+
["a = 1 a += 1 a", "2"],
|
65
|
+
["a = 1 a -= 1 a", "0"],
|
66
|
+
%w[defined?(a) false],
|
67
|
+
["a = 1 defined?(a)", "true"],
|
68
|
+
["not true", "false"],
|
69
|
+
["not false", "true"],
|
70
|
+
["not not 1", "true"],
|
71
|
+
["1 or 2", "1"],
|
72
|
+
["true or false", "true"],
|
73
|
+
["1 and 2", "2"],
|
74
|
+
["false and 2", "false"],
|
75
|
+
["true and false", "false"],
|
76
|
+
["true and true", "true"],
|
77
|
+
["1 if false", ""],
|
78
|
+
["1 if true", "1"],
|
79
|
+
["1 if true if true", "1"],
|
80
|
+
["1 unless false", "1"],
|
81
|
+
["1 unless true", ""],
|
82
|
+
["a = 0 a += 1 while a < 10 a", "10"],
|
83
|
+
["a = 0 a += 1 until a > 10 a", "11"],
|
84
|
+
["if true 1 end", "1"],
|
85
|
+
["if false 1 end", ""],
|
86
|
+
["if false 1 else 2 end", "2"],
|
87
|
+
["unless false 1 end", "1"],
|
88
|
+
["unless true 1 end", ""],
|
89
|
+
["if false 1 else if true 2 else 3 end", "2"],
|
90
|
+
["if false 1 else if false 2 else 3 end", "3"],
|
91
|
+
["unless false 1 else if false 2 else 3 end", "1"],
|
92
|
+
["if false 1 else unless false 2 else 3 end", "2"],
|
93
|
+
["a = 0\n while a < 10 a += 1 end a", "10"],
|
94
|
+
["a = 0\n until a > 10 a += 1 end a", "11"],
|
95
|
+
["until true end", ""],
|
96
|
+
["until true\nend", ""],
|
97
|
+
%w[("Good".."Bad").first Good],
|
98
|
+
['"Dorian " * 2', "Dorian Dorian "],
|
99
|
+
].each do |(input, expected)|
|
100
|
+
context input.inspect do
|
101
|
+
let(:input) { input }
|
102
|
+
|
103
|
+
it "succeeds" do
|
104
|
+
expect(subject).to eq(expected)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe Code do
|
4
|
+
subject { described_class.evaluate(input).to_s }
|
5
|
+
|
6
|
+
[
|
7
|
+
['hello = () => { "Hello" } hello', "Hello"],
|
8
|
+
["add = (a, b) => { a + b } add(1, 2)", "3"],
|
9
|
+
["add = (a:, b:) => { a + b } add(a: 1, b: 2)", "3"],
|
10
|
+
["add = (a = 1, b = 2) => { a + b } add", "3"],
|
11
|
+
["add = (a: 1, b: 2) => { a + b } add", "3"],
|
12
|
+
["add = (*args) => { args.first + args.last } add(1, 2)", "3"],
|
13
|
+
[
|
14
|
+
"add = (**kargs) => { kargs.values.first + kargs.values.last } add(a: 1, b: 2)",
|
15
|
+
"3",
|
16
|
+
],
|
17
|
+
].each do |(input, expected)|
|
18
|
+
context input.inspect do
|
19
|
+
let(:input) { input }
|
20
|
+
|
21
|
+
it "succeeds" do
|
22
|
+
expect(subject).to eq(expected)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|