redparse 0.8.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -0
  2. data/COPYING.LGPL +503 -158
  3. data/History.txt +192 -0
  4. data/Makefile +9 -0
  5. data/README.txt +72 -39
  6. data/bin/redparse +108 -14
  7. data/lib/miniredparse.rb +1543 -0
  8. data/lib/redparse.rb +971 -105
  9. data/lib/redparse/ReduceWithsFor_RedParse_1_8.rb +17412 -0
  10. data/lib/redparse/ReduceWithsFor_RedParse_1_9.rb +17633 -0
  11. data/lib/redparse/babynodes.rb +17 -0
  12. data/lib/redparse/babyparser.rb +17 -0
  13. data/lib/redparse/cache.rb +290 -6
  14. data/lib/redparse/compile.rb +6 -97
  15. data/lib/redparse/decisiontree.rb +1 -1
  16. data/lib/redparse/float_accurate_to_s.rb +30 -6
  17. data/lib/redparse/generate.rb +18 -0
  18. data/lib/redparse/node.rb +415 -124
  19. data/lib/redparse/parse_tree_server.rb +20 -2
  20. data/lib/redparse/problemfiles.rb +1 -1
  21. data/lib/redparse/pthelper.rb +17 -31
  22. data/lib/redparse/reg_more_sugar.rb +1 -1
  23. data/lib/redparse/replacing/parse_tree.rb +30 -0
  24. data/lib/redparse/replacing/ripper.rb +20 -0
  25. data/lib/redparse/replacing/ruby_parser.rb +28 -0
  26. data/lib/redparse/ripper.rb +393 -0
  27. data/lib/redparse/ripper_sexp.rb +153 -0
  28. data/lib/redparse/stackableclasses.rb +113 -0
  29. data/lib/redparse/version.rb +18 -1
  30. data/redparse.gemspec +29 -9
  31. data/rplt.txt +31 -0
  32. data/test/data/hd_with_blank_string.rb +3 -0
  33. data/test/data/pt_known_output.rb +13273 -0
  34. data/test/data/wp.pp +0 -0
  35. data/test/generate_parse_tree_server_rc.rb +17 -0
  36. data/test/rp-locatetest.rb +2 -2
  37. data/test/test_1.9.rb +338 -35
  38. data/test/test_all.rb +22 -3
  39. data/test/test_part.rb +32 -0
  40. data/test/test_redparse.rb +396 -74
  41. data/test/test_xform_tree.rb +18 -0
  42. data/test/unparse_1.9_exceptions.txt +85 -0
  43. data/test/unparse_1.9_exceptions.txt.old +81 -0
  44. metadata +71 -46
  45. data/Rakefile +0 -35
@@ -1,3 +1,20 @@
1
+ =begin
2
+ redparse - a ruby parser written in ruby
3
+ Copyright (C) 2008,2009, 2012, 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
1
18
  require 'pp'
2
19
  require 'rubygems'
3
20
  require 'rubylexer'
@@ -1,3 +1,20 @@
1
+ =begin
2
+ redparse - a ruby parser written in ruby
3
+ Copyright (C) 2012, 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
1
18
  require 'pp'
2
19
  require 'rubygems'
3
20
  require 'rubylexer'
@@ -1,7 +1,38 @@
1
+ =begin
2
+ redparse - a ruby parser written in ruby
3
+ Copyright (C) 2012, 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
1
18
  require 'digest/sha2'
19
+ require 'fileutils'
20
+ begin
21
+ require 'etc'
22
+ rescue LoadError #ignore it
23
+ end
2
24
  class RedParse
3
25
  class Cache
4
- def initialize *params
26
+ def initialize(is_file,name,*params)
27
+ divider=params.index(:/)
28
+ @input_id=params[0...divider]
29
+ @output_id=params[divider+1..-1]
30
+ @rubyversion=@input_id.pop
31
+ @name=name
32
+ @is_file=is_file
33
+ end
34
+
35
+ def old_initialize *params
5
36
  @callersfile=Digest::SHA2.hexdigest params.join(',')
6
37
  @homedir=find_home+"/.redparse/"
7
38
  Dir.mkdir @homedir unless File.exist? @homedir
@@ -13,6 +44,8 @@ class RedParse
13
44
  File.open(@homedir+"/parserdigest","wb"){|fd| fd.puts actual_digest } #update saved digest
14
45
  end
15
46
  retire_old_entries
