rubymacros 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,16 @@
1
+ === 0.1.2 / 2009-04-26
2
+
3
+ * 7 minor enhancements
4
+ * lots of nice comments added, thanks to Paul Brannan and Tatsuji Kawai
5
+ * Paul fixed the weird rdoc failure too!
6
+ * incorrect warning removed
7
+ * Value changed to Expr in parse rules
8
+ * hack to get 'rake test' to stay in 1 process (to keep netbeans happy)
9
+ * in test_form.rb, don't test deep_copy on nil forms
10
+ * all files should be world-readable now
11
+
12
+ === 0.1.0 / 2008-10-24
13
+
14
+ * 1 major enhancement
15
+ * Birthday!
16
+
data/Manifest.txt CHANGED
@@ -1,6 +1,7 @@
1
1
  COPYING.LGPL
2
2
  Manifest.txt
3
3
  README.txt
4
+ History.txt
4
5
  rubymacros.vpj
5
6
  Rakefile
6
7
  test/test_form.rb
@@ -19,3 +20,6 @@ example/simple.rb
19
20
  example/simple_wrap.rb
20
21
  example/with.rb
21
22
  example/with_wrap.rb
23
+ example/linenum.rb
24
+ example/linenum_user.rb
25
+ example/linenum_wrap.rb
data/Rakefile CHANGED
@@ -4,7 +4,18 @@ require 'rubygems'
4
4
  require 'hoe'
5
5
  require 'lib/macro/version.rb'
6
6
 
7
-
7
+
8
+ if $*==["test"]
9
+ #hack to get 'rake test' to stay in one process
10
+ #which keeps netbeans happy
11
+ $:<<"lib"
12
+ $:<<"../redparse/lib" #hack hack hack
13
+ require 'redparse'
14
+ require "test/test_all.rb"
15
+ Test::Unit::AutoRunner.run
16
+ exit
17
+ end
18
+
8
19
  readme=open("README.txt")
9
20
  readme.readline("\n== DESCRIPTION:")
10
21
  readme.readline("\n\n")
@@ -19,7 +30,7 @@ require 'lib/macro/version.rb'
19
30
  _.description=desc
20
31
  _.summary=desc[/\A[^.]+\./]
21
32
  # _.spec_extras={:bindir=>''}
22
- _.rdoc_pattern=/\A(README\.txt|lib\/.*\.rb)\Z/
33
+ # _.rdoc_pattern=/\A(README\.txt|lib\/.*\.rb)\Z/
23
34
  _.remote_rdoc_dir="/"
24
35
  end
25
36
 
@@ -0,0 +1,18 @@
1
+ macro __DIR__
2
+ :(File.dirname(__FILE__))
3
+ end
4
+
5
+ macro foo
6
+ :(
7
+ "just an example, to take up several lines,"+
8
+ "so that line numbers might be messed up"
9
+ )
10
+ end
11
+
12
+ def user_of_foo
13
+ foo
14
+ end
15
+
16
+ def linenumuser
17
+ p __LINE__ #should print 17
18
+ end
@@ -0,0 +1,7 @@
1
+ SCRIPT_LINES__={}
2
+ require 'example/linenum_wrap.rb'
3
+
4
+ p SCRIPT_LINES__.keys.grep(/linenum/).each{|k|
5
+ p k
6
+ puts SCRIPT_LINES__[k]
7
+ }
@@ -0,0 +1,4 @@
1
+ require 'macro'
2
+ Macro.require 'example/linenum.rb'
3
+
4
+ linenumuser
data/lib/macro/form.rb CHANGED
@@ -22,15 +22,25 @@
22
22
  require "redparse"
23
23
  require "macro"
24
24
  class Macro
25
+ # The syntax node for forms
25
26
  class FormNode < RedParse::ValueNode
26
27
  param_names :text
