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,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
|