yadriggy 1.0.0

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