code-ruby 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.github/workflows/rspec.yml +14 -0
  4. data/.gitignore +2 -0
  5. data/.prettierrc +3 -0
  6. data/.rspec +1 -0
  7. data/CHANGELOG.md +31 -0
  8. data/Gemfile +3 -0
  9. data/Gemfile.lock +70 -0
  10. data/LICENSE +7 -0
  11. data/README.md +103 -0
  12. data/TODO.md +1 -0
  13. data/bin/template +39 -0
  14. data/code-ruby.gemspec +22 -0
  15. data/docs/euler/1.template +14 -0
  16. data/docs/euler/2.template +16 -0
  17. data/docs/euler/3.template +16 -0
  18. data/docs/euler/4.template +11 -0
  19. data/docs/euler/5.template +14 -0
  20. data/docs/precedence.template +94 -0
  21. data/lib/code/error.rb +15 -0
  22. data/lib/code/node/base_10_decimal.rb +32 -0
  23. data/lib/code/node/base_10_integer.rb +32 -0
  24. data/lib/code/node/base_10_number.rb +19 -0
  25. data/lib/code/node/base_16_number.rb +19 -0
  26. data/lib/code/node/base_2_number.rb +19 -0
  27. data/lib/code/node/base_8_number.rb +19 -0
  28. data/lib/code/node/block.rb +17 -0
  29. data/lib/code/node/boolean.rb +22 -0
  30. data/lib/code/node/call.rb +52 -0
  31. data/lib/code/node/call_argument.rb +37 -0
  32. data/lib/code/node/chained_call.rb +38 -0
  33. data/lib/code/node/code.rb +16 -0
  34. data/lib/code/node/defined.rb +19 -0
  35. data/lib/code/node/dictionnary.rb +22 -0
  36. data/lib/code/node/dictionnary_key_value.rb +23 -0
  37. data/lib/code/node/equal.rb +36 -0
  38. data/lib/code/node/function.rb +17 -0
  39. data/lib/code/node/function_argument.rb +45 -0
  40. data/lib/code/node/group.rb +13 -0
  41. data/lib/code/node/if.rb +55 -0
  42. data/lib/code/node/if_modifier.rb +48 -0
  43. data/lib/code/node/keyword_call_argument.rb +30 -0
  44. data/lib/code/node/keyword_function_argument.rb +33 -0
  45. data/lib/code/node/list.rb +19 -0
  46. data/lib/code/node/name.rb +50 -0
  47. data/lib/code/node/negation.rb +33 -0
  48. data/lib/code/node/not_keyword.rb +13 -0
  49. data/lib/code/node/nothing.rb +12 -0
  50. data/lib/code/node/number.rb +23 -0
  51. data/lib/code/node/operation.rb +33 -0
  52. data/lib/code/node/or_keyword.rb +34 -0
  53. data/lib/code/node/power.rb +16 -0
  54. data/lib/code/node/range.rb +31 -0
  55. data/lib/code/node/regular_call_argument.rb +34 -0
  56. data/lib/code/node/regular_function_argument.rb +36 -0
  57. data/lib/code/node/rescue.rb +16 -0
  58. data/lib/code/node/statement.rb +81 -0
  59. data/lib/code/node/string.rb +17 -0
  60. data/lib/code/node/ternary.rb +26 -0
  61. data/lib/code/node/unary_minus.rb +22 -0
  62. data/lib/code/node/while.rb +42 -0
  63. data/lib/code/node.rb +14 -0
  64. data/lib/code/object/argument.rb +41 -0
  65. data/lib/code/object/boolean.rb +27 -0
  66. data/lib/code/object/decimal.rb +54 -0
  67. data/lib/code/object/dictionnary.rb +55 -0
  68. data/lib/code/object/function.rb +64 -0
  69. data/lib/code/object/integer.rb +116 -0
  70. data/lib/code/object/list.rb +217 -0
  71. data/lib/code/object/nothing.rb +23 -0
  72. data/lib/code/object/number.rb +6 -0
  73. data/lib/code/object/range.rb +158 -0
  74. data/lib/code/object/string.rb +68 -0
  75. data/lib/code/object.rb +130 -0
  76. data/lib/code/parser/addition.rb +29 -0
  77. data/lib/code/parser/and_operator.rb +28 -0
  78. data/lib/code/parser/bitwise_and.rb +28 -0
  79. data/lib/code/parser/bitwise_or.rb +29 -0
  80. data/lib/code/parser/boolean.rb +14 -0
  81. data/lib/code/parser/call.rb +90 -0
  82. data/lib/code/parser/code.rb +19 -0
  83. data/lib/code/parser/defined.rb +20 -0
  84. data/lib/code/parser/dictionnary.rb +41 -0
  85. data/lib/code/parser/equal.rb +42 -0
  86. data/lib/code/parser/equality.rb +36 -0
  87. data/lib/code/parser/function.rb +57 -0
  88. data/lib/code/parser/greater_than.rb +33 -0
  89. data/lib/code/parser/group.rb +17 -0
  90. data/lib/code/parser/if.rb +33 -0
  91. data/lib/code/parser/if_modifier.rb +28 -0
  92. data/lib/code/parser/list.rb +29 -0
  93. data/lib/code/parser/multiplication.rb +30 -0
  94. data/lib/code/parser/name.rb +89 -0
  95. data/lib/code/parser/negation.rb +19 -0
  96. data/lib/code/parser/not_keyword.rb +21 -0
  97. data/lib/code/parser/nothing.rb +17 -0
  98. data/lib/code/parser/number.rb +98 -0
  99. data/lib/code/parser/or_keyword.rb +29 -0
  100. data/lib/code/parser/or_operator.rb +28 -0
  101. data/lib/code/parser/power.rb +25 -0
  102. data/lib/code/parser/range.rb +25 -0
  103. data/lib/code/parser/rescue.rb +23 -0
  104. data/lib/code/parser/shift.rb +31 -0
  105. data/lib/code/parser/statement.rb +8 -0
  106. data/lib/code/parser/string.rb +72 -0
  107. data/lib/code/parser/ternary.rb +25 -0
  108. data/lib/code/parser/unary_minus.rb +13 -0
  109. data/lib/code/parser/while.rb +25 -0
  110. data/lib/code/parser.rb +4 -0
  111. data/lib/code-ruby.rb +11 -0
  112. data/lib/code.rb +29 -0
  113. data/lib/template/node/code_part.rb +13 -0
  114. data/lib/template/node/part.rb +19 -0
  115. data/lib/template/node/template.rb +15 -0
  116. data/lib/template/node/text_part.rb +13 -0
  117. data/lib/template/node.rb +4 -0
  118. data/lib/template/parser/template.rb +30 -0
  119. data/lib/template/parser.rb +4 -0
  120. data/lib/template/version.rb +3 -0
  121. data/lib/template-ruby.rb +11 -0
  122. data/lib/template.rb +34 -0
  123. data/spec/call_spec.rb +22 -0
  124. data/spec/code/error/type_error_spec.rb +65 -0
  125. data/spec/code/parser/boolean_spec.rb +18 -0
  126. data/spec/code/parser/call_spec.rb +66 -0
  127. data/spec/code/parser/dictionnary_spec.rb +46 -0
  128. data/spec/code/parser/function_spec.rb +32 -0
  129. data/spec/code/parser/list_spec.rb +29 -0
  130. data/spec/code/parser/name_spec.rb +15 -0
  131. data/spec/code/parser/nothing_spec.rb +19 -0
  132. data/spec/code/parser/number_spec.rb +117 -0
  133. data/spec/code/parser/string_spec.rb +30 -0
  134. data/spec/code_spec.rb +108 -0
  135. data/spec/function_spec.rb +26 -0
  136. data/spec/spec_helper.rb +3 -0
  137. data/spec/template/parser/template_spec.rb +19 -0
  138. data/spec/template_spec.rb +27 -0
  139. data/template-ruby.gemspec +24 -0
  140. 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,4 @@
1
+ class Template
2
+ class Parser
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "../template"
2
+
3
+ Template::Version = Gem::Version.new("0.2.4")
@@ -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