code-ruby 0.3.1 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dfeedb1e668e150c3039e1cee85eee9072ea16d0f619f45e71263c8c94ae707
4
- data.tar.gz: 1c2fb6de918462b02df14191ab14f7d27a92f0bea9113a54fa8344e7b8bab48e
3
+ metadata.gz: f269ce2ed25933bd413596e077a7f37fdd8b2914d4f2a40c49ae040a3d80eaa2
4
+ data.tar.gz: ca1870d839bd4ad4d46546b7320c39469fbe8f37425fb3ba0457f9584a6e4907
5
5
  SHA512:
6
- metadata.gz: c810a7b5a9ca1cbfc9c710e1ec615dd043dccf047fd220f35b71cf8e7fa2feab9cbf3fde9b1f31b57c690e25f5be1e71f4881a0efced0baea0b9f51e4a9abd78
7
- data.tar.gz: 45571c9a8247d75f3c513e52dfebc4ed3e3fe527996c5a1dc8717fb740c6fa4736c6fb26da93c57a73558318f45a4bf5dc3d2ccf38b460d570b58d1171e8a776
6
+ metadata.gz: e00e5e53b5bd74c402f7e6ea3b32d05ecf06faab892a52cf6b28e4c7c926821f33e7e7fea098b0d7350f58e20c548797bd297c5b6ff2743bbc04a7f8778b92a7
7
+ data.tar.gz: 4120703ce09203e412baa280f2dae7ea19d51beec10c08f393d8d362e7a294a3a9e21c6ed125768d88fc619f0908d252ce3bd67e2f5fe067e0c320b08a764659
data/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.4.0 / 2022-10-21
9
+
10
+ - Rewrite of how functions are called internally (no more `public_send`)
11
+ - Call ruby functions from template/code
12
+
8
13
  ## 0.3.1 / 2022-10-13
9
14
 
10
15
  - Fix Zeitwerk auto-loading issue
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec name: "template-ruby"
4
4
  gemspec name: "code-ruby"
5
+
6
+ gem "prettier"
7
+ gem "rspec"
data/Gemfile.lock CHANGED
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (0.2.4)
4
+ code-ruby (0.3.1)
5
5
  activesupport (~> 7)
6
6
  parslet (~> 2)
7
7
  zeitwerk (~> 2.6)
8
- template-ruby (0.2.4)
8
+ template-ruby (0.3.1)
9
9
  activesupport (~> 7)
10
10
  parslet (~> 2)
11
11
  zeitwerk (~> 2.6)
@@ -13,7 +13,7 @@ PATH
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (7.0.3.1)
16
+ activesupport (7.0.4)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 1.6, < 2)
19
19
  minitest (>= 5.1)
@@ -39,13 +39,13 @@ GEM
39
39
  rspec-mocks (~> 3.11.0)
40
40
  rspec-core (3.11.0)
41
41
  rspec-support (~> 3.11.0)
42
- rspec-expectations (3.11.0)
42
+ rspec-expectations (3.11.1)
43
43
  diff-lcs (>= 1.2.0, < 2.0)
44
44
  rspec-support (~> 3.11.0)
45
45
  rspec-mocks (3.11.1)
46
46
  diff-lcs (>= 1.2.0, < 2.0)
47
47
  rspec-support (~> 3.11.0)
48
- rspec-support (3.11.0)
48
+ rspec-support (3.11.1)
49
49
  syntax_tree (3.5.0)
50
50
  prettier_print
51
51
  syntax_tree-haml (1.3.1)
@@ -60,15 +60,15 @@ GEM
60
60
  tilt (2.0.11)
61
61
  tzinfo (2.0.5)
62
62
  concurrent-ruby (~> 1.0)
63
- zeitwerk (2.6.0)
63
+ zeitwerk (2.6.1)
64
64
 
65
65
  PLATFORMS
66
66
  arm64-darwin-21
67
67
 
68
68
  DEPENDENCIES
69
69
  code-ruby!
70
- prettier (~> 3)
71
- rspec (~> 3)
70
+ prettier
71
+ rspec
72
72
  template-ruby!
73
73
 
74
74
  BUNDLED WITH
data/code-ruby.gemspec CHANGED
@@ -16,7 +16,4 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency "activesupport", "~> 7"
17
17
  s.add_dependency "parslet", "~> 2"