47
+ rescue Errno::EACCES
48
+ #do nothing
16
49
  end
17
50
 
18
51
  def cachedir
@@ -76,7 +109,16 @@ class RedParse
76
109
  # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
77
110
  # least on Win32).
78
111
  #(originally stolen from rubygems)
79
- def find_home
112
+ #before trying env variables, try getpwuid, since the environment
113
+ #might have been cleansed (eg by a cgi server) or altered (eg by
114
+ #rubygems tests).
115
+ def Cache.find_home
116
+ begin
117
+ return Etc.getpwuid.dir
118
+ rescue Exception
119
+ #do nothing
120
+ end
121
+
80
122
  ['HOME', 'USERPROFILE'].each do |homekey|
81
123
  return ENV[homekey] if ENV[homekey]
82
124
  end
@@ -95,6 +137,7 @@ class RedParse
95
137
  end
96
138
  end
97
139
  end
140
+ def find_home; Cache.find_home end
98
141
  private :find_home, :entry_files, :redparse_rb_hexdigest, :retire_old_entries, :max_size, :hexdigest_of_file
99
142
 
100
143
  def hash_of_input input
@@ -105,7 +148,7 @@ class RedParse
105
148
  end
106
149
  end
107
150
 
108
- def get input
151
+ def old_get input
109
152
  hash=hash_of_input input
110
153
  cachefile=cachedir+hash
111
154
  if File.exist? cachefile
@@ -138,17 +181,17 @@ class RedParse
138
181
  end
139
182
  return result
140
183
  end
141
- rescue EOFError
184
+ rescue EOFError,Errno::EACCES
142
185
  return nil
143
186
  end
144
187
 
145
- def put input,result
188
+ def old_put input,result
146
189
  hash=hash_of_input input
