5 # Permission to use, copy, modify, and distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
9 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 alias_method :to_s, :token
23 alias_method :to_tree, :token
28 other.is_a?(Terminal) and other.token==@token
30 alias_method :==, :eql?
31 alias_method :===, :eql?
35 attr_accessor :i,:j,:category,:contents,:sub
36 def initialize(i,j,category,contents,sub=[])
44 other.is_a? Edge and hash == other.hash
46 alias_method :==, :eql?
47 alias_method :===, :eql?
49 [@i,@j,@category,@contents,@sub].hash
52 "#{active? ? 'A' : 'I'}"+
53 "<#{@i},#{@j},#{@category},[%s],[%s]>" % [@contents.join(', '),
57 unfound and unfound.length != 0
60 @contents[@sub.length..-1]
66 #raise Exception if active?
68 [@category, @sub.map{|e|e.to_tree}]
70 [@category, @contents.map{|e|e.to_tree}]
76 def initialize(grammar,log=Logger.new(STDERR))
86 # Initialise agenda with known words
87 text.each_with_index do |word,n|
89 @grammar.each do |cat,rule|
91 if r == [Terminal.new(word)]
92 agenda << Edge.new(n,n+1,cat,[],[Terminal.new(word)])
99 # Move one from agenda to chart
100 edge = agenda.delete_at(0)
106 # Add to the agenda from edges in the chart
107 new_edges(active,inactive) do |edge|
108 unless agenda.include?(edge) or (active+inactive).include?(edge)
109 @log.debug("new_edges adding: #{edge}")
114 agenda.each do |edge|
115 @log.debug("agenda: #{edge}")
117 (active+inactive).each do |edge|
118 @log.debug("chart: #{edge}")
123 # Return all spanning parses as a Set
124 inactive.select do |edge|
125 edge.i == 0 and edge.j == text.length
131 def new_edges(active,inactive)
132 bottom_up(active,inactive) do |edge|
135 fundamental(active,inactive) do |edge|
140 def bottom_up(active,inactive)
141 inactive.each do |edge|
142 @grammar.each do |r,w|
144 if ww[0] == edge.category
145 newedge = Edge.new(edge.i,edge.i,r,ww)
146 @log.debug("bottom_up: via #{edge} found #{newedge}")
154 def fundamental(active,inactive)
157 if a.j == i.i and a.nextWord == i.category
158 newedge = Edge.new(a.i,i.j,a.category,a.contents,a.sub+[i])
159 @log.debug("fundamental: via #{a} and #{i} found #{newedge}")
169 log = Logger.new(STDERR)
170 log.level = Logger::DEBUG
171 log.formatter = lambda {|sev,time,pname,msg| "#{sev}: #{msg}\n"}
173 shouldBe = Set.new([["S", [["NP", [["NP", ["time"]], ["NP", ["flies"]]]],
174 ["VP", [["V", ["like"]], ["NP", [["Det", ["an"]], ["NP", ["arrow"]]]]]]]],
175 ["S", [["NP", ["time"]], ["VP", [["V", ["flies"]], ["PP", [["PR", ["like"]],
176 ["NP", [["Det", ["an"]], ["NP", ["arrow"]]]]]]]]]]])
177 # time flies like an arrow
178 # -> [time] [flies] [like an arrow]
179 # -> [time flies] [like] [an arrow]
181 grammar = {'S'=>[%w{NP VP}],
184 'NP'=>[[Terminal.new('time')],
185 [Terminal.new('flies')],
186 [Terminal.new('arrow')],
190 'PR'=>[[Terminal.new('like')]],
191 'Det'=>[[Terminal.new('an')]],
192 'V'=>[[Terminal.new('like')],
193 [Terminal.new('flies')]]}
195 p = Parser.new(grammar,log)
196 q = p.parse(%w{time flies like an arrow})
199 puts "Test succeeded!"
200 puts "Output was #{q.inspect}"
203 puts "Should have been #{shouldBe.inspect}"
204 puts "But was actually #{q.inspect}"