rubymacros 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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