rubymacros 0.1.5 → 0.1.6
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 -0
- data/COPYING.LGPL +503 -158
- data/History.txt +115 -5
- data/Makefile +68 -0
- data/README.txt +29 -6
- data/TODO +1 -0
- data/bin/macroruby +69 -0
- data/example/__dir__.rb +18 -0
- data/example/__dir___wrap.rb +18 -0
- data/example/andand.rb +18 -0
- data/example/andand_wrap.rb +18 -0
- data/example/assert.rb +29 -8
- data/example/assert0.rb +11 -0
- data/example/assert0_wrap.rb +5 -0
- data/example/assert_does_nothing_when_disabled.rb +19 -0
- data/example/assert_wrap.rb +21 -0
- data/example/expected_output.txt +88 -0
- data/example/formless_macro.rb +123 -0
- data/example/formless_macro_wrap.rb +20 -0
- data/example/inline.rb +97 -0
- data/example/linenum.rb +19 -1
- data/example/linenum_user.rb +18 -0
- data/example/linenum_wrap.rb +18 -0
- data/example/loop.rb +18 -0
- data/example/loop_wrap.rb +18 -0
- data/example/meta.rb +25 -0
- data/example/meta_wrap.rb +20 -0
- data/example/nilresult.rb +26 -0
- data/example/nilresult_wrap.rb +21 -0
- data/example/pipeline.rb +37 -0
- data/example/rescuing.rb +33 -0
- data/example/rescuing_wrap.rb +21 -0
- data/example/role.rb +103 -0
- data/example/role_with_eval.rb +92 -0
- data/example/self_in_class.rb +27 -0
- data/example/separated_scope.rb +42 -0
- data/example/separated_scope_wrap.rb +20 -0
- data/example/simple.rb +18 -0
- data/example/simple_wrap.rb +18 -0
- data/example/unproc.rb +31 -0
- data/example/unproc_wrap.rb +21 -0
- data/example/unroll.rb +34 -0
- data/example/unroll_macros.rb +119 -0
- data/example/unroll_wrap.rb +22 -0
- data/example/with.rb +50 -7
- data/example/with_wrap.rb +19 -0
- data/lib/macro.rb +307 -72
- data/lib/macro/ReduceWithsFor_RedParse_RedParse__MacroMixin_RedParse__WithMacros_1_8.rb +18880 -0
- data/lib/macro/ReduceWithsFor_RedParse_RedParse__MacroMixin_RedParse__WithMacros_1_9.rb +19101 -0
- data/lib/macro/form.rb +136 -27
- data/lib/macro/node.rb +64 -0
- data/lib/macro/version.rb +2 -5
- data/lib/rubymacros.rb +19 -0
- data/lib/rubymacros/version.rb +23 -0
- data/lib/weakkeyhash.rb +18 -0
- data/rubymacros.gemspec +60 -0
- data/test/test_all.rb +27 -2
- data/test/test_examples.rb +91 -0
- data/test/test_expand.rb +56 -1
- data/test/test_form.rb +108 -10
- data/test/test_unroll.rb +120 -0
- metadata +93 -65
- data/Rakefile +0 -37
data/example/with_wrap.rb
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
=begin
|
2
|
+
rubymacros - a macro preprocessor for ruby
|
3
|
+
Copyright (C) 2008, 2016 Caleb Clausen
|
4
|
+
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
6
|
+
it under the terms of the GNU Lesser General Public License as published by
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU Lesser General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU Lesser General Public License
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
=end
|
18
|
+
|
1
19
|
require 'macro'
|
20
|
+
require 'regxform'
|
2
21
|
Macro.require 'example/with'
|
3
22
|
|
data/lib/macro.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=begin
|
2
2
|
rubymacros - a macro preprocessor for ruby
|
3
|
-
Copyright (C) 2008 Caleb Clausen
|
3
|
+
Copyright (C) 2008, 2016 Caleb Clausen
|
4
4
|
|
5
5
|
This program is free software: you can redistribute it and/or modify
|
6
6
|
it under the terms of the GNU Lesser General Public License as published by
|
@@ -16,9 +16,12 @@
|
|
16
16
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
=end
|
18
18
|
|
19
|
-
require 'rubygems'
|
19
|
+
begin require 'rubygems'
|
20
|
+
rescue Exception; #do nothing; hope we don't need it
|
21
|
+
end
|
20
22
|
require 'redparse'
|
21
23
|
require "macro/form"
|
24
|
+
require "macro/node"
|
22
25
|
require "macro/version"
|
23
26
|
|
24
27
|
class Object # :nodoc:
|
@@ -34,6 +37,7 @@ class Macro
|
|
34
37
|
n=RedParse::const_get(k)
|
35
38
|
self::const_set k,n if Module===n and RedParse::Node>=n
|
36
39
|
}
|
40
|
+
ListInNode=RedParse::ListInNode #Node helper module
|
37
41
|
#...and token classes from RubyLexer
|
38
42
|
RubyLexer::constants.each{|k|
|
39
43
|
t=RubyLexer::const_get(k)
|
@@ -66,9 +70,10 @@ class Macro
|
|
66
70
|
[''].concat($:).each{|pre|
|
67
71
|
pre+="/" unless %r{(\A|/)\Z}===pre
|
68
72
|
if File.exist? finally=pre+filename
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
tree=File.open(finally){|code|
|
74
|
+
#code="::Module.new do\n#{code}\nend\n" if wrap
|
75
|
+
Macro.expand(parse(code,finally),filename)
|
76
|
+
}
|
72
77
|
|
73
78
|
tree.load filename,wrap
|
74
79
|
return true
|
@@ -77,6 +82,33 @@ class Macro
|
|
77
82
|
raise LoadError, "no such file to load -- "+filename
|
78
83
|
end
|
79
84
|
|
85
|
+
def Macro.delete(name,context=::Object)
|
86
|
+
Thread.current[:Macro_being_undefined]="macro_"+name
|
87
|
+
class<<context; remove_method(Thread.current[:Macro_being_undefined]); end
|
88
|
+
if context==::Object
|
89
|
+
Macro::GLOBALS.delete name.to_sym
|
90
|
+
end
|
91
|
+
Thread.current[:Macro_being_undefined]=nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def Macro.list(*contexts)
|
95
|
+
contexts=[::Object] if contexts.empty?
|
96
|
+
contexts.map{|ctx| ctx.singleton_methods.grep(/\Amacro_/) }.flatten.map{|ctx| ctx.to_s.gsub!(/\Amacro_/,'') }
|
97
|
+
end
|
98
|
+
|
99
|
+
class ::Module
|
100
|
+
def macros
|
101
|
+
Macro.list(self)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def Macro.delete_all!(*contexts)
|
106
|
+
contexts=[::Object] if contexts.empty?
|
107
|
+
contexts.each{|ctx|
|
108
|
+
Macro.list(ctx).each{|mac| Macro.delete mac,ctx }
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
80
112
|
#like Kernel#eval, but allows macros (and forms) as well.
|
81
113
|
#beware: default for second argument is currently broken.
|
82
114
|
#best practice is to pass an explicit binding (see
|
@@ -91,8 +123,9 @@ class Macro
|
|
91
123
|
#binding should default to Binding.of_caller, but byellgch
|
92
124
|
|
93
125
|
lvars=binding ? ::Kernel.eval("local_variables()",binding) : []
|
94
|
-
|
95
|
-
tree.
|
126
|
+
code=Macro.parse(code,file,line,lvars) unless Node===code
|
127
|
+
tree=Macro.expand(code,file)
|
128
|
+
tree.eval binding,file,line
|
96
129
|
end
|
97
130
|
|
98
131
|
# A helper for Macro.eval which returns a RedParse tree for the given
|
@@ -113,7 +146,7 @@ class Macro
|
|
113
146
|
if Binding===lvars
|
114
147
|
lvars=eval "local_variables", lvars
|
115
148
|
end
|
116
|
-
|
149
|
+
::RedParse::WithMacros.new(code,file,line,lvars).parse
|
117
150
|
end
|
118
151
|
|
119
152
|
UNCOPYABLE= #Symbol|Numeric|true|false|nil|
|
@@ -126,12 +159,18 @@ class Macro
|
|
126
159
|
def Macro.quote obj
|
127
160
|
#result=
|
128
161
|
case obj
|
129
|
-
when Symbol,Numeric
|
130
|
-
when true,false,nil
|
131
|
-
when String
|
162
|
+
when Symbol,Numeric; LiteralNode[obj]
|
163
|
+
when true,false,nil; VarLikeNode[obj.inspect]
|
164
|
+
when String
|
132
165
|
obj=obj.gsub(/['\\]/){|ch| '\\'+ch }
|
133
166
|
StringNode[obj,{:@open=>"'", :@close=>"'"}]
|
134
167
|
|
168
|
+
when Reg::Formula
|
169
|
+
Reg::Deferred.defang! obj
|
170
|
+
|
171
|
+
when Reg::Reg
|
172
|
+
obj
|
173
|
+
|
135
174
|
# TODO: The following is dead code and should be removed
|
136
175
|
else
|
137
176
|
#result=:(::Macro::QuotedStore[^QuotedStore.size])
|
@@ -154,12 +193,12 @@ class Macro
|
|
154
193
|
return result if result
|
155
194
|
result=
|
156
195
|
case obj
|
157
|
-
when Symbol,Numeric,true,false,nil
|
158
|
-
when String
|
159
|
-
when Array
|
196
|
+
when Symbol,Numeric,true,false,nil; return obj
|
197
|
+
when String; seen[obj.__id__]=obj.dup
|
198
|
+
when Array
|
160
199
|
seen[obj.__id__]=dup=obj.dup
|
161
200
|
dup.map!{|x| copy x,seen}
|
162
|
-
when Hash
|
201
|
+
when Hash
|
163
202
|
result={}
|
164
203
|
seen[obj.__id__]=result
|
165
204
|
obj.each_pair{|k,v|
|
@@ -167,7 +206,7 @@ class Macro
|
|
167
206
|
}
|
168
207
|
result
|
169
208
|
when Module,Proc,IO,Method,
|
170
|
-
UnboundMethod,Thread,Continuation
|
209
|
+
UnboundMethod,Thread,Continuation
|
171
210
|
return obj
|
172
211
|
else
|
173
212
|
obj.dup
|
@@ -200,7 +239,7 @@ class Macro
|
|
200
239
|
#
|
201
240
|
def Macro.postpone node,session
|
202
241
|
return node #disable postponement
|
203
|
-
|
242
|
+
=begin was
|
204
243
|
filename=session[:filename]
|
205
244
|
unless session[:@modpath_unsure]
|
206
245
|
modpath=ConstantNode[nil,*session[:@modpath]]
|
@@ -226,6 +265,7 @@ class Macro
|
|
226
265
|
HashLiteralNode[LiteralNode[:@expand_in_defs], VarLikeNode["true"]], Macro.quote(filename)],
|
227
266
|
nil,nil]
|
228
267
|
return CallNode[expanded,evalname,[Macro.quote( filename )],nil,nil]
|
268
|
+
=end
|
229
269
|
end
|
230
270
|
|
231
271
|
class Node
|
@@ -350,7 +390,10 @@ class Macro
|
|
350
390
|
end
|
351
391
|
session[:@modpath]||=[]
|
352
392
|
session[:filename]||=filename
|
353
|
-
|
393
|
+
filename||="(eval)"
|
394
|
+
case tree
|
395
|
+
when String,IO; tree=parse(tree,filename)
|
396
|
+
end
|
354
397
|
fail unless macros.__id__==Macro::GLOBALS.__id__ #for now
|
355
398
|
tree.walk{|parent,i,subi,node|
|
356
399
|
is_node=Node===node
|
@@ -369,7 +412,19 @@ class Macro
|
|
369
412
|
|
370
413
|
if newnode
|
371
414
|
return newnode unless parent #replacement at top level
|
372
|
-
|
415
|
+
if subi
|
416
|
+
target,index=parent[i],subi
|
417
|
+
else
|
418
|
+
target,index=parent,i
|
419
|
+
end
|
420
|
+
if JustNilNode===newnode and target.class==::Array ||
|
421
|
+
case target
|
422
|
+
when UndefNode,AssigneeList,SequenceNode; true
|
423
|
+
end
|
424
|
+
target.delete_at index
|
425
|
+
else
|
426
|
+
target[index]=newnode
|
427
|
+
end
|
373
428
|
fail if recurse
|
374
429
|
end
|
375
430
|
else
|
@@ -387,7 +442,7 @@ class Macro
|
|
387
442
|
|
388
443
|
#varargs, &args and receivers are not allowed in macro definitions (yet)
|
389
444
|
fail "macro receivers not supported yet" if receiver
|
390
|
-
if
|
445
|
+
if args
|
391
446
|
last=args
|
392
447
|
# else
|
393
448
|
# last=args.last
|
@@ -398,8 +453,21 @@ class Macro
|
|
398
453
|
#macros can't be settors
|
399
454
|
fail "macro settors are not allowed" if /=$/===name
|
400
455
|
|
456
|
+
self.args||=[]
|
457
|
+
args.unshift VarNode.allocate.replace(["receiver"])
|
458
|
+
self.walk{|parent,i,subi,node| #replace self kw in body with receiver var instead
|
459
|
+
if VarLikeNode===node and node.ident=="self"
|
460
|
+
if subi
|
461
|
+
parent[i][subi]=VarNode.allocate.replace(["receiver"])
|
462
|
+
else
|
463
|
+
parent[i]=VarNode.allocate.replace(["receiver"])
|
464
|
+
end
|
465
|
+
end
|
466
|
+
true
|
467
|
+
}
|
468
|
+
|
401
469
|
#macro definitions need to be dealt with in 2 steps: registration and activation
|
402
|
-
name=self.name
|
470
|
+
# name=self.name
|
403
471
|
self[1]="macro_"+name unless /^macro_/===name
|
404
472
|
node=MethodNode[*self] #convert macro node to a method def node
|
405
473
|
huh(node.receiver) if node.receiver
|
@@ -409,15 +477,15 @@ class Macro
|
|
409
477
|
#disable postponement (delayed macros) ... i think they're not necessary
|
410
478
|
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
411
479
|
node.receiver= expand[node.receiver]
|
412
|
-
node.args.map! &expand if node.args
|
480
|
+
node.args.map!( &expand )if node.args
|
413
481
|
node.body= expand[node.body]
|
414
|
-
node.rescues.map! &expand if node.rescues
|
482
|
+
node.rescues.map!( &expand )if node.rescues
|
415
483
|
node.ensure_= expand[node.ensure_]
|
416
484
|
node.else_= expand[node.else_]
|
417
|
-
node.eval
|
485
|
+
node.eval(nil,session[:filename])
|
418
486
|
macros[name.to_sym]=::Object.method("macro_"+name)
|
419
487
|
return node,false
|
420
|
-
|
488
|
+
=begin was
|
421
489
|
#node.eval #no, not here....
|
422
490
|
|
423
491
|
newnode=Macro.postpone node, session
|
@@ -441,6 +509,7 @@ class Macro
|
|
441
509
|
#newnode=RedParse::VarLikeNode["nil", {:@value=>false,}]
|
442
510
|
#subi ? parent[i][subi]=newnode : parent[i]=newnode
|
443
511
|
return newnode,false #dont keep recursing
|
512
|
+
=end
|
444
513
|
end
|
445
514
|
end
|
446
515
|
|
@@ -478,12 +547,12 @@ class Macro
|
|
478
547
|
#if this callsite names a macro, then it is a macro
|
479
548
|
#macro=macros[name.to_sym]=::Object.method(macro) if String===macro
|
480
549
|
#refuse macro calls with receivers, blocks, varargs, or &args: not supported yet
|
481
|
-
fail "macro receivers not supported yet" if receiver
|
482
550
|
fail "macro blocky args not supported yet" if UnOpNode===args.last and args.last.ident=="&@"
|
483
551
|
fail "macro varargs calls not supported yet" if UnaryStarNode===args.last
|
484
552
|
fail if args.class!=Array
|
553
|
+
args.unshift receiver||VarLikeNode.allocate.replace(["self"])
|
485
554
|
if block
|
486
|
-
newnode=macro.call *args do |*bparams|
|
555
|
+
newnode=macro.call( *args )do |*bparams|
|
487
556
|
if !blockparams
|
488
557
|
block
|
489
558
|
else
|
@@ -494,28 +563,31 @@ class Macro
|
|
494
563
|
end
|
495
564
|
end
|
496
565
|
else
|
497
|
-
newnode=macro.call *args
|
566
|
+
newnode=macro.call( *args )
|
498
567
|
end
|
499
568
|
#subi ? parent[i][subi]=newnode : parent[i]=newnode
|
500
569
|
|
501
570
|
# and keep recursing, no matter what, by all means!!
|
502
|
-
newnode
|
503
|
-
|
504
|
-
|
571
|
+
if newnode
|
572
|
+
newnode=Macro.expand newnode,macros,session #just do it here
|
573
|
+
newnode=OneLineParenedNode[newnode] #disable newlines in macro text
|
574
|
+
else
|
575
|
+
newnode=JustNilNode.new
|
576
|
+
end
|
505
577
|
return newnode,false #and not in caller
|
506
|
-
|
507
|
-
|
578
|
+
end
|
579
|
+
end
|
508
580
|
|
509
|
-
|
510
|
-
|
581
|
+
#postpone macro expansion in methods til method defn is executed
|
582
|
+
class MethodNode
|
511
583
|
def old_macro_expand(macros,session)
|
512
584
|
if session[:@expand_in_defs]
|
513
585
|
session[:@expand_in_defs]=false
|
514
586
|
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
515
587
|
self.receiver= expand[receiver]
|
516
|
-
args.map! &expand if args
|
588
|
+
args.map!( &expand )if args
|
517
589
|
self.body= expand[body]
|
518
|
-
rescues.map! &expand if rescues
|
590
|
+
rescues.map!( &expand )if rescues
|
519
591
|
self.ensure_= expand[ensure_]
|
520
592
|
self.else_= expand[else_]
|
521
593
|
session[:@expand_in_defs]=true
|
@@ -527,17 +599,17 @@ class Macro
|
|
527
599
|
def macro_expand(macros,session)
|
528
600
|
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
529
601
|
self.receiver= expand[receiver]
|
530
|
-
args.map! &expand if args
|
602
|
+
args.map!( &expand )if args
|
531
603
|
self.body= expand[body]
|
532
|
-
rescues.map! &expand if rescues
|
604
|
+
rescues.map!( &expand )if rescues
|
533
605
|
self.ensure_= expand[ensure_]
|
534
606
|
self.else_= expand[else_]
|
535
607
|
return self,false
|
536
608
|
end
|
537
|
-
|
609
|
+
end
|
538
610
|
|
539
|
-
|
540
|
-
|
611
|
+
#disable macro definitions within classes and modules
|
612
|
+
module DisableMacros
|
541
613
|
def macro_expand(macros,session)
|
542
614
|
old_unsure=session[:@modpath_unsure]
|
543
615
|
old_namespace_type=session[:@namespace_type]
|
@@ -558,7 +630,7 @@ class Macro
|
|
558
630
|
else
|
559
631
|
unwind=1
|
560
632
|
end
|
561
|
-
session[:@modpath].push *name
|
633
|
+
session[:@modpath].push( *name )
|
562
634
|
|
563
635
|
map!{|n|
|
564
636
|
case n
|
@@ -579,25 +651,32 @@ class Macro
|
|
579
651
|
|
580
652
|
return nil,false #halt further recursion: already done
|
581
653
|
end
|
582
|
-
|
583
|
-
|
584
|
-
|
654
|
+
end
|
655
|
+
class ModuleNode; include DisableMacros; end
|
656
|
+
class ClassNode; include DisableMacros; end
|
585
657
|
|
586
|
-
|
658
|
+
class MetaClassNode
|
587
659
|
def macro_expand(macros,session)
|
588
660
|
old_unsure=session[:@modpath_unsure]
|
589
661
|
session[:@modpath_unsure]=true
|
590
|
-
map!{|n|
|
662
|
+
map!{|n|
|
663
|
+
case n
|
664
|
+
when nil
|
665
|
+
when Node; Macro.expand(n,macros,session)
|
666
|
+
when Array; n.map!{|nn| Macro.expand(nn,macros,session) }
|
667
|
+
else fail
|
668
|
+
end
|
669
|
+
}
|
591
670
|
session[:@modpath_unsure]=old_unsure
|
592
671
|
|
593
672
|
return nil,false #halt further recursion: already done
|
594
673
|
end
|
595
|
-
|
674
|
+
end
|
596
675
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
676
|
+
#postpone macro expansion (and definition) in forms until they are evaled
|
677
|
+
#(or returned from a macro)
|
678
|
+
#but not in form parameters
|
679
|
+
class FormNode < ValueNode
|
601
680
|
def macro_expand(macros,session)
|
602
681
|
#return text.to_sexp({})
|
603
682
|
|
@@ -632,9 +711,9 @@ class Macro
|
|
632
711
|
end
|
633
712
|
return result
|
634
713
|
end
|
635
|
-
|
714
|
+
end
|
636
715
|
|
637
|
-
|
716
|
+
class FormEscapeNode<ValueNode
|
638
717
|
# Convert this node to an S-expression
|
639
718
|
#
|
640
719
|
# +session+:: the context in which this macro is being processed
|
@@ -643,7 +722,7 @@ class Macro
|
|
643
722
|
nest=session[:form_nest_level]||1
|
644
723
|
carets=0
|
645
724
|
node=self
|
646
|
-
while
|
725
|
+
while FormEscapeNode===node
|
647
726
|
node=node.text
|
648
727
|
carets+=1
|
649
728
|
end
|
@@ -655,9 +734,9 @@ class Macro
|
|
655
734
|
end
|
656
735
|
|
657
736
|
def image
|
658
|
-
"^"+
|
737
|
+
"^"+body.image
|
659
738
|
end
|
660
|
-
|
739
|
+
end
|
661
740
|
|
662
741
|
class MacroNode < ValueNode
|
663
742
|
param_names(:defword_,:receiver,:name,:args,:semi_,:body,:rescues,:elses,:ensures,:endword_)
|
@@ -702,7 +781,7 @@ class Macro
|
|
702
781
|
alias ensure_ ensures
|
703
782
|
alias ensure ensures
|
704
783
|
|
705
|
-
# Performs the reverse of a parse operation (turns the
|
784
|
+
# Performs the reverse of a parse operation (turns the MacroNode into a
|
706
785
|
# string)
|
707
786
|
#
|
708
787
|
# +o+:: a list of options for unparse
|
@@ -717,7 +796,7 @@ class Macro
|
|
717
796
|
result+=")"
|
718
797
|
end
|
719
798
|
result+=unparse_nl(body,o)+body.unparse(o) if body
|
720
|
-
result+=rescues.map{|resc| resc.unparse o}.to_s
|
799
|
+
result+=rescues.map{|resc| resc.unparse o}.to_s if rescues
|
721
800
|
result+=unparse_nl(else_,o)+"else "+else_.unparse( o )+"\n" if else_
|
722
801
|
result+=unparse_nl(ensure_,o)+"ensure "+ensure_.unparse( o )+"\n" if ensure_
|
723
802
|
result+=";end"
|
@@ -727,7 +806,8 @@ class Macro
|
|
727
806
|
|
728
807
|
class OneLineParenedNode < ParenedNode
|
729
808
|
#hacky way to get unparser to not emit newlines in most cases
|
730
|
-
|
809
|
+
#I think this isn't necessary now that forms (and subnodes) have their linenums zeroed on creation
|
810
|
+
def unparse(o=default_unparse_options)
|
731
811
|
old_linenum=o[:linenum]
|
732
812
|
o[:linenum]=2**128
|
733
813
|
result=super(o)
|
@@ -737,22 +817,146 @@ class Macro
|
|
737
817
|
end
|
738
818
|
end
|
739
819
|
|
740
|
-
class
|
820
|
+
class JustNilNode<ValueNode
|
821
|
+
def unparse(o=default_unparse_options)
|
822
|
+
"nil"
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
class ::RubyLexer
|
827
|
+
|
828
|
+
module MacroMixin
|
829
|
+
#-----------------------------------
|
830
|
+
#def FUNCLIKE_KEYWORDS(orig=nil) #was
|
831
|
+
# /(?:#{orig||super}|^v$)/
|
832
|
+
#end
|
833
|
+
|
834
|
+
#-----------------------------------
|
835
|
+
def rubylexer_modules_init
|
836
|
+
super
|
837
|
+
|
838
|
+
@unary_or_binary_chars.add '^'
|
839
|
+
@always_binary_chars.remove '^'
|
840
|
+
|
841
|
+
# @FUNCLIKE_KEYWORDS=FUNCLIKE_KEYWORDS @FUNCLIKE_KEYWORDS unless @FUNCLIKE_KEYWORDS==="v" #was
|
842
|
+
end
|
843
|
+
#def keyword_v(*args,&block) _keyword_funclike(*args,&block) end #was
|
844
|
+
|
845
|
+
def keyword_macro(*args)
|
846
|
+
keywords_def(*args)
|
847
|
+
end
|
848
|
+
|
849
|
+
#-----------------------------------
|
850
|
+
def method_params?
|
851
|
+
lasttok=last_token_maybe_implicit #last_operative_token
|
852
|
+
return super unless lasttok
|
853
|
+
case lasttok.ident
|
854
|
+
when ';'
|
855
|
+
if VContext===@parsestack.last
|
856
|
+
@parsestack.pop
|
857
|
+
true
|
858
|
+
#else super #need this here...? I think not ....
|
859
|
+
end
|
860
|
+
when ')'
|
861
|
+
@moretokens<<KeywordToken.new("<doubled-parens>")
|
862
|
+
@parsestack.pop if VContext===@parsestack.last
|
863
|
+
true
|
864
|
+
when '.'
|
865
|
+
true
|
866
|
+
else super
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
#-----------------------------------
|
871
|
+
def keyword_v(str,offset,result)
|
872
|
+
result[0]=OperatorToken.new("v",offset)
|
873
|
+
@parsestack<<VContext.new(@linenum)
|
874
|
+
return result
|
875
|
+
end
|
876
|
+
|
877
|
+
#-----------------------------------
|
878
|
+
def symbol_or_op(ch) #is this even needed?
|
879
|
+
startpos= input_position
|
880
|
+
if readahead(2)==":("
|
881
|
+
result= OperatorToken.new(read(1), startpos)
|
882
|
+
result.unary=true
|
883
|
+
return result
|
884
|
+
end
|
885
|
+
super
|
886
|
+
end
|
887
|
+
|
888
|
+
#-----------------------------------
|
889
|
+
def caret(ch) #match /^=?/ (^ or ^=) (maybe unary ^ too) #is this even needed?
|
890
|
+
if @last_token_maybe_implicit&&@last_token_maybe_implicit.ident=='(' or
|
891
|
+
unary_op_expected?(ch)
|
892
|
+
result=OperatorToken.new(read(1),input_position)
|
893
|
+
result.unary=true
|
894
|
+
result
|
895
|
+
else
|
896
|
+
super
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
#-----------------------------------
|
901
|
+
def callsite_symbol(x) #is this even needed?
|
902
|
+
return if nextchar==?(
|
903
|
+
super
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
module NestedContexts
|
908
|
+
class VContext<NestedContext
|
909
|
+
def initialize(linenum)
|
910
|
+
super("v","",linenum)
|
911
|
+
end
|
912
|
+
def see evt,lexer
|
913
|
+
lexer.parsestack.pop if evt==:semi and lexer.readahead(1)!='('
|
914
|
+
end
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
end
|
919
|
+
|
920
|
+
module ::RedParse::MacroMixin
|
921
|
+
RedParse.constants.each{|k|
|
922
|
+
const_set k, RedParse::const_get(k)
|
923
|
+
}
|
924
|
+
|
925
|
+
def redparse_modules_init
|
926
|
+
if defined? @lexer and @lexer.respond_to? :enable_macros!
|
927
|
+
@lexer.enable_macros!
|
928
|
+
@lexer.extend ::RubyLexer::MacroMixin
|
929
|
+
@lexer.rubylexer_modules_init
|
930
|
+
end
|
931
|
+
# binary
|
932
|
+
@unary_or_binary_op=/^([\^:]|#@unary_or_binary_op)$/
|
933
|
+
|
934
|
+
|
935
|
+
super
|
936
|
+
end
|
937
|
+
|
741
938
|
def PRECEDENCE
|
742
939
|
result=super
|
743
|
-
return result.merge({"^@"=>result["+@"]})
|
940
|
+
return result.merge({"^@"=>result["+@"], "v"=>result[";"]})
|
744
941
|
end
|
942
|
+
|
745
943
|
def RULES
|
944
|
+
@@soft_nl||=KW(';')&-{:not_real? =>false}
|
746
945
|
[
|
747
946
|
-[KW('macro'), KW(beginsendsmatcher).~.*, KW('end'), KW(/^(do|\{)$/).~.la]>>MisparsedNode
|
748
947
|
]+super+[
|
749
|
-
-[
|
750
|
-
-[Op('
|
948
|
+
-['<+', Expr, '+>']>>FormEscapeNode,
|
949
|
+
-[Op('v'), Expr, lower_op()]>>FormEscapeNode,
|
950
|
+
-[Op('v'), Expr, /^;$|^<doubled-parens>$/, '(', Expr.-, ')']>>FormEscapeNode, #constructor needs update
|
951
|
+
-[Op('v'), Expr, ';', KW('(').~.la]>>FormEscapeNode,
|
952
|
+
-[Op('v'), Expr, @@soft_nl.la]>>:shift,
|
953
|
+
-[Op('^@'), Expr, lower_op()]>>FormEscapeNode,
|
954
|
+
-[Op(':@'), (ParenedNode&-{:size=>(0..1)})|(SequenceNode&-{:size=>0})]>>FormNode,
|
751
955
|
-['macro', CallSiteNode, KW(';'),
|
752
956
|
Expr.-, RescueNode.*, ElseNode.-, EnsureNode.-,
|
753
957
|
'end'
|
754
958
|
]>>MacroNode,
|
755
|
-
-[
|
959
|
+
-[ '(', Expr.-, ')', '<doubled-parens>', '(', Expr.-, ')', BlockNode.-, KW('do').~.la]>>CallNode, #constructor needs update
|
756
960
|
]
|
757
961
|
end
|
758
962
|
def wants_semi_context
|
@@ -768,14 +972,45 @@ class Macro
|
|
768
972
|
return @bem||=/#{super}|^macro$/
|
769
973
|
end
|
770
974
|
|
771
|
-
def
|
772
|
-
|
773
|
-
|
975
|
+
def reduce_withs_directory
|
976
|
+
"macro"
|
977
|
+
end
|
774
978
|
|
775
|
-
|
776
|
-
|
777
|
-
|
979
|
+
def rubyoperatorlist
|
980
|
+
super+%w[:@ ^@ v]
|
981
|
+
end
|
982
|
+
|
983
|
+
def rubykeywordlist
|
984
|
+
super+%w[macro v]
|
985
|
+
end
|
986
|
+
|
987
|
+
def addl_node_containers; [::Macro] end
|
988
|
+
|
989
|
+
def initialize(*args,&block)
|
990
|
+
super
|
778
991
|
end
|
779
992
|
end
|
993
|
+
Macro_ParserMixin=::RedParse::MacroMixin #old name
|
994
|
+
|
995
|
+
class ::RedParse::WithMacros < RedParse
|
996
|
+
include ::RedParse::MacroMixin
|
997
|
+
end
|
998
|
+
RedParseWithMacros=::RedParse::WithMacros #old name
|
780
999
|
end
|
781
1000
|
|
1001
|
+
#hacks to allow you to use macros within irb sessions.
|
1002
|
+
#I wonder how portable this is to rubinius, jruby, mri 1.9, etc
|
1003
|
+
module IRB
|
1004
|
+
class WorkSpace
|
1005
|
+
def evaluate(context, statements, file = __FILE__, line = __LINE__)
|
1006
|
+
Macro.eval(statements, @binding, file, line)
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
::RubyLex::ENINDENT_CLAUSE<<"macro"
|
1010
|
+
module ::RubyToken
|
1011
|
+
def_token(:TkMACRO, TkId, "macro", EXPR_FNAME)
|
1012
|
+
end
|
1013
|
+
def IRB.require(file)
|
1014
|
+
Macro.require(file)
|
1015
|
+
end
|
1016
|
+
end if defined? IRB
|