redparse 0.8.4 → 1.0.0

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 (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