logic.py 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073
  1. # Natural Language Toolkit: Logic
  2. #
  3. # Author: Dan Garrette <dhgarrette@gmail.com>
  4. #
  5. # Copyright (C) 2001-2019 NLTK Project
  6. # URL: <http://nltk.org>
  7. # For license information, see LICENSE.TXT
  8. """
  9. A version of first order predicate logic, built on
  10. top of the typed lambda calculus.
  11. """
  12. from __future__ import print_function, unicode_literals
  13. import re
  14. import operator
  15. from collections import defaultdict
  16. from functools import reduce, total_ordering
  17. from six import string_types
  18. from nltk.util import Trie
  19. from nltk.internals import Counter
  20. from nltk.compat import python_2_unicode_compatible
  21. APP = 'APP'
  22. _counter = Counter()
  23. class Tokens(object):
  24. LAMBDA = '\\'
  25. LAMBDA_LIST = ['\\']
  26. # Quantifiers
  27. EXISTS = 'exists'
  28. EXISTS_LIST = ['some', 'exists', 'exist']
  29. ALL = 'all'
  30. ALL_LIST = ['all', 'forall']
  31. # Punctuation
  32. DOT = '.'
  33. OPEN = '('
  34. CLOSE = ')'
  35. COMMA = ','
  36. # Operations
  37. NOT = '-'
  38. NOT_LIST = ['not', '-', '!']
  39. AND = '&'
  40. AND_LIST = ['and', '&', '^']
  41. OR = '|'
  42. OR_LIST = ['or', '|']
  43. IMP = '->'
  44. IMP_LIST = ['implies', '->', '=>']
  45. IFF = '<->'
  46. IFF_LIST = ['iff', '<->', '<=>']
  47. EQ = '='
  48. EQ_LIST = ['=', '==']
  49. NEQ = '!='
  50. NEQ_LIST = ['!=']
  51. # Collections of tokens
  52. BINOPS = AND_LIST + OR_LIST + IMP_LIST + IFF_LIST
  53. QUANTS = EXISTS_LIST + ALL_LIST
  54. PUNCT = [DOT, OPEN, CLOSE, COMMA]
  55. TOKENS = BINOPS + EQ_LIST + NEQ_LIST + QUANTS + LAMBDA_LIST + PUNCT + NOT_LIST
  56. # Special
  57. SYMBOLS = [x for x in TOKENS if re.match(r'^[-\\.(),!&^|>=<]*$', x)]
  58. def boolean_ops():
  59. """
  60. Boolean operators
  61. """
  62. names = ["negation", "conjunction", "disjunction", "implication", "equivalence"]
  63. for pair in zip(names, [Tokens.NOT, Tokens.AND, Tokens.OR, Tokens.IMP, Tokens.IFF]):
  64. print("%-15s\t%s" % pair)
  65. def equality_preds():
  66. """
  67. Equality predicates
  68. """
  69. names = ["equality", "inequality"]
  70. for pair in zip(names, [Tokens.EQ, Tokens.NEQ]):
  71. print("%-15s\t%s" % pair)
  72. def binding_ops():
  73. """
  74. Binding operators
  75. """
  76. names = ["existential", "universal", "lambda"]
  77. for pair in zip(names, [Tokens.EXISTS, Tokens.ALL, Tokens.LAMBDA]):
  78. print("%-15s\t%s" % pair)
  79. @python_2_unicode_compatible
  80. class LogicParser(object):
  81. """A lambda calculus expression parser."""
  82. def __init__(self, type_check=False):
  83. """
  84. :param type_check: bool should type checking be performed?
  85. to their types.
  86. """
  87. assert isinstance(type_check, bool)
  88. self._currentIndex = 0
  89. self._buffer = []
  90. self.type_check = type_check
  91. """A list of tuples of quote characters. The 4-tuple is comprised
  92. of the start character, the end character, the escape character, and
  93. a boolean indicating whether the quotes should be included in the
  94. result. Quotes are used to signify that a token should be treated as
  95. atomic, ignoring any special characters within the token. The escape
  96. character allows the quote end character to be used within the quote.
  97. If True, the boolean indicates that the final token should contain the
  98. quote and escape characters.
  99. This method exists to be overridden"""
  100. self.quote_chars = []
  101. self.operator_precedence = dict(
  102. [(x, 1) for x in Tokens.LAMBDA_LIST]
  103. + [(x, 2) for x in Tokens.NOT_LIST]
  104. + [(APP, 3)]
  105. + [(x, 4) for x in Tokens.EQ_LIST + Tokens.NEQ_LIST]
  106. + [(x, 5) for x in Tokens.QUANTS]
  107. + [(x, 6) for x in Tokens.AND_LIST]
  108. + [(x, 7) for x in Tokens.OR_LIST]
  109. + [(x, 8) for x in Tokens.IMP_LIST]
  110. + [(x, 9) for x in Tokens.IFF_LIST]
  111. + [(None, 10)]
  112. )
  113. self.right_associated_operations = [APP]
  114. def parse(self, data, signature=None):
  115. """
  116. Parse the expression.
  117. :param data: str for the input to be parsed
  118. :param signature: ``dict<str, str>`` that maps variable names to type
  119. strings
  120. :returns: a parsed Expression
  121. """
  122. data = data.rstrip()
  123. self._currentIndex = 0
  124. self._buffer, mapping = self.process(data)
  125. try:
  126. result = self.process_next_expression(None)
  127. if self.inRange(0):
  128. raise UnexpectedTokenException(self._currentIndex + 1, self.token(0))
  129. except LogicalExpressionException as e:
  130. msg = '%s\n%s\n%s^' % (e, data, ' ' * mapping[e.index - 1])
  131. raise LogicalExpressionException(None, msg)
  132. if self.type_check:
  133. result.typecheck(signature)
  134. return result
  135. def process(self, data):
  136. """Split the data into tokens"""
  137. out = []
  138. mapping = {}
  139. tokenTrie = Trie(self.get_all_symbols())
  140. token = ''
  141. data_idx = 0
  142. token_start_idx = data_idx
  143. while data_idx < len(data):
  144. cur_data_idx = data_idx
  145. quoted_token, data_idx = self.process_quoted_token(data_idx, data)
  146. if quoted_token:
  147. if not token:
  148. token_start_idx = cur_data_idx
  149. token += quoted_token
  150. continue
  151. st = tokenTrie
  152. c = data[data_idx]
  153. symbol = ''
  154. while c in st:
  155. symbol += c
  156. st = st[c]
  157. if len(data) - data_idx > len(symbol):
  158. c = data[data_idx + len(symbol)]
  159. else:
  160. break
  161. if Trie.LEAF in st:
  162. # token is a complete symbol
  163. if token:
  164. mapping[len(out)] = token_start_idx
  165. out.append(token)
  166. token = ''
  167. mapping[len(out)] = data_idx
  168. out.append(symbol)
  169. data_idx += len(symbol)
  170. else:
  171. if data[data_idx] in ' \t\n': # any whitespace
  172. if token:
  173. mapping[len(out)] = token_start_idx
  174. out.append(token)
  175. token = ''
  176. else:
  177. if not token:
  178. token_start_idx = data_idx
  179. token += data[data_idx]
  180. data_idx += 1
  181. if token:
  182. mapping[len(out)] = token_start_idx
  183. out.append(token)
  184. mapping[len(out)] = len(data)
  185. mapping[len(out) + 1] = len(data) + 1
  186. return out, mapping
  187. def process_quoted_token(self, data_idx, data):
  188. token = ''
  189. c = data[data_idx]
  190. i = data_idx
  191. for start, end, escape, incl_quotes in self.quote_chars:
  192. if c == start:
  193. if incl_quotes:
  194. token += c
  195. i += 1
  196. while data[i] != end:
  197. if data[i] == escape:
  198. if incl_quotes:
  199. token += data[i]
  200. i += 1
  201. if len(data) == i: # if there are no more chars
  202. raise LogicalExpressionException(
  203. None,
  204. "End of input reached. "
  205. "Escape character [%s] found at end." % escape,
  206. )
  207. token += data[i]
  208. else:
  209. token += data[i]
  210. i += 1
  211. if len(data) == i:
  212. raise LogicalExpressionException(
  213. None, "End of input reached. " "Expected: [%s]" % end
  214. )
  215. if incl_quotes:
  216. token += data[i]
  217. i += 1
  218. if not token:
  219. raise LogicalExpressionException(None, 'Empty quoted token found')
  220. break
  221. return token, i
  222. def get_all_symbols(self):
  223. """This method exists to be overridden"""
  224. return Tokens.SYMBOLS
  225. def inRange(self, location):
  226. """Return TRUE if the given location is within the buffer"""
  227. return self._currentIndex + location < len(self._buffer)
  228. def token(self, location=None):
  229. """Get the next waiting token. If a location is given, then
  230. return the token at currentIndex+location without advancing
  231. currentIndex; setting it gives lookahead/lookback capability."""
  232. try:
  233. if location is None:
  234. tok = self._buffer[self._currentIndex]
  235. self._currentIndex += 1
  236. else:
  237. tok = self._buffer[self._currentIndex + location]
  238. return tok
  239. except IndexError:
  240. raise ExpectedMoreTokensException(self._currentIndex + 1)
  241. def isvariable(self, tok):
  242. return tok not in Tokens.TOKENS
  243. def process_next_expression(self, context):
  244. """Parse the next complete expression from the stream and return it."""
  245. try:
  246. tok = self.token()
  247. except ExpectedMoreTokensException:
  248. raise ExpectedMoreTokensException(
  249. self._currentIndex + 1, message='Expression expected.'
  250. )
  251. accum = self.handle(tok, context)
  252. if not accum:
  253. raise UnexpectedTokenException(
  254. self._currentIndex, tok, message='Expression expected.'
  255. )
  256. return self.attempt_adjuncts(accum, context)
  257. def handle(self, tok, context):
  258. """This method is intended to be overridden for logics that
  259. use different operators or expressions"""
  260. if self.isvariable(tok):
  261. return self.handle_variable(tok, context)
  262. elif tok in Tokens.NOT_LIST:
  263. return self.handle_negation(tok, context)
  264. elif tok in Tokens.LAMBDA_LIST:
  265. return self.handle_lambda(tok, context)
  266. elif tok in Tokens.QUANTS:
  267. return self.handle_quant(tok, context)
  268. elif tok == Tokens.OPEN:
  269. return self.handle_open(tok, context)
  270. def attempt_adjuncts(self, expression, context):
  271. cur_idx = None
  272. while cur_idx != self._currentIndex: # while adjuncts are added
  273. cur_idx = self._currentIndex
  274. expression = self.attempt_EqualityExpression(expression, context)
  275. expression = self.attempt_ApplicationExpression(expression, context)
  276. expression = self.attempt_BooleanExpression(expression, context)
  277. return expression
  278. def handle_negation(self, tok, context):
  279. return self.make_NegatedExpression(self.process_next_expression(Tokens.NOT))
  280. def make_NegatedExpression(self, expression):
  281. return NegatedExpression(expression)
  282. def handle_variable(self, tok, context):
  283. # It's either: 1) a predicate expression: sees(x,y)
  284. # 2) an application expression: P(x)
  285. # 3) a solo variable: john OR x
  286. accum = self.make_VariableExpression(tok)
  287. if self.inRange(0) and self.token(0) == Tokens.OPEN:
  288. # The predicate has arguments
  289. if not isinstance(accum, FunctionVariableExpression) and not isinstance(
  290. accum, ConstantExpression
  291. ):
  292. raise LogicalExpressionException(
  293. self._currentIndex,
  294. "'%s' is an illegal predicate name. "
  295. "Individual variables may not be used as "
  296. "predicates." % tok,
  297. )
  298. self.token() # swallow the Open Paren
  299. # curry the arguments
  300. accum = self.make_ApplicationExpression(
  301. accum, self.process_next_expression(APP)
  302. )
  303. while self.inRange(0) and self.token(0) == Tokens.COMMA:
  304. self.token() # swallow the comma
  305. accum = self.make_ApplicationExpression(
  306. accum, self.process_next_expression(APP)
  307. )
  308. self.assertNextToken(Tokens.CLOSE)
  309. return accum
  310. def get_next_token_variable(self, description):
  311. try:
  312. tok = self.token()
  313. except ExpectedMoreTokensException as e:
  314. raise ExpectedMoreTokensException(e.index, 'Variable expected.')
  315. if isinstance(self.make_VariableExpression(tok), ConstantExpression):
  316. raise LogicalExpressionException(
  317. self._currentIndex,
  318. "'%s' is an illegal variable name. "
  319. "Constants may not be %s." % (tok, description),
  320. )
  321. return Variable(tok)
  322. def handle_lambda(self, tok, context):
  323. # Expression is a lambda expression
  324. if not self.inRange(0):
  325. raise ExpectedMoreTokensException(
  326. self._currentIndex + 2,
  327. message="Variable and Expression expected following lambda operator.",
  328. )
  329. vars = [self.get_next_token_variable('abstracted')]
  330. while True:
  331. if not self.inRange(0) or (
  332. self.token(0) == Tokens.DOT and not self.inRange(1)
  333. ):
  334. raise ExpectedMoreTokensException(
  335. self._currentIndex + 2, message="Expression expected."
  336. )
  337. if not self.isvariable(self.token(0)):
  338. break
  339. # Support expressions like: \x y.M == \x.\y.M
  340. vars.append(self.get_next_token_variable('abstracted'))
  341. if self.inRange(0) and self.token(0) == Tokens.DOT:
  342. self.token() # swallow the dot
  343. accum = self.process_next_expression(tok)
  344. while vars:
  345. accum = self.make_LambdaExpression(vars.pop(), accum)
  346. return accum
  347. def handle_quant(self, tok, context):
  348. # Expression is a quantified expression: some x.M
  349. factory = self.get_QuantifiedExpression_factory(tok)
  350. if not self.inRange(0):
  351. raise ExpectedMoreTokensException(
  352. self._currentIndex + 2,
  353. message="Variable and Expression expected following quantifier '%s'."
  354. % tok,
  355. )
  356. vars = [self.get_next_token_variable('quantified')]
  357. while True:
  358. if not self.inRange(0) or (
  359. self.token(0) == Tokens.DOT and not self.inRange(1)
  360. ):
  361. raise ExpectedMoreTokensException(
  362. self._currentIndex + 2, message="Expression expected."
  363. )
  364. if not self.isvariable(self.token(0)):
  365. break
  366. # Support expressions like: some x y.M == some x.some y.M
  367. vars.append(self.get_next_token_variable('quantified'))
  368. if self.inRange(0) and self.token(0) == Tokens.DOT:
  369. self.token() # swallow the dot
  370. accum = self.process_next_expression(tok)
  371. while vars:
  372. accum = self.make_QuanifiedExpression(factory, vars.pop(), accum)
  373. return accum
  374. def get_QuantifiedExpression_factory(self, tok):
  375. """This method serves as a hook for other logic parsers that
  376. have different quantifiers"""
  377. if tok in Tokens.EXISTS_LIST:
  378. return ExistsExpression
  379. elif tok in Tokens.ALL_LIST:
  380. return AllExpression
  381. else:
  382. self.assertToken(tok, Tokens.QUANTS)
  383. def make_QuanifiedExpression(self, factory, variable, term):
  384. return factory(variable, term)
  385. def handle_open(self, tok, context):
  386. # Expression is in parens
  387. accum = self.process_next_expression(None)
  388. self.assertNextToken(Tokens.CLOSE)
  389. return accum
  390. def attempt_EqualityExpression(self, expression, context):
  391. """Attempt to make an equality expression. If the next token is an
  392. equality operator, then an EqualityExpression will be returned.
  393. Otherwise, the parameter will be returned."""
  394. if self.inRange(0):
  395. tok = self.token(0)
  396. if tok in Tokens.EQ_LIST + Tokens.NEQ_LIST and self.has_priority(
  397. tok, context
  398. ):
  399. self.token() # swallow the "=" or "!="
  400. expression = self.make_EqualityExpression(
  401. expression, self.process_next_expression(tok)
  402. )
  403. if tok in Tokens.NEQ_LIST:
  404. expression = self.make_NegatedExpression(expression)
  405. return expression
  406. def make_EqualityExpression(self, first, second):
  407. """This method serves as a hook for other logic parsers that
  408. have different equality expression classes"""
  409. return EqualityExpression(first, second)
  410. def attempt_BooleanExpression(self, expression, context):
  411. """Attempt to make a boolean expression. If the next token is a boolean
  412. operator, then a BooleanExpression will be returned. Otherwise, the
  413. parameter will be returned."""
  414. while self.inRange(0):
  415. tok = self.token(0)
  416. factory = self.get_BooleanExpression_factory(tok)
  417. if factory and self.has_priority(tok, context):
  418. self.token() # swallow the operator
  419. expression = self.make_BooleanExpression(
  420. factory, expression, self.process_next_expression(tok)
  421. )
  422. else:
  423. break
  424. return expression
  425. def get_BooleanExpression_factory(self, tok):
  426. """This method serves as a hook for other logic parsers that
  427. have different boolean operators"""
  428. if tok in Tokens.AND_LIST:
  429. return AndExpression
  430. elif tok in Tokens.OR_LIST:
  431. return OrExpression
  432. elif tok in Tokens.IMP_LIST:
  433. return ImpExpression
  434. elif tok in Tokens.IFF_LIST:
  435. return IffExpression
  436. else:
  437. return None
  438. def make_BooleanExpression(self, factory, first, second):
  439. return factory(first, second)
  440. def attempt_ApplicationExpression(self, expression, context):
  441. """Attempt to make an application expression. The next tokens are
  442. a list of arguments in parens, then the argument expression is a
  443. function being applied to the arguments. Otherwise, return the
  444. argument expression."""
  445. if self.has_priority(APP, context):
  446. if self.inRange(0) and self.token(0) == Tokens.OPEN:
  447. if (
  448. not isinstance(expression, LambdaExpression)
  449. and not isinstance(expression, ApplicationExpression)
  450. and not isinstance(expression, FunctionVariableExpression)
  451. and not isinstance(expression, ConstantExpression)
  452. ):
  453. raise LogicalExpressionException(
  454. self._currentIndex,
  455. ("The function '%s" % expression)
  456. + "' is not a Lambda Expression, an "
  457. "Application Expression, or a "
  458. "functional predicate, so it may "
  459. "not take arguments.",
  460. )
  461. self.token() # swallow then open paren
  462. # curry the arguments
  463. accum = self.make_ApplicationExpression(
  464. expression, self.process_next_expression(APP)
  465. )
  466. while self.inRange(0) and self.token(0) == Tokens.COMMA:
  467. self.token() # swallow the comma
  468. accum = self.make_ApplicationExpression(
  469. accum, self.process_next_expression(APP)
  470. )
  471. self.assertNextToken(Tokens.CLOSE)
  472. return accum
  473. return expression
  474. def make_ApplicationExpression(self, function, argument):
  475. return ApplicationExpression(function, argument)
  476. def make_VariableExpression(self, name):
  477. return VariableExpression(Variable(name))
  478. def make_LambdaExpression(self, variable, term):
  479. return LambdaExpression(variable, term)
  480. def has_priority(self, operation, context):
  481. return self.operator_precedence[operation] < self.operator_precedence[
  482. context
  483. ] or (
  484. operation in self.right_associated_operations
  485. and self.operator_precedence[operation] == self.operator_precedence[context]
  486. )
  487. def assertNextToken(self, expected):
  488. try:
  489. tok = self.token()
  490. except ExpectedMoreTokensException as e:
  491. raise ExpectedMoreTokensException(
  492. e.index, message="Expected token '%s'." % expected
  493. )
  494. if isinstance(expected, list):
  495. if tok not in expected:
  496. raise UnexpectedTokenException(self._currentIndex, tok, expected)
  497. else:
  498. if tok != expected:
  499. raise UnexpectedTokenException(self._currentIndex, tok, expected)
  500. def assertToken(self, tok, expected):
  501. if isinstance(expected, list):
  502. if tok not in expected:
  503. raise UnexpectedTokenException(self._currentIndex, tok, expected)
  504. else:
  505. if tok != expected:
  506. raise UnexpectedTokenException(self._currentIndex, tok, expected)
  507. def __repr__(self):
  508. if self.inRange(0):
  509. msg = 'Next token: ' + self.token(0)
  510. else:
  511. msg = 'No more tokens'
  512. return '<' + self.__class__.__name__ + ': ' + msg + '>'
  513. def read_logic(s, logic_parser=None, encoding=None):
  514. """
  515. Convert a file of First Order Formulas into a list of {Expression}s.
  516. :param s: the contents of the file
  517. :type s: str
  518. :param logic_parser: The parser to be used to parse the logical expression
  519. :type logic_parser: LogicParser
  520. :param encoding: the encoding of the input string, if it is binary
  521. :type encoding: str
  522. :return: a list of parsed formulas.
  523. :rtype: list(Expression)
  524. """
  525. if encoding is not None:
  526. s = s.decode(encoding)
  527. if logic_parser is None:
  528. logic_parser = LogicParser()
  529. statements = []
  530. for linenum, line in enumerate(s.splitlines()):
  531. line = line.strip()
  532. if line.startswith('#') or line == '':
  533. continue
  534. try:
  535. statements.append(logic_parser.parse(line))
  536. except LogicalExpressionException:
  537. raise ValueError('Unable to parse line %s: %s' % (linenum, line))
  538. return statements
  539. @total_ordering
  540. @python_2_unicode_compatible
  541. class Variable(object):
  542. def __init__(self, name):
  543. """
  544. :param name: the name of the variable
  545. """
  546. assert isinstance(name, string_types), "%s is not a string" % name
  547. self.name = name
  548. def __eq__(self, other):
  549. return isinstance(other, Variable) and self.name == other.name
  550. def __ne__(self, other):
  551. return not self == other
  552. def __lt__(self, other):
  553. if not isinstance(other, Variable):
  554. raise TypeError
  555. return self.name < other.name
  556. def substitute_bindings(self, bindings):
  557. return bindings.get(self, self)
  558. def __hash__(self):
  559. return hash(self.name)
  560. def __str__(self):
  561. return self.name
  562. def __repr__(self):
  563. return "Variable('%s')" % self.name
  564. def unique_variable(pattern=None, ignore=None):
  565. """
  566. Return a new, unique variable.
  567. :param pattern: ``Variable`` that is being replaced. The new variable must
  568. be the same type.
  569. :param term: a set of ``Variable`` objects that should not be returned from
  570. this function.
  571. :rtype: Variable
  572. """
  573. if pattern is not None:
  574. if is_indvar(pattern.name):
  575. prefix = 'z'
  576. elif is_funcvar(pattern.name):
  577. prefix = 'F'
  578. elif is_eventvar(pattern.name):
  579. prefix = 'e0'
  580. else:
  581. assert False, "Cannot generate a unique constant"
  582. else:
  583. prefix = 'z'
  584. v = Variable("%s%s" % (prefix, _counter.get()))
  585. while ignore is not None and v in ignore:
  586. v = Variable("%s%s" % (prefix, _counter.get()))
  587. return v
  588. def skolem_function(univ_scope=None):
  589. """
  590. Return a skolem function over the variables in univ_scope
  591. param univ_scope
  592. """
  593. skolem = VariableExpression(Variable('F%s' % _counter.get()))
  594. if univ_scope:
  595. for v in list(univ_scope):
  596. skolem = skolem(VariableExpression(v))
  597. return skolem
  598. @python_2_unicode_compatible
  599. class Type(object):
  600. def __repr__(self):
  601. return "%s" % self
  602. def __hash__(self):
  603. return hash("%s" % self)
  604. @classmethod
  605. def fromstring(cls, s):
  606. return read_type(s)
  607. @python_2_unicode_compatible
  608. class ComplexType(Type):
  609. def __init__(self, first, second):
  610. assert isinstance(first, Type), "%s is not a Type" % first
  611. assert isinstance(second, Type), "%s is not a Type" % second
  612. self.first = first
  613. self.second = second
  614. def __eq__(self, other):
  615. return (
  616. isinstance(other, ComplexType)
  617. and self.first == other.first
  618. and self.second == other.second
  619. )
  620. def __ne__(self, other):
  621. return not self == other
  622. __hash__ = Type.__hash__
  623. def matches(self, other):
  624. if isinstance(other, ComplexType):
  625. return self.first.matches(other.first) and self.second.matches(other.second)
  626. else:
  627. return self == ANY_TYPE
  628. def resolve(self, other):
  629. if other == ANY_TYPE:
  630. return self
  631. elif isinstance(other, ComplexType):
  632. f = self.first.resolve(other.first)
  633. s = self.second.resolve(other.second)
  634. if f and s:
  635. return ComplexType(f, s)
  636. else:
  637. return None
  638. elif self == ANY_TYPE:
  639. return other
  640. else:
  641. return None
  642. def __str__(self):
  643. if self == ANY_TYPE:
  644. return "%s" % ANY_TYPE
  645. else:
  646. return '<%s,%s>' % (self.first, self.second)
  647. def str(self):
  648. if self == ANY_TYPE:
  649. return ANY_TYPE.str()
  650. else:
  651. return '(%s -> %s)' % (self.first.str(), self.second.str())
  652. class BasicType(Type):
  653. def __eq__(self, other):
  654. return isinstance(other, BasicType) and ("%s" % self) == ("%s" % other)
  655. def __ne__(self, other):
  656. return not self == other
  657. __hash__ = Type.__hash__
  658. def matches(self, other):
  659. return other == ANY_TYPE or self == other
  660. def resolve(self, other):
  661. if self.matches(other):
  662. return self
  663. else:
  664. return None
  665. @python_2_unicode_compatible
  666. class EntityType(BasicType):
  667. def __str__(self):
  668. return 'e'
  669. def str(self):
  670. return 'IND'
  671. @python_2_unicode_compatible
  672. class TruthValueType(BasicType):
  673. def __str__(self):
  674. return 't'
  675. def str(self):
  676. return 'BOOL'
  677. @python_2_unicode_compatible
  678. class EventType(BasicType):
  679. def __str__(self):
  680. return 'v'
  681. def str(self):
  682. return 'EVENT'
  683. @python_2_unicode_compatible
  684. class AnyType(BasicType, ComplexType):
  685. def __init__(self):
  686. pass
  687. @property
  688. def first(self):
  689. return self
  690. @property
  691. def second(self):
  692. return self
  693. def __eq__(self, other):
  694. return isinstance(other, AnyType) or other.__eq__(self)
  695. def __ne__(self, other):
  696. return not self == other
  697. __hash__ = Type.__hash__
  698. def matches(self, other):
  699. return True
  700. def resolve(self, other):
  701. return other
  702. def __str__(self):
  703. return '?'
  704. def str(self):
  705. return 'ANY'
  706. TRUTH_TYPE = TruthValueType()
  707. ENTITY_TYPE = EntityType()
  708. EVENT_TYPE = EventType()
  709. ANY_TYPE = AnyType()
  710. def read_type(type_string):
  711. assert isinstance(type_string, string_types)
  712. type_string = type_string.replace(' ', '') # remove spaces
  713. if type_string[0] == '<':
  714. assert type_string[-1] == '>'
  715. paren_count = 0
  716. for i, char in enumerate(type_string):
  717. if char == '<':
  718. paren_count += 1
  719. elif char == '>':
  720. paren_count -= 1
  721. assert paren_count > 0
  722. elif char == ',':
  723. if paren_count == 1:
  724. break
  725. return ComplexType(
  726. read_type(type_string[1:i]), read_type(type_string[i + 1 : -1])
  727. )
  728. elif type_string[0] == "%s" % ENTITY_TYPE:
  729. return ENTITY_TYPE
  730. elif type_string[0] == "%s" % TRUTH_TYPE:
  731. return TRUTH_TYPE
  732. elif type_string[0] == "%s" % ANY_TYPE:
  733. return ANY_TYPE
  734. else:
  735. raise LogicalExpressionException(None, "Unexpected character: '%s'." % type_string[0])
  736. class TypeException(Exception):
  737. def __init__(self, msg):
  738. super(TypeException, self).__init__(msg)
  739. class InconsistentTypeHierarchyException(TypeException):
  740. def __init__(self, variable, expression=None):
  741. if expression:
  742. msg = (
  743. "The variable '%s' was found in multiple places with different"
  744. " types in '%s'." % (variable, expression)
  745. )
  746. else:
  747. msg = (
  748. "The variable '%s' was found in multiple places with different"
  749. " types." % (variable)
  750. )
  751. super(InconsistentTypeHierarchyException, self).__init__(msg)
  752. class TypeResolutionException(TypeException):
  753. def __init__(self, expression, other_type):
  754. super(TypeResolutionException, self).__init__(
  755. "The type of '%s', '%s', cannot be resolved with type '%s'"
  756. % (expression, expression.type, other_type)
  757. )
  758. class IllegalTypeException(TypeException):
  759. def __init__(self, expression, other_type, allowed_type):
  760. super(IllegalTypeException, self).__init__(
  761. "Cannot set type of %s '%s' to '%s'; must match type '%s'."
  762. % (expression.__class__.__name__, expression, other_type, allowed_type)
  763. )
  764. def typecheck(expressions, signature=None):
  765. """
  766. Ensure correct typing across a collection of ``Expression`` objects.
  767. :param expressions: a collection of expressions
  768. :param signature: dict that maps variable names to types (or string
  769. representations of types)
  770. """
  771. # typecheck and create master signature
  772. for expression in expressions:
  773. signature = expression.typecheck(signature)
  774. # apply master signature to all expressions
  775. for expression in expressions[:-1]:
  776. expression.typecheck(signature)
  777. return signature
  778. class SubstituteBindingsI(object):
  779. """
  780. An interface for classes that can perform substitutions for
  781. variables.
  782. """
  783. def substitute_bindings(self, bindings):
  784. """
  785. :return: The object that is obtained by replacing
  786. each variable bound by ``bindings`` with its values.
  787. Aliases are already resolved. (maybe?)
  788. :rtype: (any)
  789. """
  790. raise NotImplementedError()
  791. def variables(self):
  792. """
  793. :return: A list of all variables in this object.
  794. """
  795. raise NotImplementedError()
  796. @python_2_unicode_compatible
  797. class Expression(SubstituteBindingsI):
  798. """This is the base abstract object for all logical expressions"""
  799. _logic_parser = LogicParser()
  800. _type_checking_logic_parser = LogicParser(type_check=True)
  801. @classmethod
  802. def fromstring(cls, s, type_check=False, signature=None):
  803. if type_check:
  804. return cls._type_checking_logic_parser.parse(s, signature)
  805. else:
  806. return cls._logic_parser.parse(s, signature)
  807. def __call__(self, other, *additional):
  808. accum = self.applyto(other)
  809. for a in additional:
  810. accum = accum(a)
  811. return accum
  812. def applyto(self, other):
  813. assert isinstance(other, Expression), "%s is not an Expression" % other
  814. return ApplicationExpression(self, other)
  815. def __neg__(self):
  816. return NegatedExpression(self)
  817. def negate(self):
  818. """If this is a negated expression, remove the negation.
  819. Otherwise add a negation."""
  820. return -self
  821. def __and__(self, other):
  822. if not isinstance(other, Expression):
  823. raise TypeError("%s is not an Expression" % other)
  824. return AndExpression(self, other)
  825. def __or__(self, other):
  826. if not isinstance(other, Expression):
  827. raise TypeError("%s is not an Expression" % other)
  828. return OrExpression(self, other)
  829. def __gt__(self, other):
  830. if not isinstance(other, Expression):
  831. raise TypeError("%s is not an Expression" % other)
  832. return ImpExpression(self, other)
  833. def __lt__(self, other):
  834. if not isinstance(other, Expression):
  835. raise TypeError("%s is not an Expression" % other)
  836. return IffExpression(self, other)
  837. def __eq__(self, other):
  838. raise NotImplementedError()
  839. def __ne__(self, other):
  840. return not self == other
  841. def equiv(self, other, prover=None):
  842. """
  843. Check for logical equivalence.
  844. Pass the expression (self <-> other) to the theorem prover.
  845. If the prover says it is valid, then the self and other are equal.
  846. :param other: an ``Expression`` to check equality against
  847. :param prover: a ``nltk.inference.api.Prover``
  848. """
  849. assert isinstance(other, Expression), "%s is not an Expression" % other
  850. if prover is None:
  851. from nltk.inference import Prover9
  852. prover = Prover9()
  853. bicond = IffExpression(self.simplify(), other.simplify())
  854. return prover.prove(bicond)
  855. def __hash__(self):
  856. return hash(repr(self))
  857. def substitute_bindings(self, bindings):
  858. expr = self
  859. for var in expr.variables():
  860. if var in bindings:
  861. val = bindings[var]
  862. if isinstance(val, Variable):
  863. val = self.make_VariableExpression(val)
  864. elif not isinstance(val, Expression):
  865. raise ValueError(
  866. 'Can not substitute a non-expression '
  867. 'value into an expression: %r' % (val,)
  868. )
  869. # Substitute bindings in the target value.
  870. val = val.substitute_bindings(bindings)
  871. # Replace var w/ the target value.
  872. expr = expr.replace(var, val)
  873. return expr.simplify()
  874. def typecheck(self, signature=None):
  875. """
  876. Infer and check types. Raise exceptions if necessary.
  877. :param signature: dict that maps variable names to types (or string
  878. representations of types)
  879. :return: the signature, plus any additional type mappings
  880. """
  881. sig = defaultdict(list)
  882. if signature:
  883. for key in signature:
  884. val = signature[key]
  885. varEx = VariableExpression(Variable(key))
  886. if isinstance(val, Type):
  887. varEx.type = val
  888. else:
  889. varEx.type = read_type(val)
  890. sig[key].append(varEx)
  891. self._set_type(signature=sig)
  892. return dict((key, sig[key][0].type) for key in sig)
  893. def findtype(self, variable):
  894. """
  895. Find the type of the given variable as it is used in this expression.
  896. For example, finding the type of "P" in "P(x) & Q(x,y)" yields "<e,t>"
  897. :param variable: Variable
  898. """
  899. raise NotImplementedError()
  900. def _set_type(self, other_type=ANY_TYPE, signature=None):
  901. """
  902. Set the type of this expression to be the given type. Raise type
  903. exceptions where applicable.
  904. :param other_type: Type
  905. :param signature: dict(str -> list(AbstractVariableExpression))
  906. """
  907. raise NotImplementedError()
  908. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  909. """
  910. Replace every instance of 'variable' with 'expression'
  911. :param variable: ``Variable`` The variable to replace
  912. :param expression: ``Expression`` The expression with which to replace it
  913. :param replace_bound: bool Should bound variables be replaced?
  914. :param alpha_convert: bool Alpha convert automatically to avoid name clashes?
  915. """
  916. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  917. assert isinstance(expression, Expression), (
  918. "%s is not an Expression" % expression
  919. )
  920. return self.visit_structured(
  921. lambda e: e.replace(variable, expression, replace_bound, alpha_convert),
  922. self.__class__,
  923. )
  924. def normalize(self, newvars=None):
  925. """Rename auto-generated unique variables"""
  926. def get_indiv_vars(e):
  927. if isinstance(e, IndividualVariableExpression):
  928. return set([e])
  929. elif isinstance(e, AbstractVariableExpression):
  930. return set()
  931. else:
  932. return e.visit(
  933. get_indiv_vars, lambda parts: reduce(operator.or_, parts, set())
  934. )
  935. result = self
  936. for i, e in enumerate(sorted(get_indiv_vars(self), key=lambda e: e.variable)):
  937. if isinstance(e, EventVariableExpression):
  938. newVar = e.__class__(Variable('e0%s' % (i + 1)))
  939. elif isinstance(e, IndividualVariableExpression):
  940. newVar = e.__class__(Variable('z%s' % (i + 1)))
  941. else:
  942. newVar = e
  943. result = result.replace(e.variable, newVar, True)
  944. return result
  945. def visit(self, function, combinator):
  946. """
  947. Recursively visit subexpressions. Apply 'function' to each
  948. subexpression and pass the result of each function application
  949. to the 'combinator' for aggregation:
  950. return combinator(map(function, self.subexpressions))
  951. Bound variables are neither applied upon by the function nor given to
  952. the combinator.
  953. :param function: ``Function<Expression,T>`` to call on each subexpression
  954. :param combinator: ``Function<list<T>,R>`` to combine the results of the
  955. function calls
  956. :return: result of combination ``R``
  957. """
  958. raise NotImplementedError()
  959. def visit_structured(self, function, combinator):
  960. """
  961. Recursively visit subexpressions. Apply 'function' to each
  962. subexpression and pass the result of each function application
  963. to the 'combinator' for aggregation. The combinator must have
  964. the same signature as the constructor. The function is not
  965. applied to bound variables, but they are passed to the
  966. combinator.
  967. :param function: ``Function`` to call on each subexpression
  968. :param combinator: ``Function`` with the same signature as the
  969. constructor, to combine the results of the function calls
  970. :return: result of combination
  971. """
  972. return self.visit(function, lambda parts: combinator(*parts))
  973. def __repr__(self):
  974. return '<%s %s>' % (self.__class__.__name__, self)
  975. def __str__(self):
  976. return self.str()
  977. def variables(self):
  978. """
  979. Return a set of all the variables for binding substitution.
  980. The variables returned include all free (non-bound) individual
  981. variables and any variable starting with '?' or '@'.
  982. :return: set of ``Variable`` objects
  983. """
  984. return self.free() | set(
  985. p for p in self.predicates() | self.constants() if re.match('^[?@]', p.name)
  986. )
  987. def free(self):
  988. """
  989. Return a set of all the free (non-bound) variables. This includes
  990. both individual and predicate variables, but not constants.
  991. :return: set of ``Variable`` objects
  992. """
  993. return self.visit(
  994. lambda e: e.free(), lambda parts: reduce(operator.or_, parts, set())
  995. )
  996. def constants(self):
  997. """
  998. Return a set of individual constants (non-predicates).
  999. :return: set of ``Variable`` objects
  1000. """
  1001. return self.visit(
  1002. lambda e: e.constants(), lambda parts: reduce(operator.or_, parts, set())
  1003. )
  1004. def predicates(self):
  1005. """
  1006. Return a set of predicates (constants, not variables).
  1007. :return: set of ``Variable`` objects
  1008. """
  1009. return self.visit(
  1010. lambda e: e.predicates(), lambda parts: reduce(operator.or_, parts, set())
  1011. )
  1012. def simplify(self):
  1013. """
  1014. :return: beta-converted version of this expression
  1015. """
  1016. return self.visit_structured(lambda e: e.simplify(), self.__class__)
  1017. def make_VariableExpression(self, variable):
  1018. return VariableExpression(variable)
  1019. @python_2_unicode_compatible
  1020. class ApplicationExpression(Expression):
  1021. r"""
  1022. This class is used to represent two related types of logical expressions.
  1023. The first is a Predicate Expression, such as "P(x,y)". A predicate
  1024. expression is comprised of a ``FunctionVariableExpression`` or
  1025. ``ConstantExpression`` as the predicate and a list of Expressions as the
  1026. arguments.
  1027. The second is a an application of one expression to another, such as
  1028. "(\x.dog(x))(fido)".
  1029. The reason Predicate Expressions are treated as Application Expressions is
  1030. that the Variable Expression predicate of the expression may be replaced
  1031. with another Expression, such as a LambdaExpression, which would mean that
  1032. the Predicate should be thought of as being applied to the arguments.
  1033. The logical expression reader will always curry arguments in a application expression.
  1034. So, "\x y.see(x,y)(john,mary)" will be represented internally as
  1035. "((\x y.(see(x))(y))(john))(mary)". This simplifies the internals since
  1036. there will always be exactly one argument in an application.
  1037. The str() method will usually print the curried forms of application
  1038. expressions. The one exception is when the the application expression is
  1039. really a predicate expression (ie, underlying function is an
  1040. ``AbstractVariableExpression``). This means that the example from above
  1041. will be returned as "(\x y.see(x,y)(john))(mary)".
  1042. """
  1043. def __init__(self, function, argument):
  1044. """
  1045. :param function: ``Expression``, for the function expression
  1046. :param argument: ``Expression``, for the argument
  1047. """
  1048. assert isinstance(function, Expression), "%s is not an Expression" % function
  1049. assert isinstance(argument, Expression), "%s is not an Expression" % argument
  1050. self.function = function
  1051. self.argument = argument
  1052. def simplify(self):
  1053. function = self.function.simplify()
  1054. argument = self.argument.simplify()
  1055. if isinstance(function, LambdaExpression):
  1056. return function.term.replace(function.variable, argument).simplify()
  1057. else:
  1058. return self.__class__(function, argument)
  1059. @property
  1060. def type(self):
  1061. if isinstance(self.function.type, ComplexType):
  1062. return self.function.type.second
  1063. else:
  1064. return ANY_TYPE
  1065. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1066. """:see Expression._set_type()"""
  1067. assert isinstance(other_type, Type)
  1068. if signature is None:
  1069. signature = defaultdict(list)
  1070. self.argument._set_type(ANY_TYPE, signature)
  1071. try:
  1072. self.function._set_type(
  1073. ComplexType(self.argument.type, other_type), signature
  1074. )
  1075. except TypeResolutionException:
  1076. raise TypeException(
  1077. "The function '%s' is of type '%s' and cannot be applied "
  1078. "to '%s' of type '%s'. Its argument must match type '%s'."
  1079. % (
  1080. self.function,
  1081. self.function.type,
  1082. self.argument,
  1083. self.argument.type,
  1084. self.function.type.first,
  1085. )
  1086. )
  1087. def findtype(self, variable):
  1088. """:see Expression.findtype()"""
  1089. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1090. if self.is_atom():
  1091. function, args = self.uncurry()
  1092. else:
  1093. # It's not a predicate expression ("P(x,y)"), so leave args curried
  1094. function = self.function
  1095. args = [self.argument]
  1096. found = [arg.findtype(variable) for arg in [function] + args]
  1097. unique = []
  1098. for f in found:
  1099. if f != ANY_TYPE:
  1100. if unique:
  1101. for u in unique:
  1102. if f.matches(u):
  1103. break
  1104. else:
  1105. unique.append(f)
  1106. if len(unique) == 1:
  1107. return list(unique)[0]
  1108. else:
  1109. return ANY_TYPE
  1110. def constants(self):
  1111. """:see: Expression.constants()"""
  1112. if isinstance(self.function, AbstractVariableExpression):
  1113. function_constants = set()
  1114. else:
  1115. function_constants = self.function.constants()
  1116. return function_constants | self.argument.constants()
  1117. def predicates(self):
  1118. """:see: Expression.predicates()"""
  1119. if isinstance(self.function, ConstantExpression):
  1120. function_preds = set([self.function.variable])
  1121. else:
  1122. function_preds = self.function.predicates()
  1123. return function_preds | self.argument.predicates()
  1124. def visit(self, function, combinator):
  1125. """:see: Expression.visit()"""
  1126. return combinator([function(self.function), function(self.argument)])
  1127. def __eq__(self, other):
  1128. return (
  1129. isinstance(other, ApplicationExpression)
  1130. and self.function == other.function
  1131. and self.argument == other.argument
  1132. )
  1133. def __ne__(self, other):
  1134. return not self == other
  1135. __hash__ = Expression.__hash__
  1136. def __str__(self):
  1137. # uncurry the arguments and find the base function
  1138. if self.is_atom():
  1139. function, args = self.uncurry()
  1140. arg_str = ','.join("%s" % arg for arg in args)
  1141. else:
  1142. # Leave arguments curried
  1143. function = self.function
  1144. arg_str = "%s" % self.argument
  1145. function_str = "%s" % function
  1146. parenthesize_function = False
  1147. if isinstance(function, LambdaExpression):
  1148. if isinstance(function.term, ApplicationExpression):
  1149. if not isinstance(function.term.function, AbstractVariableExpression):
  1150. parenthesize_function = True
  1151. elif not isinstance(function.term, BooleanExpression):
  1152. parenthesize_function = True
  1153. elif isinstance(function, ApplicationExpression):
  1154. parenthesize_function = True
  1155. if parenthesize_function:
  1156. function_str = Tokens.OPEN + function_str + Tokens.CLOSE
  1157. return function_str + Tokens.OPEN + arg_str + Tokens.CLOSE
  1158. def uncurry(self):
  1159. """
  1160. Uncurry this application expression
  1161. return: A tuple (base-function, arg-list)
  1162. """
  1163. function = self.function
  1164. args = [self.argument]
  1165. while isinstance(function, ApplicationExpression):
  1166. # (\x.\y.sees(x,y)(john))(mary)
  1167. args.insert(0, function.argument)
  1168. function = function.function
  1169. return (function, args)
  1170. @property
  1171. def pred(self):
  1172. """
  1173. Return uncurried base-function.
  1174. If this is an atom, then the result will be a variable expression.
  1175. Otherwise, it will be a lambda expression.
  1176. """
  1177. return self.uncurry()[0]
  1178. @property
  1179. def args(self):
  1180. """
  1181. Return uncurried arg-list
  1182. """
  1183. return self.uncurry()[1]
  1184. def is_atom(self):
  1185. """
  1186. Is this expression an atom (as opposed to a lambda expression applied
  1187. to a term)?
  1188. """
  1189. return isinstance(self.pred, AbstractVariableExpression)
  1190. @total_ordering
  1191. @python_2_unicode_compatible
  1192. class AbstractVariableExpression(Expression):
  1193. """This class represents a variable to be used as a predicate or entity"""
  1194. def __init__(self, variable):
  1195. """
  1196. :param variable: ``Variable``, for the variable
  1197. """
  1198. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1199. self.variable = variable
  1200. def simplify(self):
  1201. return self
  1202. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  1203. """:see: Expression.replace()"""
  1204. assert isinstance(variable, Variable), "%s is not an Variable" % variable
  1205. assert isinstance(expression, Expression), (
  1206. "%s is not an Expression" % expression
  1207. )
  1208. if self.variable == variable:
  1209. return expression
  1210. else:
  1211. return self
  1212. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1213. """:see Expression._set_type()"""
  1214. assert isinstance(other_type, Type)
  1215. if signature is None:
  1216. signature = defaultdict(list)
  1217. resolution = other_type
  1218. for varEx in signature[self.variable.name]:
  1219. resolution = varEx.type.resolve(resolution)
  1220. if not resolution:
  1221. raise InconsistentTypeHierarchyException(self)
  1222. signature[self.variable.name].append(self)
  1223. for varEx in signature[self.variable.name]:
  1224. varEx.type = resolution
  1225. def findtype(self, variable):
  1226. """:see Expression.findtype()"""
  1227. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1228. if self.variable == variable:
  1229. return self.type
  1230. else:
  1231. return ANY_TYPE
  1232. def predicates(self):
  1233. """:see: Expression.predicates()"""
  1234. return set()
  1235. def __eq__(self, other):
  1236. """Allow equality between instances of ``AbstractVariableExpression``
  1237. subtypes."""
  1238. return (
  1239. isinstance(other, AbstractVariableExpression)
  1240. and self.variable == other.variable
  1241. )
  1242. def __ne__(self, other):
  1243. return not self == other
  1244. def __lt__(self, other):
  1245. if not isinstance(other, AbstractVariableExpression):
  1246. raise TypeError
  1247. return self.variable < other.variable
  1248. __hash__ = Expression.__hash__
  1249. def __str__(self):
  1250. return "%s" % self.variable
  1251. class IndividualVariableExpression(AbstractVariableExpression):
  1252. """This class represents variables that take the form of a single lowercase
  1253. character (other than 'e') followed by zero or more digits."""
  1254. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1255. """:see Expression._set_type()"""
  1256. assert isinstance(other_type, Type)
  1257. if signature is None:
  1258. signature = defaultdict(list)
  1259. if not other_type.matches(ENTITY_TYPE):
  1260. raise IllegalTypeException(self, other_type, ENTITY_TYPE)
  1261. signature[self.variable.name].append(self)
  1262. def _get_type(self):
  1263. return ENTITY_TYPE
  1264. type = property(_get_type, _set_type)
  1265. def free(self):
  1266. """:see: Expression.free()"""
  1267. return set([self.variable])
  1268. def constants(self):
  1269. """:see: Expression.constants()"""
  1270. return set()
  1271. class FunctionVariableExpression(AbstractVariableExpression):
  1272. """This class represents variables that take the form of a single uppercase
  1273. character followed by zero or more digits."""
  1274. type = ANY_TYPE
  1275. def free(self):
  1276. """:see: Expression.free()"""
  1277. return set([self.variable])
  1278. def constants(self):
  1279. """:see: Expression.constants()"""
  1280. return set()
  1281. class EventVariableExpression(IndividualVariableExpression):
  1282. """This class represents variables that take the form of a single lowercase
  1283. 'e' character followed by zero or more digits."""
  1284. type = EVENT_TYPE
  1285. class ConstantExpression(AbstractVariableExpression):
  1286. """This class represents variables that do not take the form of a single
  1287. character followed by zero or more digits."""
  1288. type = ENTITY_TYPE
  1289. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1290. """:see Expression._set_type()"""
  1291. assert isinstance(other_type, Type)
  1292. if signature is None:
  1293. signature = defaultdict(list)
  1294. if other_type == ANY_TYPE:
  1295. # entity type by default, for individuals
  1296. resolution = ENTITY_TYPE
  1297. else:
  1298. resolution = other_type
  1299. if self.type != ENTITY_TYPE:
  1300. resolution = resolution.resolve(self.type)
  1301. for varEx in signature[self.variable.name]:
  1302. resolution = varEx.type.resolve(resolution)
  1303. if not resolution:
  1304. raise InconsistentTypeHierarchyException(self)
  1305. signature[self.variable.name].append(self)
  1306. for varEx in signature[self.variable.name]:
  1307. varEx.type = resolution
  1308. def free(self):
  1309. """:see: Expression.free()"""
  1310. return set()
  1311. def constants(self):
  1312. """:see: Expression.constants()"""
  1313. return set([self.variable])
  1314. def VariableExpression(variable):
  1315. """
  1316. This is a factory method that instantiates and returns a subtype of
  1317. ``AbstractVariableExpression`` appropriate for the given variable.
  1318. """
  1319. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1320. if is_indvar(variable.name):
  1321. return IndividualVariableExpression(variable)
  1322. elif is_funcvar(variable.name):
  1323. return FunctionVariableExpression(variable)
  1324. elif is_eventvar(variable.name):
  1325. return EventVariableExpression(variable)
  1326. else:
  1327. return ConstantExpression(variable)
  1328. class VariableBinderExpression(Expression):
  1329. """This an abstract class for any Expression that binds a variable in an
  1330. Expression. This includes LambdaExpressions and Quantified Expressions"""
  1331. def __init__(self, variable, term):
  1332. """
  1333. :param variable: ``Variable``, for the variable
  1334. :param term: ``Expression``, for the term
  1335. """
  1336. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1337. assert isinstance(term, Expression), "%s is not an Expression" % term
  1338. self.variable = variable
  1339. self.term = term
  1340. def replace(self, variable, expression, replace_bound=False, alpha_convert=True):
  1341. """:see: Expression.replace()"""
  1342. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1343. assert isinstance(expression, Expression), (
  1344. "%s is not an Expression" % expression
  1345. )
  1346. # if the bound variable is the thing being replaced
  1347. if self.variable == variable:
  1348. if replace_bound:
  1349. assert isinstance(expression, AbstractVariableExpression), (
  1350. "%s is not a AbstractVariableExpression" % expression
  1351. )
  1352. return self.__class__(
  1353. expression.variable,
  1354. self.term.replace(variable, expression, True, alpha_convert),
  1355. )
  1356. else:
  1357. return self
  1358. else:
  1359. # if the bound variable appears in the expression, then it must
  1360. # be alpha converted to avoid a conflict
  1361. if alpha_convert and self.variable in expression.free():
  1362. self = self.alpha_convert(unique_variable(pattern=self.variable))
  1363. # replace in the term
  1364. return self.__class__(
  1365. self.variable,
  1366. self.term.replace(variable, expression, replace_bound, alpha_convert),
  1367. )
  1368. def alpha_convert(self, newvar):
  1369. """Rename all occurrences of the variable introduced by this variable
  1370. binder in the expression to ``newvar``.
  1371. :param newvar: ``Variable``, for the new variable
  1372. """
  1373. assert isinstance(newvar, Variable), "%s is not a Variable" % newvar
  1374. return self.__class__(
  1375. newvar, self.term.replace(self.variable, VariableExpression(newvar), True)
  1376. )
  1377. def free(self):
  1378. """:see: Expression.free()"""
  1379. return self.term.free() - set([self.variable])
  1380. def findtype(self, variable):
  1381. """:see Expression.findtype()"""
  1382. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1383. if variable == self.variable:
  1384. return ANY_TYPE
  1385. else:
  1386. return self.term.findtype(variable)
  1387. def visit(self, function, combinator):
  1388. """:see: Expression.visit()"""
  1389. return combinator([function(self.term)])
  1390. def visit_structured(self, function, combinator):
  1391. """:see: Expression.visit_structured()"""
  1392. return combinator(self.variable, function(self.term))
  1393. def __eq__(self, other):
  1394. r"""Defines equality modulo alphabetic variance. If we are comparing
  1395. \x.M and \y.N, then check equality of M and N[x/y]."""
  1396. if isinstance(self, other.__class__) or isinstance(other, self.__class__):
  1397. if self.variable == other.variable:
  1398. return self.term == other.term
  1399. else:
  1400. # Comparing \x.M and \y.N. Relabel y in N with x and continue.
  1401. varex = VariableExpression(self.variable)
  1402. return self.term == other.term.replace(other.variable, varex)
  1403. else:
  1404. return False
  1405. def __ne__(self, other):
  1406. return not self == other
  1407. __hash__ = Expression.__hash__
  1408. @python_2_unicode_compatible
  1409. class LambdaExpression(VariableBinderExpression):
  1410. @property
  1411. def type(self):
  1412. return ComplexType(self.term.findtype(self.variable), self.term.type)
  1413. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1414. """:see Expression._set_type()"""
  1415. assert isinstance(other_type, Type)
  1416. if signature is None:
  1417. signature = defaultdict(list)
  1418. self.term._set_type(other_type.second, signature)
  1419. if not self.type.resolve(other_type):
  1420. raise TypeResolutionException(self, other_type)
  1421. def __str__(self):
  1422. variables = [self.variable]
  1423. term = self.term
  1424. while term.__class__ == self.__class__:
  1425. variables.append(term.variable)
  1426. term = term.term
  1427. return (
  1428. Tokens.LAMBDA
  1429. + ' '.join("%s" % v for v in variables)
  1430. + Tokens.DOT
  1431. + "%s" % term
  1432. )
  1433. @python_2_unicode_compatible
  1434. class QuantifiedExpression(VariableBinderExpression):
  1435. @property
  1436. def type(self):
  1437. return TRUTH_TYPE
  1438. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1439. """:see Expression._set_type()"""
  1440. assert isinstance(other_type, Type)
  1441. if signature is None:
  1442. signature = defaultdict(list)
  1443. if not other_type.matches(TRUTH_TYPE):
  1444. raise IllegalTypeException(self, other_type, TRUTH_TYPE)
  1445. self.term._set_type(TRUTH_TYPE, signature)
  1446. def __str__(self):
  1447. variables = [self.variable]
  1448. term = self.term
  1449. while term.__class__ == self.__class__:
  1450. variables.append(term.variable)
  1451. term = term.term
  1452. return (
  1453. self.getQuantifier()
  1454. + ' '
  1455. + ' '.join("%s" % v for v in variables)
  1456. + Tokens.DOT
  1457. + "%s" % term
  1458. )
  1459. class ExistsExpression(QuantifiedExpression):
  1460. def getQuantifier(self):
  1461. return Tokens.EXISTS
  1462. class AllExpression(QuantifiedExpression):
  1463. def getQuantifier(self):
  1464. return Tokens.ALL
  1465. @python_2_unicode_compatible
  1466. class NegatedExpression(Expression):
  1467. def __init__(self, term):
  1468. assert isinstance(term, Expression), "%s is not an Expression" % term
  1469. self.term = term
  1470. @property
  1471. def type(self):
  1472. return TRUTH_TYPE
  1473. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1474. """:see Expression._set_type()"""
  1475. assert isinstance(other_type, Type)
  1476. if signature is None:
  1477. signature = defaultdict(list)
  1478. if not other_type.matches(TRUTH_TYPE):
  1479. raise IllegalTypeException(self, other_type, TRUTH_TYPE)
  1480. self.term._set_type(TRUTH_TYPE, signature)
  1481. def findtype(self, variable):
  1482. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1483. return self.term.findtype(variable)
  1484. def visit(self, function, combinator):
  1485. """:see: Expression.visit()"""
  1486. return combinator([function(self.term)])
  1487. def negate(self):
  1488. """:see: Expression.negate()"""
  1489. return self.term
  1490. def __eq__(self, other):
  1491. return isinstance(other, NegatedExpression) and self.term == other.term
  1492. def __ne__(self, other):
  1493. return not self == other
  1494. __hash__ = Expression.__hash__
  1495. def __str__(self):
  1496. return Tokens.NOT + "%s" % self.term
  1497. @python_2_unicode_compatible
  1498. class BinaryExpression(Expression):
  1499. def __init__(self, first, second):
  1500. assert isinstance(first, Expression), "%s is not an Expression" % first
  1501. assert isinstance(second, Expression), "%s is not an Expression" % second
  1502. self.first = first
  1503. self.second = second
  1504. @property
  1505. def type(self):
  1506. return TRUTH_TYPE
  1507. def findtype(self, variable):
  1508. """:see Expression.findtype()"""
  1509. assert isinstance(variable, Variable), "%s is not a Variable" % variable
  1510. f = self.first.findtype(variable)
  1511. s = self.second.findtype(variable)
  1512. if f == s or s == ANY_TYPE:
  1513. return f
  1514. elif f == ANY_TYPE:
  1515. return s
  1516. else:
  1517. return ANY_TYPE
  1518. def visit(self, function, combinator):
  1519. """:see: Expression.visit()"""
  1520. return combinator([function(self.first), function(self.second)])
  1521. def __eq__(self, other):
  1522. return (
  1523. (isinstance(self, other.__class__) or isinstance(other, self.__class__))
  1524. and self.first == other.first
  1525. and self.second == other.second
  1526. )
  1527. def __ne__(self, other):
  1528. return not self == other
  1529. __hash__ = Expression.__hash__
  1530. def __str__(self):
  1531. first = self._str_subex(self.first)
  1532. second = self._str_subex(self.second)
  1533. return Tokens.OPEN + first + ' ' + self.getOp() + ' ' + second + Tokens.CLOSE
  1534. def _str_subex(self, subex):
  1535. return "%s" % subex
  1536. class BooleanExpression(BinaryExpression):
  1537. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1538. """:see Expression._set_type()"""
  1539. assert isinstance(other_type, Type)
  1540. if signature is None:
  1541. signature = defaultdict(list)
  1542. if not other_type.matches(TRUTH_TYPE):
  1543. raise IllegalTypeException(self, other_type, TRUTH_TYPE)
  1544. self.first._set_type(TRUTH_TYPE, signature)
  1545. self.second._set_type(TRUTH_TYPE, signature)
  1546. class AndExpression(BooleanExpression):
  1547. """This class represents conjunctions"""
  1548. def getOp(self):
  1549. return Tokens.AND
  1550. def _str_subex(self, subex):
  1551. s = "%s" % subex
  1552. if isinstance(subex, AndExpression):
  1553. return s[1:-1]
  1554. return s
  1555. class OrExpression(BooleanExpression):
  1556. """This class represents disjunctions"""
  1557. def getOp(self):
  1558. return Tokens.OR
  1559. def _str_subex(self, subex):
  1560. s = "%s" % subex
  1561. if isinstance(subex, OrExpression):
  1562. return s[1:-1]
  1563. return s
  1564. class ImpExpression(BooleanExpression):
  1565. """This class represents implications"""
  1566. def getOp(self):
  1567. return Tokens.IMP
  1568. class IffExpression(BooleanExpression):
  1569. """This class represents biconditionals"""
  1570. def getOp(self):
  1571. return Tokens.IFF
  1572. class EqualityExpression(BinaryExpression):
  1573. """This class represents equality expressions like "(x = y)"."""
  1574. def _set_type(self, other_type=ANY_TYPE, signature=None):
  1575. """:see Expression._set_type()"""
  1576. assert isinstance(other_type, Type)
  1577. if signature is None:
  1578. signature = defaultdict(list)
  1579. if not other_type.matches(TRUTH_TYPE):
  1580. raise IllegalTypeException(self, other_type, TRUTH_TYPE)
  1581. self.first._set_type(ENTITY_TYPE, signature)
  1582. self.second._set_type(ENTITY_TYPE, signature)
  1583. def getOp(self):
  1584. return Tokens.EQ
  1585. ### Utilities
  1586. class LogicalExpressionException(Exception):
  1587. def __init__(self, index, message):
  1588. self.index = index
  1589. Exception.__init__(self, message)
  1590. class UnexpectedTokenException(LogicalExpressionException):
  1591. def __init__(self, index, unexpected=None, expected=None, message=None):
  1592. if unexpected and expected:
  1593. msg = "Unexpected token: '%s'. " "Expected token '%s'." % (
  1594. unexpected,
  1595. expected,
  1596. )
  1597. elif unexpected:
  1598. msg = "Unexpected token: '%s'." % unexpected
  1599. if message:
  1600. msg += ' ' + message
  1601. else:
  1602. msg = "Expected token '%s'." % expected
  1603. LogicalExpressionException.__init__(self, index, msg)
  1604. class ExpectedMoreTokensException(LogicalExpressionException):
  1605. def __init__(self, index, message=None):
  1606. if not message:
  1607. message = 'More tokens expected.'
  1608. LogicalExpressionException.__init__(
  1609. self, index, 'End of input found. ' + message
  1610. )
  1611. def is_indvar(expr):
  1612. """
  1613. An individual variable must be a single lowercase character other than 'e',
  1614. followed by zero or more digits.
  1615. :param expr: str
  1616. :return: bool True if expr is of the correct form
  1617. """
  1618. assert isinstance(expr, string_types), "%s is not a string" % expr
  1619. return re.match(r'^[a-df-z]\d*$', expr) is not None
  1620. def is_funcvar(expr):
  1621. """
  1622. A function variable must be a single uppercase character followed by
  1623. zero or more digits.
  1624. :param expr: str
  1625. :return: bool True if expr is of the correct form
  1626. """
  1627. assert isinstance(expr, string_types), "%s is not a string" % expr
  1628. return re.match(r'^[A-Z]\d*$', expr) is not None
  1629. def is_eventvar(expr):
  1630. """
  1631. An event variable must be a single lowercase 'e' character followed by
  1632. zero or more digits.
  1633. :param expr: str
  1634. :return: bool True if expr is of the correct form
  1635. """
  1636. assert isinstance(expr, string_types), "%s is not a string" % expr
  1637. return re.match(r'^e\d*$', expr) is not None
  1638. def demo():
  1639. lexpr = Expression.fromstring
  1640. print('=' * 20 + 'Test reader' + '=' * 20)
  1641. print(lexpr(r'john'))
  1642. print(lexpr(r'man(x)'))
  1643. print(lexpr(r'-man(x)'))
  1644. print(lexpr(r'(man(x) & tall(x) & walks(x))'))
  1645. print(lexpr(r'exists x.(man(x) & tall(x) & walks(x))'))
  1646. print(lexpr(r'\x.man(x)'))
  1647. print(lexpr(r'\x.man(x)(john)'))
  1648. print(lexpr(r'\x y.sees(x,y)'))
  1649. print(lexpr(r'\x y.sees(x,y)(a,b)'))
  1650. print(lexpr(r'(\x.exists y.walks(x,y))(x)'))
  1651. print(lexpr(r'exists x.x = y'))
  1652. print(lexpr(r'exists x.(x = y)'))
  1653. print(lexpr('P(x) & x=y & P(y)'))
  1654. print(lexpr(r'\P Q.exists x.(P(x) & Q(x))'))
  1655. print(lexpr(r'man(x) <-> tall(x)'))
  1656. print('=' * 20 + 'Test simplify' + '=' * 20)
  1657. print(lexpr(r'\x.\y.sees(x,y)(john)(mary)').simplify())
  1658. print(lexpr(r'\x.\y.sees(x,y)(john, mary)').simplify())
  1659. print(lexpr(r'all x.(man(x) & (\x.exists y.walks(x,y))(x))').simplify())
  1660. print(lexpr(r'(\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x))(\x.bark(x))').simplify())
  1661. print('=' * 20 + 'Test alpha conversion and binder expression equality' + '=' * 20)
  1662. e1 = lexpr('exists x.P(x)')
  1663. print(e1)
  1664. e2 = e1.alpha_convert(Variable('z'))
  1665. print(e2)
  1666. print(e1 == e2)
  1667. def demo_errors():
  1668. print('=' * 20 + 'Test reader errors' + '=' * 20)
  1669. demoException('(P(x) & Q(x)')
  1670. demoException('((P(x) &) & Q(x))')
  1671. demoException('P(x) -> ')
  1672. demoException('P(x')
  1673. demoException('P(x,')
  1674. demoException('P(x,)')
  1675. demoException('exists')
  1676. demoException('exists x.')
  1677. demoException('\\')
  1678. demoException('\\ x y.')
  1679. demoException('P(x)Q(x)')
  1680. demoException('(P(x)Q(x)')
  1681. demoException('exists x -> y')
  1682. def demoException(s):
  1683. try:
  1684. Expression.fromstring(s)
  1685. except LogicalExpressionException as e:
  1686. print("%s: %s" % (e.__class__.__name__, e))
  1687. def printtype(ex):
  1688. print("%s : %s" % (ex.str(), ex.type))
  1689. if __name__ == '__main__':
  1690. demo()
  1691. # demo_errors()