147
190
  File.open(cachedir+hash, "wb"){|fd|
148
191
  begin
149
192
  Thread.current["Marshal.ignore_sclass"]=true
150
193
  Marshal.dump(result,fd)
151
- rescue TypeError=>e #dump failed
194
+ rescue TypeError #dump failed
152
195
  File.unlink cachedir+hash
153
196
  begin
154
197
  require 'ron'
@@ -168,5 +211,246 @@ class RedParse
168
211
  warn "cache write failed for:\n#{result.inspect}"
169
212
  File.unlink cachedir+hash
170
213
  end
214
+
215
+ def get input,output_type="parsed"
216
+ output_type=@rubyversion.to_s+output_type
217
+ if @is_file
218
+ Cache.read_for_file(input,@name,@input_id,output_type,@output_id)
219
+ else
220
+ Cache.read_for_str(input,@input_id,output_type,@output_id)
221
+ end
222
+ end
223
+
224
+ def put input,result,output_type="parsed"
225
+ output_type=@rubyversion.to_s+output_type
226
+ if @is_file
227
+ Cache.write_for_file(input,@name,@input_id,output_type,@output_id,result)
228
+ else
229
+ Cache.write_for_str(input,@input_id,output_type,@output_id,result)
230
+ end
231
+ end
232
+ end
233
+
234
+ CACHEDIRNAME="obj"
235
+ GLOBALCACHEDIRNAME=".ruby-"+CACHEDIRNAME
236
+
237
+ def Cache.get input,name,input_id,output_type,outputter_id
238
+ if String===input
239
+ read_for_str(input,input_id,output_type,outputter_id)
240
+ else
241
+ read_for_file(input,name,input_id,output_type,outputter_id)
242
+ end
243
+ end
244
+
245
+ def Cache.put input,name,input_id,output_type,outputter_id,result
246
+ if String===input
247
+ write_for_str(input,input_id,output_type,outputter_id,result)
248
+ else
249
+ write_for_file(input,name,input_id,output_type,outputter_id,result)
250
+ end
251
+ end
252
+
253
+ def Cache.read input,name,input_id,output_type,outputter_id
254
+ result=get input,name,input_id,output_type,outputter_id
255
+ return result if result
256
+
257
+ result=yield
258
+ put input,name,input_id,output_type,outputter_id,result
259
+ end
260
+
261
+ def Cache.cachename_for_file(name,output_type)
262
+ cache_fn=File.join(File.dirname(name),CACHEDIRNAME,File.basename(name))+"."+output_type
263
+ dir=File.dirname(cache_fn)
264
+ begin
265
+ Dir.mkdir dir unless File.exists?(dir)
266
+ raise unless File.writable?(dir)
267
+ rescue Exception #chosen dir is not writeable
268
+ cache_fn=File.join(find_home,GLOBALCACHEDIRNAME,name)+"."+output_type
269
+ FileUtils.mkdir_p( File.dirname( cache_fn ) )
270
+ end
271
+ return cache_fn
272
+ end
273
+
274
+ def Cache.cachename_for_str(str,options,output_type)
275
+ options=options.to_a.sort_by{|k,v| k}.map{|k,v| k+'='+v} if Hash===options
276
+ options=options.join(',') unless String===options
277
+ #options.gsub!('%n','%%n'); options.gsub!("\n",'%n')
278
+ #fail if /\n/===options
279
+ #str=options+"\n"+str
280
+ options=Digest::SHA2.hexdigest(options) if options.size>100
281
+ digest=Digest::SHA2.hexdigest(str)
282
+ cache_fn=File.join(find_home, GLOBALCACHEDIRNAME,"-e",options,digest)+"."+output_type
283
+ FileUtils.mkdir_p( File.dirname( cache_fn ) )
284
+ return cache_fn
285
+ end
286
+
287
+ HEADER_REX=/\A\n\#encoded\ with\ (ascii|Ron|Marshal)\n
288
+ \#(.*)\n
289
+ \#([0-9A-Fa-f]+)\n
290
+ \#([0-9A-Fa-f]+)\n
291
+ \#\s{0,9}([0-9]{1,9})\s{0,9}\n\z
292
+ /x
293
+
294
+ #encoder, options, inputhash, outputterhash, size
295
+ def Cache.read_trailer(f)
296
+ f.seek(-(9*3+2+1),IO::SEEK_END)
297
+ size=f.read[/\n\# *([0-9]+) *\n\z/,1].to_i
298
+ return if size.zero?
299
+ f.seek(-size,IO::SEEK_END)
300
+ buf=f.read
301
+ return unless result=buf.match(HEADER_REX)
302
+ result=result.to_a
303
+ result.shift
304
+ return result
305
+ end
306
+
307
+ def Cache.attempt_read(cache_fn,inputdigest,options,output_identity,want_file=false)
308
+ return if !File.exist? cache_fn
309
+
310
+ #warn "...reading from cache file #{cache_fn}"
311
+ #warn "...options=#{options.inspect}"
312
+
313
+ outid=Digest::SHA2.hexdigest output_identity.join(',')
314
+
315
+ cache_f=File.open(cache_fn,"rb")
316
+ pos=cache_f.pos
317
+ options=options.to_s
318
+
319
+ encoder,saved_options,saved_inputdigest,saved_outid=read_trailer cache_f
320
+ error=case
321
+ when !encoder; "trailer not found"
322
+ when saved_inputdigest!=inputdigest; "input changed"
323
+ when saved_outid!=outid; "outputter changed"
324
+ when saved_options!=options; "options changed from #{saved_options.inspect} to #{options.inspect}"
325
+ end
326
+ if error
327
+ #warn "...cache read failed because #{error}"
328
+ cache_f.close
329
+ cache_f=nil
330
+ return
331
+ end
332
+
333
+ case encoder
334
+ when 'ascii'
335
+ return cache_f if want_file
336
+ return cache_f.read
337
+ when 'Ron'
338
+ begin
339
+ require 'ron'
340
+ return Ron.load( cache_f.read )
341
+ rescue Exception=>e
342
+ warn "#{e.class}: #{e}"
343
+ warn "cache ron read failed for:#{cache_fn}"
344
+ return nil
345
+ end
346
+ when 'Marshal'
347
+ cache_f.pos=pos
348
+ begin
349
+ return Marshal.load( cache_f )
350
+ rescue Exception=>e
351
+ warn "#{e.class}: #{e}"
352
+ warn "cache read failed for:#{cache_fn}"
353
+ return nil
354
+ end
355
+ else
356
+ warn "unrecognized RedParse::Cache encoder type: #{encoder}"
357
+ return nil
358
+ end
359
+ end
360
+
361
+ def Cache.attempt_write(cache_fn,inputdigest,options,output_identity,result)
362
+ #STDERR.write "...writing to cache file #{cache_fn}... "
363
+
364
+ if result.respond_to? :sysread
365
+ path=result.path
366
+ begin
367
+ FileUtils.move(path,cache_fn)
368
+ rescue Exception
369
+ FileUtils.copy(path,cache_fn)
370
+ end
371
+ encoder= "ascii"
372
+ #STDERR.puts 'file'
373
+ cache_f=File.open(cache_fn,"a")
374
+ else
375
+ cache_f=File.open(cache_fn,"wb")
376
+
377
+ if String===result
378
+ cache_f.write result
379
+ encoder= "ascii"
380
+ #STDERR.puts 'ascii'
381
+ else begin
382
+ Thread.current["Marshal.ignore_sclass"]=true
383
+ Marshal.dump(result,cache_f)
384
+ encoder= "Marshal"
385
+ #STDERR.puts 'Marshal'
386
+ rescue TypeError #dump failed
387
+ #STDERR.write "Marshal failed => "
388
+ cache_f.close
389
+ cache_f=File.open(cache_fn,"wb")
390
+ begin
391
+ require 'ron'
392
+ cache_f.write Ron.dump(result)
393
+ encoder='Ron'
394
+ #STDERR.puts "Ron"
395
+ rescue Exception
396
+ #STDERR.puts "Ron failed"
397
+ File.unlink(cache_fn)
398
+ return
399
+ end
400
+ ensure
401
+ Thread.current["Marshal.ignore_sclass"]=nil
402
+ end end
403
+ end
404
+
405
+ outid=Digest::SHA2.hexdigest output_identity.join(',')
406
+ trailer= "\n#encoded with #{encoder}\n##{options}\n##{inputdigest}\n##{outid}\n"
407
+ sz=trailer.size+2
408
+ szsz=sz.to_s.size
409
+ sz+=szsz
410
+ sz+=1 unless szsz==sz.to_s.size
411
+ trailer<< "##{sz}\n"
412
+ fail 'bad trailer size' unless trailer[/^\#([0-9]+)\n\Z/,1].to_i==trailer.size
413
+ cache_f.write trailer
414
+
415
+ return result
416
+ ensure
417
+ cache_f.close if cache_f
418
+ #STDERR.puts("...options=#{options.inspect}")
419
+ end
420
+
421
+ def Cache.read_for_file(input,name,options,output_type,output_identity,&if_missing)
422
+ name=File.expand_path(name)
423
+ inputdigest=Digest::SHA2.file(name).hexdigest
424
+ cache_fn=cachename_for_file name,output_type
425
+ result=attempt_read(cache_fn,inputdigest,options,output_identity,:want_file)
426
+ return result if result
427
+
428
+ if if_missing and result=if_missing.call
429
+ write_for_file(input,name,options,output_type,output_identity,result)
430
+ end
431
+ end
432
+
433
+ def Cache.write_for_file(input,name,options,output_type,output_identity,result)
434
+ name=File.expand_path(name)
435
+ inputdigest=Digest::SHA2.file(name).hexdigest
436
+ cache_fn=cachename_for_file name,output_type
437
+ attempt_write(cache_fn,inputdigest,options,output_identity,result)
438
+ end
439
+
440
+ def Cache.read_for_str(input,options,output_type,output_identity,&if_missing)
441
+ inputdigest=Digest::SHA2.hexdigest(input)
442
+ cache_fn= cachename_for_str input,options,output_type
443
+ result=attempt_read(cache_fn,inputdigest,options,output_identity)
444
+ return result if result
445
+
446
+ if if_missing and result=if_missing.call
447
+ write_for_str(input,options,output_type,output_identity,result)
448
+ end
449
+ end
450
+
451
+ def Cache.write_for_str(input,options,output_type,output_identity,result)
452
+ inputdigest=Digest::SHA2.hexdigest(input)
453
+ cache_fn= cachename_for_str input,options,output_type
454
+ attempt_write(cache_fn,inputdigest,options,output_identity,result)
171
455
  end
172
456
  end
@@ -1,6 +1,6 @@
1
1
  =begin
2
2
  redparse - a ruby parser written in ruby
3
- Copyright (C) 2008,2009 Caleb Clausen
3
+ Copyright (C) 2008,2009, 2012, 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
@@ -35,6 +35,7 @@ require "redparse/node"
35
35
  require "redparse/reg_more_sugar"
36
36
  require "redparse/generate"
37
37
  require "redparse/cache"
38
+ require "redparse/stackableclasses"
38
39
 
39
40
  class RedParse
40
41
 
@@ -143,101 +144,8 @@ if defined? END_ATTACK
143
144
 
144
145
  end
145
146
 
146
-
147
-
148
-
149
- #just the left side (the stack/lookahead matchers)
150
- def LEFT
151
- @rules.map{|r| r.left.subregs }.flatten
152
- end
153
-
154
- #remove lookahead and lookback decoration
155
- def LEFT_NO_LOOKING
156
- l=LEFT()
157
- l.map!{|m|
158
- case m #
159
- when Reg::LookAhead,Reg::LookBack; m.subregs[0]
160
- when Proc; []
161
- else m #
162
- end #
163
- }
164
- l
165
- end
166
-
167
- #all classes mentioned in rules, on left and right sides
168
- def STACKABLE_CLASSES #
169
- return @sc_result unless @sc_result.nil?
170
- @sc_result=false
171
- l=LEFT_NO_LOOKING()
172
- l=l.map{|lm| sc_juice lm}.flatten.compact
173
- r= @rules.map{|rr| rr.right }.grep(Class) #classes in productions
174
- result=l+r
175
- @sc_result=result.grep(Class).uniq
176
- fail if @sc_result.empty?
177
- return @sc_result
178
- end
147
+ include RedParse::StackableClasses
179
148
 
180
- def juice(m)
181
- case m #
182
- when Class;
183
- return [m] unless @subclasses_of
184
- result=[m] # and subclasses too
185
- i=0
186
- while item=result[i]
187
- #p item
188
- result.concat @subclasses_of[item]
189
- i += 1
190
- end
191
- result
192
- when String,Regexp; juice(RedParse.KW(m))
193
- when Reg::And; m.subregs.map{|x| juice(x).flatten.compact}.inject{|sum,rr| sum&rr}
194
- when Reg::Or; m.subregs.map( &method(:juice) )
195
- when Reg::Not;
196
- m=m.subregs[0]
197
- if Class===m or (Reg::Or===m and
198
- m.subregs.inject{|sum,x| sum && (Class===x) })
199
- j=juice(m)
200
- STACKABLE_CLASSES()-j.flatten.compact rescue j
201
- else
202
- STACKABLE_CLASSES()
203
- end
204
- else STACKABLE_CLASSES()
205
- end
206
- end
207
-
208
- def sc_juice(m)
209
- case m #
210
- when Class; [m]
211
- when String,Regexp; juice(RedParse.KW(m))
212
- # when String,Regexp; [KeywordToken]
213
- when Reg::And; m.subregs.map{|x| sc_juice(x)}.compact.map{|x| x.flatten.compact}.inject{|sum,rr| sum&rr }
214
- when Reg::Or; m.subregs.map( &method(:sc_juice) )
215
- when Reg::Not; sc_juice(m.subregs[0])
216
- when Reg::LookAhead, Reg::LookBack; sc_juice(m.subregs[0])
217
- else []
218
- end
219
- end
220
-
221
- def LOOKAHEAD_CLASSES rule
222
- last=rule.left.subregs.last
223
- return STACKABLE_CLASSES() unless Reg::LookAhead===last
224
- la= last.subregs[0]
225
- return juice(la).flatten.compact
226
- end
227
- #
228
- def TOS_CLASSES rule
229
- i=-1
230
- mats=rule.left.subregs
231
- m=mats[i]
232
- m=mats[i-=1] if Reg::LookAhead===m || Proc===m
233
- result=[]
234
- while Reg::Repeat===m and m.times.min.zero?
235
- result<<juice(m.subregs[0])
236
- m=mats[i-=1]
237
- end
238
- return (result+juice(m)).flatten.compact
239
- end
240
-
241
149
  def [](i)
242
150
  @rules[i]
243
151
  end
@@ -1386,9 +1294,10 @@ end
1386
1294
 
1387
1295
  states=all_states
1388
1296
  # @rules=expanded_RULES
1389
- @inputs=nil #Marshal no like it
1390
1297
 
1298
+ false && \
1391
1299
  begin
1300
+ @inputs=nil #Marshal no like it
1392
1301
  p :dumping
1393
1302
  Marshal.dump(self,f=open("cached_parse_tables.drb","wb"))
1394
1303
  p :dump_done!
@@ -1399,7 +1308,7 @@ end
1399
1308
  @inputs=enumerate_exemplars
1400
1309
  end
1401
1310
  end
1402
- f.close
1311
+ f.close if f
1403
1312
 
1404
1313
  #look for unused dotted rules and actions
1405
1314
  #also states with drs past the end