ppr 0.0.2

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.
@@ -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