rubymacros 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING.LGPL ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/Manifest.txt ADDED
@@ -0,0 +1,21 @@
1
+ COPYING.LGPL
2
+ Manifest.txt
3
+ README.txt
4
+ rubymacros.vpj
5
+ Rakefile
6
+ test/test_form.rb
7
+ test/test_expand.rb
8
+ lib/macro.rb
9
+ lib/macro/form.rb
10
+ lib/macro/version.rb
11
+ example/andand.rb
12
+ example/assert.rb
13
+ example/assert_wrap.rb
14
+ example/__dir__.rb
15
+ example/__dir___wrap.rb
16
+ example/loop.rb
17
+ example/loop_wrap.rb
18
+ example/simple.rb
19
+ example/simple_wrap.rb
20
+ example/with.rb
21
+ example/with_wrap.rb
data/README.txt ADDED
@@ -0,0 +1,119 @@
1
+ = RubyMacros
2
+ * http://rubymacros.rubyforge.org
3
+ * http://rubyforge.org/projects/rubymacros
4
+
5
+ == DESCRIPTION:
6
+ RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a
7
+ purely textual substitution scheme, RubyMacros can manipulate and morph
8
+ Ruby parse trees (in the form of RedParse Nodes) at parse time in just about
9
+ any way you see fit.
10
+
11
+ Macros are programmed in ruby itself. And since parse trees are represented
12
+ in RedParse format, they're easier to use (programatically) and more object-
13
+ oriented than other available ruby parsetree formats. (RedParse Node format
14
+ is actually designed to be straightforward to use and to represent the
15
+ structure of ruby source code very closely.)
16
+
17
+ == Benefits:
18
+ * Powerful and easy metaprogramming
19
+ * Create better DSLs
20
+ * Manipulate syntax trees to suit yourself
21
+ * Access local variables and other caller context unavailable to methods
22
+ * Macros as inline methods: should be slightly faster than equivalent methods
23
+
24
+ == Drawbacks:
25
+ Although in theory already as powerful as lisp macros, the current
26
+ implementation has a number of problems which added together make it merely
27
+ a proof of concept or toy at this point:
28
+ * pre-processing is very, very slow (because of RedParse)
29
+ * macro calls must be inside some sort of method;
30
+ * straight out macro calls at the top level won't work
31
+ * macros can't have blocks or receivers
32
+ * some ruby syntax is unsupported in files using macros
33
+ * files using macros must be loaded via Macro.require;
34
+ * Kernel#require will not recognize macros
35
+ * RedParse Node tree format will be changing slightly
36
+ * macros cannot be scoped
37
+ * no variable (or other) hygiene
38
+
39
+ == Requirements:
40
+ RubyMacros requires RedParse.
41
+
42
+ == Install:
43
+ gem install rubymacros
44
+
45
+ == Examples:
46
+ macro simple(a,b)
47
+ :(^a+^b)
48
+ end
49
+ def simple_user
50
+ p simple(1,2) #prints 3
51
+ end
52
+
53
+ #loop as a macro, should be a bit faster than the #loop method
54
+ macro loop(body)
55
+ :(while true
56
+ ^body
57
+ end
58
+ )
59
+ end
60
+
61
+ #for more examples, see the examples/ directory
62
+
63
+ == New Syntax:
64
+ I have invented 3 new syntactical constructions in order to allow reasonably
65
+ easy to use macros. Macros themselves look just like methods except that
66
+ 'macro' instead of 'def' is used to start the macro definition off. A form
67
+ literal is an expression surrounded by ':(' and ')'. The form escape operator
68
+ is '^'. '^' is a unary operator of fairly high precedence.
69
+
70
+ == Forms and Form Escapes:
71
+ Forms are an essential adjunct to macros. Forms represent quoted source
72
+ code, which has been parsed but not evaled yet. When a form literal is
73
+ executed, it returns a RedParse::Node representing the parse tree for the
74
+ enclosed source code. Within a form literal, a ^, used as a unary operator,
75
+ will escape the expression it controls, so that instead of being part of the
76
+ form's data, it is executed at the same time as the form literal, and the
77
+ result of an escaped expression (which should be a Node) is interpolated
78
+ into the form at that point. The whole effect is much like that of string
79
+ interpolations (#{}) inside string literals.
80
+
81
+ == How Macros Work
82
+ Typically, macros return a single form literal, which contains form escape
83
+ expressions within it which make use of the macro's parameters. However,
84
+ macro bodies may contain anything at all; more complicated macros will
85
+ likely not contain any forms. (Likewise, form literals may be used outside
86
+ macros, but the utility of doing so may be minimal.)
87
+
88
+ At parse time (well, really at method definition time, but in effect it's
89
+ much the same thing) method bodies are scanned for callsites which have the
90
+ names of known macros. When such a call is found, it is expanded as follows.
91
+ The parsetrees for the arguments to the callsite are passed as arguments to
92
+ the macro. The macro is expected to return a parsetree, which replaces the
93
+ macro callsite in the parsetree which contained it.
94
+
95
+
96
+
97
+ == Known Problems
98
+ * need to insert extra parens around form params and macro texts
99
+ * a variety of parsetrees are kept around forever for no good reason
100
+ * a few warnings and disabled tests in unit tests
101
+ * however, huge rediculous piles of RedParse warnings when running 'rake test'
102
+
103
+ == License:
104
+ Copyright (C) 2008 Caleb Clausen
105
+
106
+ This program is free software: you can redistribute it and/or modify
107
+ it under the terms of the GNU Lesser General Public License as published by
108
+ the Free Software Foundation, either version 3 of the License, or
109
+ (at your option) any later version.
110
+
111
+ This program is distributed in the hope that it will be useful,
112
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
113
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
114
+ GNU Lesser General Public License for more details.
115
+
116
+ You should have received a copy of the GNU Lesser General Public License
117
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
118
+
119
+
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ # Copyright (C) 2008 Caleb Clausen
2
+ # Distributed under the terms of Ruby's license.
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'lib/macro/version.rb'
6
+
7
+
8
+ readme=open("README.txt")
9
+ readme.readline("\n== DESCRIPTION:")
10
+ readme.readline("\n\n")
11
+ desc=readme.readline("\n\n")
12
+
13
+ hoe=Hoe.new("rubymacros", Macro::VERSION) do |_|
14
+ _.author = "Caleb Clausen"
15
+ _.email = "rubymacros-owner @at@ inforadical .dot. net"
16
+ _.url = ["http://rubymacros.rubyforge.org/", "http://rubyforge.org/projects/rubymacros/"]
17
+ _.extra_deps << ['redparse', '>= 0.8.0']
18
+ _.test_globs=["test/*"]
19
+ _.description=desc
20
+ _.summary=desc[/\A[^.]+\./]
21
+ # _.spec_extras={:bindir=>''}
22
+ _.rdoc_pattern=/\A(README\.txt|lib\/.*\.rb)\Z/
23
+ _.remote_rdoc_dir="/"
24
+ end
25
+
26
+
@@ -0,0 +1,7 @@
1
+ macro __DIR__
2
+ :(File.dirname(__FILE__))
3
+ end
4
+
5
+ def diruser
6
+ p __DIR__
7
+ end
@@ -0,0 +1,4 @@
1
+ require 'macro'
2
+ Macro.require 'example/__dir__.rb'
3
+
4
+ diruser
data/example/andand.rb ADDED
@@ -0,0 +1,3 @@
1
+ macro andand(a,b)
2
+ :(^a and ^b)
3
+ end
data/example/assert.rb ADDED
@@ -0,0 +1,42 @@
1
+ #this file uses macros! won't parse in normal ruby
2
+ $Debug=1
3
+ if $Debug
4
+ macro assert(cond)
5
+ if RedParse::OpNode===cond and /\A[=!]=\Z/===cond.op
6
+ left,op,right=*cond
7
+ :(fail 'expected '+^left.unparse({})+"(==#{^left}) to be "+
8
+ ^op+" "+^right.unparse({})+"(==#{^right})" unless ^cond)
9
+ else
10
+ :(fail "expected #{:(^^cond)}, but was not true" unless ^cond)
11
+ end
12
+ end
13
+ else
14
+ macro assert(cond)
15
+ end
16
+ end
17
+
18
+
19
+ #assert_equal... bah, who needs it?
20
+
21
+ def test_assert
22
+ a=1
23
+ b=2
24
+
25
+ assert a #ok
26
+ assert a!=b #ok
27
+
28
+ begin
29
+ assert(a==b) #oops, fails. msg="expected a(==1) to be == b(==2)"
30
+ rescue Exception=>e
31
+ assert("expected a(==1) to be == b(==2)"== e.message) #better be ok
32
+ else fail
33
+ end
34
+
35
+ begin
36
+ assert(nil) #oops, fails. msg="expected nil, but was not true"
37
+ rescue Exception=>e
38
+ assert("expected nil, but was not true"== e.message) #better be ok
39
+ #ok, that message didn't make a lot of sense...
40
+ else fail
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ require 'macro'
2
+ Macro.require 'example/assert'
3
+
4
+ test_assert
data/example/loop.rb ADDED
@@ -0,0 +1,23 @@
1
+ =begin doesn't work yet
2
+ macro loop(&body)
3
+ :(while true
4
+ ^body
5
+ )
6
+ end
7
+ =end
8
+
9
+ macro loop(body)
10
+ :(while true
11
+ ^body
12
+ end
13
+ )
14
+ end
15
+
16
+ def loop_user
17
+ i=0
18
+ loop(
19
+ p i
20
+ i+=1
21
+ break if i>=10
22
+ )
23
+ end
@@ -0,0 +1,4 @@
1
+ require 'macro'
2
+ Macro.require 'example/loop.rb'
3
+
4
+ loop_user
data/example/simple.rb ADDED
@@ -0,0 +1,4 @@
1
+ macro simple(a,b) :(^a+^b) end
2
+ def simple_user
3
+ p simple(1,2)
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'macro'
2
+ Macro.require 'example/simple'
3
+
4
+ simple_user
data/example/with.rb ADDED
@@ -0,0 +1,56 @@
1
+ =begin doesn't work yet
2
+
3
+ #change the default receiver within the block from self to new_default
4
+ macro with(new_default,&block)
5
+ block.walk{|parent,i,subi,node|
6
+ if RedParse::CallNode===node and node.receiver.nil?
7
+ node.receiver=new_default
8
+ end
9
+ true
10
+ }
11
+ return block
12
+ end
13
+
14
+ #used like this:
15
+ class Foo
16
+ def bar
17
+ @quux=999
18
+ p with "baz" do
19
+ [
20
+ @quux, #=>999, not nil
21
+ size #=>3, not 99
22
+ ]
23
+ end
24
+ end
25
+
26
+ def size; 99 end
27
+ end
28
+ Foo.new.bar
29
+
30
+
31
+ =end
32
+
33
+ #change the default receiver within the block from self to new_default
34
+ macro with(new_default,block)
35
+ block.walk{|parent,i,subi,node|
36
+ if RedParse::CallNode===node and node.receiver.nil?
37
+ node.receiver=new_default
38
+ end
39
+ true
40
+ }
41
+ return block
42
+ end
43
+
44
+ #used like this:
45
+ class Foo
46
+ def bar
47
+ @quux=999
48
+ p with "baz", [
49
+ @quux, #=>999, not nil
50
+ size #=>3, not 99
51
+ ]
52
+ end
53
+
54
+ def size; 99 end
55
+ end
56
+ Foo.new.bar
@@ -0,0 +1,3 @@
1
+ require 'macro'
2
+ Macro.require 'example/with'
3
+
data/lib/macro/form.rb ADDED
@@ -0,0 +1,146 @@
1
+ =begin
2
+ rubymacros - a macro preprocessor for ruby
3
+ Copyright (C) 2008 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
+
19
+ #warn '$LOAD_PATH hacked up to include latest redparse'
20
+ #$: << "../redparse/lib"
21
+
22
+ require "redparse"
23
+ require "macro"
24
+ class Macro
25
+ class FormNode < RedParse::ValueNode
26
+ param_names :text
27
+ def initialize(colon,text)
28
+ if RedParse::VarLikeNode===text
29
+ @transform=HashLiteralNode[]
30
+ super text
31
+ return
32
+ end
33
+
34
+ fail unless ParenedNode===text && text.size==1
35
+ text=text.body
36
+
37
+ super text
38
+ rebuild_transform
39
+ end
40
+
41
+ def rebuild_transform
42
+ @transform=HashLiteralNode[]
43
+ @parameters=[]
44
+
45
+ walkers=proc{|rcvr,wraplayers| #curry
46
+ rcvr.walk{|*args|
47
+ node=args.last
48
+ case node
49
+ when FormParameterNode
50
+ target=node.wraplevel
51
+ fail if wraplayers > target
52
+ if wraplayers==target #skip this parameter if it doesn't have enough wrappers
53
+ @parameters << node #remember parameter (and implicitly, location)
54
+
55
+ nil# and stop further recursion
56
+ else
57
+ true
58
+ end
59
+ when FormNode
60
+ #walk form with same walker we're using now, except an extra layer of form parameters
61
+ #must be present for them to be considered 'our' parameters
62
+ walkers[node.text,wraplayers+1]
63
+ nil #don't recurse in this node again, we just did it
64
+ else true
65
+ end
66
+ } if rcvr.respond_to? :walk
67
+ }
68
+ walkers[text,1]
69
+
70
+ @parameters.each{|orig_escd|
71
+ escd=orig_escd
72
+ escd=escd.val while FormParameterNode===escd
73
+ @transform.push LiteralNode[orig_escd.__id__], escd
74
+ }
75
+
76
+ return self
77
+ end
78
+
79
+ def each_parameter(&block)
80
+ @parameters.each(&block) if @parameters
81
+ end
82
+
83
+ def deep_copy transform={}
84
+ super(transform).rebuild_transform
85
+ end
86
+
87
+ def unparse o
88
+ ":("+text.unparse(o)+")"
89
+ end
90
+
91
+ def reify transform
92
+ transform.each_pair{|k,v|
93
+ transform[k]=Macro.quote(v) unless Node===v or VarNameToken===v
94
+ }
95
+ deep_copy(transform)
96
+ end
97
+
98
+ def parsetree
99
+ parses_like.parsetree
100
+ end
101
+
102
+
103
+
104
+ module ::Macro::Names
105
+ COUNT=[0]
106
+ def self.request form
107
+ result="Number_#{COUNT[0]+=1}"
108
+ const_set result, form
109
+ return result
110
+ end
111
+ end
112
+
113
+ #CallSiteNode=RedParse::CallSiteNode
114
+ #ConstantNode=RedParse::ConstantNode
115
+ def parses_like
116
+ CallSiteNode[CallSiteNode[ConstantNode[nil,"Macro","Names",formname], "reify", [@transform],nil,nil], "text", nil,nil,nil]
117
+ #:(::Macro::Names::^(formname).reify.text)
118
+ end
119
+
120
+ def formname
121
+ @formname ||= ::Macro::Names.request(self)
122
+ end
123
+ end
124
+
125
+
126
+ class FormParameterNode < RedParse::ValueNode #not to appear in final parsetree?
127
+ param_names :val
128
+
129
+ def initialize(*args)
130
+ super(args.last)
131
+ end
132
+
133
+ def unparse o
134
+ "^"+val.unparse(o)
135
+ end
136
+
137
+ def wraplevel
138
+ return val.wraplevel+1 if FormParameterNode===val
139
+ return 1
140
+ end
141
+
142
+ def inspect
143
+ val.unparse({})
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,22 @@
1
+ =begin
2
+ rubymacros - a macro preprocessor for ruby
3
+ Copyright (C) 2008 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
+
19
+
20
+ class Macro
21
+ VERSION="0.1.0"
22
+ end