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.
Files changed (63) hide show
  1. checksums.yaml +4 -0
  2. data/COPYING.LGPL +503 -158
  3. data/History.txt +115 -5
  4. data/Makefile +68 -0
  5. data/README.txt +29 -6
  6. data/TODO +1 -0
  7. data/bin/macroruby +69 -0
  8. data/example/__dir__.rb +18 -0
  9. data/example/__dir___wrap.rb +18 -0
  10. data/example/andand.rb +18 -0
  11. data/example/andand_wrap.rb +18 -0
  12. data/example/assert.rb +29 -8
  13. data/example/assert0.rb +11 -0
  14. data/example/assert0_wrap.rb +5 -0
  15. data/example/assert_does_nothing_when_disabled.rb +19 -0
  16. data/example/assert_wrap.rb +21 -0
  17. data/example/expected_output.txt +88 -0
  18. data/example/formless_macro.rb +123 -0
  19. data/example/formless_macro_wrap.rb +20 -0
  20. data/example/inline.rb +97 -0
  21. data/example/linenum.rb +19 -1
  22. data/example/linenum_user.rb +18 -0
  23. data/example/linenum_wrap.rb +18 -0
  24. data/example/loop.rb +18 -0
  25. data/example/loop_wrap.rb +18 -0
  26. data/example/meta.rb +25 -0
  27. data/example/meta_wrap.rb +20 -0
  28. data/example/nilresult.rb +26 -0
  29. data/example/nilresult_wrap.rb +21 -0
  30. data/example/pipeline.rb +37 -0
  31. data/example/rescuing.rb +33 -0
  32. data/example/rescuing_wrap.rb +21 -0
  33. data/example/role.rb +103 -0
  34. data/example/role_with_eval.rb +92 -0
  35. data/example/self_in_class.rb +27 -0
  36. data/example/separated_scope.rb +42 -0
  37. data/example/separated_scope_wrap.rb +20 -0
  38. data/example/simple.rb +18 -0
  39. data/example/simple_wrap.rb +18 -0
  40. data/example/unproc.rb +31 -0
  41. data/example/unproc_wrap.rb +21 -0
  42. data/example/unroll.rb +34 -0
  43. data/example/unroll_macros.rb +119 -0
  44. data/example/unroll_wrap.rb +22 -0
  45. data/example/with.rb +50 -7
  46. data/example/with_wrap.rb +19 -0
  47. data/lib/macro.rb +307 -72
  48. data/lib/macro/ReduceWithsFor_RedParse_RedParse__MacroMixin_RedParse__WithMacros_1_8.rb +18880 -0
  49. data/lib/macro/ReduceWithsFor_RedParse_RedParse__MacroMixin_RedParse__WithMacros_1_9.rb +19101 -0
  50. data/lib/macro/form.rb +136 -27
  51. data/lib/macro/node.rb +64 -0
  52. data/lib/macro/version.rb +2 -5
  53. data/lib/rubymacros.rb +19 -0
  54. data/lib/rubymacros/version.rb +23 -0
  55. data/lib/weakkeyhash.rb +18 -0
  56. data/rubymacros.gemspec +60 -0
  57. data/test/test_all.rb +27 -2
  58. data/test/test_examples.rb +91 -0
  59. data/test/test_expand.rb +56 -1
  60. data/test/test_form.rb +108 -10
  61. data/test/test_unroll.rb +120 -0
  62. metadata +93 -65
  63. data/Rakefile +0 -37
@@ -1,3 +1,28 @@
1
- require 'test/test_form'
2
- require 'test/test_expand'
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
+
19
+ $:<<File.expand_path(File.dirname(__FILE__))
20
+ require 'test_form'
21
+ require 'test_expand'
22
+
23
+ #require 'test_unroll'
24
+
25
+ #require 'macro'
26
+ #Macro.
27
+ require 'test_examples'
3
28
 
