rubymacros 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -0
- data/Rakefile +1 -2
- data/TODO +3 -0
- data/example/assert.rb +11 -15
- data/example/assert_wrap.rb +1 -0
- data/lib/macro.rb +65 -10
- data/lib/macro/form.rb +16 -10
- data/lib/macro/version.rb +1 -1
- metadata +4 -4
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 0.1.5 / 2009-07-04
|
2
|
+
* 4 Major Enhancements:
|
3
|
+
* all macros are now immediate, not delayed
|
4
|
+
* macro calls can accept blocks
|
5
|
+
* macro calls need not be inside a method
|
6
|
+
* form escapes can stand in for a method name in callsites
|
7
|
+
* 4 Minor Enhancements:
|
8
|
+
* macros can expand to nop by returning nil
|
9
|
+
* form escapes whose value is a symbol become callsites now
|
10
|
+
* forms can now be catenated together with + in the obvious way
|
11
|
+
* HashLiteralNode can be treated somewhat like a real hash, using #get
|
12
|
+
|
1
13
|
=== 0.1.4 / 2009-05-21
|
2
14
|
* 1 Major Enhancement:
|
3
15
|
* line numbers are now preserved in preprocessed code;
|
data/Rakefile
CHANGED
@@ -19,8 +19,7 @@ require 'lib/macro/version.rb'
|
|
19
19
|
|
20
20
|
readme=open("README.txt")
|
21
21
|
readme.readline("\n== DESCRIPTION:")
|
22
|
-
readme.readline("\n
|
23
|
-
desc=readme.readline("\n\n")
|
22
|
+
desc=readme.readline("\n==")
|
24
23
|
|
25
24
|
hoe=Hoe.new("rubymacros", Macro::VERSION) do |_|
|
26
25
|
_.author = "Caleb Clausen"
|
data/TODO
CHANGED
@@ -4,6 +4,9 @@ macro options
|
|
4
4
|
precedence for operator macros
|
5
5
|
hygienic vs unhygienic
|
6
6
|
|
7
|
+
need to insert extra parens around form params
|
8
|
+
except only if it stands in for an rvalue...
|
9
|
+
not if its an lvalue, mvalue, cvalue, etc (bvalue?)
|
7
10
|
static selector namespaces?
|
8
11
|
|
9
12
|
other types of macro:
|
data/example/assert.rb
CHANGED
@@ -1,18 +1,14 @@
|
|
1
1
|
#this file uses macros! won't parse in normal ruby
|
2
|
-
|
3
|
-
if $Debug
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
macro assert(cond)
|
3
|
+
if $Debug
|
4
|
+
if RedParse::OpNode===cond and /\A[=!]=\Z/===cond.op
|
5
|
+
left,op,right=*cond
|
6
|
+
:(fail 'expected '+^left.unparse+"(==#{^left}) to be "+
|
7
|
+
^op+" "+^right.unparse+"(==#{^right})" unless ^cond)
|
8
|
+
else
|
9
|
+
:(fail "expected #{:(^^cond)}, but was not true" unless ^cond)
|
10
|
+
end
|
11
11
|
end
|
12
|
-
end
|
13
|
-
else
|
14
|
-
macro assert(cond)
|
15
|
-
end
|
16
12
|
end
|
17
13
|
|
18
14
|
|
@@ -29,7 +25,7 @@ def test_assert
|
|
29
25
|
assert(a==b) #oops, fails. msg="expected a(==1) to be == b(==2)"
|
30
26
|
rescue Exception=>e
|
31
27
|
assert("expected a(==1) to be == b(==2)"== e.message) #better be ok
|
32
|
-
else
|
28
|
+
else puts "exception expected but was not seen"
|
33
29
|
end
|
34
30
|
|
35
31
|
begin
|
@@ -37,6 +33,6 @@ def test_assert
|
|
37
33
|
rescue Exception=>e
|
38
34
|
assert("expected nil, but was not true"== e.message) #better be ok
|
39
35
|
#ok, that message didn't make a lot of sense...
|
40
|
-
else
|
36
|
+
else puts "exception expected but was not seen"
|
41
37
|
end
|
42
38
|
end
|
data/example/assert_wrap.rb
CHANGED
data/lib/macro.rb
CHANGED
@@ -16,16 +16,11 @@
|
|
16
16
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
=end
|
18
18
|
|
19
|
-
#$:.unshift "../redparse/lib"
|
20
|
-
#warn "$: hacked up to find latest redparse"
|
21
|
-
|
22
19
|
require 'rubygems'
|
23
20
|
require 'redparse'
|
24
21
|
require "macro/form"
|
25
22
|
require "macro/version"
|
26
23
|
|
27
|
-
warn "need to insert extra parens around form params"
|
28
|
-
|
29
24
|
class Object # :nodoc:
|
30
25
|
#as close as I can get to an empty binding (used below in Macro.eval)
|
31
26
|
def Object.new_binding() # :nodoc:
|
@@ -204,6 +199,8 @@ class Macro
|
|
204
199
|
# +session+:: the context in which this macro is being processed
|
205
200
|
#
|
206
201
|
def Macro.postpone node,session
|
202
|
+
return node #disable postponement
|
203
|
+
|
207
204
|
filename=session[:filename]
|
208
205
|
unless session[:@modpath_unsure]
|
209
206
|
modpath=ConstantNode[nil,*session[:@modpath]]
|
@@ -409,8 +406,19 @@ class Macro
|
|
409
406
|
node[0]=ParenedNode[ConstantNode[nil,"Object"]] #all macros are global for now... til we get scoped macros
|
410
407
|
#sets receiver
|
411
408
|
|
409
|
+
#disable postponement (delayed macros) ... i think they're not necessary
|
410
|
+
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
411
|
+
node.receiver= expand[node.receiver]
|
412
|
+
node.args.map! &expand if node.args
|
413
|
+
node.body= expand[node.body]
|
414
|
+
node.rescues.map! &expand if node.rescues
|
415
|
+
node.ensure_= expand[node.ensure_]
|
416
|
+
node.else_= expand[node.else_]
|
417
|
+
node.eval
|
418
|
+
macros[name.to_sym]=::Object.method("macro_"+name)
|
419
|
+
return node,false
|
420
|
+
|
412
421
|
#node.eval #no, not here....
|
413
|
-
# macros[name.to_sym]=self.name
|
414
422
|
|
415
423
|
newnode=Macro.postpone node, session
|
416
424
|
|
@@ -471,14 +479,27 @@ class Macro
|
|
471
479
|
#macro=macros[name.to_sym]=::Object.method(macro) if String===macro
|
472
480
|
#refuse macro calls with receivers, blocks, varargs, or &args: not supported yet
|
473
481
|
fail "macro receivers not supported yet" if receiver
|
474
|
-
fail "macro
|
475
|
-
fail "macro block args not supported yet" if UnOpNode===args.last and args.last.ident=="&@"
|
482
|
+
fail "macro blocky args not supported yet" if UnOpNode===args.last and args.last.ident=="&@"
|
476
483
|
fail "macro varargs calls not supported yet" if UnaryStarNode===args.last
|
477
484
|
fail if args.class!=Array
|
478
|
-
|
485
|
+
if block
|
486
|
+
newnode=macro.call *args do |*bparams|
|
487
|
+
if !blockparams
|
488
|
+
block
|
489
|
+
else
|
490
|
+
bparams=KWCallNode["nil"] if bparams.empty?
|
491
|
+
#warning: scoping rules for lvars in blocks not enforced here
|
492
|
+
#(rather serious violation of variable hygiene)
|
493
|
+
ParenedNode[ AssignNode[MultiAssign[*blockparams],'=',bparams]+block ]
|
494
|
+
end
|
495
|
+
end
|
496
|
+
else
|
497
|
+
newnode=macro.call *args
|
498
|
+
end
|
479
499
|
#subi ? parent[i][subi]=newnode : parent[i]=newnode
|
480
500
|
|
481
501
|
# and keep recursing, no matter what, by all means!!
|
502
|
+
newnode||=NopNode.new
|
482
503
|
newnode=Macro.expand newnode,macros,session #just do it here
|
483
504
|
newnode=OneLineParenedNode[newnode] #disable newlines in macro text
|
484
505
|
return newnode,false #and not in caller
|
@@ -487,7 +508,7 @@ class Macro
|
|
487
508
|
|
488
509
|
#postpone macro expansion in methods til method defn is executed
|
489
510
|
class MethodNode
|
490
|
-
def
|
511
|
+
def old_macro_expand(macros,session)
|
491
512
|
if session[:@expand_in_defs]
|
492
513
|
session[:@expand_in_defs]=false
|
493
514
|
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
@@ -503,6 +524,16 @@ class Macro
|
|
503
524
|
return Macro.postpone(self,session),false
|
504
525
|
end
|
505
526
|
end
|
527
|
+
def macro_expand(macros,session)
|
528
|
+
expand=proc{|x| Node===x ? Macro.expand(x,macros,session) : x}
|
529
|
+
self.receiver= expand[receiver]
|
530
|
+
args.map! &expand if args
|
531
|
+
self.body= expand[body]
|
532
|
+
rescues.map! &expand if rescues
|
533
|
+
self.ensure_= expand[ensure_]
|
534
|
+
self.else_= expand[else_]
|
535
|
+
return self,false
|
536
|
+
end
|
506
537
|
end
|
507
538
|
|
508
539
|
#disable macro definitions within classes and modules
|
@@ -622,6 +653,10 @@ class Macro
|
|
622
653
|
return super
|
623
654
|
end
|
624
655
|
end
|
656
|
+
|
657
|
+
def image
|
658
|
+
"^"+first.image
|
659
|
+
end
|
625
660
|
end
|
626
661
|
|
627
662
|
class MacroNode < ValueNode
|
@@ -641,6 +676,25 @@ class Macro
|
|
641
676
|
fail "unrecognized method header: #{header}"
|
642
677
|
end
|
643
678
|
@data=replace [receiver,header,args,body,rescues,else_,ensure_]
|
679
|
+
|
680
|
+
=begin hmm, maybe not a good idea....
|
681
|
+
#quote parameters to yield within macro
|
682
|
+
walk{|cntr,i,subi,item|
|
683
|
+
case item
|
684
|
+
when KWCallNode;
|
685
|
+
if item.name=="yield"
|
686
|
+
raise ArgumentError if item.block or item.blockparams
|
687
|
+
if item.params
|
688
|
+
raise ArgumentError if UnAmpNode===item.params.last
|
689
|
+
item.params.map!{|param| FormNode.new(nil,ParenedNode[param]) }
|
690
|
+
end
|
691
|
+
false
|
692
|
+
else true
|
693
|
+
end
|
694
|
+
else true
|
695
|
+
end
|
696
|
+
}
|
697
|
+
=end
|
644
698
|
end
|
645
699
|
|
646
700
|
alias else_ elses
|
@@ -698,6 +752,7 @@ class Macro
|
|
698
752
|
Expr.-, RescueNode.*, ElseNode.-, EnsureNode.-,
|
699
753
|
'end'
|
700
754
|
]>>MacroNode,
|
755
|
+
-[ParenedNode, '(', Expr.-, ')', BlockNode.-, KW('do').~.la]>>CallNode,
|
701
756
|
]
|
702
757
|
end
|
703
758
|
def wants_semi_context
|
data/lib/macro/form.rb
CHANGED
@@ -48,6 +48,11 @@ class Macro
|
|
48
48
|
rebuild_transform
|
49
49
|
end
|
50
50
|
|
51
|
+
def initialize_ivars
|
52
|
+
rebuild_transform
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
51
56
|
# Initialize the transform and create all the form escapes that are
|
52
57
|
# used in this form
|
53
58
|
def rebuild_transform
|
@@ -114,15 +119,20 @@ class Macro
|
|
114
119
|
":("+text.unparse(o)+")"
|
115
120
|
end
|
116
121
|
|
117
|
-
# Called when the form is evaluated to
|
118
|
-
#
|
122
|
+
# Called when the form is evaluated to convert the abstract form
|
123
|
+
# of the parse tree into a concrete form that can be modified (makes a
|
119
124
|
# copy of the form).
|
120
125
|
#
|
121
126
|
# +transform+:: the transform to use in the deep copy
|
122
127
|
#
|
123
128
|
def reify transform
|
124
129
|
transform.each_pair{|k,v|
|
125
|
-
|
130
|
+
case v
|
131
|
+
when Node; next
|
132
|
+
when Symbol; v=CallNode[nil,v.to_s]
|
133
|
+
else v=Macro.quote v
|
134
|
+
end
|
135
|
+
transform[k]=v
|
126
136
|
}
|
127
137
|
deep_copy(transform)
|
128
138
|
end
|
@@ -161,17 +171,13 @@ class Macro
|
|
161
171
|
# RedParse::SomeNodeType[ some transform of code ]
|
162
172
|
#
|
163
173
|
def parses_like
|
164
|
-
|
165
|
-
|
166
|
-
# the below expression (so that we have one "name" for the form
|
167
|
-
# instead of two representations of the same name)
|
168
|
-
CallSiteNode[CallSiteNode[ConstantNode[nil,"Macro","Names",formname], "reify", [@transform],nil,nil], "text", nil,nil,nil]
|
169
|
-
#:(::Macro::Names::^(formname).reify.text)
|
174
|
+
CallSiteNode[CallSiteNode[formname, "reify", [@transform]], "text"]
|
175
|
+
#:(^(formname).reify(^@transform).text)
|
170
176
|
end
|
171
177
|
|
172
178
|
# Lazily evaluate the name of the form and return it
|
173
179
|
def formname
|
174
|
-
@formname ||=
|
180
|
+
@formname ||= ConstantNode[nil,"Macro","Names",::Macro::Names.request(self)]
|
175
181
|
end
|
176
182
|
end
|
177
183
|
|
data/lib/macro/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubymacros
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caleb Clausen
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-07-07 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.12.2
|
34
34
|
version:
|
35
|
-
description: Macros are programmed in ruby itself. And since parse trees are represented in RedParse format, they're easier to use (programatically) and more object-oriented than other available ruby parsetree formats. (RedParse Node format is actually designed to be straightforward to use and to represent the structure of ruby source code very closely.)
|
35
|
+
description: RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a purely textual substitution scheme, RubyMacros can manipulate and morph Ruby parse trees (in the form of RedParse Nodes) at parse time in just about any way you see fit. Macros are programmed in ruby itself. And since parse trees are represented in RedParse format, they're easier to use (programatically) and more object-oriented than other available ruby parsetree formats. (RedParse Node format is actually designed to be straightforward to use and to represent the structure of ruby source code very closely.) ==
|
36
36
|
email: rubymacros-owner @at@ inforadical .dot. net
|
37
37
|
executables: []
|
38
38
|
|
@@ -97,6 +97,6 @@ rubyforge_project: rubymacros
|
|
97
97
|
rubygems_version: 1.3.1
|
98
98
|
signing_key:
|
99
99
|
specification_version: 2
|
100
|
-
summary:
|
100
|
+
summary: RubyMacros is a lisp-like macro pre-processor for Ruby.
|
101
101
|
test_files:
|
102
102
|
- test/test_all.rb
|