18
18
  s.add_dependency "zeitwerk", "~> 2.6"
19
-
20
- s.add_development_dependency "prettier", "~> 3"
21
- s.add_development_dependency "rspec", "~> 3"
22
19
  end
data/lib/code/error.rb CHANGED
@@ -11,5 +11,8 @@ class Code
11
11
 
12
12
  class ArgumentError < ::Code::Error
13
13
  end
14
+
15
+ class IncompatibleContext < ::Code::Error
16
+ end
14
17
  end
15
18
  end
@@ -22,6 +22,7 @@ class Code
22
22
  def evaluate(**args)
23
23
  if @right
24
24
  left = @left.evaluate(**args)
25
+
25
26
  @right.reduce(left) do |acc, element|
26
27
  element.evaluate(**args.merge(object: acc))
27
28
  end
@@ -16,7 +16,9 @@ class Code
16
16
  arguments =
17
17
  @arguments.map do |argument|
18
18
  ::Code::Object::Argument.new(
19
- argument.evaluate(**args.merge(object: nil)),
19
+ argument.evaluate(
20
+ **args.merge(object: ::Code::Object::Global.new),
21
+ ),
20
22
  name: argument.name,
21
23
  splat: argument.splat?,
22
24
  keyword_splat: argument.keyword_splat?,
@@ -26,7 +28,7 @@ class Code
26
28
 
27
29
  if @block
28
30
  arguments << ::Code::Object::Argument.new(
29
- @block.evaluate(**args.merge(object: nil)),
31
+ @block.evaluate(**args.merge(object: ::Code::Object::Global.new)),
30
32
  block: true,
31
33
  )
32
34
  end
@@ -5,9 +5,8 @@ class Code
5
5
  if codes.to_s.blank?
6
6
  @codes = []
7
7
  else
8
- @codes = codes.map do |code|
9
- ::Code::Node::Code.new(code.fetch(:code))
10
- end
8
+ @codes =
9
+ codes.map { |code| ::Code::Node::Code.new(code.fetch(:code)) }
11
10
  end
12
11
  end
13
12
 
@@ -8,41 +8,14 @@ class Code
8
8
  def evaluate(**args)
9
9
  context = args.fetch(:context)
10
10
  arguments = args.fetch(:arguments, [])
11
- object = args.fetch(:object, nil)
12
- io = args.fetch(:io)
11
+ object = args.fetch(:object)
12
+ globals = args.multi_fetch(*::Code::GLOBALS)
13
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
- ::Code::Object::Nothing.new
37
- elsif name == "print"
38
- arguments.each { |argument| io.print argument.value }
39
- ::Code::Object::Nothing.new
40
- elsif name == "context"
41
- return ::Code::Object::Nothing.new if arguments.size != 1
42
- context[arguments.first&.value] || ::Code::Object::Nothing.new
43
- else
44
- raise ::Code::Error::Undefined.new("#{name} undefined")
45
- end
14
+ object.call(
15
+ operator: name,
16
+ arguments: arguments,
17
+ **globals
18
+ )
46
19
  end
47
20
 
48
21
  private
@@ -74,7 +74,7 @@ class Code
74
74
  end
75
75
 
76
76
  def evaluate(**args)
77
- @statement.evaluate(**args)
77
+ @statement.evaluate(**args.merge(object: ::Code::Object::Global.new))
78
78
  end
79
79
  end
80
80
  end
@@ -5,16 +5,21 @@ class Code
5
5
  if string.to_s.blank?
6
6
  @string = []
7
7
  elsif string.is_a?(Array)
8
- @string = string.map do |component|
9
- ::Code::Node::StringComponent.new(component)
10
- end
8
+ @string =
9
+ string.map do |component|
10
+ ::Code::Node::StringComponent.new(component)
11
+ end
11
12
  else
12
13
  @string = [::Code::Node::StringCharacters.new(string)]
13
14
  end
14
15
  end
15
16
 
16
17
  def evaluate(**args)
17
- string = @string.map { |component| component.evaluate(**args) }.map(&:to_s).join
18
+ string =
19
+ @string
20
+ .map { |component| component.evaluate(**args) }
21
+ .map(&:to_s)
22
+ .join
18
23
  ::Code::Object::String.new(string)
19
24
  end
20
25
  end
@@ -3,13 +3,13 @@ class Code
3
3
  class StringComponent < Node
4
4
  def initialize(component)
5
5
  if component.key?(:characters)
6
- @component = ::Code::Node::StringCharacters.new(
7
- component.fetch(:characters)
8
- )
6
+ @component =
7
+ ::Code::Node::StringCharacters.new(component.fetch(:characters))
9
8
  elsif component.key?(:interpolation)
10
- @component = ::Code::Node::StringInterpolation.new(
11
- component.fetch(:interpolation)
12
- )
9
+ @component =
10
+ ::Code::Node::StringInterpolation.new(
11
+ component.fetch(:interpolation),
12
+ )
13
13
  else
14
14
  raise NotImplementedError.new(component.inspect)
15
15
  end
data/lib/code/node.rb CHANGED
@@ -3,11 +3,11 @@ class Code
3
3
  private
4
4
 
5
5
  def simple_call(object, operator = nil, value = nil, **args)
6
+ args = args.multi_fetch(*::Code::GLOBALS)
6
7
  object.call(
7
8
  operator: operator && ::Code::Object::String.new(operator.to_s),
8
9
  arguments: [value && ::Code::Object::Argument.new(value)].compact,
9
- context: args.fetch(:context),
10
- io: args.fetch(:io),
10
+ **args
11
11
  )
12
12
  end
13
13
  end
@@ -1,6 +1,6 @@
1
1
  class Code
2
2
  class Object
3
- class Argument < ::Code::Object
3
+ class Argument
4
4
  attr_reader :value, :name, :splat, :keyword_splat, :block
5
5
 
6
6
  def initialize(
@@ -19,14 +19,51 @@ class Code
19
19
  operator = args.fetch(:operator, nil)
20
20
  arguments = args.fetch(:arguments, [])
21
21
 
22
- if %w[% - / * **].detect { |o| operator == o }
23
- number_operation(operator.to_sym, arguments)
24
- elsif %w[< <= > >=].detect { |o| operator == o }
25
- comparaison(operator.to_sym, arguments)
26
- elsif %w[<< >> & | ^].detect { |o| operator == o }
27
- integer_operation(operator.to_sym, arguments)
22
+ if operator == "%"
23
+ sig(arguments, ::Code::Object::Number)
24
+ modulo(arguments.first.value)
28
25
  elsif operator == "+"
29
- plus(arguments)
26
+ sig(arguments, ::Code::Object)
27
+ plus(arguments.first.value)
28
+ elsif operator == "-"
29
+ sig(arguments, ::Code::Object::Number)
30
+ minus(arguments.first.value)
31
+ elsif operator == "/"
32
+ sig(arguments, ::Code::Object::Number)
33
+ division(arguments.first.value)
34
+ elsif operator == "*"
35
+ sig(arguments, ::Code::Object::Number)
36
+ multiplication(arguments.first.value)
37
+ elsif operator == "**"
38
+ sig(arguments, ::Code::Object::Number)
39
+ power(arguments.first.value)
40
+ elsif operator == "<"
41
+ sig(arguments, ::Code::Object::Number)
42
+ inferior(arguments.first.value)
43
+ elsif operator == "<="
44
+ sig(arguments, ::Code::Object::Number)
45
+ inferior_or_equal(arguments.first.value)
46
+ elsif operator == ">"
47
+ sig(arguments, ::Code::Object::Number)
48
+ superior(arguments.first.value)
49
+ elsif operator == ">="
50
+ sig(arguments, ::Code::Object::Number)
51
+ superior_or_equal(arguments.first.value)
52
+ elsif operator == "<<"
53
+ sig(arguments, ::Code::Object::Number)
54
+ left_shift(arguments.first.value)
55
+ elsif operator == ">>"
56
+ sig(arguments, ::Code::Object::Number)
57
+ right_shift(arguments.first.value)
58
+ elsif operator == "&"
59
+ sig(arguments, ::Code::Object::Number)
60
+ bitwise_and(arguments.first.value)
61
+ elsif operator == "|"
62
+ sig(arguments, ::Code::Object::Number)
63
+ bitwise_or(arguments.first.value)
64
+ elsif operator == "^"
65
+ sig(arguments, ::Code::Object::Number)
66
+ bitwise_xor(arguments.first.value)
30
67
  else
31
68
  super
32
69
  end
@@ -42,34 +79,69 @@ class Code
42
79
 
43
80
  private
44
81
 
45
- def number_operation(operator, arguments)
46
- sig(arguments, ::Code::Object::Number)
47
- other = arguments.first.value
48
- ::Code::Object::Decimal.new(raw.public_send(operator, other.raw))
82
+ def modulo(other)
83
+ ::Code::Object::Decimal.new(raw % other.raw)
49
84
  end
50
85
 
51
- def integer_operation(operator, arguments)
52
- sig(arguments, ::Code::Object::Number)
53
- other = arguments.first.value
54
- ::Code::Object::Integer.new(raw.to_i.public_send(operator, other.raw.to_i))
55
- end
56
-
57
- def comparaison(operator, arguments)
58
- sig(arguments, ::Code::Object::Number)
59
- other = arguments.first.value
60
- ::Code::Object::Boolean.new(raw.public_send(operator, other.raw))
61
- end
62
-
63
- def plus(arguments)
64
- sig(arguments, ::Code::Object)
65
- other = arguments.first.value
66
-
86
+ def plus(other)
67
87
  if other.is_a?(::Code::Object::Number)
68
88
  ::Code::Object::Decimal.new(raw + other.raw)
69
89
  else
70
90
  ::Code::Object::String.new(to_s + other.to_s)
71
91
  end
72
92
  end
93
+
94
+ def minus(other)
95
+ ::Code::Object::Decimal.new(raw - other.raw)
96
+ end
97
+
98
+ def division(other)
99
+ ::Code::Object::Decimal.new(raw / other.raw)
100
+ end
101
+
102
+ def multiplication(other)
103
+ ::Code::Object::Decimal.new(raw * other.raw)
104
+ end
105
+
106
+ def power(other)
107
+ ::Code::Object::Decimal.new(raw**other.raw)
108
+ end
109
+
110
+ def inferior(other)
111
+ ::Code::Object::Boolean.new(raw < other.raw)
112
+ end
113
+
114
+ def inferior_or_equal(other)
115
+ ::Code::Object::Boolean.new(raw <= other.raw)
116
+ end
117
+
118
+ def superior(other)
119
+ ::Code::Object::Boolean.new(raw > other.raw)
120
+ end
121
+
122
+ def superior_or_equal(other)
123
+ ::Code::Object::Boolean.new(raw >= other.raw)
124
+ end
125
+
126
+ def left_shift(other)
127
+ ::Code::Object::Integer.new(raw.to_i << other.raw.to_i)
128
+ end
129
+
130
+ def right_shift(other)
131
+ ::Code::Object::Integer.new(raw.to_i >> other.raw.to_i)
132
+ end
133
+
134
+ def bitwise_and(other)
135
+ ::Code::Object::Integer.new(raw.to_i & other.raw.to_i)
136
+ end
137
+
138
+ def bitwise_or(other)
139
+ ::Code::Object::Integer.new(raw.to_i | other.raw.to_i)
140
+ end
141
+
142
+ def bitwise_xor(other)
143
+ ::Code::Object::Integer.new(raw.to_i ^ other.raw.to_i)
144
+ end
73
145
  end
74
146
  end
75
147
  end
@@ -10,20 +10,35 @@ class Code
10
10
  def call(**args)
11
11
  operator = args.fetch(:operator, nil)
12
12
  arguments = args.fetch(:arguments, [])
13
- context = args.fetch(:context)
14
- io = args.fetch(:io)
13
+ globals = args.multi_fetch(*::Code::GLOBALS)
15
14
 
16
15
  if operator == "values"
17
- values(arguments)
16
+ sig(arguments)
17
+ values
18
+ elsif operator == "keys"
19
+ sig(arguments)
20
+ keys
18
21
  elsif operator == "each"
19
- each(arguments, context: context, io: io)
22
+ sig(arguments, ::Code::Object::Function)
23
+ each(arguments.first.value, **globals)
20
24
  elsif key?(operator)
21
- fetch(operator)
25
+ result = fetch(operator)
26
+
27
+ if result.is_a?(::Code::Object::Function)
28
+ result.call(**args.merge(operator: nil))
29
+ else
30
+ sig(arguments)
31
+ result
32
+ end
22
33
  else
23
34
  super
24
35
  end
25
36
  end
26
37
 
38
+ def merge(other)
39
+ ::Code::Object::Dictionnary.new(raw.merge(other.raw))
40
+ end
41
+
27
42
  def fetch(key)
28
43
  raw.fetch(key)
29
44
  end
@@ -54,24 +69,25 @@ class Code
54
69
 
55
70
  private
56
71
 
57
- def values(arguments)
58
- sig(arguments)
72
+ def keys
73
+ ::Code::Object::List.new(raw.keys)
74
+ end
75
+
76
+ def values
59
77
  ::Code::Object::List.new(raw.values)
60
78
  end
61
79
 
62
- def each(arguments, context:, io:)
63
- sig(arguments, ::Code::Object::Function)
64
- argument = arguments.first.value
80
+ def each(argument, **globals)
65
81
  raw.each do |key, value|
66
82
  argument.call(
67
83
  arguments: [
68
84
  ::Code::Object::Argument.new(key),
69
- ::Code::Object::Argument.new(value)
85
+ ::Code::Object::Argument.new(value),
70
86
  ],
71
- context: context,
72
- io: io,
87
+ **globals,
73
88
  )
74
89
  end
90
+
75
91
  self
76
92
  end
77
93
  end
@@ -9,11 +9,10 @@ class Code
9
9
  def call(**args)
10
10
  operator = args.fetch(:operator, nil)
11
11
  arguments = args.fetch(:arguments, [])
12
- context = args.fetch(:context)
13
- io = args.fetch(:io)
12
+ globals = args.multi_fetch(*::Code::GLOBALS)
14
13
 
15
- if operator.nil?
16
- call_function(args: arguments, context: context, io: io)
14
+ if operator.nil? || operator == "call"
15
+ call_function(args: arguments, globals: globals)
17
16
  else
18
17
  super
19
18
  end
@@ -31,8 +30,8 @@ class Code
31
30
 
32
31
  attr_reader :arguments, :body
33
32
 
34
- def call_function(args:, context:, io:)
35
- new_context = context.deep_dup
33
+ def call_function(args:, globals:)
34
+ new_context = globals[:context].deep_dup
36
35
 
37
36
  arguments.each.with_index do |argument, index|
38
37
  if argument.regular?
@@ -46,19 +45,22 @@ class Code
46
45
  )
47
46
  else
48
47
  arg = args[index]&.value
49
- arg = argument.evaluate(context: new_context, io: io) if arg.nil?
48
+ arg = argument.evaluate(**globals) if arg.nil?
50
49
  new_context[argument.name] = arg
51
50
  end
52
51
  elsif argument.keyword?
53
52
  arg = args.detect { |arg| arg.name == argument.name }&.value
54
- arg = argument.evaluate(context: new_context, io: io) if arg.nil?
53
+ arg = argument.evaluate(**globals) if arg.nil?
55
54
  new_context[argument.name] = arg
56
55
  else
57
56
  raise NotImplementedError
58
57
  end
59
58
  end
60
59
 
61
- body.evaluate(context: new_context, io: io)
60
+ body.evaluate(
61
+ **globals,
62
+ context: new_context,
63
+ )
62
64
  end
63
65
  end
64
66
  end
@@ -0,0 +1,37 @@
1
+ class Code
2
+ class Object
3
+ class Global < ::Code::Object
4
+ def call(**args)
5
+ operator = args.fetch(:operator, nil)
6
+ arguments = args.fetch(:arguments, [])
7
+ context = args.fetch(:context)
8
+ io = args.fetch(:io)
9
+ globals = args.multi_fetch(*::Code::GLOBALS)
10
+
11
+ if operator == "print"
12
+ io.print(*arguments.map(&:value))
13
+ ::Code::Object::Nothing.new
14
+ elsif operator == "puts"
15
+ io.puts(*arguments.map(&:value))
16
+ ::Code::Object::Nothing.new
17
+ elsif operator == "context"
18
+ sig(arguments, ::Code::Object::String)
19
+ context[arguments.first.value] || ::Code::Object::Nothing.new
20
+ elsif operator == "eval"
21
+ sig(arguments, ::Code::Object::String)
22
+ Code.evaluate(arguments.first.value.raw)
23
+ else
24
+ result = context[operator]
25
+
26
+ if result && result.is_a?(::Code::Object::Function)
27
+ result.call(**args.merge(operator: nil))
28
+ elsif result
29
+ result
30
+ else
31
+ raise ::Code::Error::Undefined.new("#{operator} is not defined")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end