28
+
29
+ # Create a new form node. The user should normally not call this
30
+ # function. Form nodes are created by the parser.
31
+ #
32
+ # +colon+:: A colon token
33
+ # +text+:: A ParenedNode or VarLikeNode
34
+ #
27
35
  def initialize(colon,text)
36
+ # Certain node types need to be quoted
28
37
  if RedParse::VarLikeNode===text
29
38
  @transform=HashLiteralNode[]
30
39
  super text
31
40
  return
32
41
  end
33
42
 
43
+ # Sanity check - make sure this is a valid ParenedNode
34
44
  fail unless ParenedNode===text && text.size==1
35
45
  text=text.body
36
46
 
@@ -38,7 +48,10 @@ class Macro
38
48
  rebuild_transform
39
49
  end
40
50
 
51
+ # Initialize the transform and create all the form escapes that are
52
+ # used in this form
41
53
  def rebuild_transform
54
+ # TODO: this method needs to be better documented/refactored
42
55
  @transform=HashLiteralNode[]
43
56
  @parameters=[]
44
57
 
@@ -76,18 +89,37 @@ class Macro
76
89
  return self
77
90
  end
78
91
 
92
+ # Iterate over all the parameters in this form
93
+ #
94
+ # +block+:: the block to call for each parameter
95
+ #
79
96
  def each_parameter(&block)
80
97
  @parameters.each(&block) if @parameters
81
98
  end
82
99
 
100
+ # Make a deep copy of this form
101
+ #
102
+ # +transform+:: TODO
103
+ #
83
104
  def deep_copy transform={}
84
105
  super(transform).rebuild_transform
85
106
  end
86
107
 
108
+ # Performs the reverse of a parse operation (turns the node into a
109
+ # string)
110
+ #
111
+ # +o+:: a list of options for unparse
112
+ #
87
113
  def unparse o
88
114
  ":("+text.unparse(o)+")"
89
115
  end
90
116
 
117
+ # Called when the form is evaluated to ensure that the abstract form
118
+ # in the parse tree is a concrete form that can be modified (makes a
119
+ # copy of the form).
120
+ #
121
+ # +transform+:: the transform to use in the deep copy
122
+ #
91
123
  def reify transform
