template-ruby 0.1.0 → 0.2.2

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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.gitignore +1 -0
  4. data/.prettierrc +3 -0
  5. data/CHANGELOG.md +22 -0
  6. data/Gemfile.lock +5 -5
  7. data/README.md +10 -0
  8. data/bin/template +39 -0
  9. data/docs/euler/1.template +14 -0
  10. data/docs/euler/2.template +16 -0
  11. data/docs/euler/3.template +16 -0
  12. data/docs/euler/4.template +11 -0
  13. data/docs/euler/5.template +14 -0
  14. data/docs/precedence.template +94 -0
  15. data/lib/code/error.rb +15 -0
  16. data/lib/code/node/base_10_decimal.rb +12 -7
  17. data/lib/code/node/base_10_integer.rb +13 -4
  18. data/lib/code/node/base_10_number.rb +3 -3
  19. data/lib/code/node/base_16_number.rb +2 -2
  20. data/lib/code/node/base_2_number.rb +2 -2
  21. data/lib/code/node/base_8_number.rb +2 -2
  22. data/lib/code/node/block.rb +17 -0
  23. data/lib/code/node/boolean.rb +13 -4
  24. data/lib/code/node/call.rb +41 -8
  25. data/lib/code/node/call_argument.rb +37 -0
  26. data/lib/code/node/chained_call.rb +38 -0
  27. data/lib/code/node/code.rb +4 -7
  28. data/lib/code/node/defined.rb +19 -0
  29. data/lib/code/node/dictionnary.rb +11 -7
  30. data/lib/code/node/dictionnary_key_value.rb +3 -3
  31. data/lib/code/node/equal.rb +36 -0
  32. data/lib/code/node/function.rb +17 -0
  33. data/lib/code/node/function_argument.rb +45 -0
  34. data/lib/code/node/group.rb +13 -0
  35. data/lib/code/node/if.rb +55 -0
  36. data/lib/code/node/if_modifier.rb +48 -0
  37. data/lib/code/node/keyword_call_argument.rb +30 -0
  38. data/lib/code/node/keyword_function_argument.rb +33 -0
  39. data/lib/code/node/list.rb +10 -4
  40. data/lib/code/node/name.rb +37 -4
  41. data/lib/code/node/negation.rb +33 -0
  42. data/lib/code/node/not_keyword.rb +13 -0
  43. data/lib/code/node/nothing.rb +2 -2
  44. data/lib/code/node/number.rb +3 -3
  45. data/lib/code/node/operation.rb +33 -0
  46. data/lib/code/node/or_keyword.rb +34 -0
  47. data/lib/code/node/power.rb +16 -0
  48. data/lib/code/node/range.rb +31 -0
  49. data/lib/code/node/regular_call_argument.rb +34 -0
  50. data/lib/code/node/regular_function_argument.rb +36 -0
  51. data/lib/code/node/rescue.rb +16 -0
  52. data/lib/code/node/statement.rb +53 -3
  53. data/lib/code/node/string.rb +2 -2
  54. data/lib/code/node/ternary.rb +26 -0
  55. data/lib/code/node/unary_minus.rb +22 -0
  56. data/lib/code/node/while.rb +42 -0
  57. data/lib/code/node.rb +10 -0
  58. data/lib/code/object/argument.rb +41 -0
  59. data/lib/code/object/boolean.rb +8 -9
  60. data/lib/code/object/decimal.rb +32 -9
  61. data/lib/code/object/dictionnary.rb +33 -10
  62. data/lib/code/object/function.rb +64 -0
  63. data/lib/code/object/integer.rb +94 -7
  64. data/lib/code/object/list.rb +190 -10
  65. data/lib/code/object/nothing.rb +10 -9
  66. data/lib/code/object/number.rb +6 -0
  67. data/lib/code/object/range.rb +158 -0
  68. data/lib/code/object/string.rb +37 -7
  69. data/lib/code/object.rb +121 -2
  70. data/lib/code/parser/addition.rb +29 -0
  71. data/lib/code/parser/and_operator.rb +28 -0
  72. data/lib/code/parser/bitwise_and.rb +28 -0
  73. data/lib/code/parser/bitwise_or.rb +29 -0
  74. data/lib/code/parser/call.rb +77 -3
  75. data/lib/code/parser/code.rb +2 -1
  76. data/lib/code/parser/defined.rb +20 -0
  77. data/lib/code/parser/equal.rb +42 -0
  78. data/lib/code/parser/equality.rb +36 -0
  79. data/lib/code/parser/function.rb +57 -0
  80. data/lib/code/parser/greater_than.rb +33 -0
  81. data/lib/code/parser/group.rb +17 -0
  82. data/lib/code/parser/if.rb +33 -0
  83. data/lib/code/parser/if_modifier.rb +28 -0
  84. data/lib/code/parser/multiplication.rb +30 -0
  85. data/lib/code/parser/name.rb +44 -4
  86. data/lib/code/parser/negation.rb +19 -0
  87. data/lib/code/parser/not_keyword.rb +21 -0
  88. data/lib/code/parser/nothing.rb +2 -2
  89. data/lib/code/parser/or_keyword.rb +29 -0
  90. data/lib/code/parser/or_operator.rb +28 -0
  91. data/lib/code/parser/power.rb +25 -0
  92. data/lib/code/parser/range.rb +25 -0
  93. data/lib/code/parser/rescue.rb +23 -0
  94. data/lib/code/parser/shift.rb +31 -0
  95. data/lib/code/parser/statement.rb +1 -4
  96. data/lib/code/parser/string.rb +7 -1
  97. data/lib/code/parser/ternary.rb +25 -0
  98. data/lib/code/parser/unary_minus.rb +13 -0
  99. data/lib/code/parser/while.rb +25 -0
  100. data/lib/code.rb +5 -7
  101. data/lib/template/node/code_part.rb +2 -2
  102. data/lib/template/node/part.rb +2 -2
  103. data/lib/template/node/template.rb +4 -2
  104. data/lib/template/node/text_part.rb +1 -1
  105. data/lib/template/parser/template.rb +6 -2
  106. data/lib/template-ruby.rb +4 -0
  107. data/lib/template.rb +9 -4
  108. data/spec/call_spec.rb +22 -0
  109. data/spec/code/error/type_error_spec.rb +65 -0
  110. data/spec/code/parser/boolean_spec.rb +1 -1
  111. data/spec/code/parser/call_spec.rb +38 -11
  112. data/spec/code/parser/dictionnary_spec.rb +11 -11
  113. data/spec/code/parser/function_spec.rb +32 -0
  114. data/spec/code/parser/list_spec.rb +5 -5
  115. data/spec/code/parser/nothing_spec.rb +1 -1
  116. data/spec/code/parser/number_spec.rb +35 -35
  117. data/spec/code/parser/string_spec.rb +3 -2
  118. data/spec/code_spec.rb +75 -3
  119. data/spec/function_spec.rb +26 -0
  120. data/spec/spec_helper.rb +2 -0
  121. data/spec/template/parser/template_spec.rb +1 -1
  122. data/spec/template_spec.rb +3 -6
  123. data/template-ruby.gemspec +6 -3
  124. metadata +76 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51afb6ed8605217e5960882e6b007cdd3b96dd8c3f72e0cb372e67b55d96e88b
