rpdf2txt 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. data/History.txt +5 -0
  2. data/LICENCE +515 -0
  3. data/Manifest.txt +126 -0
  4. data/README.txt +30 -0
  5. data/Rakefile +24 -0
  6. data/bin/rpdf2txt +58 -0
  7. data/config.save +12 -0
  8. data/install.rb +1098 -0
  9. data/lib/rpdf2txt-rockit/base_extensions.rb +73 -0
  10. data/lib/rpdf2txt-rockit/bootstrap.rb +120 -0
  11. data/lib/rpdf2txt-rockit/bounded_lru_cache.rb +43 -0
  12. data/lib/rpdf2txt-rockit/conflict_resolution.rb +302 -0
  13. data/lib/rpdf2txt-rockit/directed_graph.rb +401 -0
  14. data/lib/rpdf2txt-rockit/glr_parser.rb +393 -0
  15. data/lib/rpdf2txt-rockit/grammar.rb +644 -0
  16. data/lib/rpdf2txt-rockit/graphdrawing.rb +107 -0
  17. data/lib/rpdf2txt-rockit/graphviz_dot.rb +63 -0
  18. data/lib/rpdf2txt-rockit/indexable.rb +53 -0
  19. data/lib/rpdf2txt-rockit/lalr_parsetable_generator.rb +144 -0
  20. data/lib/rpdf2txt-rockit/parse_table.rb +273 -0
  21. data/lib/rpdf2txt-rockit/parsetable_generation.rb +164 -0
  22. data/lib/rpdf2txt-rockit/parsing_ambiguities.rb +84 -0
  23. data/lib/rpdf2txt-rockit/profiler.rb +168 -0
  24. data/lib/rpdf2txt-rockit/reduce_actions_generator.rb +523 -0
  25. data/lib/rpdf2txt-rockit/rockit.rb +76 -0
  26. data/lib/rpdf2txt-rockit/rockit_grammar_ast_eval.rb +187 -0
  27. data/lib/rpdf2txt-rockit/rockit_grammars_parser.rb +126 -0
  28. data/lib/rpdf2txt-rockit/sourcecode_dumpable.rb +181 -0
  29. data/lib/rpdf2txt-rockit/stringscanner.rb +54 -0
  30. data/lib/rpdf2txt-rockit/syntax_tree.rb +452 -0
  31. data/lib/rpdf2txt-rockit/token.rb +364 -0
  32. data/lib/rpdf2txt-rockit/version.rb +3 -0
  33. data/lib/rpdf2txt/attributesparser.rb +42 -0
  34. data/lib/rpdf2txt/cmapparser.rb +65 -0
  35. data/lib/rpdf2txt/data/_cmap.grammar +11 -0
  36. data/lib/rpdf2txt/data/_cmap_range.grammar +15 -0
  37. data/lib/rpdf2txt/data/_pdfattributes.grammar +32 -0
  38. data/lib/rpdf2txt/data/cmap.grammar +11 -0
  39. data/lib/rpdf2txt/data/cmap.rb +37 -0
  40. data/lib/rpdf2txt/data/cmap_range.grammar +15 -0
  41. data/lib/rpdf2txt/data/cmap_range.rb +43 -0
  42. data/lib/rpdf2txt/data/fonts/Courier-Bold.afm +342 -0
  43. data/lib/rpdf2txt/data/fonts/Courier-BoldOblique.afm +342 -0
  44. data/lib/rpdf2txt/data/fonts/Courier-Oblique.afm +342 -0
  45. data/lib/rpdf2txt/data/fonts/Courier.afm +342 -0
  46. data/lib/rpdf2txt/data/fonts/Helvetica-Bold.afm +2827 -0
  47. data/lib/rpdf2txt/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  48. data/lib/rpdf2txt/data/fonts/Helvetica-Oblique.afm +3051 -0
  49. data/lib/rpdf2txt/data/fonts/Helvetica.afm +3051 -0
  50. data/lib/rpdf2txt/data/fonts/License-Adobe.txt +65 -0
  51. data/lib/rpdf2txt/data/fonts/Symbol.afm +213 -0
  52. data/lib/rpdf2txt/data/fonts/Times-Bold.afm +2588 -0
  53. data/lib/rpdf2txt/data/fonts/Times-BoldItalic.afm +2384 -0
  54. data/lib/rpdf2txt/data/fonts/Times-Italic.afm +2667 -0
  55. data/lib/rpdf2txt/data/fonts/Times-Roman.afm +2419 -0
  56. data/lib/rpdf2txt/data/fonts/ZapfDingbats.afm +225 -0
  57. data/lib/rpdf2txt/data/pdfattributes.grammar +32 -0
  58. data/lib/rpdf2txt/data/pdfattributes.rb +71 -0
  59. data/lib/rpdf2txt/data/pdftext.grammar +102 -0
  60. data/lib/rpdf2txt/data/pdftext.rb +146 -0
  61. data/lib/rpdf2txt/default_handler.rb +352 -0
  62. data/lib/rpdf2txt/lzw.rb +69 -0
  63. data/lib/rpdf2txt/object.rb +1114 -0
  64. data/lib/rpdf2txt/parser.rb +169 -0
  65. data/lib/rpdf2txt/symbol.rb +408 -0
  66. data/lib/rpdf2txt/text.rb +182 -0
  67. data/lib/rpdf2txt/text_state.rb +434 -0
  68. data/lib/rpdf2txt/textparser.rb +42 -0
  69. data/test/data/3392_obj +0 -0
  70. data/test/data/397_decrypted +15 -0
  71. data/test/data/450_decrypted +153 -0
  72. data/test/data/450_obj +0 -0
  73. data/test/data/452_decrypted +125 -0
  74. data/test/data/454_decrypted +108 -0
  75. data/test/data/456_decrypted +106 -0
  76. data/test/data/458_decrypted +111 -0
  77. data/test/data/458_obj +0 -0
  78. data/test/data/460_decrypted +118 -0
  79. data/test/data/460_obj +0 -0
  80. data/test/data/463_decrypted +117 -0
  81. data/test/data/465_decrypted +107 -0
  82. data/test/data/465_obj +0 -0
  83. data/test/data/90_obj +0 -0
  84. data/test/data/90_obj_comp +1 -0
  85. data/test/data/decrypted +0 -0
  86. data/test/data/encrypt_obj +0 -0
  87. data/test/data/encrypt_string +0 -0
  88. data/test/data/encrypt_string_128bit +0 -0
  89. data/test/data/encrypted_object_stream.pdf +0 -0
  90. data/test/data/firststream +1 -0
  91. data/test/data/index.pdfobj +0 -0
  92. data/test/data/index_2bit.pdfobj +0 -0
  93. data/test/data/index_masked.pdfobj +0 -0
  94. data/test/data/indexed.pdfobj +0 -0
  95. data/test/data/indexed_2bit.pdfobj +0 -0
  96. data/test/data/indexed_masked.pdfobj +0 -0
  97. data/test/data/inline.png +0 -0
  98. data/test/data/logo.png +0 -0
  99. data/test/data/lzw.pdfobj +0 -0
  100. data/test/data/lzw_index.pdfobj +0 -0
  101. data/test/data/page_tree.pdf +148 -0
  102. data/test/data/pdf_20.png +0 -0
  103. data/test/data/pdf_21.png +0 -0
  104. data/test/data/pdf_22.png +0 -0
  105. data/test/data/pdf_50.png +0 -0
  106. data/test/data/png.pdfobj +0 -0
  107. data/test/data/space_bug_stream.txt +119 -0
  108. data/test/data/stream.txt +292 -0
  109. data/test/data/stream_kerning_bug.txt +13 -0
  110. data/test/data/stream_kerning_bug2.txt +6 -0
  111. data/test/data/test.pdf +0 -0
  112. data/test/data/test.txt +8 -0
  113. data/test/data/test_text.txt +42 -0
  114. data/test/data/working_obj +0 -0
  115. data/test/data/working_obj2 +0 -0
  116. data/test/mock.rb +149 -0
  117. data/test/suite.rb +30 -0
  118. data/test/test_pdf_object.rb +1802 -0
  119. data/test/test_pdf_parser.rb +1340 -0
  120. data/test/test_pdf_text.rb +789 -0
  121. data/test/test_space_bug_05_2004.rb +87 -0
  122. data/test/test_stream.rb +194 -0
  123. data/test/test_text_state.rb +315 -0
  124. data/usage-en.txt +112 -0
  125. data/user-stories/UserStories_Rpdf2Txt.txt +34 -0
  126. data/user-stories/documents/swissmedicjournal/04_2004.pdf +0 -0
  127. metadata +220 -0