@@ -0,0 +1,91 @@
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
+
19
+ require 'test/unit'
20
+ require "macro"
21
+
22
+ def def_example_test(name,expect)
23
+ define_method "test_example_#{(name).gsub('/','Y')}" do
24
+ out,err=capture_std_out_err{
25
+ load name
26
+ }
27
+ # p [expect,out]
28
+ assert_equal expect,out
29
+ assert empty_but_for_warns?(err), "expected no warnings, but saw these:\n"+err.gsub!(/^/," ")
30
+ end
31
+ end
32
+
33
+ def capture_std_out_err #dangerous... hard to debug
34
+ old={:O=>STDOUT.dup,:o=>$stdout,:E=>STDERR.dup,:e=>$stderr}
35
+ o1,o2=IO::pipe
36
+ e1,e2=IO::pipe
37
+ STDOUT.reopen(o2)
38
+ STDERR.reopen(e2)
39
+ $stdout=STDOUT
40
+ $stderr=STDERR
41
+ begin
42
+ yield
43
+ ensure
44
+ STDOUT.reopen old[:O]
45
+ STDERR.reopen old[:E]
46
+ $stdout,$stderr=old[:o],old[:e]
47
+ end
48
+
49
+ o2.close; e2.close
50
+ out=o1.read
51
+ err=e1.read
52
+ o1.close; e1.close
53
+
54
+ return out,err
55
+ end
56
+
57
+ def empty_but_for_warns? str
58
+ str=str.dup
59
+ str.gsub!(/^([^:]+:\d+: warning: .*)$/){STDERR.puts $1;''}
60
+ str.gsub!(/\n{2,}/,"\n")
61
+ /\A\Z/===str
62
+ end
63
+
64
+ class ExamplesTest<Test::Unit::TestCase
65
+ def setup
66
+ Macro.delete_all!
67
+ end
68
+
69
+ def self.example_dir
70
+ macropath=$LOADED_FEATURES.grep(/macro\.rb$/)[0]
71
+ unless %r{^[/\\]}===macropath
72
+ macropath=$LOAD_PATH.find{|dir| File.exist? dir+"/macro.rb"}
73
+ else
74
+ macropath=File.dirname(macropath)
75
+ end
76
+ File.dirname(macropath)+"/example/"
77
+ end
78
+
79
+ StringNode=RedParse::StringNode
80
+ can=File.read(example_dir+"/expected_output.txt").gsub(/^#.*$/,'').split(/^(.*) :\n/)
81
+ can.shift
82
+ #code=:()
83
+ # warn "unroll example disabled for now"
84
+ while name=can.shift
85
+ expect=can.shift
86
+ #code+=:(
87
+ def_example_test(name,expect) #unless /unroll/===name
88
+ end
89
+ #Macro.eval code
90
+
91
+ end
@@ -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
@@ -19,11 +19,18 @@
19
19
  # TODO: add a test for a method definition inside of a method definition
20
20
  # to ensure that the inner method definition is properly postponed
21
21
 
22
+ $VERBOSE=1
22
23
 
23
24
 
24
25
  require 'test/unit'
25
26
  require "macro"
27
+
28
+
26
29
  class ExpandTest < Test::Unit::TestCase
30
+ def setup
31
+ Macro.delete_all!
32
+ end
33
+
27
34
  def test_simple_expand
28
35
  Macro.eval "macro simple(a,b) :(^a+^b) end"
29
36
  ttt=RedParse::CallNode[nil, "p", [RedParse::CallNode[nil, "simple", [RedParse::LiteralNode[1],
@@ -36,4 +43,52 @@ class ExpandTest < Test::Unit::TestCase
36
43
  ttt.macro_expand(Macro::GLOBALS,{})
37
44
  assert_equal ttt.unparse,'p((1+2))'
38
45
  end
46
+
47
+ def test_expands_to_nil
48
+ Macro.eval "macro nilmacro; nil end"
49
+ tree=Macro.parse "foo; nilmacro; bar"
50
+ tree=Macro.expand tree
51
+ assert RedParse::SequenceNode===tree
52
+ assert_equal 2, tree.size
53
+ assert RedParse::CallNode===tree.first
54
+ assert_equal "foo", tree.first.name
55
+ assert RedParse::CallNode===tree.last
56
+ assert_equal "bar", tree.last.name
57
+
58
+ tree=Macro.parse "nilmacro; bar"
59
+ tree=Macro.expand tree
60
+ tree=tree.first if RedParse::SequenceNode===tree and tree.size==1
61
+ assert RedParse::CallNode===tree
62
+ assert_equal "bar", tree.name
63
+
64
+ tree=Macro.parse "foo; nilmacro"
65
+ tree=Macro.expand tree
66
+ tree=tree.first if RedParse::SequenceNode===tree and tree.size==1
67
+ assert RedParse::CallNode===tree
68
+ assert_equal "foo", tree.name
69
+
70
+ tree=Macro.parse "nilmacro"
71
+ tree=Macro.expand tree
72
+ assert RubyMacros::JustNilNode===tree
73
+ assert_equal tree.unparse, 'nil'
74
+
75
+ tree=Macro.parse "if nilmacro; p :oops; end"
76
+ tree=Macro.expand tree
77
+ assert RedParse::IfNode===tree
78
+ assert RubyMacros::JustNilNode===tree.condition
79
+ assert_equal 'if nil;p :oops;;end', tree.unparse
80
+ end
81
+
82
+ def test_unparse_form_escape_on_assign_lhs
83
+ tree=Macro.parse "(^a)=^b"
84
+ assert_match( /\(\^a\) *= *\^b/, tree.unparse )
85
+
86
+ tree=Macro.parse '(^x),(^w), =^y'
87
+ assert_match( /\(\^x\), \(\^w\), *= *\^y/, tree.unparse )
88
+ end
89
+
90
+ def test_call_method_on_form
91
+ $bar=2
92
+ assert_operator 0, :<, Macro.eval( ":(foo ^$bar).size" )
93
+ end
39
94
  end
@@ -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,6 +16,7 @@
16
16
  along with this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  =end
18
18
 
19
+ $VERBOSE=1
19
20
 
20
21
  require "macro"
21
22
  require 'test/unit'
@@ -26,10 +27,30 @@ require 'pp'
26
27
 
27
28
 
28
29
  class FormTest< Test::Unit::TestCase
30
+ def setup
31
+ Macro.delete_all!
32
+ end
33
+
29
34
  EXAMPLES=TestCases::TESTCASES
30
35
 
31
- EXAMPLES.each_with_index{|x,i|
36
+ SLOW=ENV['SLOW']
37
+
38
+ warn "some form tests disabled; set SLOW to enable them" unless SLOW
39
+ warn "some Macro.expand tests disabled; set SLOW to enable them" unless SLOW
40
+
41
+ nullfile= File.open(File.exist?( "/dev/null" )? "/dev/null" : "dev_null","w")
42
+
43
+ EXAMPLES.uniq.each_with_index{|x,i|
32
44
  next if /__END__/===x
45
+ begin
46
+ #old_VERBOSE=$VERBOSE;$VERBOSE=false
47
+ oldSTDERR=STDERR.dup; STDERR.reopen(nullfile)
48
+ catch(:foo){ eval "BEGIN{throw :foo};"+x }
49
+ rescue SyntaxError
50
+ next
51
+ #ensure $VERBOSE=old_VERBOSE
52
+ ensure STDERR.reopen(oldSTDERR)
53
+ end
33
54
  if / \^[^\s]/===x #and x.size>1000
34
55
  while x['^']
35
56
  warn "disabling tests of '#{x[/^.*\^.*$/]}'"
@@ -37,22 +58,52 @@ class FormTest< Test::Unit::TestCase
37
58
  end
38
59
  next
39
60
  end
40
- define_method "test_form_around_#{x.gsub(/[^ -~]/){|ch| "\\x"+ch[0].to_s(16)}}" do
61
+ x.gsub!('v','vv') #hacky
62
+ escaped=x.gsub(/[^ -~]/){|ch|
63
+ ch=ch[0]
64
+ ch=ch.getbyte 0 if ch.respond_to? :getbyte
65
+ "\\x"+ch.to_s(16)
66
+ }
67
+ define_method "test_form_around_#{escaped}" do
41
68
  check x
42
- end
69
+ end if SLOW or rand<0.5
70
+ define_method "test_form_and_escape_around_#{escaped}" do
71
+ if /\A *undef /===x
72
+ err=RedParse::ParseError
73
+ end
74
+ begin
75
+ check_for_syntax_error x
76
+ rescue err||NilClass #ignore
77
+ else fail "#{err} expected, but none occurred" if err
78
+ end
79
+ end if SLOW or rand<0.5 unless /\A\s*(?:=begin|return|next|break)/===x
80
+ define_method "test_form_and_call_and_escape_around_#{escaped}" do
81
+ if /\A *undef /===x
82
+ err=RedParse::ParseError
83
+ end
84
+ begin
85
+ check_for_syntax_error x, ":(foo(^","\n))"
86
+ rescue err||NilClass #ignore
87
+ else fail "#{err} expected, but none occurred" if err
88
+ end
89
+ end if SLOW or rand<0.5 unless /\A\s*(?:=begin|return|next|break)/===x
90
+ define_method "test_Macro.expand_of_#{escaped}" do
91
+ check_for_syntax_error x,"",""
92
+ end if SLOW or rand<0.5
43
93
  }
94
+ warn "#{__FILE__}:#{__LINE__}: warning: tests of :(^...) and Macro.expand are weak"
44
95
 
45
- def check(code)
46
- begin
96
+ def check(code,pre="\n",post="\n")
97
+ #begin
47
98
  # puts code
48
- begin as_form=Macro.eval(":(\n"+code+"\n)")
99
+ begin as_form=Macro.eval(":("+pre+code+post+")")
49
100
  rescue Exception=>formexc
50
101
  0
51
102
  end
52
103
 
53
104
  begin
54
105
  as_tree=RedParse.new(" \n"+code).parse
55
- as_tree=RedParse::VarLikeNode["nil", {:@value=>false}] if RedParse::NopNode===as_tree
106
+ as_tree=RedParse::SequenceNode[] if RedParse::NopNode===as_tree
56
107
  rescue Exception=>treeexc
57
108
  0
58
109
  end
@@ -63,17 +114,44 @@ class FormTest< Test::Unit::TestCase
63
114
  # as_form.delete_extraneous_ivars! if as_form
64
115
  # as_tree.delete_extraneous_ivars! if as_tree
65
116
 
117
+ if as_tree and as_form and as_tree.offset != as_form.offset
118
+ warn "form and parse tree did not have same offsets, input: '#{code}'"
119
+ as_form.instance_variable_set :@offset, as_tree.offset
120
+ end
66
121
  assert_equal as_tree, as_form
67
122
 
68
123
  assert_equal as_form, as_form.deep_copy if as_form
69
- end
124
+ #end
125
+ end
126
+ def check_for_syntax_error(code,pre=":(^",post="\n)")
127
+ # puts code
128
+ as_form=Macro.expand(pre+code+post)
129
+
130
+ assert_equal as_form, as_form.deep_copy
131
+ assert_unparses_wo_syntax_error(as_form) #weak weak
132
+ end
133
+
134
+ @@auwose=0
135
+ def assert_unparses_wo_syntax_error(tree) #weak test
136
+ warn "using assert_unparses_wo_syntax_error, which is weak" if (@@auwose+=1)==1
137
+ catch(:foo){
138
+ old_STDERR=$stderr
139
+ $stderr=open("/dev/null",'w') if File.exist? "/dev/null"
140
+ begin
141
+ eval "BEGIN{throw :foo};"+tree.unparse #weak test
142
+ ensure $stderr=old_STDERR if File.exist? "/dev/null"
143
+ end
144
+ }
70
145
  end
71
146
  end
72
147
 
73
148
  class FormParameterTest< Test::Unit::TestCase
149
+ def setup
150
+ Macro.delete_all!
151
+ end
74
152
  DATA=[
75
153
  "1", "1.1", "nil", "false", "true",
76
- ":sym", "[1,2,3]", "{'a'=>4,:b=>6}", "'s'",
154
+ "[1,2,3]", "{'a'=>4,:b=>6}", "'s'",
77
155
  ]
78
156
  DIFFICULT_CHILDREN=[
79
157
  "RedParse::ConstantNode[nil,'Object']","(a=[];a<<a)"
@@ -107,4 +185,24 @@ class FormParameterTest< Test::Unit::TestCase
107
185
  assert_equal( $b.inspect, $a.inspect) #byaah, not the best way....
108
186
  }
109
187
  end
188
+
189
+ def test_escape_from_inner_form
190
+ Macro.parse ":(:(ielf=^rec))"
191
+ Macro.eval ":(:(ielf=^rec))"
192
+ end
193
+
194
+ def test_marshal_of_form_param_with_hash
195
+
196
+ #why is this failing??? seems like a marshal bug??
197
+ #seems to be fixed in mri 1.9.2
198
+ tree=Macro.parse ":(^{'ffdf'=>4})"
199
+ assert_nothing_raised{
200
+ dumped=Marshal.dump tree
201
+ Marshal.load dumped
202
+ }
203
+ assert_nothing_raised{
204
+ dumped=Marshal.dump tree
205
+ Marshal.load dumped
206
+ }
207
+ end
110
208
  end
@@ -0,0 +1,120 @@
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
+
19
+ require 'test/unit'
20
+ require 'macro'
21
+ Macro.require 'example/unroll'
22
+
23
+ class UnrollTest<Test::Unit::TestCase
24
+ def setup
25
+ Macro.delete_all!
26
+ end
27
+
28
+ def setup(seed=Time.now.to_i)
29
+ srand(seed)
30
+ @data=loop()
31
+ end
32
+
33
+ def choose(list)
34
+ list[rand(list.size)]
35
+ end
36
+
37
+ def loop
38
+ choose(LOOPS).
39
+ gsub!("<<<cond>>>", loop_condition).
40
+ gsub!("<<<body>>>", loop_body)
41
+ end
42
+
43
+ def loop_condition
44
+ choose(LOOP_CONDITIONS)
45
+ end
46
+
47
+ def loop_body max=4
48
+ x=rand(20)
49
+ size=
50
+ if x==0; 0
51
+ else case x/2
52
+ when 1; 1
53
+ when 2; 2
54
+ when 9; 10
55
+ else 4
56
+ end
57
+ end
58
+ result=(1..size).map{ choose(LOOP_BODY_PARTS) }.join("\n")
59
+ if max>=0
60
+ result.gsub!("<<<body>>>", loop_body(max-1))
61
+ else
62
+ result.gsub!("<<<body>>>", "nil")
63
+ end
64
+ result.gsub!("redo(","log(")
65
+ result
66
+ end
67
+
68
+ LOOPS=[
69
+ "while <<<cond>>>\n <<<body>>>\nend\n",
70
+ "until <<<cond>>>\n <<<body>>>\nend\n",
71
+ "(\n<<<body>>>\n)while <<<cond>>>\n",
72
+ "(\n<<<body>>>\n)until <<<cond>>>\n",
73
+ "begin\n<<<body>>>\nend while <<<cond>>>\n",
74
+ "begin\n<<<body>>>\nend until <<<cond>>>\n",
75
+ ]
76
+ LOOP_CONDITIONS=[
77
+ "true", "false", "rand<0.5", "rand<0.95"
78
+ ]
79
+ LOOP_BODY_PARTS=[
80
+ "log(rand(100))",
81
+ "<<<fctl>>> if(rand<0.5)",
82
+ "<<<fctl>>> unless(rand<0.5)",
83
+ "<<<fctl>>>(<<<body>>>) if(rand<0.5)",
84
+ "<<<fctl>>>(<<<body>>>) unless(rand<0.5)",
85
+ "if(rand<0.5)\n <<<body>>>\nend",
86
+ "unless(rand<0.5)\n <<<body>>>\nend",
87
+ "log((<<<body>>>))",
88
+ ]
89
+
90
+ FCTLS=%w[break next redo]
91
+
92
+ def clear_log; @log=[] end
93
+ def log x; @log<<x end
94
+
95
+ def test_unroll
96
+ 10.times{
97
+ clear_log
98
+ normal=eval(@data)
99
+ nlog=@log
100
+
101
+ clear_log
102
+ macrod=Macro.eval("unroll #@data")
103
+ mlog=@log
104
+
105
+ assert_equal normal,macrod
106
+ assert_equal nlog,mlog
107
+ }
108
+ end
109
+
110
+ def test_unroll_thorough times=1000,keys=(1..times).map{rand(0x1_0000_0000)}
111
+ keys.each{|key|
112
+ begin
113
+ setup key
114
+ test_unroll
115
+ rescue Exception=>e
116
+ raise e.class,"using setup key #{key}"+e.message,e.backtrace
117
+ end
118
+ }
119
+ end
120
+ end