92
124
  transform.each_pair{|k,v|
93
125
  transform[k]=Macro.quote(v) unless Node===v or VarNameToken===v
@@ -95,6 +127,7 @@ class Macro
95
127
  deep_copy(transform)
96
128
  end
97
129
 
130
+ # Transform this node into a ParseTree parse tree
98
131
  def parsetree
99
132
  parses_like.parsetree
100
133
  end
@@ -103,6 +136,11 @@ class Macro
103
136
 
104
137
  module ::Macro::Names
105
138
  COUNT=[0]
139
+
140
+ # Sets up the mapping from a form name to a form literal
141
+ #
142
+ # +form+:: the name of the form
143
+ #
106
144
  def self.request form
107
145
  result="Number_#{COUNT[0]+=1}"
108
146
  const_set result, form
@@ -112,35 +150,62 @@ class Macro
112
150
 
113
151
  #CallSiteNode=RedParse::CallSiteNode
114
152
  #ConstantNode=RedParse::ConstantNode
153
+
154
+ # Turn the form into something that is legal ruby (since :(..) is
155
+ # not legal ruby syntax). Thus the form is changed from the syntax:
156
+ #
157
+ # :(code)
158
+ #
159
+ # to:
160
+ #
161
+ # RedParse::SomeNodeType[ some transform of code ]
162
+ #
115
163
  def parses_like
164
+ # TODO: either split this into mulitple lines or multiple methods
165
+ # possibly formname() should return the ConstantNode portion of
166
+ # the below expression (so that we have one "name" for the form
167
+ # instead of two representations of the same name)
116
168
  CallSiteNode[CallSiteNode[ConstantNode[nil,"Macro","Names",formname], "reify", [@transform],nil,nil], "text", nil,nil,nil]
117
169
  #:(::Macro::Names::^(formname).reify.text)
118
170
  end
119
171
 
172
+ # Lazily evaluate the name of the form and return it
120
173
  def formname
121
174
  @formname ||= ::Macro::Names.request(self)
122
175
  end
123
176
  end
124
177
 
125
178
 
126
- class FormParameterNode < RedParse::ValueNode #not to appear in final parsetree?
127
- param_names :val
179
+ # The syntax node for a form escape
180
+ class FormParameterNode < RedParse::ValueNode
181
+ param_names :val
128
182
 
129
- def initialize(*args)
130
- super(args.last)
131
- end
183
+ # Called by the parser to create a new form parameter node.
184
+ #
185
+ # +args+:: TODO
186
+ #
187
+ def initialize(*args)
188
+ super(args.last)
189
+ end
132
190
 
133
- def unparse o
134
- "^"+val.unparse(o)
135
- end
191
+ # Performs the reverse of a parse operation (turns the node into a
192
+ # string)
193
+ #
194
+ # +o+:: a list of options for unparse
195
+ #
196
+ def unparse o
197
+ "^"+val.unparse(o)
198
+ end
136
199
 
137
- def wraplevel
138
- return val.wraplevel+1 if FormParameterNode===val
139
- return 1
140
- end
200
+ # The number of carats (^) that occur in the escape. Note that
201
+ # this method is recursive.
202
+ def wraplevel
203
+ return val.wraplevel+1 if FormParameterNode===val
204
+ return 1
205
+ end
141
206
 
142
- def inspect
143
- val.unparse({})
144
- end
207
+ def inspect
208
+ val.unparse({})
209
+ end
145
210
  end
146
211
  end
data/lib/macro/version.rb CHANGED
@@ -18,5 +18,5 @@
18
18
 
19
19
 
20
20
  class Macro
21
- VERSION="0.1.1"
21
+ VERSION="0.1.2"
22
22
  end
data/lib/macro.rb CHANGED
@@ -19,7 +19,6 @@
19
19
  #$:.unshift "../redparse/lib"
20
20
  #warn "$: hacked up to find latest redparse"
21
21
 
22
- warn "rubygems require disabled"
23
22
  require 'rubygems'
24
23
  require 'redparse'
25
24
  require "macro/form"
@@ -27,6 +26,13 @@ require "macro/version"
27
26
 
28
27
  warn "need to insert extra parens around form params and macro texts"
29
28
 
29
+ class Object # :nodoc:
30
+ #as close as I can get to an empty binding (used below in Macro.eval)
31
+ def Object.new_binding() # :nodoc:
32
+ binding
33
+ end
34
+ end
35
+
30
36
  class Macro
31
37
  #import Node classes from RedParse
32
38
  RedParse::constants.each{|k|
@@ -46,6 +52,9 @@ class Macro
46
52
 
47
53
  #like Kernel#require, but allows macros (and forms) as well.
48
54
  #c extensions (.dll,.so,etc) cannot be loaded via this method.
55
+ #
56
+ # +filename+:: the name of the feature to require
57
+ #
49
58
  def Macro.require(filename)
50
59
  filename+='.rb' unless filename[/\.rb\Z/]
51
60
  return if $".include? filename
@@ -53,12 +62,11 @@ class Macro
53
62
  load filename
54
63
  end
55
64
 
56
- #as close as I can get to an empty binding
57
- def Object.new_binding() # :nodoc:
58
- binding
59
- end
60
-
61
65
  #like Kernel#load, but allows macros (and forms) as well.
66
+ #
67
+ # +filename+:: the name of the file to load
68
+ # +wrap+:: whether to wrap the loaded file in an anonymous module
69
+ #
62
70
  def Macro.load(filename,wrap=false)
63
71
  [''].concat($:).each{|pre|
64
72
  pre+="/" unless %r{(\A|/)\Z}===pre
@@ -78,6 +86,12 @@ class Macro
78
86
  #beware: default for second argument is currently broken.
79
87
  #best practice is to pass an explicit binding (see
80
88
  #Kernel#binding) for now.
89
+ #
90
+ # +code+:: a string of code to evaluate
91
+ # +binding+:: the binding in which to evaluate the code
92
+ # +file+:: the name of the file this code came from
93
+ # +line+:: the line number this code came from
94
+ #
81
95
  def Macro.eval(code,binding=nil,file="(eval)",line=1)
82
96
  #binding should default to Binding.of_caller, but byellgch
83
97
 
@@ -86,6 +100,16 @@ class Macro
86
100
  tree.eval binding||::Object.new_binding,file,line
87
101
  end
88
102
 
103
+ # A helper for Macro.eval which returns a RedParse tree for the given
104
+ # code string.
105
+ #
106
+ # +code+:: a string of code to evaluate
107
+ # +binding+:: the binding in which to evaluate the code
108
+ # +file+:: the name of the file this code came from
109
+ # +line+:: the line number this code came from
110
+ # +lvars+:: a list of local variables (empty unless called
111
+ # recursively)
112
+ #
89
113
  def Macro.parse(code,file="(eval)",line=1,lvars=[])
90
114
  if Binding===file or Array===file
91
115
  lvars=file
@@ -100,6 +124,10 @@ class Macro
100
124
  UNCOPYABLE= #Symbol|Numeric|true|false|nil|
101
125
  Module|Proc|IO|Method|UnboundMethod|Thread|Continuation
102
126
 
127
+ # Return a quoted node for the given scalar
128
+ #
129
+ # +obj+:: any object or node
130
+ #
103
131
  def Macro.quote obj
104
132
  #result=
105
133
  case obj
@@ -108,6 +136,8 @@ class Macro
108
136
  when String:
109
137
  obj=obj.gsub(/['\\]/){|ch| '\\'+ch }
110
138
  StringNode[obj,{:@open=>"'", :@close=>"'"}]
139
+
140
+ # TODO: The following is dead code and should be removed
111
141
  else
112
142
  #result=:(::Macro::QuotedStore[^QuotedStore.size])
113
143
  result=CallNode[ConstantNode[nil,"Macro","QuotedStore"],"[]",
@@ -123,6 +153,7 @@ class Macro
123
153
  end
124
154
  end
125
155
 
156
+ # TODO: dead code (only used by the dead else block above).
126
157
  def Macro.copy obj,seen={}
127
158
  result=seen[obj.__id__]
128
159
  return result if result
@@ -152,6 +183,26 @@ class Macro
152
183
  return result
153
184
  end
154
185
 
186
+ # Create a node to postpone the macro (or method) definition until it
187
+ # is actually executed. For example, in the following code:
188
+ #
189
+ # if foo
190
+ # macro bar
191
+ # ...
192
+ # end
193
+ # else
194
+ # macro bar
195
+ # ...
196
+ # end
197
+ # end
198
+ #
199
+ # without postponing macro definition, the latter macro would always
200
+ # override the former.
201
+ #
202
+ # +node+:: the RedParse node for the entire method or macro defintion
203
+ # that is being postponed
204
+ # +session+:: the context in which this macro is being processed
205
+ #
155
206
  def Macro.postpone node,session
156
207
  filename=session[:filename]
157
208
  unless session[:@modpath_unsure]
@@ -186,6 +237,13 @@ class Macro
186
237
  #beware: default for binding is currently broken.
187
238
  #best practice is to pass an explicit binding (see
188
239
  #Kernel#binding) for now.
240
+ #
241
+ # +binding+:: the binding in which to evaluate the node
242
+ # +file+:: for purpose of evaluation, the name of the file this node
243
+ # came from
244
+ # +line+:: for purpose of evaluation, the line number this node came
245
+ # from
246
+ #
189
247
  def eval(binding=nil,file=nil,line=nil)
190
248
  #binding should default to Binding.of_caller, but.... that's expensive
191
249
 
@@ -204,6 +262,12 @@ class Macro
204
262
  ::Kernel.eval unparsed, binding||::Object.new_binding,file||'(eval)',line||1
205
263
  end
206
264
 
265
+ # A helper for Macro.load and Macro.eval. The source code for the
266
+ # node is saved to a file so that it can be viewed in the debugger.
267
+ #
268
+ # +name+:: the name of the file being loaded
269
+ # +wrap+:: whether to wrap the loaded file in an anonymous module
270
+ #
207
271
  def load(name='',wrap=false)
208
272
  expanded_tree=self #Macro.expand(deep_copy,::Macro::GLOBALS)
209
273
 
@@ -233,7 +297,13 @@ class Macro
233
297
  return true
234
298
  end
235
299
 
300
+ # Convert this node to an S-expression
301
+ #
302
+ # +session+:: the context in which this macro is being processed
303
+ #
236
304
  def to_sexp session
305
+ # TODO: this (and all other functions similarly named) is possibly
306
+ # dead code
237
307
  self.class.name+"["+
238
308
  map{|param| call_to_sexp param,session }.join(", ")+
239
309
  ", {"+instance_variables.map{|iv|
@@ -464,7 +534,7 @@ class Macro
464
534
  if old_modpath
465
535
  session[:@modpath]=old_modpath
466
536
  else
467
- session[:@modpath].pop unwind
537
+ unwind.times{ session[:@modpath].pop }
468
538
  end
469
539
  session[:@namespace_type]=old_namespace_type
470
540
  session[:@modpath_unsure]=old_unsure
@@ -507,6 +577,10 @@ class Macro
507
577
  return parses_like,false #halt further recursion: already done where necessary
508
578
  end
509
579
 
580
+ # Convert this node to an S-expression
581
+ #
582
+ # +session+:: the context in which this macro is being processed
583
+ #
510
584
  def to_sexp session
511
585
  nest=session[:form_nest_level]
512
586
  session[:form_nest_level]=nest ? nest+1 : 2
@@ -523,6 +597,10 @@ class Macro
523
597
  end
524
598
 
525
599
  class FormParameterNode<ValueNode
600
+ # Convert this node to an S-expression
601
+ #
602
+ # +session+:: the context in which this macro is being processed
603
+ #
526
604
  def to_sexp session
527
605
  nest=session[:form_nest_level]||1
528
606
  carets=0
@@ -563,6 +641,11 @@ class Macro
563
641
  alias ensure_ ensures
564
642
  alias ensure ensures
565
643
 
644
+ # Performs the reverse of a parse operation (turns the node into a
645
+ # string)
646
+ #
647
+ # +o+:: a list of options for unparse
648
+ #
566
649
  def unparse o
567
650
  result="macro "
568
651
  result+=receiver.unparse(o)+'.' if receiver
@@ -591,10 +674,10 @@ class Macro
591
674
  [
592
675
  -[KW('macro'), KW(beginsendsmatcher).~.*, KW('end'), KW(/^(do|\{)$/).~.la]>>MisparsedNode
593
676
  ]+super+[
594
- -[Op('^@'), Value, LowerOp]>>FormParameterNode,
677
+ -[Op('^@'), Expr, LowerOp]>>FormParameterNode,
595
678
  -[Op(':@'), (ParenedNode&-{:size=>1})|(VarLikeNode&-{:ident=>"nil"})]>>FormNode,
596
679
  -['macro', CallSiteNode, KW(';'),
597
- Value.-, RescueNode.*, ElseNode.-, EnsureNode.-,
680
+ Expr.-, RescueNode.*, ElseNode.-, EnsureNode.-,
598
681
  'end'
599
682
  ]>>MacroNode,
600
683
  ]
@@ -602,6 +685,12 @@ class Macro
602
685
  def wants_semi_context
603
686
  super|KW('macro')
604
687
  end
688
+
689
+ # A regex for all the keywords that can be terminated with the 'end'
690
+ # keyword
691
+ #
692
+ # We use the base class's list, and add the 'macro' keyword to it.
693
+ #
605
694
  def beginsendsmatcher
606
695
  return @bem||=/#{super}|^macro$/
607
696
  end
@@ -609,6 +698,9 @@ class Macro
609
698
  def initialize(*args)
610
699
  super
611
700
  @lexer.enable_macros! if @lexer.respond_to? :enable_macros!
701
+
702
+ # Add ^ to the list of operators that could be either unary or
703
+ # binary
612
704
  @unary_or_binary_op=/^([\^:]|#@unary_or_binary_op)$/o
613
705
  end
614
706
  end
data/test/test_expand.rb CHANGED
@@ -16,6 +16,10 @@
16
16
  along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  =end
18
18
 
19
+ # TODO: add a test for a method definition inside of a method definition
20
+ # to ensure that the inner method definition is properly postponed
21
+
22
+
19
23
 
20
24
  require 'test/unit'
21
25
  require "macro"
data/test/test_form.rb CHANGED
@@ -21,7 +21,7 @@ require "macro"
21
21
  require 'test/unit'
22
22
  require 'rubygems'
23
23
  require 'rubylexer'
24
- require "test/code/testcases" #from rubylexer
24
+ require "rubylexer/test/testcases" #from rubylexer
25
25
  require 'pp'
26
26
 
27
27
 
@@ -30,7 +30,7 @@ class FormTest< Test::Unit::TestCase
30
30
 
31
31
  EXAMPLES.each_with_index{|x,i|
32
32
  next if /__END__/===x
33
- if / \^[^\s]/===x and x.size>1000
33
+ if / \^[^\s]/===x #and x.size>1000
34
34
  while x['^']
35
35
  warn "disabling tests of '#{x[/^.*\^.*$/]}'"
36
36
  x[/^.*\^.*$/]=''
@@ -65,7 +65,7 @@ class FormTest< Test::Unit::TestCase
65
65
 
66
66
  assert_equal as_tree, as_form
67
67
 
68
- assert_equal as_form, as_form.deep_copy
68
+ assert_equal as_form, as_form.deep_copy if as_form
69
69
  end
70
70
  end
71
71
  end
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.1
4
+ version: 0.1.2
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: 2008-10-25 00:00:00 +01:00
12
+ date: 2009-04-30 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.8.1
33
+ version: 1.12.2
34
34
  version:
35
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.)
36
36
  email: rubymacros-owner @at@ inforadical .dot. net
@@ -41,10 +41,12 @@ extensions: []
41
41
  extra_rdoc_files:
42
42
  - Manifest.txt
43
43
  - README.txt
44
+ - History.txt
44
45
  files:
45
46
  - COPYING.LGPL
46
47
  - Manifest.txt
47
48
  - README.txt
49
+ - History.txt
48
50
  - rubymacros.vpj
49
51
  - Rakefile
50
52
  - test/test_form.rb
@@ -63,6 +65,9 @@ files:
63
65
  - example/simple_wrap.rb
64
66
  - example/with.rb
65
67
  - example/with_wrap.rb
68
+ - example/linenum.rb
69
+ - example/linenum_user.rb
70
+ - example/linenum_wrap.rb
66
71
  has_rdoc: true
67
72
  homepage: http://rubymacros.rubyforge.org/
68
73
  post_install_message:
@@ -86,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
91
  requirements: []
87
92
 
88
93
  rubyforge_project: rubymacros
89
- rubygems_version: 1.3.0
94
+ rubygems_version: 1.3.1
90
95
  signing_key:
91
96
  specification_version: 2
92
97
  summary: Macros are programmed in ruby itself.