template-ruby 0.1.0 → 0.2.2

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