4
- data.tar.gz: 18bfdbb2a0a22b736aa9282f2875021d4e5398f0d61c1c35f402c4a1a8e2d4dd
3
+ metadata.gz: 5e240c31089e37775df02ba1ea0646922413b26860318456d19ed5d2b38ed077
4
+ data.tar.gz: 25f45bb4ef78e1f62e63a960ce3fa46245b60dfab8fd96a273caf823ee2b016f
5
5
  SHA512:
6
- metadata.gz: 7cd8c95144f5a535b704f79cbb559d1868bfad164d8c08a261ae915afb94d3c06b02fae4eb2cc51000a49547a48a282cbf35043e7d769ff890338c78958f4b42
7
- data.tar.gz: 00cd29f362d8bb6b93ae782daaef8a87e4dcade2c299ce9504e2579faa5558005fa4305706006fa21b54b7c9b3d5b4ef74d561d7a28775454112c6181d4be00c
6
+ metadata.gz: a2870be4d39f138c269946c935985d2de54c4364307630011b8de8095d50dd7a72de3a71f401414c64e416832c2d4f6e9a8e92d7e6038855725e35c7e0cbbdd9
7
+ data.tar.gz: 77fd004c7464e2715726dfec039925e8c4653d6a78993cf6d1c6cecb717f08fcb5e04f9cebf7acf130bb3ed7fcac7891e65ef8fe0db37ab9c66724ef9b2cd923
data/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ indent_style = space
8
+ indent_size = 2
9
+ trim_trailing_whitespace = true
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  *.gem
2
+ examples.txt
data/.prettierrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "rubyPlugins": "plugin/trailing_comma"
3
+ }
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.2.2 / 2022-08-31
9
+
10
+ - Fix parsing error when the template is empty, e.g. ""
11
+
12
+ ## 0.2.1 / 2022-08-31
13
+
14
+ - Fix parsing error on empty code like `Hello {`
15
+
16
+ ## 0.2.0 / 2022-08-30
17
+
18
+ - Programming language capable of solving the first 5 Project Euler problems
19
+
20
+ ## 0.1.0 / 2022-07-28
21
+
22
+ - Initial version with interpolation
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- template-ruby (0.1.0)
4
+ template-ruby (0.2.2)
5
5
  activesupport (~> 7)
