code-ruby 0.2.4
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.
- 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,22 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Boolean < Node
|
4
|
+
TRUE = "true"
|
5
|
+
FALSE = "false"
|
6
|
+
|
7
|
+
def initialize(boolean)
|
8
|
+
@boolean = boolean
|
9
|
+
end
|
10
|
+
|
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
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Call < Node
|
4
|
+
def initialize(call)
|
5
|
+
@left = ::Code::Node::Statement.new(call.fetch(:left))
|
6
|
+
|
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
|
20
|
+
end
|
21
|
+
|
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
|
39
|
+
|
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
|
50
|
+
end
|
51
|
+
end
|
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
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Code < Node
|
4
|
+
def initialize(statements)
|
5
|
+
statements = [] if statements.to_s.blank?
|
6
|
+
|
7
|
+
@statements =
|
8
|
+
statements.map { |statement| ::Code::Node::Statement.new(statement) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def evaluate(**args)
|
12
|
+
@statements.map { |statement| statement.evaluate(**args) }.last
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Dictionnary < Node
|
4
|
+
def initialize(key_values)
|
5
|
+
if key_values.blank?
|
6
|
+
@key_values = []
|
7
|
+
else
|
8
|
+
@key_values =
|
9
|
+
key_values.map do |key_value|
|
10
|
+
::Code::Node::DictionnaryKeyValue.new(key_value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(**args)
|
16
|
+
::Code::Object::Dictionnary.new(
|
17
|
+
@key_values.map { |key_value| key_value.evaluate(**args) }.to_h,
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class DictionnaryKeyValue < Node
|
4
|
+
def initialize(key_value)
|
5
|
+
@key = key_value.fetch(:key)
|
6
|
+
|
7
|
+
if @key.is_a?(Array)
|
8
|
+
@key = ::Code::Node::Code.new(@key)
|
9
|
+
elsif @key.key?(:name)
|
10
|
+
@key = ::Code::Node::String.new(@key[:name])
|
11
|
+
else
|
12
|
+
@key = ::Code::Node::Statement.new(@key)
|
13
|
+
end
|
14
|
+
|
15
|
+
@value = ::Code::Node::Code.new(key_value.fetch(:value))
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate(**args)
|
19
|
+
[@key.evaluate(**args), @value.evaluate(**args)]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Equal < Node
|
4
|
+
def initialize(equal)
|
5
|
+
@left = equal.fetch(:left).fetch(:name)
|
6
|
+
@operator = equal.fetch(:operator)
|
7
|
+
@right = ::Code::Node::Statement.new(equal.fetch(:right))
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(**args)
|
11
|
+
right = @right.evaluate(**args)
|
12
|
+
context = args.fetch(:context)
|
13
|
+
|
14
|
+
if operator
|
15
|
+
if context[left]
|
16
|
+
context[left] = simple_call(context[left], operator, right, **args)
|
17
|
+
else
|
18
|
+
raise ::Code::Error::UndefinedVariable.new("#{left} is undefined")
|
19
|
+
end
|
20
|
+
else
|
21
|
+
context[left] = right
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def operator
|
28
|
+
@operator.to_s[0...-1].to_sym.presence
|
29
|
+
end
|
30
|
+
|
31
|
+
def left
|
32
|
+
::Code::Object::String.new(@left.to_s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Function < Node
|
4
|
+
def initialize(function)
|
5
|
+
@body = ::Code::Node::Code.new(function.fetch(:body))
|
6
|
+
@arguments = function.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
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class FunctionArgument < Node
|
4
|
+
def initialize(argument)
|
5
|
+
if argument.key?(:regular)
|
6
|
+
@argument =
|
7
|
+
::Code::Node::RegularFunctionArgument.new(argument.fetch(:regular))
|
8
|
+
elsif argument.key?(:keyword)
|
9
|
+
@argument =
|
10
|
+
::Code::Node::KeywordFunctionArgument.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 splat?
|
21
|
+
@argument.splat?
|
22
|
+
end
|
23
|
+
|
24
|
+
def keyword_splat?
|
25
|
+
@argument.keyword_splat?
|
26
|
+
end
|
27
|
+
|
28
|
+
def name
|
29
|
+
@argument.name
|
30
|
+
end
|
31
|
+
|
32
|
+
def block?
|
33
|
+
@argument.block?
|
34
|
+
end
|
35
|
+
|
36
|
+
def regular?
|
37
|
+
@argument.is_a?(::Code::Node::RegularFunctionArgument)
|
38
|
+
end
|
39
|
+
|
40
|
+
def keyword?
|
41
|
+
@argument.is_a?(::Code::Node::KeywordFunctionArgument)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/code/node/if.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class If < Node
|
4
|
+
IF_KEYWORD = "if"
|
5
|
+
UNLESS_KEYWORD = "unless"
|
6
|
+
|
7
|
+
class Else
|
8
|
+
def initialize(else_parsed)
|
9
|
+
if else_parsed.key?(:operator)
|
10
|
+
@operator = else_parsed.fetch(:operator)
|
11
|
+
@statement =
|
12
|
+
::Code::Node::Statement.new(else_parsed.fetch(:statement))
|
13
|
+
end
|
14
|
+
|
15
|
+
@body = ::Code::Node::Code.new(else_parsed.fetch(:body))
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :operator, :body, :statement
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(if_parsed)
|
22
|
+
@if_operator = if_parsed.fetch(:if_operator)
|
23
|
+
@if_statement =
|
24
|
+
::Code::Node::Statement.new(if_parsed.fetch(:if_statement))
|
25
|
+
@if_body = ::Code::Node::Code.new(if_parsed.fetch(:if_body))
|
26
|
+
@elses = if_parsed.fetch(:elses, [])
|
27
|
+
@elses.map! { |else_parsed| ::Code::Node::If::Else.new(else_parsed) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def evaluate(**args)
|
31
|
+
if_object = @if_statement.evaluate(**args)
|
32
|
+
|
33
|
+
if @if_operator == IF_KEYWORD && if_object.truthy?
|
34
|
+
@if_body.evaluate(**args)
|
35
|
+
elsif @if_operator == UNLESS_KEYWORD && if_object.falsy?
|
36
|
+
@if_body.evaluate(**args)
|
37
|
+
else
|
38
|
+
@elses.each do |else_node|
|
39
|
+
if else_node.operator == IF_KEYWORD
|
40
|
+
else_object = else_node.statement.evaluate(**args)
|
41
|
+
return else_node.body.evaluate(**args) if else_object.truthy?
|
42
|
+
elsif else_node.operator == UNLESS_KEYWORD
|
43
|
+
else_object = else_node.statement.evaluate(**args)
|
44
|
+
return else_node.body.evaluate(**args) if else_object.falsy?
|
45
|
+
elsif else_node.operator.nil?
|
46
|
+
return else_node.body.evaluate(**args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
::Code::Object::Nothing.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class IfModifier < Node
|
4
|
+
IF_KEYWORD = :if
|
5
|
+
UNLESS_KEYWORD = :unless
|
6
|
+
WHILE_KEYWORD = :while
|
7
|
+
UNTIL_KEYWORD = :until
|
8
|
+
|
9
|
+
def initialize(if_modifier)
|
10
|
+
@left = ::Code::Node::Statement.new(if_modifier.fetch(:left))
|
11
|
+
@operator = if_modifier.fetch(:operator)
|
12
|
+
@right = ::Code::Node::Statement.new(if_modifier.fetch(:right))
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(**args)
|
16
|
+
if operator == IF_KEYWORD
|
17
|
+
right = @right.evaluate(**args)
|
18
|
+
|
19
|
+
right.truthy? ? @left.evaluate(**args) : ::Code::Object::Nothing.new
|
20
|
+
elsif operator == UNLESS_KEYWORD
|
21
|
+
right = @right.evaluate(**args)
|
22
|
+
|
23
|
+
right.truthy? ? ::Code::Object::Nothing.new : @left.evaluate(**args)
|
24
|
+
elsif operator == WHILE_KEYWORD
|
25
|
+
left = ::Code::Object::Nothing.new
|
26
|
+
|
27
|
+
left = @left.evaluate(**args) while @right.evaluate(**args).truthy?
|
28
|
+
|
29
|
+
left
|
30
|
+
elsif operator == UNTIL_KEYWORD
|
31
|
+
left = ::Code::Object::Nothing.new
|
32
|
+
|
33
|
+
left = @left.evaluate(**args) until @right.evaluate(**args).truthy?
|
34
|
+
|
35
|
+
left
|
36
|
+
else
|
37
|
+
raise NotImplementedError.new(operator.inspect)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def operator
|
44
|
+
@operator.to_sym
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class KeywordCallArgument < Node
|
4
|
+
def initialize(argument)
|
5
|
+
@name = argument.fetch(:name)
|
6
|
+
@value = ::Code::Node::Code.new(argument.fetch(:value))
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate(**args)
|
10
|
+
@value.evaluate(**args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
::Code::Object::String.new(@name.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def block?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def splat?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def keyword_splat?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class KeywordFunctionArgument < Node
|
4
|
+
def initialize(argument)
|
5
|
+
@name = argument.fetch(:name)
|
6
|
+
|
7
|
+
if argument.key?(:default)
|
8
|
+
@default = ::Code::Node::Code.new(argument.fetch(:default))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate(**args)
|
13
|
+
@default ? @default.evaluate(**args) : ::Code::Object::Nothing.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
::Code::Object::String.new(@name.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
def splat?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def keyword_splat?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def block?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class List < Node
|
4
|
+
def initialize(codes)
|
5
|
+
@codes =
|
6
|
+
codes
|
7
|
+
.map do |code|
|
8
|
+
code.fetch(:code).presence &&
|
9
|
+
::Code::Node::Code.new(code.fetch(:code))
|
10
|
+
end
|
11
|
+
.compact
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate(**args)
|
15
|
+
::Code::Object::List.new(@codes.map { |code| code.evaluate(**args) })
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Name < Node
|
4
|
+
def initialize(name)
|
5
|
+
@name = name
|
6
|
+
end
|
7
|
+
|
8
|
+
def evaluate(**args)
|
9
|
+
context = args.fetch(:context)
|
10
|
+
arguments = args.fetch(:arguments, [])
|
11
|
+
object = args.fetch(:object, nil)
|
12
|
+
io = args.fetch(:io)
|
13
|
+
|
14
|
+
if object
|
15
|
+
object.call(
|
16
|
+
context: context,
|
17
|
+
operator: name,
|
18
|
+
arguments: arguments,
|
19
|
+
io: io,
|
20
|
+
)
|
21
|
+
elsif context.key?(name)
|
22
|
+
object = context[name]
|
23
|
+
|
24
|
+
if object.is_a?(::Code::Object::Function)
|
25
|
+
object.call(
|
26
|
+
context: context,
|
27
|
+
operator: nil,
|
28
|
+
arguments: arguments,
|
29
|
+
io: io,
|
30
|
+
)
|
31
|
+
else
|
32
|
+
object
|
33
|
+
end
|
34
|
+
elsif name == "puts"
|
35
|
+
arguments.each { |argument| io.puts argument.value }
|
36
|
+
|
37
|
+
::Code::Object::Nothing.new
|
38
|
+
else
|
39
|
+
raise ::Code::Error::Undefined.new("#{name} undefined")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def name
|
46
|
+
::Code::Object::String.new(@name.to_s)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Negation < Node
|
4
|
+
EXCLAMATION_POINT = "!"
|
5
|
+
PLUS = "+"
|
6
|
+
|
7
|
+
def initialize(negation)
|
8
|
+
@operator = negation.fetch(:operator)
|
9
|
+
@statement = ::Code::Node::Statement.new(negation.fetch(:statement))
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate(**args)
|
13
|
+
object = @statement.evaluate(**args)
|
14
|
+
|
15
|
+
if operator == EXCLAMATION_POINT
|
16
|
+
if object.truthy?
|
17
|
+
::Code::Object::Boolean.new(false)
|
18
|
+
else
|
19
|
+
::Code::Object::Boolean.new(true)
|
20
|
+
end
|
21
|
+
elsif operator == PLUS
|
22
|
+
object
|
23
|
+
else
|
24
|
+
raise NotImplementedError.new(operator)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :operator
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class NotKeyword < Node
|
4
|
+
def initialize(not_keyword)
|
5
|
+
@statement = ::Code::Node::Statement.new(not_keyword)
|
6
|
+
end
|
7
|
+
|
8
|
+
def evaluate(**args)
|
9
|
+
::Code::Object::Boolean.new(!@statement.evaluate(**args).truthy?)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Code
|
2
|
+
class Node
|
3
|
+
class Number < Node
|
4
|
+
def initialize(number)
|
5
|
+
if number.key?(:base_2)
|
6
|
+
@number = ::Code::Node::Base2Number.new(number[:base_2])
|
7
|
+
elsif number.key?(:base_8)
|
8
|
+
@number = ::Code::Node::Base8Number.new(number[:base_8])
|
9
|
+
elsif number.key?(:base_10)
|
10
|
+
@number = ::Code::Node::Base10Number.new(number[:base_10])
|
11
|
+
elsif number.key?(:base_16)
|
12
|
+
@number = ::Code::Node::Base16Number.new(number[:base_16])
|
13
|
+
else
|
14
|
+
raise NotImplementedErorr.new(number.inspect)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate(**args)
|
19
|
+
@number.evaluate(**args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|