1 """Base classes for grammatical rules.
2
3 Base formalism rules: this provides the base classes for formalisms
4 to define their rules with. All behaviour in here should be core CCG
5 rule behaviour common to all CCG formalisms.
6 Subclasses these rules in specific formalisms.
7
8 """
9 """
10 ============================== License ========================================
11 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
12
13 This file is part of The Jazz Parser.
14
15 The Jazz Parser is free software: you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation, either version 3 of the License, or
18 (at your option) any later version.
19
20 The Jazz Parser is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
27
28 ============================ End license ======================================
29
30 """
31 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
32
33 import logging
34 import copy
35
36 from jazzparser import settings
37
38
39 logger = logging.getLogger("main_logger")
40
42 readable_rule = "Undefined"
43
44 - def __init__(self, formalism, *args, **kwargs):
61
63 """
64 *** This should be overridden by subclasses. ***
65 Applies the rule to combine the categories in cat_list.
66
67 Note that the returned semantics should always be in beta-normal form.
68
69 @return: a list of the possible categories resulting from the
70 application if the rule is valid for the given arguments,
71 otherwise None.
72
73 """
74 raise NotImplementedError, "Called abstract Rule.apply_rule()"
75
79
81 """
82 Performs the semantic processing involved in applying the rule to these
83 arguments.
84
85 This doesn't do any checks on the syntactic type. If it's not used
86 in a situation where you know that the syntactic part of the application
87 will work, it could produce a non-sensical semantics or even raise
88 errors. It's designed for speeding up applying a rule to many signs
89 known to have the same syntactic type (so that the syntactic checks
90 only need to be done once).
91
92 Depending on the formalism, this may not be any faster than calling
93 L{apply_rule} and getting the semantics from the results. In fact,
94 this is the default behaviour. Any sensible formalism will provide
95 a faster implementation of this method, though.
96
97 @return: list of the Semantics objects that would be the logical
98 form parts of the results of the rule application.
99
100 """
101 return [res.semantics for res in self.apply_rule(cat_list)]
102
104 """
105 Rule for standard CCG application.
106 """
108 """
109 An application rule. May be forward or backward, depending on
110 the direction given in XML element.
111
112 """
113 self.forward = (kwargs.get('dir', "forward") == "forward")
114 if self.forward:
115 self.name = ">"
116 self.internal_name = "appf"
117 else:
118 self.name = "<"
119 self.internal_name = "appb"
120 self.arity = 2
121 if self.forward:
122 self.readable_rule = "X/Y Y => X"
123 else:
124 self.readable_rule = "Y X\\Y => X"
125
126 super(ApplicationRuleBase, self).__init__(*args, **kwargs)
127
128 - def apply_rule(self, cat_list, conditions=None, proc_sems=None):
129 """
130 conditions should be a callable that takes the functor followed
131 by the argument and returns a boolean. It will be run after
132 the basic applicability tests to check whether the rule should
133 be applied.
134 If a function is given in proc_sems, it will be called to
135 process the semantics of the inputs before the resultant
136 semantics is built.
137 """
138
139
140 if len(cat_list) != 2:
141 return None
142
143
144 if self.forward:
145
146 functor = cat_list[0]
147 argument = cat_list[1]
148 else:
149
150 functor = cat_list[1]
151 argument = cat_list[0]
152
153
154 if not self.formalism.Syntax.is_complex_category(functor.category):
155 return None
156
157
158 if functor.category.slash.forward != self.forward:
159 return None
160
161 if conditions is not None:
162 if not conditions(functor, argument):
163 return None
164
165 functor_cat = functor.category.copy()
166 argument_cat = argument.category.copy()
167 self.formalism.distinguish_categories(functor_cat, argument_cat)
168
169
170
171 unification_result = self.formalism.unify(functor_cat.argument, argument_cat, grammar=self.grammar)
172 if unification_result is None:
173
174 return None
175
176
177
178
179
180
181 category = unification_result.constraints.apply(functor_cat.result)
182
183
184 new_functor = functor.semantics.copy()
185 new_argument = argument.semantics.copy()
186
187 if proc_sems is not None:
188 proc_sems(new_functor, new_argument)
189
190 semantics = self.formalism.Semantics.apply(new_functor, new_argument, grammar=self.grammar)
191
192 result = self.formalism.Syntax.Sign(category, semantics)
193
194 if settings.WARN_ABOUT_FREE_VARS:
195 free_vars = semantics.lf.get_unbound_variables()
196 if free_vars:
197 logger.warn("Found free variables after application: %s in %s" % (",".join(["%s" % var for var in free_vars]), semantics))
198
199 return [result]
200
201
202
204 """
205 Rule for standard CCG composition.
206 """
208 super(CompositionRuleBase, self).__init__(*args, **kwargs)
209 forward = (kwargs.get("dir", "forward") == "forward")
210 harmonic = (kwargs.get("harmonic", "true") == "true")
211 if forward:
212 if harmonic:
213 self.name = ">B"
214 self.readable_rule = "X/Y Y/Z =>B X/Z"
215 self.internal_name = "compf"
216 else:
217 self.name = ">Bx"
218 self.readable_rule = "X/Y Y\\Z =>Bx X\\Z"
219 self.internal_name = "xcompf"
220 else:
221 if harmonic:
222 self.name = "<B"
223 self.readable_rule = "Y\\Z X\\Y =>B X\\Z"
224 self.internal_name = "compb"
225 else:
226 self.name = "<Bx"
227 self.readable_rule = "Y/Z X\\Y =>Bx X/Z"
228 self.internal_name = "xcompb"
229
230 self.forward = forward
231 self.harmonic = harmonic
232 self.arity = 2
233
234 - def apply_rule(self, cat_list, conditions=None, proc_sems=None):
235
236
237 if len(cat_list) != 2:
238 return None
239
240 first = cat_list[0]
241 second = cat_list[1]
242
243 if not self.formalism.Syntax.is_complex_category(first.category) or \
244 not self.formalism.Syntax.is_complex_category(second.category):
245 return None
246
247 if self.harmonic:
248 if first.category.slash.forward != self.forward or \
249 second.category.slash.forward != self.forward:
250 return None
251 else:
252 if not first.category.slash.forward or \
253 second.category.slash.forward:
254 return None
255
256 if conditions is not None:
257
258 if not conditions(first, second):
259 return None
260
261
262 first_cat = first.category.copy()
263 second_cat = second.category.copy()
264 self.formalism.distinguish_categories(first_cat, second_cat)
265
266
267 if self.forward:
268 middle1 = first_cat.argument
269 middle2 = second_cat.result
270 else:
271 middle1 = first_cat.result
272 middle2 = second_cat.argument
273
274
275 unification_result = self.formalism.unify(middle1, middle2, grammar=self.grammar)
276 if unification_result is None:
277 return None
278
279
280
281
282 if self.forward:
283 result = first_cat.result
284 argument = second_cat.argument
285 else:
286 result = second_cat.result
287 argument = first_cat.argument
288
289 if not self.forward:
290
291 unification_result.apply_all_mappings(result)
292
293 result = unification_result.constraints.apply(result)
294
295 if self.forward:
296
297 unification_result.apply_all_mappings(argument)
298
299 argument = unification_result.constraints.apply(argument)
300
301
302
303 slash = self.formalism.Syntax.Slash(self.forward == self.harmonic)
304 category = self.formalism.Syntax.ComplexCategory(
305 result, \
306 slash, \
307 argument)
308
309
310 if self.forward:
311 fun_f = first.semantics.copy()
312 fun_g = second.semantics.copy()
313 else:
314 fun_g = first.semantics.copy()
315 fun_f = second.semantics.copy()
316
317
318 from jazzparser.formalisms.base.semantics.lambdacalc import distinguish_variables
319 distinguish_variables(fun_f, fun_g)
320
321 if proc_sems is not None:
322 proc_sems(fun_f, fun_g)
323 semantics = self.formalism.Semantics.compose(fun_f, fun_g)
324
325
326 semantics.beta_reduce(grammar=self.grammar)
327
328 if settings.WARN_ABOUT_FREE_VARS:
329 free_vars = semantics.lf.get_unbound_variables()
330 if free_vars:
331 logger.warn("Found free variables after composition: %s in %s" % (",".join(["%s" % var for var in free_vars]), semantics))
332
333 result = self.formalism.Syntax.Sign(category, semantics)
334 return [result]
335