6
6
  parslet (~> 2)
7
- zeitwerk (~> 2)
7
+ zeitwerk (~> 2.6)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
@@ -21,7 +21,7 @@ GEM
21
21
  tilt
22
22
  i18n (1.12.0)
23
23
  concurrent-ruby (~> 1.0)
24
- minitest (5.16.2)
24
+ minitest (5.16.3)
25
25
  parslet (2.0.0)
26
26
  prettier (3.2.0)
27
27
  syntax_tree (>= 2.7.1)
@@ -42,9 +42,9 @@ GEM
42
42
  diff-lcs (>= 1.2.0, < 2.0)
43
43
  rspec-support (~> 3.11.0)
44
44
  rspec-support (3.11.0)
45
- syntax_tree (3.2.1)
45
+ syntax_tree (3.5.0)
46
46
  prettier_print
47
- syntax_tree-haml (1.3.0)
47
+ syntax_tree-haml (1.3.1)
48
48
  haml (>= 5.2)
49
49
  prettier_print
50
50
  syntax_tree (>= 2.0.1)
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # `template-ruby`
2
+
3
+ A templating programming language
4
+
5
+ Like `Hello {name}` with `{name: "Dorian"}` gives `Hello Dorian`.
6
+
7
+ ```ruby
8
+ > Template.render("Hello {name}", '{name: "Dorian"}')
9
+ => "Hello Dorian"
10
+ ```
data/bin/template ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require_relative "../lib/template-ruby"
5
+
6
+ options = {}
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
10
+
11
+ opts.on("-i INPUT", "--input=INPUT", "Input in the template language") do |input|
12
+ if File.exists?(input)
13
+ input = File.read(input)
14
+ end
15
+
16
+ options[:input] = input
17
+ end
18
+
19
+ opts.on("-c CONTEXT", "--context=CONTEXT", "Context in the code language") do |context|
20
+ if File.exists?(context)
21
+ context = File.read(context)
22
+ end
23
+
24
+ options[:context] = context
25
+ end
26
+
27
+ opts.on("-p", "--parse", "Get parser results for input") do |parse|
28
+ options[:parse] = parse
29
+ end
30
+ end.parse!
31
+
32
+ input = options.fetch(:input, "")
33
+ context = options.fetch(:context, "")
34
+
35
+ if options[:parse]
36
+ pp ::Template::Parser::Template.new.parse(input)
37
+ else
38
+ Template.render(input, context, io: $stdout)
39
+ end
@@ -0,0 +1,14 @@
1
+ {
2
+ sum = 0
3
+
4
+ i = 1
5
+
6
+ while i < 1000
7
+ if i % 3 == 0 or i % 5 == 0
8
+ sum += i
9
+ end
10
+
11
+ i += 1
12
+ end
13
+
14
+ sum
@@ -0,0 +1,16 @@
1
+ {
2
+ a = 1
3
+ b = 2
4
+ sum = 2
5
+
6
+ while b < 4000000
7
+ c = a + b
8
+ a = b
9
+ b = c
10
+
11
+ if b % 2 == 0
12
+ sum += b
13
+ end
14
+ end
15
+
16
+ sum
@@ -0,0 +1,16 @@
1
+ {
2
+ n = 600851475143
3
+ max = 0
4
+
5
+ i = 2
6
+
7
+ until n == 1
8
+ while n % i == 0
9
+ max = i
10
+ n = n / i
11
+ end
12
+
13
+ i += 1
14
+ end
15
+
16
+ max
@@ -0,0 +1,11 @@
1
+ {
2
+
3
+ min = 1
4
+ max = 999
5
+
6
+
7
+ (min..max).map do |i|
8
+ ((min..max).to_list.reverse.detect do |j|
9
+ (i * j).to_string.reverse == (i * j).to_string
10
+ end || 0) * i
11
+ end.max
@@ -0,0 +1,14 @@
1
+ {
2
+ min = 1
3
+ max = 20
4
+
5
+ step = [2, 3, 5, 7, 11, 13, 17].reduce do |acc, el|
6
+ acc * el
7
+ end
8
+
9
+ (step..(step * step)).step(step).detect do |i|
10
+ (min..max).all? do |j|
11
+ i % j == 0
12
+ end
13
+ end
14
+ }
@@ -0,0 +1,94 @@
1
+ - Code
2
+ - {a = "Go" a += "od" a}
3
+ - Statement
4
+ - {a = "Good"}
5
+ - {
6
+ a = 0
7
+ while a < 10
8
+ a += 1
9
+ "Good"
10
+ end
11
+ }
12
+ - While
13
+ - {
14
+ a = 0
15
+ until a > 10
16
+ a += 1
17
+ "Good"
18
+ end
19
+ }
20
+ - {
21
+ a = 0
22
+ while
23
+ if a > 10
24
+ false
25
+ else
26
+ true
27
+ end
28
+ a += 1
29
+ "Good"
30
+ end
31
+ }
32
+ - If
33
+ - {if true "Good" end}
34
+ - {if true if false "Bad" else "Good" end}
35
+ - IfModifier
36
+ - {"Good" if true}
37
+ - OrKeyword
38
+ - {false or "Good"}
39
+ - NotKeyword
40
+ - {not false and "Good"}
41
+ - Defined
42
+ - {a = 1 "Good" if defined?(a)}
43
+ - Equal
44
+ - {a = "Good"}
45
+ - Rescue
46
+ - {0 > "String" rescue "Good"}
47
+ - Ternary
48
+ - {nothing ? "Bad" : "Good"}
49
+ - Range
50
+ - {("Good".."Bad").first}
51
+ - OrOperator
52
+ - {false || "Good"}
53
+ - AndOperator
54
+ - {"Bad" && "Good"}
55
+ - Equality
56
+ - {1 == 1 ? "Good"}
57
+ - GreaterThan
58
+ - {2 > 1 ? "Good"}
59
+ - BitwiseOr
60
+ - {2 | 1 == 3 ? "Good"}
61
+ - BitwiseAnd
62
+ - {2 & 1 == 0 ? "Good"}
63
+ - Shift
64
+ - {1 << 1 == 2 ? "Good"}
65
+ - Addition
66
+ - {1 + 1 == 2 ? "Good"}
67
+ - Multiplication
68
+ - {1 * 2 == 2 ? "Good"}
69
+ - UnaryMinus
70
+ - {-1 == 1 - 2 ? "Good"}
71
+ - Power
72
+ - {2 ** 2 == 4 ? "Good"}
73
+ - Negation
74
+ - {!false ? "Good"}
75
+ - Function
76
+ - {good = () => { "Good" } good}
77
+ - Call
78
+ - {["Good"].first}
79
+ - Dictionnary
80
+ - {{a: "Good"}.a}
81
+ - List
82
+ - {["Good", "Bad"].first}
83
+ - String
84
+ - {"Good"}
85
+ - Number
86
+ - {1 == 1 ? "Good"}
87
+ - Boolean
88
+ - {true ? "Good"}
89
+ - Nothing
90
+ - {nothing ? "Bad" : "Good"}
91
+ - Group
92
+ - {(a = 0 a += 1 a) == 1 ? "Good"}
93
+ - Name
94
+ - {a = "Good" a}
data/lib/code/error.rb ADDED
@@ -0,0 +1,15 @@
1
+ class Code
2
+ class Error < StandardError
3
+ class TypeError < ::Code::Error
4
+ end
5
+
6
+ class Undefined < ::Code::Error
7
+ end
8
+
9
+ class UndefinedVariable < ::Code::Error
10
+ end
11
+
12
+ class ArgumentError < ::Code::Error
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  class Code
2
2
  class Node
3
- class Base10Decimal
3
+ class Base10Decimal < Node
4
4
  def initialize(number)
5
5
  @sign = number[:sign]
6
6
  @whole = number.fetch(:whole)
@@ -11,12 +11,17 @@ class Code
11
11
  end
12
12
  end
13
13
 
14
- def evaluate(context)
15
- @exponent = @exponent.evaluate(context) if @exponent
16
- ::Code::Object::Decimal.new(
17
- "#{sign}#{whole}.#{decimal}",
18
- exponent: @exponent
19
- )
14
+ def evaluate(**args)
15
+ if @exponent
16
+ exponent = @exponent.evaluate(**args)
17
+
18
+ ::Code::Object::Decimal.new(
19
+ "#{sign}#{whole}.#{decimal}",
20
+ exponent: exponent,
21
+ )
22
+ else
23
+ ::Code::Object::Decimal.new("#{sign}#{whole}.#{decimal}")
24
+ end
20
25
  end
21
26
 
22
27
  private
@@ -1,6 +1,6 @@
1
1
  class Code
2
2
  class Node
3
- class Base10Integer
3
+ class Base10Integer < Node
4
4
  def initialize(number)
5
5
  @sign = number[:sign]
6
6
  @whole = number.fetch(:whole)
@@ -10,9 +10,18 @@ class Code
10
10
  end
11
11
  end
12
12
 
13
- def evaluate(context)
14
- @exponent = @exponent.evaluate(context) if @exponent
15
- ::Code::Object::Integer.new("#{sign}#{whole}", exponent: @exponent)
13
+ def evaluate(**args)
14
+ if @exponent
15
+ exponent = @exponent.evaluate(**args)
16
+
17
+ if exponent.is_a?(::Code::Object::Decimal)
18
+ ::Code::Object::Decimal.new("#{sign}#{whole}", exponent: exponent)
19
+ else
20
+ ::Code::Object::Integer.new("#{sign}#{whole}", exponent: exponent)
21
+ end
22
+ else
23
+ ::Code::Object::Integer.new("#{sign}#{whole}")
24
+ end
16
25
  end
17
26
 
18
27
  private
@@ -1,6 +1,6 @@
1
1
  class Code
2
2
  class Node
3
- class Base10Number
3
+ class Base10Number < Node
4
4
  def initialize(number)
5
5
  if number.key?(:integer)
6
6
  @number = ::Code::Node::Base10Integer.new(number[:integer])
@@ -11,8 +11,8 @@ class Code
11
11
  end
12
12
  end
13
13
 
14
- def evaluate(context)
15
- @number.evaluate(context)
14
+ def evaluate(**args)
15
+ @number.evaluate(**args)
16
16
  end
17
17
  end
18
18
  end
@@ -1,11 +1,11 @@
1
1
  class Code
2
2
  class Node
3
- class Base16Number
3
+ class Base16Number < Node
4
4
  def initialize(number)
5
5
  @number = number
6
6
  end
7
7
 
8
- def evaluate(context)
8
+ def evaluate(**args)
9
9
  ::Code::Object::Integer.new(number)
10
10
  end
11
11
 
@@ -1,11 +1,11 @@
1
1
  class Code
2
2
  class Node
3
- class Base2Number
3
+ class Base2Number < Node
4
4
  def initialize(number)
5
5
  @number = number
6
6
  end
7
7
 
8
- def evaluate(context)
8
+ def evaluate(**args)
9
9
  ::Code::Object::Integer.new(number)
10
10
  end
11
11
 
@@ -1,11 +1,11 @@
1
1
  class Code
2
2
  class Node
3
- class Base8Number
3
+ class Base8Number < Node
4
4
  def initialize(number)
5
5
  @number = number
6
6
  end
7
7
 
8
- def evaluate(context)
8
+ def evaluate(**args)
9
9
  ::Code::Object::Integer.new(number)
10
10
  end
11
11
 
@@ -0,0 +1,17 @@
1
+ class Code
2
+ class Node
3
+ class Block < Node
4
+ def initialize(block)
5
+ @body = ::Code::Node::Code.new(block.fetch(:body))
6
+ @arguments = block.fetch(:arguments, [])
7
+ @arguments.map! do |argument|
8
+ ::Code::Node::FunctionArgument.new(argument)
9
+ end
10
+ end
11
+
12
+ def evaluate(**args)
13
+ ::Code::Object::Function.new(arguments: @arguments, body: @body)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,12 +1,21 @@
1
1
  class Code
2
2
  class Node
3
- class Boolean
3
+ class Boolean < Node
4
+ TRUE = "true"
5
+ FALSE = "false"
6
+
4
7
  def initialize(boolean)
5
- @boolean = boolean == "true" ? true : false
8
+ @boolean = boolean
6
9
  end
7
10
 
8
- def evaluate(_context)
9
- ::Code::Object::Boolean.new(@boolean)
11
+ def evaluate(**args)
12
+ if @boolean == TRUE
13
+ ::Code::Object::Boolean.new(true)
14
+ elsif @boolean == FALSE
15
+ ::Code::Object::Boolean.new(false)
16
+ else
17
+ raise NotImplementedError, @boolean.inspect
18
+ end
10
19
  end
11
20
  end
12
21
  end
@@ -1,19 +1,52 @@
1
1
  class Code
2
2
  class Node
3
- class Call
3
+ class Call < Node
4
4
  def initialize(call)
5
5
  @left = ::Code::Node::Statement.new(call.fetch(:left))
6
- @right = ::Code::Node::Statement.new(call.fetch(:right))
7
- end
8
6
 
9
- def evaluate(context)
10
- context = left.evaluate(context)
11
- right.evaluate(context)
7
+ @arguments = call.fetch(:arguments, [])
8
+ @arguments.map! { |argument| ::Code::Node::CallArgument.new(argument) }
9
+
10
+ if call.key?(:right)
11
+ @right =
12
+ call
13
+ .fetch(:right)
14
+ .map { |right| ::Code::Node::ChainedCall.new(right) }
15
+ end
16
+
17
+ if call.key?(:block)
18
+ @block = ::Code::Node::Block.new(call.fetch(:block))
19
+ end
12
20
  end
13
21
 
14
- private
22
+ def evaluate(**args)
23
+ if @right
24
+ left = @left.evaluate(**args)
25
+ @right.reduce(left) do |acc, element|
26
+ element.evaluate(**args.merge(object: acc))
27
+ end
28
+ else
29
+ arguments =
30
+ @arguments.map do |argument|
31
+ ::Code::Object::Argument.new(
32
+ argument.evaluate(**args),
33
+ name: argument.name,
34
+ splat: argument.splat?,
35
+ keyword_splat: argument.keyword_splat?,
36
+ block: argument.block?,
37
+ )
38
+ end
15
39
 
16
- attr_reader :left, :right
40
+ if @block
41
+ arguments << ::Code::Object::Argument.new(
42
+ @block.evaluate(**args),
43
+ block: true,
44
+ )
45
+ end
46
+
47
+ @left.evaluate(**args.merge(arguments: arguments))
48
+ end
49
+ end
17
50
  end
18
51
  end
19
52
  end
@@ -0,0 +1,37 @@
1
+ class Code
2
+ class Node
3
+ class CallArgument < Node
4
+ def initialize(argument)
5
+ if argument.key?(:regular)
6
+ @argument =
7
+ ::Code::Node::RegularCallArgument.new(argument.fetch(:regular))
8
+ elsif argument.key?(:keyword)
9
+ @argument =
10
+ ::Code::Node::KeywordCallArgument.new(argument.fetch(:keyword))
11
+ else
12
+ raise NotImplementedError.new(argument.inspect)
13
+ end
14
+ end
15
+
16
+ def evaluate(**args)
17
+ @argument.evaluate(**args)
18
+ end
19
+
20
+ def name
21
+ @argument.name
22
+ end
23
+
24
+ def block?
25
+ @argument.block?
26
+ end
27
+
28
+ def splat?
29
+ @argument.splat?
30
+ end
31
+
32
+ def keyword_splat?
33
+ @argument.keyword_splat?
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ class Code
2
+ class Node
3
+ class ChainedCall < Node
4
+ def initialize(chained_call)
5
+ @name = ::Code::Node::Name.new(chained_call.fetch(:name))
6
+
7
+ @arguments = chained_call.fetch(:arguments, [])
8
+ @arguments.map! { |argument| ::Code::Node::CallArgument.new(argument) }
9
+
10
+ if chained_call.key?(:block)
11
+ @block = ::Code::Node::Block.new(chained_call.fetch(:block))
12
+ end
13
+ end
14
+
15
+ def evaluate(**args)
16
+ arguments =
17
+ @arguments.map do |argument|
18
+ ::Code::Object::Argument.new(
19
+ argument.evaluate(**args.merge(object: nil)),
20
+ name: argument.name,
21
+ splat: argument.splat?,
22
+ keyword_splat: argument.keyword_splat?,
23
+ block: argument.block?,
24
+ )
25
+ end
26
+
27
+ if @block
28
+ arguments << ::Code::Object::Argument.new(
29
+ @block.evaluate(**args.merge(object: nil)),
30
+ block: true,
31
+ )
32
+ end
33
+
34
+ @name.evaluate(**args.merge(arguments: arguments))
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,18 +1,15 @@
1
- require "active_support"
2
- require "active_support/core_ext/object/blank"
3
-
4
1
  class Code
5
2
  class Node
6
- class Code
3
+ class Code < Node
7
4
  def initialize(statements)
8
- statements = [] if statements.blank?
5
+ statements = [] if statements.to_s.blank?
9
6
 
10
7
  @statements =
11
8
  statements.map { |statement| ::Code::Node::Statement.new(statement) }
12
9
  end
13
10
 
14
- def evaluate(context)
15
- @statements.map { |statement| statement.evaluate(context) }.last
11
+ def evaluate(**args)
12
+ @statements.map { |statement| statement.evaluate(**args) }.last
16
13
  end
17
14
  end
18
15
  end
@@ -0,0 +1,19 @@
1
+ class Code
2
+ class Node
3
+ class Defined < Node
4
+ def initialize(defined)
5
+ @name = defined.fetch(:name)
6
+ end
7
+
8
+ def evaluate(**args)
9
+ ::Code::Object::Boolean.new(args.fetch(:context).key?(name))
10
+ end
11
+
12
+ private
13
+
14
+ def name
15
+ ::Code::Object::String.new(@name.to_s)
16
+ end
17
+ end
18
+ end
19
+ end