yadriggy 1.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.
@@ -0,0 +1,73 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy/eval_all'
4
+
5
+ # Yadriggy is a platform for building embedded DSLs.
6
+ #
7
+ # It means mistletoe in Japanese.
8
+ #
9
+ module Yadriggy
10
+ class ASTnode
11
+
12
+ # @return [String] the human-readable location of this AST node.
13
+ def source_location_string
14
+ loc = source_location
15
+ "#{loc[0]}:#{loc[1]}"
16
+ end
17
+
18
+ # @return [Array] a tuple of the file name, the line number,
19
+ # and the column of this AST node.
20
+ def source_location
21
+ g = GetLocation.new
22
+ g.evaluate(self)
23
+ if g.unknown? && !self.parent.nil?
24
+ self.parent.source_location
25
+ else
26
+ g.result(root.file_name)
27
+ end
28
+ end
29
+
30
+ # @private
31
+ class GetLocation < EvalAll
32
+ def initialize
33
+ @unknown = true
34
+ @line_no = 0
35
+ @column = 0
36
+ end
37
+
38
+ def unknown?() @unknown end
39
+
40
+ def result(file_name)
41
+ [file_name, @line_no, @column]
42
+ end
43
+
44
+ def name(expr)
45
+ super
46
+ if @unknown
47
+ @unknown = false
48
+ @line_no = expr.line_no
49
+ @column = expr.column
50
+ else
51
+ if expr.line_no < @line_no
52
+ @line_no = expr.line_no
53
+ @column = expr.column
54
+ elsif expr.line_no == @line_no && expr.column < @column
55
+ @column = expr.column
56
+ end
57
+ end
58
+ end
59
+
60
+ def symbol(expr)
61
+ name(expr)
62
+ end
63
+
64
+ def number(expr)
65
+ name(expr)
66
+ end
67
+
68
+ def string_literal(expr)
69
+ name(expr)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,428 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ module Yadriggy
4
+ # Undefined value.
5
+ Undef = :undef
6
+
7
+ class ASTnode
8
+ # The value of the name represented by this AST node
9
+ # if the value is immutable. Otherwise, {Yadriggy::Undef}.
10
+ #
11
+ def const_value() Undef end
12
+
13
+ # The immutable value of the name represented by this AST node
14
+ # when it is evaluated in the context of `clazz`.
15
+ # If the value is mutable, {Yadriggy::Undef}.
16
+ #
17
+ # @param [Module] clazz the context.
18
+ def const_value_in_class(clazz) const_value end
19
+
20
+ # The runtime value of the variable, method name, etc.
21
+ # represented by this AST node.
22
+ # The default behavior returns {Yadriggy::Undef}.
23
+ # Subclasses override this method.
24
+ #
25
+ def value() Undef end
26
+
27
+ # The runtime value of this AST node
28
+ # when it is evaluated in the context of `clazz`.
29
+ #
30
+ # @param [Module] clazz the context.
31
+ def value_in_class(clazz) value end
32
+
33
+ # Gets the class including this AST node.
34
+ # @return [Module] the context.
35
+ def get_context_class
36
+ c = root.context
37
+ if c.is_a?(Proc)
38
+ c.binding.receiver.class
39
+ else # c is Method or UnboundMethod
40
+ c.owner
41
+ end
42
+ end
43
+
44
+ # Gets the receiver object.
45
+ # @return [Object] the receiver object that this proc or method
46
+ # is bound to.
47
+ def get_receiver_object
48
+ c = root.context
49
+ if c.is_a?(Proc)
50
+ c.binding.receiver
51
+ elsif c.is_a?(Method)
52
+ c.receiver
53
+ else
54
+ nil
55
+ end
56
+ end
57
+
58
+ # Checks whether the object is a `Proc` or a method.
59
+ # @param [Object] p the tested object.
60
+ # @return [Booelan] true if `p` is a `Proc`, `Method`, or lambda object.
61
+ def is_proc?(p)
62
+ p.is_a?(Proc) || p.is_a?(Method)
63
+ end
64
+ end
65
+
66
+ class Identifier
67
+ def value()
68
+ c = root.context
69
+ if c.is_a?(Proc)
70
+ if c.binding.local_variable_defined?(name)
71
+ c.binding.local_variable_get(name)
72
+ else
73
+ Undef
74
+ end
75
+ else # c is Method or UnboundMethod
76
+ Undef
77
+ end
78
+ end
79
+ end
80
+
81
+ class VariableCall
82
+ # Gets an ASTree object representing the method body
83
+ # invoked by this AST node.
84
+ # Undef may be returned if the method is not found.
85
+ #
86
+ def value()
87
+ mth = invoked_method
88
+ if mth == Undef
89
+ Undef
90
+ else
91
+ root.reify(mth) || Undef
92
+ end
93
+ end
94
+
95
+ # Returns Undef because const_value is supposed to return
96
+ # the resulting value of executing this variable call.
97
+ #
98
+ def const_value() Undef end
99
+
100
+ # Gets a Method object called by this AST node.
101
+ # If this AST node belongs to UnboundMethod,
102
+ # Undef will be returned.
103
+ #
104
+ def invoked_method()
105
+ obj = get_receiver_object
106
+ if obj.nil?
107
+ Undef
108
+ else
109
+ begin
110
+ obj.method(@name)
111
+ rescue NameError
112
+ Undef
113
+ end
114
+ end
115
+ end
116
+
117
+ # Gets the resulting value of the invocation of this method.
118
+ #
119
+ def do_invocation()
120
+ obj = get_receiver_object
121
+ if obj.nil?
122
+ Undef
123
+ else
124
+ begin
125
+ obj.send(@name)
126
+ rescue NameError
127
+ Undef
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ class Label
134
+ # Gets the name of this label.
135
+ #
136
+ def value() @name end
137
+
138
+ # Gets the name of this label.
139
+ #
140
+ def const_value() @name end
141
+ end
142
+
143
+ class Const
144
+ def value()
145
+ value_in_class(get_context_class)
146
+ end
147
+
148
+ def const_value() value end
149
+
150
+ def value_in_class(clazz)
151
+ if clazz.const_defined?(@name)
152
+ clazz.const_get(@name)
153
+ else
154
+ Undef
155
+ end
156
+ end
157
+
158
+ def const_value_in_class(clazz)
159
+ value_in_class(clazz)
160
+ end
161
+ end
162
+
163
+ class Reserved
164
+ # Gets self, true, or false. Otherwise, Undef.
165
+ #
166
+ def value()
167
+ if @name == 'self'
168
+ get_receiver_object || Undef
169
+ elsif @name == 'true'
170
+ true
171
+ elsif @name == 'false'
172
+ false
173
+ else
174
+ Undef
175
+ end
176
+ end
177
+
178
+ def const_value() value end
179
+ end
180
+
181
+ class GlobalVariable
182
+ # The current value of this global variable.
183
+ #
184
+ def value()
185
+ eval(@name)
186
+ end
187
+ end
188
+
189
+ class InstanceVariable
190
+ def value()
191
+ if name.start_with?("@@")
192
+ clazz = get_context_class
193
+ if clazz.class_variable_defined?(name)
194
+ return clazz.class_variable_get(name)
195
+ end
196
+ elsif name.start_with?("@")
197
+ obj = get_receiver_object
198
+ if obj&.instance_variable_defined?(name)
199
+ return obj.instance_variable_get(name)
200
+ end
201
+ end
202
+
203
+ Undef
204
+ end
205
+
206
+ def value_in_class(clazz)
207
+ if name.start_with?("@@")
208
+ if clazz.class_variable_defined?(name)
209
+ return clazz.class_variable_get(name)
210
+ else
211
+ Undef
212
+ end
213
+ else
214
+ value
215
+ end
216
+ end
217
+
218
+ def const_value()
219
+ if name.start_with?("@@")
220
+ clazz = get_context_class
221
+ if clazz.frozen? && clazz.class_variable_defined?(name)
222
+ return clazz.class_variable_get(name)
223
+ end
224
+ elsif name.start_with?("@")
225
+ obj = get_receiver_object
226
+ if obj.frozen? && obj&.instance_variable_defined?(name)
227
+ return obj.instance_variable_get(name)
228
+ end
229
+ end
230
+
231
+ Undef
232
+ end
233
+ end
234
+
235
+ class Super
236
+ # Gets an ASTree object representing the method body
237
+ # invoked by this call to super.
238
+ # Undef may be returned if the method is not found.
239
+ #
240
+ def value()
241
+ mth = invoked_method
242
+ if mth == Undef
243
+ Undef
244
+ else
245
+ root.reify(mth) || Undef
246
+ end
247
+ end
248
+
249
+ # Gets the super method (Method or UnboundMethod object) or nil.
250
+ # If this AST node is not a part of method body,
251
+ # Undef is returned.
252
+ #
253
+ def invoked_method()
254
+ mthd = root.context
255
+ if mthd.is_a?(Method) || mthd.is_a?(UnboundMethod)
256
+ mthd.super_method
257
+ else
258
+ Undef
259
+ end
260
+ end
261
+ end
262
+
263
+ class Number
264
+ # This is defined by attr_reader.
265
+ # def value() @value end
266
+
267
+ def const_value() value end
268
+ end
269
+
270
+ class ArrayLiteral
271
+ def value()
272
+ elements.map {|e| e.value }
273
+ end
274
+
275
+ def value_in_class(klass)
276
+ elements.map {|e| e.value_in_class(klass) }
277
+ end
278
+
279
+ def const_value()
280
+ elements.map {|e| e.const_value }
281
+ end
282
+ end
283
+
284
+ class StringLiteral
285
+ # This is defined by attr_reader.
286
+ # def value() @value end
287
+
288
+ def const_value() value end
289
+ end
290
+
291
+ class SymbolLiteral
292
+ # Gets the symbol represented by this node.
293
+ def value()
294
+ name.to_sym
295
+ end
296
+
297
+ def const_value() value end
298
+ end
299
+
300
+ class ConstPathRef
301
+ def value()
302
+ value_in_class(get_context_class)
303
+ end
304
+
305
+ def value_in_class(klass)
306
+ if scope.nil?
307
+ clazz = Object
308
+ else
309
+ clazz = scope.value_in_class(klass)
310
+ return Undef if clazz == Undef
311
+ end
312
+
313
+ unless clazz.is_a?(Module)
314
+ raise "unknown scope #{scope.class.name} #{clazz&.to_s || 'nil'}"
315
+ end
316
+
317
+ name.value_in_class(clazz)
318
+ end
319
+
320
+ def const_value() value end
321
+
322
+ def const_value_in_class(clazz)
323
+ value_in_class(clazz)
324
+ end
325
+ end
326
+
327
+ class Unary
328
+ def value()
329
+ send_op_to_value(@expr.value)
330
+ end
331
+
332
+ def value_in_class(klass)
333
+ send_op_to_value(@expr.value_in_class(klass))
334
+ end
335
+
336
+ def const_value()
337
+ send_op_to_value(@expr.const_value)
338
+ end
339
+
340
+ def const_value_in_class(klass)
341
+ send_op_to_value(@expr.const_value_in_class(klass))
342
+ end
343
+
344
+ private
345
+ def send_op_to_value(v)
346
+ if v == Undef || is_proc?(v) || !v.class.public_method_defined?(@op)
347
+ Undef
348
+ else
349
+ v.send(@op)
350
+ end
351
+ end
352
+ end
353
+
354
+ class Binary
355
+ def value()
356
+ send_op_to_value(@left.value, @right.value)
357
+ end
358
+
359
+ def value_in_class(klass)
360
+ send_op_to_value(@expr.value_in_class(klass),
361
+ @right.value_in_class(klass))
362
+ end
363
+
364
+ def const_value()
365
+ send_op_to_value(@left.const_value, @right.const_value)
366
+ end
367
+
368
+ def const_value_in_class(klass)
369
+ send_op_to_value(@expr.const_value_in_class(klass),
370
+ @right.const_value_in_class(klass))
371
+ end
372
+
373
+ private
374
+ def send_op_to_value(v, w)
375
+ if v == Undef || w == Undef || is_proc?(v) || is_proc?(w) ||
376
+ !v.class.public_method_defined?(@op)
377
+ Undef
378
+ else
379
+ v.send(@op, w)
380
+ end
381
+ end
382
+ end
383
+
384
+ class Assign
385
+ def value() Undef end
386
+
387
+ def value_in_class(klass) Undef end
388
+
389
+ def const_value() Undef end
390
+
391
+ def const_value_in_class(klass) Undef end
392
+ end
393
+
394
+ class Call
395
+ # Gets the invoked method or Undef.
396
+ #
397
+ def value()
398
+ if @receiver.nil?
399
+ lookup_method(get_receiver_object)
400
+ else
401
+ lookup_method(@receiver.value)
402
+ end
403
+ end
404
+
405
+ def value_in_class(klass)
406
+ if @receiver.nil?
407
+ lookup_method(get_receiver_object)
408
+ else
409
+ lookup_method(@receiver.value_in_class(klass))
410
+ end
411
+ end
412
+
413
+ private
414
+ def lookup_method(obj)
415
+ if obj.nil? || obj == Undef
416
+ Undef
417
+ else
418
+ begin
419
+ obj.method(@name.name)
420
+ rescue NameError
421
+ Undef
422
+ end
423
+ end
424
+ end
425
+
426
+ end
427
+ end
428
+