code-ruby 1.9.11 → 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/lib/code/parser/name.rb +5 -1
- data/spec/code/object/function_spec.rb +126 -1
- data/spec/code/parser_spec.rb +1 -0
- data/spec/code_spec.rb +3 -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
|
data/lib/code/parser/name.rb
CHANGED
|
@@ -139,6 +139,10 @@ class Code
|
|
|
139
139
|
str("?")
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
def hashtag
|
|
143
|
+
str("#")
|
|
144
|
+
end
|
|
145
|
+
|
|
142
146
|
def special_name
|
|
143
147
|
str("...") | str("..") | str(".") | str("**") | str("*") | str("&")
|
|
144
148
|
end
|
|
@@ -148,7 +152,7 @@ class Code
|
|
|
148
152
|
opening_curly_bracket | closing_curly_bracket | opening_parenthesis |
|
|
149
153
|
closing_parenthesis | opening_square_bracket |
|
|
150
154
|
closing_square_bracket | single_quote | double_quote | lesser |
|
|
151
|
-
greater | asterisk
|
|
155
|
+
greater | asterisk | hashtag
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
def character
|
|
@@ -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/parser_spec.rb
CHANGED
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"],
|
|
@@ -482,6 +484,7 @@ RSpec.describe Code do
|
|
|
482
484
|
['Time.zone = "Etc/UTC"', '"Etc/UTC"'],
|
|
483
485
|
%w[Json.parse("1") 1],
|
|
484
486
|
%w[{a:1}.to_query "a=1"],
|
|
487
|
+
["dorian = 1 dorian#.to_something", "1"],
|
|
485
488
|
["", ""]
|
|
486
489
|
].each do |input, expected|
|
|
487
490
|
it "#{input} == #{expected}" do
|
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
|