code-ruby 1.9.12 → 2.0.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 +4 -4
- data/Gemfile.lock +1 -1
- data/VERSION +1 -1
- data/lib/code/node/call.rb +5 -2
- data/lib/code/node/function.rb +2 -2
- data/lib/code/node/right_operation.rb +4 -0
- data/lib/code/object/dictionary.rb +1 -1
- data/lib/code/object/function.rb +94 -5
- data/lib/code/object/global.rb +4 -1
- data/lib/code/object/super.rb +52 -0
- data/lib/code/parser/call.rb +2 -1
- data/spec/code/object/function_spec.rb +126 -1
- data/spec/code_spec.rb +2 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f52de5a641a1d879b4ac472d5f291a1c101a42797362019d6a2c3854e6bf90a2
|
|
4
|
+
data.tar.gz: 32645dbb24c34523d7848852f59a2de98786de33b8f1782ad514c8e8a317ebb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1dc8fe275e70f5fdc948f082408ae64cb1ab8aaaa73d1f73fb9b8c9eccb180f987dc2d10d7ace3a94ecb85678a9c8ccd95b3c7737c95cd349d6ff3cdb35872a5
|
|
7
|
+
data.tar.gz: 6317f857f1c7984212ca971650dda29e5d9b6fff92d677c4b6c069041cbfda0f92a2bb78d1345c5f11e5eb51d0f9a9d2285bfb60423ef0dd94fb21344e9bec12
|
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2.0.0
|
data/lib/code/node/call.rb
CHANGED
|
@@ -13,8 +13,8 @@ class Code
|
|
|
13
13
|
@body = Code.new(parsed.delete(:body).presence)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def evaluate(**
|
|
17
|
-
Object::Function.new(@parameters, @body)
|
|
16
|
+
def evaluate(**args)
|
|
17
|
+
Object::Function.new(@parameters, @body, args.fetch(:context))
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -22,7 +22,9 @@ class Code
|
|
|
22
22
|
return if parsed.blank?
|
|
23
23
|
|
|
24
24
|
@name = parsed.delete(:name).presence
|
|
25
|
+
@explicit_arguments = parsed.key?(:arguments)
|
|
25
26
|
@arguments = parsed.delete(:arguments).presence || []
|
|
27
|
+
@arguments = [@arguments] unless @arguments.is_a?(Array)
|
|
26
28
|
@arguments.map! { |argument| CallArgument.new(argument) }
|
|
27
29
|
|
|
28
30
|
return unless parsed.key?(:block)
|
|
@@ -52,6 +54,7 @@ class Code
|
|
|
52
54
|
args.fetch(:object).call(
|
|
53
55
|
operator: name,
|
|
54
56
|
arguments: Object::List.new(arguments),
|
|
57
|
+
explicit_arguments: @explicit_arguments,
|
|
55
58
|
**args
|
|
56
59
|
)
|
|
57
60
|
end
|
data/lib/code/node/function.rb
CHANGED
|
@@ -41,6 +41,10 @@ class Code
|
|
|
41
41
|
right = @right&.evaluate(**args) || Object::Nothing.new
|
|
42
42
|
left = @left&.resolve(**args) || Object::Nothing.new
|
|
43
43
|
|
|
44
|
+
if @operator != EQUAL && right.nothing?
|
|
45
|
+
return @left&.evaluate(**args) || Object::Nothing.new
|
|
46
|
+
end
|
|
47
|
+
|
|
44
48
|
left.call(
|
|
45
49
|
operator: @operator,
|
|
46
50
|
arguments: Object::List.new([right]),
|
data/lib/code/object/function.rb
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
class Code
|
|
4
4
|
class Object
|
|
5
5
|
class Function < Object
|
|
6
|
-
attr_reader :code_parameters, :code_body
|
|
6
|
+
attr_reader :code_parameters, :code_body, :definition_context, :parent
|
|
7
7
|
|
|
8
|
-
def initialize(*args, **_kargs, &_block)
|
|
8
|
+
def initialize(*args, parent: nil, methods: nil, **_kargs, &_block)
|
|
9
9
|
@code_parameters =
|
|
10
10
|
List
|
|
11
11
|
.new(args.first)
|
|
@@ -14,6 +14,10 @@ class Code
|
|
|
14
14
|
.to_code
|
|
15
15
|
|
|
16
16
|
@code_body = Code.new(args.second.presence)
|
|
17
|
+
@definition_context = args.third if args.third.is_a?(Context)
|
|
18
|
+
@parent = parent.to_code
|
|
19
|
+
self.methods = methods.to_code
|
|
20
|
+
self.methods = Dictionary.new if self.methods.nothing?
|
|
17
21
|
|
|
18
22
|
self.raw = List.new([code_parameters, code_body])
|
|
19
23
|
end
|
|
@@ -26,15 +30,52 @@ class Code
|
|
|
26
30
|
case code_operator.to_s
|
|
27
31
|
when "", "call"
|
|
28
32
|
sig(args) { signature_for_call }
|
|
29
|
-
code_call(
|
|
33
|
+
code_call(
|
|
34
|
+
*code_arguments.raw,
|
|
35
|
+
explicit_arguments: args.fetch(:explicit_arguments, true),
|
|
36
|
+
**globals
|
|
37
|
+
)
|
|
38
|
+
when "extend"
|
|
39
|
+
sig(args) { Function }
|
|
40
|
+
code_extend(code_arguments.code_first)
|
|
41
|
+
when /=$/
|
|
42
|
+
sig(args) { Object }
|
|
43
|
+
code_set(code_operator.to_s.chop, code_value)
|
|
44
|
+
when ->(operator) { code_has_key?(operator).truthy? }
|
|
45
|
+
result = code_fetch(code_operator)
|
|
46
|
+
|
|
47
|
+
if result.is_a?(Function)
|
|
48
|
+
result.call(**args, operator: nil, bound_self: self)
|
|
49
|
+
else
|
|
50
|
+
sig(args)
|
|
51
|
+
result
|
|
52
|
+
end
|
|
30
53
|
else
|
|
31
54
|
super
|
|
32
55
|
end
|
|
33
56
|
end
|
|
34
57
|
|
|
35
|
-
def code_call(*arguments, **globals)
|
|
58
|
+
def code_call(*arguments, explicit_arguments: true, bound_self: nil, **globals)
|
|
36
59
|
code_arguments = arguments.to_code
|
|
37
|
-
code_context = Context.new({}, globals[:context])
|
|
60
|
+
code_context = Context.new({}, definition_context || globals[:context])
|
|
61
|
+
code_self = bound_self.to_code
|
|
62
|
+
code_self = captured_self if code_self.nothing? && captured_self
|
|
63
|
+
code_self = Dictionary.new if code_self.nil? || code_self.nothing?
|
|
64
|
+
|
|
65
|
+
code_context.code_set("self", code_self)
|
|
66
|
+
|
|
67
|
+
if parent.is_a?(Function)
|
|
68
|
+
code_context.code_set(
|
|
69
|
+
"super",
|
|
70
|
+
Super.new(
|
|
71
|
+
parent,
|
|
72
|
+
code_arguments,
|
|
73
|
+
code_self,
|
|
74
|
+
definition_context || globals[:context],
|
|
75
|
+
explicit_arguments: explicit_arguments
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
end
|
|
38
79
|
|
|
39
80
|
code_parameters.raw.each.with_index do |code_parameter, index|
|
|
40
81
|
code_argument =
|
|
@@ -113,6 +154,54 @@ class Code
|
|
|
113
154
|
def code_to_string
|
|
114
155
|
String.new("<#{self.class.name} #{raw}>")
|
|
115
156
|
end
|
|
157
|
+
|
|
158
|
+
def code_extend(function)
|
|
159
|
+
code_function = function.to_code
|
|
160
|
+
|
|
161
|
+
Function.new(
|
|
162
|
+
code_function.code_parameters,
|
|
163
|
+
code_function.code_body.raw,
|
|
164
|
+
code_function.definition_context,
|
|
165
|
+
parent: self,
|
|
166
|
+
methods: methods.code_deep_duplicate
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def code_fetch(key)
|
|
171
|
+
methods.code_fetch(key)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def code_get(key)
|
|
175
|
+
methods.code_get(key)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def code_has_key?(key)
|
|
179
|
+
methods.code_has_key?(key)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def code_set(key, value)
|
|
183
|
+
methods.code_set(key, value)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private
|
|
187
|
+
|
|
188
|
+
def captured_self
|
|
189
|
+
self_from(definition_context)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def self_from(context)
|
|
193
|
+
return if context.blank?
|
|
194
|
+
|
|
195
|
+
current = context
|
|
196
|
+
|
|
197
|
+
while current
|
|
198
|
+
return current.code_fetch("self") if current.code_has_key?("self").truthy?
|
|
199
|
+
|
|
200
|
+
current = current.parent
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
nil
|
|
204
|
+
end
|
|
116
205
|
end
|
|
117
206
|
end
|
|
118
207
|
end
|
data/lib/code/object/global.rb
CHANGED
|
@@ -199,7 +199,10 @@ class Code
|
|
|
199
199
|
code_context = code_context.code_lookup!(code_operator)
|
|
200
200
|
code_result = code_context.code_fetch(code_operator)
|
|
201
201
|
|
|
202
|
-
if code_result.is_a?(
|
|
202
|
+
if code_result.is_a?(Super)
|
|
203
|
+
code_result.call(**args, operator: nil)
|
|
204
|
+
elsif code_result.is_a?(Function) &&
|
|
205
|
+
args.fetch(:explicit_arguments, false)
|
|
203
206
|
code_result.call(**args, operator: nil)
|
|
204
207
|
else
|
|
205
208
|
sig(args)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Code
|
|
4
|
+
class Object
|
|
5
|
+
class Super < Function
|
|
6
|
+
def initialize(
|
|
7
|
+
parent,
|
|
8
|
+
forwarded_arguments,
|
|
9
|
+
code_self,
|
|
10
|
+
definition_context,
|
|
11
|
+
explicit_arguments: false
|
|
12
|
+
)
|
|
13
|
+
@parent = parent.to_code
|
|
14
|
+
@forwarded_arguments = forwarded_arguments.to_code
|
|
15
|
+
@code_self = code_self.to_code
|
|
16
|
+
@definition_context = definition_context
|
|
17
|
+
self.methods = Dictionary.new
|
|
18
|
+
self.raw = @parent.raw
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(**args)
|
|
22
|
+
code_arguments = args.fetch(:arguments, List.new).to_code
|
|
23
|
+
explicit_arguments = args.fetch(:explicit_arguments, false)
|
|
24
|
+
|
|
25
|
+
@parent.code_call(
|
|
26
|
+
*(arguments_for(code_arguments, explicit_arguments).raw),
|
|
27
|
+
explicit_arguments: explicit_arguments,
|
|
28
|
+
bound_self: @code_self,
|
|
29
|
+
**multi_fetch(args, *GLOBALS).merge(context: parent_context)
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def parent_context
|
|
36
|
+
context = Context.new({}, @definition_context)
|
|
37
|
+
context.code_set("self", @code_self)
|
|
38
|
+
context
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def arguments_for(code_arguments, explicit_arguments)
|
|
42
|
+
if explicit_arguments && code_arguments.raw.empty?
|
|
43
|
+
List.new
|
|
44
|
+
elsif code_arguments.any?
|
|
45
|
+
code_arguments
|
|
46
|
+
else
|
|
47
|
+
@forwarded_arguments
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/code/parser/call.rb
CHANGED
|
@@ -151,7 +151,8 @@ class Code
|
|
|
151
151
|
|
|
152
152
|
def root
|
|
153
153
|
(
|
|
154
|
-
name.aka(:name) <<
|
|
154
|
+
name.aka(:name) <<
|
|
155
|
+
(whiltespace_without_newline? << arguments.aka(:arguments)).maybe <<
|
|
155
156
|
(whiltespace_without_newline? << block.aka(:block)).maybe
|
|
156
157
|
).aka(:call)
|
|
157
158
|
end
|
|
@@ -28,7 +28,7 @@ RSpec.describe Code::Object::Function do
|
|
|
28
28
|
|
|
29
29
|
context "invalid" do
|
|
30
30
|
[
|
|
31
|
-
"f = (x) => {} f",
|
|
31
|
+
"f = (x) => {} f()",
|
|
32
32
|
"f = (x:) => {} f(1)",
|
|
33
33
|
"f = (x:) => {} f(y: 1)"
|
|
34
34
|
].each do |input|
|
|
@@ -37,4 +37,129 @@ RSpec.describe Code::Object::Function do
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
it "captures self for constructor-like functions that return self" do
|
|
42
|
+
result =
|
|
43
|
+
Code.evaluate(<<~CODE)
|
|
44
|
+
User = (given_name:, family_name:, birth_date:) => {
|
|
45
|
+
self.given_name = given_name.to_string.presence
|
|
46
|
+
self.family_name = family_name.to_string.presence
|
|
47
|
+
self.birth_date = birth_date.presence&.to_date
|
|
48
|
+
self.full_name = () => {
|
|
49
|
+
[self.given_name, self.family_name].compact.join(" ")
|
|
50
|
+
}
|
|
51
|
+
self.age = () => {
|
|
52
|
+
return unless(self.birth_date)
|
|
53
|
+
|
|
54
|
+
self.birth_date.year
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return(self)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
user = User(given_name: "Dorian", family_name: "Marie", birth_date: "1992-08-11")
|
|
61
|
+
[user.given_name, user.family_name, user.full_name, user.age]
|
|
62
|
+
CODE
|
|
63
|
+
|
|
64
|
+
expect(result).to eq(
|
|
65
|
+
Code::Object::List.new(
|
|
66
|
+
[
|
|
67
|
+
Code::Object::String.new("Dorian"),
|
|
68
|
+
Code::Object::String.new("Marie"),
|
|
69
|
+
Code::Object::String.new("Dorian Marie"),
|
|
70
|
+
Code::Object::Integer.new(1992)
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "supports constructor methods on functions" do
|
|
77
|
+
result =
|
|
78
|
+
Code.evaluate(<<~CODE)
|
|
79
|
+
User = (given_name:, family_name:) => {
|
|
80
|
+
self.given_name = given_name
|
|
81
|
+
self.family_name = family_name
|
|
82
|
+
self.full_name = () => {
|
|
83
|
+
[self.given_name, self.family_name].join(" ")
|
|
84
|
+
}
|
|
85
|
+
return(self)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
User.all = () => {
|
|
89
|
+
[
|
|
90
|
+
User(given_name: "Dorian", family_name: "Marie"),
|
|
91
|
+
User(given_name: "Ada", family_name: "Lovelace")
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
User.first = () => {
|
|
96
|
+
User.all.first
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
User.first.full_name
|
|
100
|
+
CODE
|
|
101
|
+
|
|
102
|
+
expect(result).to eq(Code::Object::String.new("Dorian Marie"))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "supports extending constructors and forwarding super arguments" do
|
|
106
|
+
result =
|
|
107
|
+
Code.evaluate(<<~CODE)
|
|
108
|
+
Person = (given_name:, family_name:) => {
|
|
109
|
+
self.given_name = given_name
|
|
110
|
+
self.family_name = family_name
|
|
111
|
+
self.full_name = () => {
|
|
112
|
+
[self.given_name, self.family_name].join(" ")
|
|
113
|
+
}
|
|
114
|
+
return(self)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
Employee = Person.extend((employee_id:, given_name:, family_name:) => {
|
|
118
|
+
super
|
|
119
|
+
self.employee_id = employee_id
|
|
120
|
+
return(self)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
employee = Employee(employee_id: "EMP-001", given_name: "Dorian", family_name: "Marie")
|
|
124
|
+
[employee.employee_id, employee.full_name]
|
|
125
|
+
CODE
|
|
126
|
+
|
|
127
|
+
expect(result).to eq(
|
|
128
|
+
Code::Object::List.new(
|
|
129
|
+
[
|
|
130
|
+
Code::Object::String.new("EMP-001"),
|
|
131
|
+
Code::Object::String.new("Dorian Marie")
|
|
132
|
+
]
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "distinguishes super from super()" do
|
|
138
|
+
result =
|
|
139
|
+
Code.evaluate(<<~CODE)
|
|
140
|
+
Person = (given_name:, family_name:) => {
|
|
141
|
+
self.full_name = () => {
|
|
142
|
+
[given_name, family_name].join(" ")
|
|
143
|
+
}
|
|
144
|
+
return(self)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Anonymous = Person.extend(() => {
|
|
148
|
+
super()
|
|
149
|
+
self.full_name = () => { "anonymous" }
|
|
150
|
+
return(self)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
[Anonymous().full_name, Person(given_name: "Ada", family_name: "Lovelace").full_name]
|
|
154
|
+
CODE
|
|
155
|
+
|
|
156
|
+
expect(result).to eq(
|
|
157
|
+
Code::Object::List.new(
|
|
158
|
+
[
|
|
159
|
+
Code::Object::String.new("anonymous"),
|
|
160
|
+
Code::Object::String.new("Ada Lovelace")
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
end
|
|
40
165
|
end
|
data/spec/code_spec.rb
CHANGED
|
@@ -345,6 +345,8 @@ RSpec.describe Code do
|
|
|
345
345
|
["a = 1 a += 1 a", "2"],
|
|
346
346
|
["a = 1 a += 1 a", "2"],
|
|
347
347
|
["a = 1 a -= 1 a", "0"],
|
|
348
|
+
["a = 3 a -= 1 if false a", "3"],
|
|
349
|
+
["a = 3\n(a -= 1) if false\na", "3"],
|
|
348
350
|
["a = 1 a /= 2 a", "0.5"],
|
|
349
351
|
["a = 1 a <<= 1 a", "2"],
|
|
350
352
|
["a = 1 a >>= 1 a", "0"],
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: code-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Marié
|
|
@@ -285,6 +285,7 @@ files:
|
|
|
285
285
|
- lib/code/object/range.rb
|
|
286
286
|
- lib/code/object/smtp.rb
|
|
287
287
|
- lib/code/object/string.rb
|
|
288
|
+
- lib/code/object/super.rb
|
|
288
289
|
- lib/code/object/time.rb
|
|
289
290
|
- lib/code/object/url.rb
|
|
290
291
|
- lib/code/parser.rb
|