ppr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,130 @@
1
+ # require 'yaml'
2
+
3
+ ######################################################################
4
+ # Safer string generator. #
5
+ ######################################################################
6
+
7
+
8
+ ## Class which allows to safely execute a proc which generate safely
9
+ # a string into a stream.
10
+ class SaferGenerator
11
+
12
+ ## The exception raised when the safer processed failed.
13
+ class SaferException < RuntimeError
14
+ end
15
+
16
+ ## The list of dangerous constants of Object.
17
+ DANGER_CONSTANTS = [ :File, :IO, :Dir ]
18
+
19
+ ## The list of dangerous methods of Kernel
20
+ DANGER_METHODS = [ :system, :`, :open ]
21
+
22
+
23
+ ## Creates a new safe context with while removing Kernel methods and
24
+ # constants from +black_list+ in addition to the default dangerous
25
+ # ones.
26
+ def initialize(*black_list)
27
+ # Set the black list of methods.
28
+ @black_methods = black_list.select do |symbol|
29
+ symbol.to_s[0].match(/[a-z_]/)
30
+ end
31
+ # Set the black list of constants.
32
+ @black_constants = black_list.select do |symbol|
33
+ symbol.to_s[0].match(/[A-Z]/)
34
+ end
35
+ end
36
+
37
+ ## Strips all the Kernel methods and constants appart from the
38
+ # elements of the white list.
39
+ # Also strip Object from dangerous methods and constants apart
40
+ # from the elements of the white list.
41
+ def secure
42
+ # Gather the methods to strip.
43
+ methods = DANGER_METHODS + @black_methods
44
+ # Gather the constants to strip.
45
+ constants = DANGER_CONSTANTS + @black_constants
46
+ # Strip the dangerous methods.
47
+ methods.each do |meth|
48
+ Kernel.send(:undef_method,meth)
49
+ end
50
+ # Strip the dangerous constants from Object.
51
+ constants.each do |cst|
52
+ Object.send(:remove_const,cst)
53
+ end
54
+ end
55
+
56
+
57
+ ## Executes +block+ in a safe context for generating text into a +stream+.
58
+ #
59
+ # If no stream is given, returns the result as a string instead.
60
+ def run(stream = nil, &block)
61
+ unless stream
62
+ # No stream given
63
+ to_return = true
64
+ stream = StringIO.new("")
65
+ end
66
+ # Creates the pipe for communicating with the block.
67
+ rd,wr = IO.pipe
68
+ # Creates a process for executing the block.
69
+ pid = fork
70
+ if pid then
71
+ # This is the parent: waits for the block execution result.
72
+ # No need to write on the pipe. close it.
73
+ wr.close
74
+ # Read the result of the process and send it to stream
75
+ until rd.eof?
76
+ stream << rd.read
77
+ end
78
+ # No more need of rd.
79
+ rd.close
80
+ # Wait the end of the child process
81
+ Process.wait(pid)
82
+ # Where there a trouble?
83
+ unless $?.exited? then
84
+ # pid did not exit, internal error.
85
+ raise "*Internal error*: safer process #{pid} did not exit."
86
+ end
87
+ if $?.exitstatus !=0 then
88
+ # Reconstruct the exception from the stream, the exit
89
+ # status is the number of line to use.
90
+ e0 = Marshal.load( stream.string.each_line.
91
+ to_a[-$?.exitstatus..-1].join )
92
+ # Then resend the eception encapsulated into another one
93
+ # telling the safer process failed.
94
+ begin
95
+ raise e0
96
+ rescue Exception => e1
97
+ raise SaferException.new("*Error*: exception occured in safer process #{pid}.")
98
+ end
99
+ end
100
+ else
101
+ # This is the child: enter in safe mode and execute the block.
102
+ # No need to write on the pipe. close it.
103
+ rd.close
104
+ # Secure.
105
+ secure
106
+ # Execute the block.
107
+ begin
108
+ block.call(wr)
109
+ rescue Exception => e
110
+ # The exception is serialized and passed to the main process
111
+ # through the pipe.
112
+ e = Marshal.dump(e)
113
+ wr << "\n" << e
114
+ # The exit status is the number of line of the serialized
115
+ # exception.
116
+ exit!(e.each_line.count)
117
+ end
118
+ # No more need of wr.
119
+ wr.close
120
+ # End the process without any error.
121
+ exit!(0)
122
+ end
123
+ # Is there a string to return?
124
+ if to_return then
125
+ return stream.string
126
+ else
127
+ return nil
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,57 @@
1
+ ######################################################################
2
+ ## Program testing KeywordSearcher ##
3
+ ######################################################################
4
+
5
+ require "ppr.rb"
6
+
7
+ # Create the keyword searcher
8
+ puts "Creating the keyword searcher..."
9
+ $searcher = KeywordSearcher.new
10
+
11
+ # Prepare the input
12
+ puts "Creating the input text..."
13
+ $input = "Histoire de rationalisation : Le Renard et les Raisins\n" +
14
+ "Certain Renard Gascon, d'autres disent Normand,\n" +
15
+ "Mourant presque de faim, vit au haut d'une treille\n" +
16
+ "Des Raisins mûrs apparemment,\n" +
17
+ "Et couverts d'une peau vermeille.\n" +
18
+ "Le galand en eût fait volontiers un repas ;\n" +
19
+ "Mais comme il n'y pouvait atteindre :\n" +
20
+ "\"Ils sont trop verts, dit-il, et bons pour des goujats. \"\n" +
21
+ "Fit-il pas mieux que de se plaindre ?"
22
+
23
+ # Prepare the keywords to search
24
+ $keywords = [ "Renard", "Normand", "an" ]
25
+ $keywords.each.with_index do |keyword,i|
26
+ puts "Adding keyword #{keyword} associated with #{i}..."
27
+ $searcher[keyword] = i
28
+ end
29
+
30
+ # Prepare the expected result.
31
+ $expected = [ [0,33..38], [0,63..68], [1,94..100], [2,107..108],[2,224..225] ]
32
+
33
+
34
+ # Interate over the keywords founds in the text.
35
+ ok = true
36
+ $searcher.each_in($input).with_index do |entry_range, i|
37
+ entry, range = *entry_range
38
+ print "Got entry=#{entry} (#{$keywords[entry]}) at range=#{range}..."
39
+ unless $input[range] == $keywords[entry] then
40
+ puts "\nError: at range=#{range} there is #{$input[range]}"
41
+ ok = false
42
+ end
43
+ unless [entry,range] == $expected[i] then
44
+ puts "\nError: invalid result."
45
+ puts "Got #{[entry,range]} but expecting #{$expected[i]}."
46
+ ok = false
47
+ end
48
+ puts " ok."
49
+ end
50
+
51
+
52
+
53
+ if ok then
54
+ puts "Success."
55
+ else
56
+ puts "Failure."
57
+ end
@@ -0,0 +1,7 @@
1
+ .defR repr(word,num)
2
+ :< word
3
+ num = num.to_i - 1
4
+ if num > 0 then
5
+ :< "repr(#{word},#{num})"
6
+ end
7
+ .end
@@ -0,0 +1,291 @@
1
+ ######################################################################
2
+ ## Program testing Ppr ##
3
+ ######################################################################
4
+
5
+ require "ppr.rb"
6
+
7
+ # Function for testing a preprocessor
8
+ def test_preprocessor(preprocessor,input,expected)
9
+ # Prepare the output and the input streams
10
+ puts "Preparing the input and ouput streams..."
11
+ output = StringIO.new("")
12
+ # Process the input and exepected arguments.
13
+ if !input.respond_to?(:each_line) or input.is_a?(String) then
14
+ # input is actually a file name, open it.
15
+ input = File.open(input.to_s,"r")
16
+ end
17
+ if !expected.respond_to?(:each_line) or expected.is_a?(String) then
18
+ # expected is actually a file name, open it.
19
+ expected = StringIO.new(File.read(expected.to_s))
20
+ end
21
+
22
+ # Apply the preprocessor
23
+ puts "Applying the preprocessor..."
24
+ preprocessor.preprocess(input,output)
25
+
26
+ # Check the result
27
+ puts "Checking the result..."
28
+ output.rewind
29
+ check = output.string == expected.read
30
+
31
+ unless check
32
+ puts "*Error*: invalid expansion result."
33
+ iline = output.string.each_line
34
+ expected.rewind
35
+ expected.each_line.with_index do |exp_line,i|
36
+ line = iline.next
37
+ puts "exp_line=#{exp_line}"
38
+ puts "line=#{line}"
39
+ if exp_line != line then
40
+ puts "Expected line #{i+1}:\n#{exp_line}"
41
+ puts "got:\n#{line}"
42
+ end
43
+ end
44
+ return false
45
+ end
46
+ return true
47
+ end
48
+
49
+ # Function for testing a preprocessor on +string+ which should raise an
50
+ # +exception+ string.
51
+ def test_preprocessor_exception(preprocessor,string,exception)
52
+ input = StringIO.new(string)
53
+ output = StringIO.new("")
54
+ begin
55
+ $ppr.preprocess(input,output)
56
+ puts "*Error*: preprocessed without exception."
57
+ return false
58
+ rescue Exception => e
59
+ if e.to_s.include?(exception.to_s) then
60
+ puts "Got exception: <#{e}> as expected."
61
+ return true
62
+ else
63
+ puts "*Error*: unexpected exception.",
64
+ "Got <#{e}> but expecting <#{exception}>."
65
+ return false
66
+ end
67
+ end
68
+ end
69
+
70
+ # Test the default preprocessor
71
+
72
+ puts "Building the default preprocessor with one expansion parameter..."
73
+ $ppr = Ppr::Preprocessor.new({"hello" => "Hello"})
74
+ puts "Testing it..."
75
+ $success = test_preprocessor($ppr,"#{File.dirname(__FILE__)}/test_ppr.txt",
76
+ "#{File.dirname(__FILE__)}/test_ppr_exp.txt")
77
+
78
+ # if $success then
79
+ # puts "Success."
80
+ # else
81
+ # puts "Failure."
82
+ # end
83
+ # exit
84
+
85
+ puts "\nBuilding a preprocessor with redefined keywords and one expansion parameter."
86
+ $ppr1 = Ppr::Preprocessor.new({hello: "Hello"},
87
+ apply: "RUBY", applyR: "RUBYR",
88
+ define: "MACRO", defineR: "MACRO_R",
89
+ assign: "ASSIGN",
90
+ endm: "ENDM",
91
+ expand: "~>", separator: /^|[^\w]|$/, glue: "@@")
92
+ puts "Testing it..."
93
+ $success &= test_preprocessor($ppr1,"#{File.dirname(__FILE__)}/test_ppr2.txt",
94
+ "#{File.dirname(__FILE__)}/test_ppr_exp.txt")
95
+
96
+ puts "Testing invalid proprocessor with identical strings for different keywords...."
97
+ begin
98
+ ppr = Ppr::Preprocessor.new(apply: "APPLY", applyR: "APPY_R",
99
+ define:"APPLY")
100
+ # Should not be there.
101
+ puts "*Error*: preprocessor built with invalid arguments did not raise any expcetion."
102
+ $success = false
103
+ rescue Exception => e
104
+ puts "Got exception as expected: #{e.to_s}."
105
+ end
106
+
107
+
108
+ puts "\nTesting invalid macro definitions..."
109
+ print "... Unfinished macro... "
110
+ $success &= test_preprocessor_exception($ppr, ".def\n :< 'Hello' \n",
111
+ ":1) macro definition without name.")
112
+
113
+ print "... Incomplete arguments... "
114
+ $success &= test_preprocessor_exception($ppr,
115
+ "\n.def HE(\n :< 'Hello' \n.end\n",
116
+ ":2) invalid arguments for macro definition.")
117
+
118
+ puts "\nTesting invalid macro call..."
119
+ print "... Unfinished macro call case #0... "
120
+ $success &= test_preprocessor_exception($ppr,
121
+ "\n\n.def HE(name)\n :< 'Hello ',name \n.end\nHE(",
122
+ "HE:6) incomplete arguments in macro call.")
123
+ print "... Unfinished macro call case #1... "
124
+ $success &= test_preprocessor_exception($ppr,
125
+ "\n.def HE(name)\n :< 'Hello ',name \n.end\nHE(A",
126
+ "HE:5) incomplete arguments in macro call.")
127
+ print "... Unfinished macro call case #2... "
128
+ $success &= test_preprocessor_exception($ppr,
129
+ ".def HE(name)\n :< 'Hello ',name \n.end\nHE(A,",
130
+ "HE:4) incomplete arguments in macro call.")
131
+ print "... Unfinished macro call case #3... "
132
+ $success &= test_preprocessor_exception($ppr,
133
+ "\n.def HE(name)\n :< 'Hello ',name \n.end\nHE(A,B",
134
+ "HE:5) incomplete arguments in macro call.")
135
+ print "... macro call with too many arguments... "
136
+ $success &= test_preprocessor_exception($ppr,
137
+ "\n.def HE(name)\n :< 'Hello ',name \n.end\nHE(A,B)",
138
+ "HE:5):2: invalid number of argument: got 2, but expecting 1")
139
+
140
+ puts "\nTesting call of macro with invalid code..."
141
+ print "... Syntax error in macro code... "
142
+ $success &= test_preprocessor_exception($ppr,
143
+ "\n\n\n.def HE(name)\n :< 1,2 \n.end\nHE(Foo)",
144
+ "HE:7):5: syntax error")
145
+ print "... Division by zero in macro code... "
146
+ $success &= test_preprocessor_exception($ppr,
147
+ "\n.def HE(name)\n :< 1/0 \n.end\nHE(Foo)",
148
+ "HE:5):3: divided by 0")
149
+ print "... Undefined symbol in macro code... "
150
+ $success &= test_preprocessor_exception($ppr,
151
+ "\n.def HE(name)\n :< foobar \n.end\nHE(Foo)",
152
+ "HE:5):2: undefined local variable or method")
153
+
154
+
155
+ # puts "\nBuilding a preprocessor with an invalid escape character."
156
+ # $raised = false
157
+ # begin
158
+ # $ppr = Ppr::Preprocessor.new(escape: "HA")
159
+ # rescue Exception => e
160
+ # puts "As expected an exception has been raised (#{e.inspect})."
161
+ # $raised = true
162
+ # end
163
+ # unless $raised then
164
+ # puts "Error: an exception should have been raised."
165
+ # $success = false
166
+ # end
167
+
168
+
169
+ puts "\n\nNow going to Test the examples of the documentation."
170
+ puts "Testing example 1..."
171
+ $success &= test_preprocessor($ppr,
172
+ StringIO.new(
173
+ 'Example 1:
174
+ .do
175
+ :< "Hello world!"
176
+ .end'
177
+ ), StringIO.new(
178
+ 'Example 1:
179
+ Hello world!') )
180
+
181
+ puts "\nTesting example 2..."
182
+ $success &= test_preprocessor($ppr,
183
+ StringIO.new(
184
+ 'Example 2:
185
+ .def hello(world)
186
+ :< "Hello #{world}!"
187
+ .end
188
+ hello(Foo)
189
+ hello( Bar )'
190
+ ), StringIO.new(
191
+ 'Example 2:
192
+ Hello Foo!
193
+ Hello Bar !') )
194
+
195
+ puts "\nTesting example 3..."
196
+ $success &= test_preprocessor($ppr,
197
+ StringIO.new(
198
+ 'Example 3:
199
+ .def hello(world) :< "Hello #{world}!"
200
+ .doR
201
+ :< "hello(WORLD)"
202
+ .end'
203
+ ), StringIO.new(
204
+ 'Example 3:
205
+ Hello WORLD!') )
206
+
207
+ puts "\nTesting example 4..."
208
+ $success &= test_preprocessor($ppr,
209
+ StringIO.new(
210
+ 'Example 4:
211
+ .defR sum(num)
212
+ num = num.to_i
213
+ if num > 2 then
214
+ :< "(+ sum(#{num-1}) #{num} )"
215
+ else
216
+ :< "(+ 1 2 )"
217
+ end
218
+ .end
219
+ Some lisp: sum(5)'
220
+ ), StringIO.new(
221
+ 'Example 4:
222
+ Some lisp: (+ (+ (+ (+ 1 2 ) 3 ) 4 ) 5 )') )
223
+
224
+ puts "\nTesting example 5..."
225
+ $success &= test_preprocessor($ppr,
226
+ StringIO.new(
227
+ 'Example 5:
228
+ .assign he :< "Hello"
229
+ .do :< @he + " world!\n"
230
+ .def hehe :< @he+@he
231
+ hehe'
232
+ ), StringIO.new(
233
+ 'Example 5:
234
+ Hello world!
235
+ HelloHello') )
236
+
237
+ puts "\nTesting example 6..."
238
+ $success &= test_preprocessor($ppr,
239
+ StringIO.new(
240
+ 'Example 6:
241
+ .load :< "foo.inc"
242
+ .def foo :< "FooO"
243
+ .load :< "foo.inc"'
244
+ ), StringIO.new(
245
+ 'Example 6:
246
+ foo and bar
247
+ FooO and bar
248
+ ') )
249
+
250
+ # Rebuild ppr to avoid conflict with example 6.
251
+ $ppr = Ppr::Preprocessor.new
252
+ puts "\nTesting example 7..."
253
+ $success &= test_preprocessor($ppr,
254
+ StringIO.new(
255
+ 'Example 7:
256
+ .require :< "foo.inc"
257
+ .def foo :< "FooO"
258
+ .require :< "foo.inc"'
259
+ ), StringIO.new(
260
+ 'Example 7:
261
+ foo and bar
262
+ ') )
263
+
264
+ puts "\nTesting example 8..."
265
+ $success &= test_preprocessor($ppr,
266
+ StringIO.new(
267
+ 'Example 8:
268
+ .if :< (1 == 1)
269
+ .def is :< "IS"
270
+ This is true.
271
+ .else
272
+ This is false.
273
+ .endif
274
+ .if :< (1 == 0)
275
+ This is really true.
276
+ .else
277
+ This is really false.
278
+ .endif'
279
+ ), StringIO.new(
280
+ 'Example 8:
281
+ This IS true.
282
+ This IS really false.
283
+ ') )
284
+
285
+
286
+
287
+ if $success then
288
+ puts "Success."
289
+ else
290
+ puts "Failure."
291
+ end