pils 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,128 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # This is a language config file.
18
+
19
+ module Pils
20
+ module De
21
+ module Small
22
+
23
+ CAS = %w(nom gen dat acc) #[:nom, :gen, :dat, :acc]
24
+ NUM = %w(sg pl) #[:sg, :pl]
25
+ GEN = %w(m f n) # [:m, :f, :n]
26
+ PER = %w(1 2 3) # [:er, :zw, :dr]
27
+ MOD = %w(imp ind) # [:imp, :fin, :inf]
28
+ TMP = %w(pres)
29
+
30
+ def self.add_german_verb(lex, grundform, zweitform, praetform, partform, semantic_component)
31
+ lex.add_wordform Wordform.new(normalize_forms("#{grundform}e"), "V_fin_sg_1_pres".to_sym, {}, semantic_component.clone )
32
+ lex.add_wordform Wordform.new(normalize_forms("#{zweitform}st"), "V_fin_sg_2_pres".to_sym, {}, semantic_component.clone )
33
+ lex.add_wordform Wordform.new(normalize_forms("#{zweitform}t"), "V_fin_sg_3_pres".to_sym, {}, semantic_component.clone )
34
+ lex.add_wordform Wordform.new(normalize_forms("#{grundform}en"), "V_fin_pl_1_pres".to_sym, {}, semantic_component.clone )
35
+ lex.add_wordform Wordform.new(normalize_forms("#{grundform}t"), "V_fin_pl_2_pres".to_sym, {}, semantic_component.clone )
36
+ lex.add_wordform Wordform.new(normalize_forms("#{grundform}en"), "V_fin_pl_3_pres".to_sym, {}, semantic_component.clone )
37
+ end
38
+
39
+ def self.add_german_noun(lex, semantic_component, gender, forms)
40
+ lex.add_wordform(Wordform.new(forms[0], "N_nom_sg_#{gender}_3".to_sym, {}, semantic_component.clone ))
41
+ lex.add_wordform(Wordform.new(forms[1], "N_gen_sg_#{gender}_3".to_sym, {}, semantic_component.clone ))
42
+ lex.add_wordform(Wordform.new(forms[2], "N_dat_sg_#{gender}_3".to_sym, {}, semantic_component.clone ))
43
+ lex.add_wordform(Wordform.new(forms[3], "N_acc_sg_#{gender}_3".to_sym, {}, semantic_component.clone ))
44
+ lex.add_wordform(Wordform.new(forms[4], "N_nom_pl_#{gender}_3".to_sym, {}, semantic_component.clone ))
45
+ lex.add_wordform(Wordform.new(forms[5], "N_gen_pl_#{gender}_3".to_sym, {}, semantic_component.clone ))
46
+ lex.add_wordform(Wordform.new(forms[6], "N_dat_pl_#{gender}_3".to_sym, {}, semantic_component.clone ))
47
+ lex.add_wordform(Wordform.new(forms[7], "N_acc_pl_#{gender}_3".to_sym, {}, semantic_component.clone ))
48
+ end
49
+
50
+ def self.normalize_forms(form)
51
+ form = form.gsub(/ee/, 'e')
52
+ form
53
+ end
54
+
55
+ def self.define_grammar
56
+ include Pils::Parsing
57
+ g = Grammar.new()
58
+ sentence = Cat.new(:S)
59
+ g.starting_cats = [ sentence ]
60
+
61
+ # S -> NP VP, both numerus
62
+ NUM.each do |num|
63
+ PER.each do |per|
64
+ TMP.each do |tmp|
65
+ GEN.each do |gen|
66
+ r = Rule.new(Cat.new(:S), [ Cat.new("NP_nom_#{num}_#{gen}_#{per}".to_sym, :agens), Cat.new("VP_fin_#{num}_#{per}_#{tmp}".to_sym, :agens)], {})
67
+ g.rules << r
68
+ end
69
+ r = Rule.new(Cat.new("VP_fin_#{num}_#{per}_#{tmp}".to_sym, :agens), [ Cat.new("V_fin_#{num}_#{per}_#{tmp}".to_sym, :agens)], {})
70
+ g.rules << r
71
+ end
72
+ end
73
+ end
74
+
75
+ # NP rules
76
+ CAS.each do |cas|
77
+ NUM.each do |num|
78
+ GEN.each do |gen|
79
+ #PER.each do |per|
80
+ r = Rule.new(Cat.new("NP_#{cas}_#{num}_#{gen}_3".to_sym), [ Cat.new("DETD_#{cas}_#{num}_#{gen}_3".to_sym), Cat.new("NPX_#{cas}_#{num}_#{gen}_3".to_sym)], {})
81
+ g.rules << r
82
+ r = Rule.new(Cat.new("NPX_#{cas}_#{num}_#{gen}_3".to_sym), [ Cat.new("N_#{cas}_#{num}_#{gen}_3".to_sym, :agens)], {})
83
+ g.rules << r
84
+ #r = FetaRule.new(FetaCat.new("NPX_#{cas}_#{num}_#{gen}_#{per}".to_sym), [ FetaCat.new("ADJA_#{cas}_#{num}_#{gen}_#{per}".to_sym), FetaCat.new("NPX_#{cas}_#{num}_#{gen}_#{per}".to_sym)], {})
85
+ #g.rules << r
86
+ #end
87
+ end
88
+ end
89
+ end
90
+
91
+ return g
92
+ end
93
+
94
+ def self.define_lexicon
95
+ lexicon = Lexicon.new()
96
+
97
+ # Definite Artikel
98
+
99
+ defin = %w(der des dem den die der den die die der der die die der den die das des dem das die der den die)
100
+
101
+ id = 0
102
+ GEN.each do |gen|
103
+ NUM.each do |num|
104
+ CAS.each do |cas|
105
+ form = defin[id]
106
+ id = id + 1
107
+ # DETD_nom_sg_f_3
108
+ lexicon.add_wordform Wordform.new(form, "DETD_#{cas}_#{num}_#{gen}_3".to_sym, {}, {det: :yes})
109
+ end
110
+ end
111
+ end
112
+
113
+ # some nouns
114
+
115
+ # add_german_noun(lexicon, :car, :n, %w(Auto Autos Auto Auto Autos Autos Autos Autos))
116
+ add_german_noun(lexicon, {species: :dog}, :m, %w(Hund Hundes Hund Hund Hunde Hunde Hunden Hunde))
117
+ add_german_noun(lexicon, {species: :cat}, :f, %w(Katze Katze Katze Katze Katzen Katzen Katzen Katzen))
118
+ add_german_noun(lexicon, {species: :pig}, :n, %w(Schwein Schweins Schwein Schwein Schweine Schweine Schweinen Schweine))
119
+ add_german_verb(lexicon, "fauch", "fauch", "faucht", "faucht", {sound: :hiss} )
120
+ add_german_verb(lexicon, "miau", "miau", "miaut", "miaut", {sound: :meow} )
121
+ add_german_verb(lexicon, "bell", "bell", "bellt", "bellt", {sound: :bark} )
122
+ add_german_verb(lexicon, "grunz", "grunz", "grunzt", "grunzt", {sound: :oink} )
123
+
124
+ return lexicon
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,31 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # This module encapsulates all classes related to parsing natural
18
+ # language.
19
+ module Pils
20
+ module Parsing
21
+
22
+ end
23
+ end
24
+
25
+ require "pils/parsing/cat"
26
+ require "pils/parsing/rule"
27
+ require "pils/parsing/grammar"
28
+ require "pils/parsing/wordform"
29
+ require "pils/parsing/lexicon"
30
+ require "pils/parsing/tree"
31
+ require "pils/parsing/parser"
@@ -0,0 +1,62 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+
18
+ module Pils
19
+ module Parsing
20
+ class Cat
21
+ attr_accessor :grammar
22
+ attr_accessor :rule
23
+ attr_accessor :cat
24
+ attr_accessor :feat
25
+ attr_accessor :descriptor
26
+
27
+
28
+ def initialize(new_cat, new_desc = nil, new_feat={})
29
+ @cat = new_cat
30
+ @descriptor = new_desc
31
+ @feat = Pils::Structures::Avm.new()
32
+ new_feat.each do |k,v|
33
+ @feat[k] = v
34
+ end
35
+ end
36
+
37
+ # true if the grammar lets you expand this symbol
38
+ # def expandible?()
39
+ # grammar.rules.each do |r|
40
+ # return true if self < r.left
41
+ # end#
42
+ # end
43
+
44
+ # true if this cat matches other one.
45
+ def <(other)
46
+ return false if self.cat != other.cat
47
+ return self.feat < other.feat
48
+ end
49
+
50
+ def display
51
+ "%s" % [cat.to_s, feat.values.collect{|v| v.to_s}.join(',')]
52
+ end
53
+
54
+ def to_s
55
+ self.display
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
62
+
@@ -0,0 +1,47 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+
18
+ module Pils
19
+ module Parsing
20
+ class Grammar
21
+
22
+ attr_accessor :starting_cats
23
+ attr_accessor :rules
24
+
25
+ def initialize()
26
+ @rules = []
27
+ @starting_cats = []
28
+ end
29
+
30
+ def expandible?(cat)
31
+ !@rules.find{|r| r.expandible?(cat)}.nil?
32
+ end
33
+
34
+ def expand(cat)
35
+ @rules.select{|r| r.expandible?(cat)}
36
+ end
37
+
38
+ def describe_rules
39
+ @rules.each do |rule|
40
+ Pils::log rule.display
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,100 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+
18
+ module Pils
19
+ module Parsing
20
+ class Lexicon
21
+
22
+ attr_accessor :entries
23
+ attr_accessor :definite_articles
24
+
25
+ def initialize
26
+ @entries = Set.new
27
+ @definite_articles = [
28
+ 'der', 'des', 'dem', 'den', 'die', 'der', 'den', 'die',
29
+ 'die', 'der', 'der', 'die', 'die', 'der', 'den', 'die',
30
+ 'das', 'des', 'dem', 'das', 'die', 'der', 'den', 'die'
31
+ ]
32
+ @indefinite_articles = [
33
+ 'ein', 'eines', 'einem', 'einen', '', '', '', '',
34
+ 'eine', 'einer', 'einer', 'eine', '', '', '', '',
35
+ 'ein', 'eines', 'einem', 'ein', '', '', '', ''
36
+ ]
37
+ end
38
+
39
+ def definite_article(kase=:nom, number=:sg, gender=:m)
40
+ pos = 0
41
+ pos = 8 if gender==:f
42
+ pos = 16 if gender==:n
43
+ pos = pos + 4 if number==:pl
44
+ pos = pos + 1 if kase==:gen
45
+ pos = pos + 2 if kase==:dat
46
+ pos = pos + 3 if kase==:acc
47
+ @definite_articles[pos]
48
+ end
49
+
50
+ def indefinite_article(kase=:nom, number=:sg, gender=:m)
51
+ pos = 0
52
+ pos = 8 if gender==:f
53
+ pos = 16 if gender==:n
54
+ pos = pos + 4 if number==:pl
55
+ pos = pos + 1 if kase==:gen
56
+ pos = pos + 2 if kase==:dat
57
+ pos = pos + 3 if kase==:acc
58
+ @indefinite_articles[pos]
59
+ end
60
+
61
+
62
+ def add_wordform(new_form)
63
+ @entries << new_form
64
+ end
65
+
66
+
67
+ def get(params={})
68
+ temp = @entries
69
+ # Pils::log ">>>> " + temp.to_a.collect{|a| ("%s(%s)" % [a.form,a.cat])}.join(', ')
70
+ return {} if (temp.empty? || params[:form] =~ /^\s*$/ )
71
+ if params.has_key?(:form) && !(params[:form].nil?)
72
+ # Pils::log "§FORM"
73
+ # temp.each do |t|
74
+ # puts t
75
+ # puts t.form
76
+ # end
77
+ # puts params[:form]
78
+ temp = temp.select{|t| !(t.form.nil?) && t.form.downcase==params[:form].downcase}
79
+ end
80
+ if params.has_key?(:cat) && !(params[:cat].nil?)
81
+ # Pils::log "§CAT"
82
+ temp = temp.select{|t| t.cat==params[:cat]}
83
+ end
84
+ if params.has_key?(:grammar)
85
+ g = Pils::Structures::Avm.new(params[:grammar])
86
+ temp = temp.select{|t| g < t.grammar}
87
+ end
88
+ temp
89
+ end
90
+
91
+
92
+ def describe
93
+ @entries.each do |entry|
94
+ Pils::log "// %s" % [entry.display.to_s]
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,310 @@
1
+ # This file is part of Pils.
2
+ #
3
+ # Pils is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation, either version 3 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # Pils is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the
14
+ # GNU Lesser General Public License along with Pils.
15
+ # If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module Pils
18
+ module Parsing
19
+ class Parser
20
+
21
+ attr_accessor :tokens
22
+
23
+ attr_accessor :syntax
24
+ attr_accessor :semantics
25
+
26
+ attr_accessor :syntax_position
27
+ attr_accessor :token_position
28
+
29
+ attr_accessor :stack
30
+
31
+ attr_accessor :grammar
32
+ attr_accessor :lexicon
33
+
34
+
35
+ def tokenize(str)
36
+ str.split(/\s+/)
37
+ end
38
+
39
+ def init(ptokens)
40
+ # set start states
41
+ @tokens = ptokens
42
+ @token_position = 0
43
+ # syntactic state
44
+ # set state to first start symbol, stack to remaining start symbols
45
+
46
+ @syntax = Tree.new(@grammar.starting_cats.first, nil) # Pils::Parsing::
47
+ @syntax_position = 0
48
+
49
+ # semantic state
50
+ @semantics = {}
51
+ # stack
52
+ @stack = []
53
+
54
+ Pils::log @tokens
55
+ end
56
+
57
+ def word_substitutions
58
+ # current category in syntax
59
+ # current word
60
+ # is there a lexicon entry for form, cat, gram?
61
+
62
+ @lexicon_matches = @lexicon.get({
63
+ form: @tokens[@token_position],
64
+ cat: @head_cat.obj.cat,
65
+ grammar: @head_cat.obj.grammar
66
+ })
67
+ # Pils::log "Lexical matches: %i" % @lexicon_matches.count
68
+ # Pils::log "Lexical matches:\n - %s" % @lexicon_matches.collect{|m| m.display }.join("\n - ")
69
+ @lexicon_matches
70
+ end
71
+
72
+ def expand_cat
73
+ rules = @grammar.expand(@head_cat.obj)
74
+ # Pils::log "Erwartet wird: %s" % @head_cat.obj.display
75
+ # exit(0) if @head_cat.obj.display == "NP_nom_sg_m_3"
76
+ # Pils::log " Ahnen %s" % @head_cat.ancestors_from_root.collect{|a| a.obj }.join('>')
77
+ # Pils::log " Descriptor %s" % @head_path.join('>')
78
+
79
+
80
+ # Pils::log "Anwendbare Regeln: %i" % rules.count
81
+ # rules.each_with_index do |rule,ind|
82
+ # Pils::log " %3i - %s ==> %s"% [ ind, rule.left.display, rule.right.collect{|r| r.display}.join(' ')]
83
+ # end
84
+
85
+ first_rule, *rules_tail = *rules
86
+
87
+ if rules_tail.count>0
88
+ rules_tail.each do |rule|
89
+ # create a new alternative state.
90
+ new_syntax = @syntax.clone
91
+ new_semantics = @semantics.clone
92
+ # apply rule!
93
+ new_child_list = []
94
+ new_head_cat = new_syntax.leaf_at(@syntax_position)
95
+ rule.right.each do |x|
96
+ new_child_list << Tree.new(x)
97
+ end
98
+ new_head_cat.set_children(new_child_list, true)
99
+ new_state = [new_syntax, new_semantics, @syntax_position, @token_position]
100
+ @stack << new_state
101
+ end
102
+ end
103
+ # expand current tree
104
+ # Pils::log @syntax.display
105
+ new_child_list = []
106
+ first_rule.right.each do |x|
107
+ new_child_list << Tree.new(x)
108
+ end
109
+ @head_cat.set_children(new_child_list, true)
110
+ # Pils::log @syntax.display
111
+ sync
112
+
113
+ end
114
+
115
+ def sync
116
+ @head_cat = @syntax.leaf_at(@syntax_position)
117
+ if @head_cat
118
+ @head_path = @head_cat.ancestors_from_root.collect{|a| a.obj.descriptor}.reject{|d| d.nil?}
119
+ end
120
+ # Pils::log @head_cat.obj.display
121
+
122
+ end
123
+
124
+ def parse!(max_iterations=50)
125
+ @result = true
126
+ @iter = 0
127
+ puts @token_position
128
+ puts @tokens
129
+ while (@result===true || @result===false) && (@tokens.size-@token_position>=1) && @iter<max_iterations
130
+ @result = parse
131
+ @iter = @iter + 1
132
+ # Pils::log ''
133
+ # Pils::log '+---+'
134
+ # Pils::log '+%3i+' % @iter
135
+ # Pils::log '+---+'
136
+
137
+ end
138
+ # Pils::log Pils::log '+ --- +'
139
+ # Pils::log Pils::log @result
140
+ # Pils::log Pils::log '+ --- +'
141
+ # Pils::log @result.class.name
142
+ if @result.kind_of?(Hash)
143
+ @tokens.each_with_index do |token,n|
144
+ @result[:syntax].set_wordform_at(n, token)
145
+ end
146
+ end
147
+ return @result
148
+ end
149
+
150
+ def parse
151
+ # categorial replacement?
152
+ # gibt es für das Blatt an aktueller Position eine Expansion?
153
+ # Pils::log '*' * 48
154
+ # Pils::log display_tokens
155
+ # Pils::log '*' * 48
156
+ # Pils::log @syntax.display
157
+ # Pils::log '*' * 48
158
+ # Pils::log @head_cat.obj if @head_cat
159
+ # Pils::log '*' * 48
160
+ # Pils::log @syntax.leaf_count
161
+ # Pils::log @syntax_position
162
+ # Pils::log @syntax.leaf_at(@syntax_position-1).obj unless @syntax_position==0
163
+ # Pils::log '*' * 48
164
+ sync
165
+ # Pils::log @syntax.leaf_count
166
+ # Pils::log @syntax.leaves
167
+ # Pils::log @syntax.leaves.count
168
+ # Pils::log @syntax.leaves.reject{|l| l.obj.nil? }
169
+ # Pils::log @syntax.leaves.reject{|l| l.obj.nil? }.count
170
+
171
+ # Pils::log @syntax_position
172
+ (0..(@syntax.leaf_count-1)).each do |u|
173
+ current_leaf = @syntax.leaf_at(u)
174
+ # Pils::log " %2i %s" % [u, current_leaf]
175
+ # Pils::log " %s" % [u, current_leaf.obj]
176
+ end
177
+ # stack:
178
+ # Pils::log "STACK:"
179
+ #@stack.each do |n|
180
+ # Pils::log " -- %2i %2i %s" % [n[2], n[3], n[0].display]
181
+ #end
182
+ # Pils::log @syntax.leaf_at(@syntax_position-1).obj unless @syntax_position==0
183
+ # Pils::log '*' * 48
184
+
185
+ # Pils::log "# A. CATEGORY EXPANSION"
186
+ # Pils::log @head_cat
187
+ # gib fehler aus, wenn nach sync keine head cat da ist
188
+ if @head_cat.nil? && !(@token_position > (@tokens.count-1))
189
+ retrieve_from_stack
190
+ return false
191
+ end
192
+
193
+ # Pils::log " looking for expansions for #{@head_cat.obj}" if @head_cat
194
+
195
+ # Jetzt: Wenn es fuer diese Kategorie eine Ableitung gibt, fuehre diese durch.
196
+ success = false
197
+ if @head_cat
198
+ # Pils::log @grammar.expandible?(@head_cat.obj)
199
+ if @grammar.expandible?(@head_cat.obj)
200
+ # get first result.
201
+ # extend tree.
202
+ # set new tree as syntax.
203
+ expand_cat
204
+ success = true
205
+ end
206
+ return true if success
207
+ # Pils::log Pils::log "KEINE CATEXPANSION MOEGLICH. Daher jetzt Wortabgleich."
208
+ end
209
+ # Wortabgleich!
210
+ # Passt das aktuelle Wort auf das momentane Token?
211
+ # Wenn ja: Setze beide Positionen eins weiter.
212
+ # und trage die Semantik in die Semantikliste ein.
213
+ # Pils::log "# B. WORD EXPANSION"
214
+ # Pils::log " looking for a word for #{@head_cat.obj}"
215
+ subs = word_substitutions
216
+ if subs.count > 0
217
+ # wir haben Wortersetzungen! Nimm die erste. Erzeuge einen
218
+ # neuen Zustand im aktuellen System.
219
+ # Setze die restlichen in die Warteliste.
220
+ head, *tail = *subs
221
+ if tail
222
+ tail.each do |t|
223
+ new_syntax = @syntax.clone
224
+ new_semantics = @semantics.clone
225
+ new_state = [new_syntax, new_semantics, @syntax_position, @token_position]
226
+ @stack << new_state
227
+ end
228
+ # Pils::log "Stack: %i" % @stack.size
229
+ # @stack.each do |n|
230
+ # Pils::log " -- %2i %2i %s" % [n[2], n[3], n[0].display]
231
+ # end
232
+ end
233
+ if head
234
+ # obtain semantics for word form
235
+ # get path descriptor from tree
236
+ # Pils::log " --SEM- %s %s" % [head.form, head.semantics ]
237
+ semobj = @semantics
238
+ @head_path.each do |p|
239
+ semobj[p] = {} unless semobj.has_key?(p)
240
+ semobj = semobj[p]
241
+ end
242
+
243
+ head_path_descriptor = @head_path.collect{|h| "[:#{h}]"}.join('')
244
+ # Pils::log "HP : %s" % @head_path.to_s
245
+ # Pils::log "HPD: %s" % head_path_descriptor
246
+ # Pils::log @head_cat.ancestors.collect{|a| a.display}
247
+ # Pils::log @head_cat.ancestors_from_root.collect{|a| a.display}
248
+
249
+ head.semantics.keys.each do |key|
250
+ # Pils::log " @semantics#{head_path_descriptor}[key] = head.semantics[key]"
251
+ instance_eval("@semantics#{head_path_descriptor}[key] = head.semantics[key]")
252
+ # semobj[key] = head[key]
253
+ end
254
+
255
+
256
+ @token_position = @token_position + 1
257
+ @syntax_position = @syntax_position + 1
258
+ sync
259
+ # TODO: Store semantics
260
+ end
261
+ success = true
262
+ end
263
+ # Pils::log " now looking at #{@head_cat ? @head_cat.obj : 'END'}"
264
+ # Pils::log " token pos %i / %i " % [@token_position, (@tokens.count-1)]
265
+ # check if end is nigh
266
+ if @head_cat.nil? && @token_position > (@tokens.count-1)
267
+ # Pils::log "END IS NIGH!"
268
+ # Pils::log @syntax.display
269
+ # Pils::log @semantics # JSON.pretty_generate(@semantics)
270
+ return {syntax: @syntax.clone, semantics: @semantics.clone}
271
+ # exit(0)
272
+ end
273
+ return true if success
274
+
275
+ retrieve_from_stack
276
+
277
+ # Pils::log @syntax.display
278
+ # Pils::log "Now expecting: %s" % @syntax.leaf_at(@syntax_position).obj.to_s
279
+
280
+ # Pils::log @stack.size
281
+ return success
282
+ end
283
+
284
+ # gets the next possible state from stack, discards the current one.
285
+ def retrieve_from_stack
286
+ # Pils::log "We retrieve the next object from the stack. %i" % @stack.size
287
+ return nil if @stack.nil? || @stack.empty?
288
+ new_state, *state_rest = @stack
289
+ @stack = state_rest
290
+ @syntax = new_state[0]
291
+ @semantics = new_state[1]
292
+ @syntax_position = new_state[2]
293
+ @token_position = new_state[3]
294
+ sync
295
+ # Pils::log " becomes %i" % @stack.size
296
+ # @stack.each do |n|
297
+ # Pils::log " -- %2i %2i %s" % [n[2], n[3], n[0].display]
298
+ # end
299
+ end
300
+
301
+
302
+ def display_tokens
303
+ r = @tokens.slice(0,@token_position)
304
+ s = @tokens - r
305
+ t = [r, "*", s].flatten
306
+ t.join(" ")
307
+ end
308
+ end
309
+ end
310
+ end