@@ -0,0 +1,73 @@
1
+ class Array
2
+ def equality_uniq
3
+ uniq_elements = []
4
+ self.each {|e| uniq_elements.push(e) unless uniq_elements.index(e)}
5
+ uniq_elements
6
+ end
7
+
8
+ def delete_at_indices(indices = [])
9
+ not_deleted = Array.new
10
+ self.each_with_index {|e,i| not_deleted.push(e) if !indices.include?(i)}
11
+ not_deleted
12
+ end
13
+ end
14
+
15
+ class DefaultInitArray < Array
16
+ def initialize(*args, &initblock)
17
+ super(*args)
18
+ @initblock = initblock
19
+ end
20
+
21
+ def [](index)
22
+ super(index) || (self[index] = @initblock.call(index))
23
+ end
24
+ end
25
+
26
+ class ArrayOfArrays < DefaultInitArray
27
+ @@create_array = proc{|i| Array.new}
28
+ def initialize(*args)
29
+ super(*args, &@@create_array)
30
+ end
31
+ end
32
+
33
+ class ArrayOfHashes < DefaultInitArray
34
+ @@create_hash = proc{|i| Hash.new}
35
+ def initialize(*args)
36
+ super(*args, &@@create_hash)
37
+ end
38
+ end
39
+
40
+ # Hash which takes a block that is called to give a default value when a key
41
+ # has the value nil in the hash.
42
+ class DefaultInitHash < Hash
43
+ def initialize(*args, &initblock)
44
+ super(*args)
45
+ @initblock = initblock
46
+ end
47
+
48
+ def [](key)
49
+ #super(key) || (self[key] = @initblock.call(key))
50
+ fetch(key) {
51
+ store(key, @initblock.call(key))
52
+ }
53
+ end
54
+ end
55
+
56
+ unless Object.constants.include?("TimesClass")
57
+ TimesClass = (RUBY_VERSION < "1.7") ? Time : Process
58
+ end
59
+
60
+ def time_and_puts(string, &block)
61
+ if $TIME_AND_PUTS_VERBOSE
62
+ print string; STDOUT.flush
63
+ end
64
+ starttime = [Time.new, TimesClass.times]
65
+ block.call
66
+ endtime = [Time.new, TimesClass.times]
67
+ duration = endtime[0] - starttime[0]
68
+ begin
69
+ load = [((endtime[1].utime+endtime[1].stime)-(starttime[1].utime+starttime[1].stime))/duration*100.0, 100.0].min
70
+ puts " (%.2f s %.2f%%)" % [duration, load] if $TIME_AND_PUTS_VERBOSE
71
+ rescue FloatDomainError
72
+ end
73
+ end
@@ -0,0 +1,120 @@
1
+ # This is a version of the grammar for Rockit grammars in code. It is used
2
+ # to bootstrap the rockit grammar parser.
3
+ #
4
+ # NOTE: The handcoded grammar in this file may not be
5
+ # fully in sync with the rockit-grammar.grammar. This is not
6
+ # needed as long as it can be used to parse the file. However,
7
+ # YOU SHOULD NOT USE THE HANDCODED GRAMMAR IN THIS FILE AS A REFERENCE
8
+ # TO ROCKIT GRAMMARS.
9
+ #
10
+ # Overview of bootstrapping process:
11
+ # 1. Generate a parser (rp) for Rockit grammars from this in-code grammar.
12
+ # The parser MAY NOT BE FULLY UP-TO-DATE but it must be able to parse
13
+ # the rockit-grammar.grammar file.
14
+ # 2. Parse the rockit-grammar.grammar file with rp.
15
+ # 3. Generate source code for up-to-date rockit parser.
16
+ #
17
+ require 'rpdf2txt-rockit/rockit'
18
+ require 'rpdf2txt-rockit/base_extensions'
19
+
20
+ module Parse
21
+ RockitTokens = [
22
+ blank = t("Blank", /\s+/n, :Skip),
23
+ comment = t("Comment", /#.*$/n, :Skip),
24
+ string = t("String", /('((\\')|[^'])*')|("((\\")|[^"])*")/n),
25
+ regexp = t("Regexp", /\/((\\\/)|[^\/])*\/[iomx]*/n),
26
+ arrow = t("Arrow", /(->)|(::=)|(:)/n),
27
+ symbol_name = t("SymbolName", /[A-Z][A-Za-z]*/n),
28
+ production_reference = t("ProductionReference", /[A-Z][A-Za-z]*\d+/n)
29
+ ]
30
+
31
+ RockitTokenProds = [
32
+ prod(:Tokens, ['Tokens', plus(:TokenSpec)], stb(:^, [:_, :tokens])),
33
+ prod(:TokenSpec,
34
+ [symbol_name, '=', ore(string, regexp), maybe(:TokenOpts)],
35
+ stb(nil, [:tokenname, :_, :regexp, :options])),
36
+ prod(:TokenOpts, ['[', /:Skip/in, ']'], stb(:^, [:_, :options, :_]))
37
+ ]
38
+
39
+ def rockit_tokens_parser
40
+ Parse.parser_from_grammar(Grammar.new("RockitTokens", RockitTokenProds,
41
+ RockitTokens))
42
+ end
43
+
44
+ RockitPrioritiesProds = [
45
+ prod(:Priorities, ['Priorities', plus(:Priority)], stb(:^, [:_, :prios])),
46
+ prod(:Priority, [ore('left(', 'right('), liste(:ProdRef, ','), ')'],
47
+ stb(:Associativity, [:relation, :productionrefs, :_])),
48
+ prod(:Priority, [:ProdRef, plus(ore('>', '='), :ProdRef)],
49
+ stb(:Precedence, [:first, :rest])),
50
+ prod(:Priority, [:Priority, ','], stb(:^, [:prio, :_])),
51
+ prod(:ProdRef, [ore(symbol_name, production_reference)], stb(:^))
52
+ ]
53
+
54
+ def rockit_priorities_parser
55
+ Parse.parser_from_grammar(Grammar.new("RockitPriorities",
56
+ RockitPrioritiesProds,
57
+ RockitTokens))
58
+ end
59
+
60
+ RockitProductionsProds = [
61
+ prod(:Productions, ['Productions', plus(:Prod)],
62
+ stb(:Productions, [:_, :productions])),
63
+ prod(:Prod, [symbol_name, arrow, liste(:Alt, '|')],
64
+ stb(nil, [:nonterminal, :_, :alts])),
65
+ prod(:Alt, [plus(:Element), maybe(:AstSpec)],
66
+ stb(nil, [:elements, :astspec])),
67
+ prod(:Element, [symbol_name], stb(:^)),
68
+ prod(:Element, [ore(string, regexp)], stb(:ImplicitToken, [:regexp])),
69
+ prod(:Element, [:Element, '?'], stb(:Maybe, [:element, :_])),
70
+ prod(:Element, [:Element, '+'], stb(:Plus, [:element, :_])),
71
+ prod(:Element, [:Element, '*'], stb(:Mult, [:element, :_])),
72
+ prod(:Element, ['(', liste(:Element, '|'), ')'],
73
+ stb(:Or, [:_,:elements, :_])),
74
+ prod(:Element, ['(', plus(:Element), ')'],
75
+ stb(:Sequence, [:_, :elements, :_])),
76
+ prod(:Element, ['list(', :Element, ',', :Element, ')'],
77
+ stb(:List, [:_, :element, :_, :delimiter])),
78
+ prod(:AstSpec, ['[', maybe(:ProdSpec), maybe(:ElemSpecs), ']'],
79
+ stb(nil, [:_, :prodspec, :elemspecs, :_])),
80
+ prod(:ElemSpecs, [': ', liste(:ElemSpec, ',')], stb(:^, [:_, :specs])),
81
+ prod(:ElemSpec, [ore(/[a-z]+/n, '_')], stb(:^)),
82
+ prod(:ProdSpec, [ore(symbol_name, '^')], stb(:^, [:name])),
83
+ ]
84
+
85
+ def rockit_productions_parser
86
+ Parse.parser_from_grammar(Grammar.new("RockitProductions",
87
+ RockitProductionsProds,
88
+ RockitTokens))
89
+ end
90
+ module_function :rockit_productions_parser
91
+
92
+ RockitProds = [
93
+ prod(:Grammar,
94
+ ['Grammar', /[A-Za-z]+([-_]*[A-Za-z\d]+)*/n,
95
+ maybe(:Tokens), :Productions, maybe(:Priorities)],
96
+ stb(:Grammar, [:_, :language, :tokens, :productions, :priorities]))
97
+ ] + RockitTokenProds + RockitProductionsProds + RockitPrioritiesProds
98
+
99
+ def rockit_grammars_bootstrap_parser
100
+ Parse.parser_from_grammar(Grammar.new("RockitGrammar",
101
+ RockitProds, RockitTokens))
102
+ end
103
+ module_function :rockit_grammars_bootstrap_parser
104
+ end
105
+
106
+ if __FILE__ == $0
107
+ $TIME_AND_PUTS_VERBOSE = true
108
+ grammarfile = ARGV[0] || "rockit-grammar.grammar"
109
+ parser_filename = ARGV[1] || "rockit_grammars_parser.rb"
110
+ module_name = ARGV[3] || "Parse"
111
+ parser_name = as_module_method_named(module_name,
112
+ ARGV[2] || (ARGV[0] ? "parser" :
113
+ "rockit_grammars_parser"))
114
+ time_and_puts("Boostrapping by generating parser from handcoded grammar") {
115
+ $bootstrap_parser = Parse.rockit_grammars_bootstrap_parser
116
+ }
117
+ Parse.generate_parser_from_file_to_file(grammarfile, parser_filename,
118
+ parser_name, module_name,
119
+ $bootstrap_parser)
120
+ end
@@ -0,0 +1,43 @@
1
+ # Cache as hash with bounded size. Will delete least recently used (LRU) entry
2
+ # if full when new key-value pair added.
3
+ # NOTE: Not thread safe...
4
+ class BoundedLruCache
5
+ attr_accessor :max_size
6
+
7
+ def initialize(max_size = 2**30-1, anObject = nil)
8
+ @hash, @uses, @max_size = Hash.new(anObject), Array.new, max_size
9
+ end
10
+
11
+ def []=(key, val)
12
+ delete_least_recently_used if @hash.length >= @max_size
13
+ latest_used_key(key)
14
+ @hash[key] = val
15
+ end
16
+
17
+ def [](key)
18
+ res = @hash[key]
19
+ latest_used_key(key) if res
20
+ res
21
+ end
22
+
23
+ # Delegate undefined methods to the hash
24
+ def method_missing(methodId, *args)
25
+ if @hash.respond_to?(methodId)
26
+ @hash.send(methodId, *args)
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def latest_used_key(key)
35
+ @uses.delete(key)
36
+ @uses.push(key)
37
+ end
38
+
39
+ def delete_least_recently_used
40
+ lru = @uses.shift
41
+ @hash.delete lru
42
+ end
43
+ end
@@ -0,0 +1,302 @@
1
+ require 'rpdf2txt-rockit/sourcecode_dumpable'
2
+ # A ProductionPriorities object knows the relative priorities between
3
+ # productions and can resolve which subTreeProducitons are valid at
4
+ # certain positions in a production. In essence it is a partial ordering
5
+ # among the productions of a grammar.
6
+ #
7
+ # For more info about this kind of tree filtering conflict resolution
8
+ # see Eelco Visser's thesis from 1997.
9
+ #
10
+ # IDEA: Change to a more general handling of associativity using associativity
11
+ # vectors � la Ziemovit Laski's Bertha parser gen. Each production/rule has
12
+ # an assoc. vector with ones in the positions were subtrees having the same
13
+ # precedence is allowed. Left associativity is thus expressed as an assoc.
14
+ # vector with a one in the first position and zeros in the rest. Right
15
+ # associativity is zeros in all but the last position etc. See the papers
16
+ # on Bertha on www.ziemovitlaski.net.
17
+ #
18
+ Relation = Struct.new("Relation", :left, :relation, :right)
19
+ class Relation
20
+ include SourceCodeDumpable
21
+
22
+ def inspect
23
+ "#{relation.inspect}(#{left.inspect}, #{right.inspect})"
24
+ end
25
+
26
+ def to_src(name = nil, nameHash = {})
27
+ left_src = nameHash[left] ? nameHash[left] : left.to_src(nil, nameHash)
28
+ right_src = nameHash[right] ? nameHash[right] : right.to_src(nil, nameHash)
29
+ assign_to(name,
30
+ new_of_my_type(as_code(left_src),
31
+ as_code(relation.inspect),
32
+ as_code(right_src)))
33
+ end
34
+ end
35
+ class RelationCircularityException < Exception; end
36
+
37
+ class ProductionPriorities
38
+ include SourceCodeDumpable
39
+
40
+ def initialize(relations = [])
41
+ @left, @right, @non_associativity = Hash.new, Hash.new, Hash.new
42
+ @higher, @equal = Hash.new(nil), Hash.new(nil)
43
+ @in_conflict, @transitivity_stack = Hash.new(false), []
44
+ init_relations(relations)
45
+ end
46
+
47
+ # Precedence relations can be :HIGHER, :EQUAL or :LOWER. When not set the
48
+ # (default) relation is a non-relation, ie. the productions are not related
49
+ # and can thus not have a (precedence) conflict.
50
+ def set_precedence(p1, p2, precedenceRelation = :HIGHER)
51
+ return if p1 == p2
52
+ @in_conflict[p1] = true; @in_conflict[p2] = true
53
+ if precedenceRelation == :LOWER
54
+ precedenceRelation = :HIGHER
55
+ p1, p2 = p2, p1
56
+ end
57
+ if (precedenceRelation == :HIGHER and
58
+ (higher_precedence?(p2,p1) or equal_precedence?(p2,p1))) or
59
+ (precedenceRelation == :EQUAL and
60
+ (higher_precedence?(p2,p1) or higher_precedence?(p1,p2)))
61
+ raise RelationCircularityException
62
+ end
63
+ transitivity(p1, p2, precedenceRelation)
64
+ set_basic_precedence(p1, p2, precedence_hash(precedenceRelation))
65
+ if :EQUAL == precedenceRelation
66
+ set_basic_precedence(p2, p1, precedence_hash(precedenceRelation))
67
+ end
68
+ end
69
+
70
+ def higher_precedence(a)
71
+ @higher[a] || []
72
+ end
73
+
74
+ def higher_precedence?(a,b)
75
+ higher_precedence(a).include?(b)
76
+ end
77
+
78
+ def equal_precedence(a)
79
+ @equal[a] || []
80
+ end
81
+
82
+ def equal_precedence?(a,b)
83
+ equal_precedence(a).include?(b) or equal_precedence(b).include?(a)
84
+ end
85
+
86
+ def set_associativity(production1, production2, associativityRelation)
87
+ @in_conflict[production1] = true; @in_conflict[production2] = true
88
+ if associativityRelation == :LEFT
89
+ associativity_hash = @left
90
+ elsif associativityRelation == :RIGHT
91
+ associativity_hash = @right
92
+ else
93
+ associativity_hash = @non_associativity
94
+ end
95
+ begin
96
+ associativity_hash[production1].push production2
97
+ rescue NameError
98
+ associativity_hash[production1] = [production2]
99
+ end
100
+ end
101
+
102
+ def in_some_conflict?(production)
103
+ @in_conflict[production]
104
+ end
105
+
106
+ # Is there a conflict with having a child derived from 'subTreeProduction'
107
+ # at position 'position' in 'production'?
108
+ def conflict?(subTreeProduction, position, production)
109
+ # If production yielding subtree has higher precedence
110
+ # than production yielding root node or left or right associativity
111
+ # conflict
112
+ precedence_conflict?(subTreeProduction, production) or
113
+ left_or_non_associativity_conflict?(subTreeProduction, position,
114
+ production) or
115
+ right_or_non_associativity_conflict?(subTreeProduction, position,
116
+ production)
117
+ end
118
+
119
+ def precedence_conflict?(subTreeProduction, production)
120
+ higher_precedence?(production, subTreeProduction)
121
+ end
122
+
123
+ def left_or_non_associativity_conflict?(subTreeProduction, position, production)
124
+ if position == 0 or
125
+ position != production.elements.length-1
126
+ # No conflict unless the position is rightmost and there is something
127
+ # preceding it
128
+ false
129
+ else
130
+ left_associative?(subTreeProduction, production) or
131
+ non_associative?(subTreeProduction, production)
132
+ end
133
+ end
134
+
135
+ def right_or_non_associativity_conflict?(subTreeProduction, position,
136
+ production)
137
+ if position != 0 or
138
+ position == production.elements.length-1
139
+ # No conflict unless the position is leftmost and there is something
140
+ # after it
141
+ false
142
+ else
143
+ right_associative?(subTreeProduction, production) or
144
+ non_associative?(subTreeProduction, production)
145
+ end
146
+ end
147
+
148
+ def each(&block)
149
+ each_production_pair(@left, :LEFT, &block)
150
+ each_production_pair(@right, :RIGHT, &block)
151
+ each_production_pair(@non_associativity, :NONASSOC, &block)
152
+ each_production_pair(@higher, :HIGHER, &block)
153
+ each_production_pair(@equal, :EQUAL, &block)
154
+ end
155
+
156
+ def each_production_pair(hash, relation, &block)
157
+ hash.each do |p1, ps|
158
+ ps.each do |p2|
159
+ block.call(Relation.new(p1, relation, p2))
160
+ end
161
+ end
162
+ end
163
+
164
+ def to_src(name = nil, nameHash = {})
165
+ str = relations.to_src("relations", nameHash) + "\n"
166
+ str + assign_to(name, new_of_my_type(as_code("relations")))
167
+ end
168
+
169
+ def relations
170
+ relations = Array.new
171
+ self.each {|relation| relations.push relation}
172
+ relations
173
+ end
174
+
175
+ protected
176
+
177
+ def set_basic_precedence(a, b, hash)
178
+ begin
179
+ hash[a].push(b) unless hash[a].include?(b)
180
+ rescue NameError
181
+ hash[a] = [b]
182
+ end
183
+ end
184
+
185
+ def precedence_hash(relation)
186
+ case relation
187
+ when :HIGHER
188
+ @higher
189
+ when :EQUAL
190
+ @equal
191
+ else
192
+ raise ArgumentError, "Invalid relation #{relation.inspect}"
193
+ end
194
+ end
195
+
196
+ def transitivity(a, b, relation)
197
+ return if @transitivity_stack.include?([a,b,relation])
198
+ @transitivity_stack.push [a,b,relation]
199
+ higher_precedence(b).each {|c| set_precedence(a,c, :HIGHER)}
200
+ one_step_higher_than(a).each {|h| set_precedence(h,b, :HIGHER)}
201
+ equal_precedence(a).each {|c| set_precedence(c,b, relation)}
202
+ equal_precedence(b).each {|c| set_precedence(a,c, relation)}
203
+ if relation == :EQUAL
204
+ higher_precedence(a).each {|c| set_precedence(b,c, :HIGHER)}
205
+ end
206
+ @transitivity_stack.pop
207
+ end
208
+
209
+ def one_step_higher_than(a)
210
+ @higher.keys.select {|p| p != a and higher_precedence?(p, a)}
211
+ end
212
+
213
+ def init_relations(relations)
214
+ relations.each do |relation|
215
+ case relation.relation
216
+ when :LEFT, :RIGHT, :NONASSOC
217
+ set_associativity(relation.left, relation.right, relation.relation)
218
+ when :HIGHER, :LOWER, :EQUAL
219
+ set_precedence(relation.left, relation.right, relation.relation)
220
+ else
221
+ raise ArgumentError, "Unknown relation #{relation.inspect}"
222
+ end
223
+ end
224
+ end
225
+
226
+ def left_associative?(production1, production2)
227
+ check_associativity(production1, production2, @left) or
228
+ check_associativity(production2, production1, @left)
229
+ end
230
+
231
+ def right_associative?(production1, production2)
232
+ check_associativity(production1, production2, @right) or
233
+ check_associativity(production2, production1, @right)
234
+ end
235
+
236
+ def non_associative?(production1, production2)
237
+ check_associativity(production1, production2, @non_associativity) or
238
+ check_associativity(production2, production1, @non_associativity)
239
+ end
240
+
241
+ def check_associativity(production1, production2, associativityHash)
242
+ begin
243
+ associativityHash[production1].include?(production2)
244
+ rescue NameError
245
+ false
246
+ end
247
+ end
248
+
249
+ def precedence(production1, production2)
250
+ return :EQUAL if production1 == production2
251
+ begin
252
+ @precedence[production1][production2]
253
+ rescue NameError
254
+ nil # For the case when production1 is not related to anyone
255
+ end
256
+ end
257
+ end
258
+
259
+ # Short hand funcs for specifying priorities
260
+ def priorities(relations = [], *rest)
261
+ relations += rest
262
+ ProductionPriorities.new(relations.flatten.flatten)
263
+ end
264
+
265
+ def left(p1, *rest)
266
+ assocs(:LEFT, true, p1, *rest)
267
+ end
268
+
269
+ def right(p1, *rest)
270
+ assocs(:RIGHT, true, p1, *rest)
271
+ end
272
+
273
+ def nonassoc(p1, *rest)
274
+ assocs(:NONASSOC, true, p1, *rest)
275
+ end
276
+
277
+ def assocs(rel, allPairs, p1, *rest)
278
+ return assocs(rel, allPairs, *(p1+rest)) if p1.kind_of?(Array)
279
+ if rest.length == 0
280
+ assocs(rel, allPairs, p1, p1)
281
+ elsif rest.length == 1
282
+ [Relation.new(p1, rel, rest[0])]
283
+ else
284
+ if allPairs
285
+ rest.map {|prod| assocs(rel, allPairs, p1, prod)} +
286
+ assocs(rel, allPairs, rest.shift, *rest)
287
+ else
288
+ rest.unshift p1
289
+ (0...(rest.length-1)).map do |i|
290
+ assocs(rel, allPairs, rest[i], rest[i+1])
291
+ end.flatten
292
+ end
293
+ end
294
+ end
295
+
296
+ def decreasing_precedence(p1, p2, *rest)
297
+ assocs(:HIGHER, false, p1, p2, *rest)
298
+ end
299
+
300
+ def equal_precedence(p1, p2, *rest)
301
+ assocs(:EQUAL, false, p1, p2, *rest)
302
+ end