code-ruby 0.3.1 → 0.4.0

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