This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

566 line
20 KiB

  1. from __future__ import absolute_import
  2. import unittest
  3. import logging
  4. import os
  5. import sys
  6. try:
  7. from cStringIO import StringIO as cStringIO
  8. except ImportError:
  9. # Available only in Python 2.x, 3.x only has io.StringIO from below
  10. cStringIO = None
  11. from io import (
  12. StringIO as uStringIO,
  13. open,
  14. )
  15. logging.basicConfig(level=logging.INFO)
  16. from lark.lark import Lark
  17. from lark.common import GrammarError, ParseError
  18. from lark.lexer import LexError
  19. __path__ = os.path.dirname(__file__)
  20. def _read(n, *args):
  21. with open(os.path.join(__path__, n), *args) as f:
  22. return f.read()
  23. class TestParsers(unittest.TestCase):
  24. def test_same_ast(self):
  25. "Tests that Earley and LALR parsers produce equal trees"
  26. g = Lark("""start: "(" name_list ("," "*" NAME)? ")"
  27. name_list: NAME | name_list "," NAME
  28. NAME: /\w+/ """, parser='lalr')
  29. l = g.parse('(a,b,c,*x)')
  30. g = Lark("""start: "(" name_list ("," "*" NAME)? ")"
  31. name_list: NAME | name_list "," NAME
  32. NAME: /\w/+ """)
  33. l2 = g.parse('(a,b,c,*x)')
  34. assert l == l2, '%s != %s' % (l.pretty(), l2.pretty())
  35. def test_infinite_recurse(self):
  36. g = """start: a
  37. a: a | "a"
  38. """
  39. self.assertRaises(GrammarError, Lark, g, parser='lalr')
  40. l = Lark(g, parser='earley')
  41. self.assertRaises(ParseError, l.parse, 'a')
  42. class TestEarley(unittest.TestCase):
  43. def test_anon_in_scanless(self):
  44. # Fails an Earley implementation without special handling for empty rules,
  45. # or re-processing of already completed rules.
  46. g = Lark(r"""start: B
  47. B: ("ab"|/[^b]/)*
  48. """, lexer=None)
  49. self.assertEqual( g.parse('abc').children[0], 'abc')
  50. def test_earley_scanless(self):
  51. g = Lark("""start: A "b" c
  52. A: "a"+
  53. c: "abc"
  54. """, parser="earley", lexer=None)
  55. x = g.parse('aaaababc')
  56. def test_earley_scanless2(self):
  57. grammar = """
  58. start: statement+
  59. statement: "r"
  60. | "c" /[a-z]/+
  61. %ignore " "
  62. """
  63. program = """c b r"""
  64. l = Lark(grammar, parser='earley', lexer=None)
  65. l.parse(program)
  66. def test_earley_scanless3(self):
  67. "Tests prioritization and disambiguation for pseudo-terminals (there should be only one result)"
  68. grammar = """
  69. start: A A
  70. A: "a"+
  71. """
  72. l = Lark(grammar, parser='earley', lexer=None)
  73. res = l.parse("aaa")
  74. self.assertEqual(res.children, ['aa', 'a'])
  75. def test_earley_scanless4(self):
  76. grammar = """
  77. start: A A?
  78. A: "a"+
  79. """
  80. l = Lark(grammar, parser='earley', lexer=None)
  81. res = l.parse("aaa")
  82. self.assertEqual(res.children, ['aaa'])
  83. def _make_parser_test(LEXER, PARSER):
  84. def _Lark(grammar, **kwargs):
  85. return Lark(grammar, lexer=LEXER, parser=PARSER, **kwargs)
  86. class _TestParser(unittest.TestCase):
  87. def test_basic1(self):
  88. g = _Lark("""start: a+ b a* "b" a*
  89. b: "b"
  90. a: "a"
  91. """)
  92. r = g.parse('aaabaab')
  93. self.assertEqual( ''.join(x.data for x in r.children), 'aaabaa' )
  94. r = g.parse('aaabaaba')
  95. self.assertEqual( ''.join(x.data for x in r.children), 'aaabaaa' )
  96. self.assertRaises(ParseError, g.parse, 'aaabaa')
  97. def test_basic2(self):
  98. # Multiple parsers and colliding tokens
  99. g = _Lark("""start: B A
  100. B: "12"
  101. A: "1" """)
  102. g2 = _Lark("""start: B A
  103. B: "12"
  104. A: "2" """)
  105. x = g.parse('121')
  106. assert x.data == 'start' and x.children == ['12', '1'], x
  107. x = g2.parse('122')
  108. assert x.data == 'start' and x.children == ['12', '2'], x
  109. @unittest.skipIf(cStringIO is None, "cStringIO not available")
  110. def test_stringio_bytes(self):
  111. """Verify that a Lark can be created from file-like objects other than Python's standard 'file' object"""
  112. _Lark(cStringIO(b'start: a+ b a* "b" a*\n b: "b"\n a: "a" '))
  113. def test_stringio_unicode(self):
  114. """Verify that a Lark can be created from file-like objects other than Python's standard 'file' object"""
  115. _Lark(uStringIO(u'start: a+ b a* "b" a*\n b: "b"\n a: "a" '))
  116. def test_unicode(self):
  117. g = _Lark(u"""start: UNIA UNIB UNIA
  118. UNIA: /\xa3/
  119. UNIB: /\u0101/
  120. """)
  121. g.parse(u'\xa3\u0101\u00a3')
  122. @unittest.skipIf(LEXER is None, "Regexps >1 not supported with scanless parsing")
  123. def test_unicode2(self):
  124. g = _Lark(r"""start: UNIA UNIB UNIA UNIC
  125. UNIA: /\xa3/
  126. UNIB: "a\u0101b\ "
  127. UNIC: /a?\u0101c\n/
  128. """)
  129. g.parse(u'\xa3a\u0101b\\ \u00a3\u0101c\n')
  130. def test_unicode3(self):
  131. g = _Lark(r"""start: UNIA UNIB UNIA UNIC
  132. UNIA: /\xa3/
  133. UNIB: "\u0101"
  134. UNIC: /\u0203/ /\n/
  135. """)
  136. g.parse(u'\xa3\u0101\u00a3\u0203\n')
  137. def test_stack_for_ebnf(self):
  138. """Verify that stack depth isn't an issue for EBNF grammars"""
  139. g = _Lark(r"""start: a+
  140. a : "a" """)
  141. g.parse("a" * (sys.getrecursionlimit()*2 ))
  142. def test_expand1_lists_with_one_item(self):
  143. g = _Lark(r"""start: list
  144. ?list: item+
  145. item : A
  146. A: "a"
  147. """)
  148. r = g.parse("a")
  149. # because 'list' is an expand-if-contains-one rule and we only provided one element it should have expanded to 'item'
  150. self.assertSequenceEqual([subtree.data for subtree in r.children], ('item',))
  151. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  152. self.assertEqual(len(r.children), 1)
  153. def test_expand1_lists_with_one_item_2(self):
  154. g = _Lark(r"""start: list
  155. ?list: item+ "!"
  156. item : A
  157. A: "a"
  158. """)
  159. r = g.parse("a!")
  160. # because 'list' is an expand-if-contains-one rule and we only provided one element it should have expanded to 'item'
  161. self.assertSequenceEqual([subtree.data for subtree in r.children], ('item',))
  162. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  163. self.assertEqual(len(r.children), 1)
  164. def test_dont_expand1_lists_with_multiple_items(self):
  165. g = _Lark(r"""start: list
  166. ?list: item+
  167. item : A
  168. A: "a"
  169. """)
  170. r = g.parse("aa")
  171. # because 'list' is an expand-if-contains-one rule and we've provided more than one element it should *not* have expanded
  172. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  173. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  174. self.assertEqual(len(r.children), 1)
  175. # Sanity check: verify that 'list' contains the two 'item's we've given it
  176. [list] = r.children
  177. self.assertSequenceEqual([item.data for item in list.children], ('item', 'item'))
  178. def test_dont_expand1_lists_with_multiple_items_2(self):
  179. g = _Lark(r"""start: list
  180. ?list: item+ "!"
  181. item : A
  182. A: "a"
  183. """)
  184. r = g.parse("aa!")
  185. # because 'list' is an expand-if-contains-one rule and we've provided more than one element it should *not* have expanded
  186. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  187. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  188. self.assertEqual(len(r.children), 1)
  189. # Sanity check: verify that 'list' contains the two 'item's we've given it
  190. [list] = r.children
  191. self.assertSequenceEqual([item.data for item in list.children], ('item', 'item'))
  192. def test_empty_expand1_list(self):
  193. g = _Lark(r"""start: list
  194. ?list: item*
  195. item : A
  196. A: "a"
  197. """)
  198. r = g.parse("")
  199. # because 'list' is an expand-if-contains-one rule and we've provided less than one element (i.e. none) it should *not* have expanded
  200. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  201. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  202. self.assertEqual(len(r.children), 1)
  203. # Sanity check: verify that 'list' contains no 'item's as we've given it none
  204. [list] = r.children
  205. self.assertSequenceEqual([item.data for item in list.children], ())
  206. def test_empty_expand1_list_2(self):
  207. g = _Lark(r"""start: list
  208. ?list: item* "!"?
  209. item : A
  210. A: "a"
  211. """)
  212. r = g.parse("")
  213. # because 'list' is an expand-if-contains-one rule and we've provided less than one element (i.e. none) it should *not* have expanded
  214. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  215. # regardless of the amount of items: there should be only *one* child in 'start' because 'list' isn't an expand-all rule
  216. self.assertEqual(len(r.children), 1)
  217. # Sanity check: verify that 'list' contains no 'item's as we've given it none
  218. [list] = r.children
  219. self.assertSequenceEqual([item.data for item in list.children], ())
  220. def test_empty_flatten_list(self):
  221. g = _Lark(r"""start: list
  222. list: | item "," list
  223. item : A
  224. A: "a"
  225. """)
  226. r = g.parse("")
  227. # Because 'list' is a flatten rule it's top-level element should *never* be expanded
  228. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  229. # Sanity check: verify that 'list' contains no 'item's as we've given it none
  230. [list] = r.children
  231. self.assertSequenceEqual([item.data for item in list.children], ())
  232. @unittest.skipIf(True, "Flattening list isn't implemented (and may never be)")
  233. def test_single_item_flatten_list(self):
  234. g = _Lark(r"""start: list
  235. list: | item "," list
  236. item : A
  237. A: "a"
  238. """)
  239. r = g.parse("a,")
  240. # Because 'list' is a flatten rule it's top-level element should *never* be expanded
  241. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  242. # Sanity check: verify that 'list' contains exactly the one 'item' we've given it
  243. [list] = r.children
  244. self.assertSequenceEqual([item.data for item in list.children], ('item',))
  245. @unittest.skipIf(True, "Flattening list isn't implemented (and may never be)")
  246. def test_multiple_item_flatten_list(self):
  247. g = _Lark(r"""start: list
  248. #list: | item "," list
  249. item : A
  250. A: "a"
  251. """)
  252. r = g.parse("a,a,")
  253. # Because 'list' is a flatten rule it's top-level element should *never* be expanded
  254. self.assertSequenceEqual([subtree.data for subtree in r.children], ('list',))
  255. # Sanity check: verify that 'list' contains exactly the two 'item's we've given it
  256. [list] = r.children
  257. self.assertSequenceEqual([item.data for item in list.children], ('item', 'item'))
  258. @unittest.skipIf(True, "Flattening list isn't implemented (and may never be)")
  259. def test_recurse_flatten(self):
  260. """Verify that stack depth doesn't get exceeded on recursive rules marked for flattening."""
  261. g = _Lark(r"""start: a | start a
  262. a : A
  263. A : "a" """)
  264. # Force PLY to write to the debug log, but prevent writing it to the terminal (uses repr() on the half-built
  265. # STree data structures, which uses recursion).
  266. g.parse("a" * (sys.getrecursionlimit() // 4))
  267. def test_token_collision(self):
  268. g = _Lark("""start: "Hello" NAME
  269. NAME: /\w/+
  270. %ignore " "
  271. """)
  272. x = g.parse('Hello World')
  273. self.assertSequenceEqual(x.children, ['World'])
  274. x = g.parse('Hello HelloWorld')
  275. self.assertSequenceEqual(x.children, ['HelloWorld'])
  276. # def test_string_priority(self):
  277. # g = _Lark("""start: (A | /a?bb/)+
  278. # A: "a" """)
  279. # x = g.parse('abb')
  280. # self.assertEqual(len(x.children), 2)
  281. # # This parse raises an exception because the lexer will always try to consume
  282. # # "a" first and will never match the regular expression
  283. # # This behavior is subject to change!!
  284. # # Thie won't happen with ambiguity handling.
  285. # g = _Lark("""start: (A | /a?ab/)+
  286. # A: "a" """)
  287. # self.assertRaises(LexError, g.parse, 'aab')
  288. def test_undefined_rule(self):
  289. self.assertRaises(GrammarError, _Lark, """start: a""")
  290. def test_undefined_token(self):
  291. self.assertRaises(GrammarError, _Lark, """start: A""")
  292. def test_rule_collision(self):
  293. g = _Lark("""start: "a"+ "b"
  294. | "a"+ """)
  295. x = g.parse('aaaa')
  296. x = g.parse('aaaab')
  297. def test_rule_collision2(self):
  298. g = _Lark("""start: "a"* "b"
  299. | "a"+ """)
  300. x = g.parse('aaaa')
  301. x = g.parse('aaaab')
  302. x = g.parse('b')
  303. @unittest.skipIf(LEXER is None, "Regexps >1 not supported with scanless parsing")
  304. def test_regex_embed(self):
  305. g = _Lark("""start: A B C
  306. A: /a/
  307. B: /${A}b/
  308. C: /${B}c/
  309. """)
  310. x = g.parse('aababc')
  311. def test_token_embed(self):
  312. g = _Lark("""start: A B C
  313. A: "a"
  314. B: A "b"
  315. C: B "c"
  316. """)
  317. x = g.parse('aababc')
  318. def test_token_not_anon(self):
  319. """Tests that "a" is matched as A, rather than an anonymous token.
  320. That means that "a" is not filtered out, despite being an 'immediate string'.
  321. Whether or not this is the intuitive behavior, I'm not sure yet.
  322. Perhaps the right thing to do is report a collision (if such is relevant)
  323. -Erez
  324. """
  325. g = _Lark("""start: "a"
  326. A: "a" """)
  327. x = g.parse('a')
  328. self.assertEqual(len(x.children), 1, '"a" should not be considered anonymous')
  329. self.assertEqual(x.children[0].type, "A")
  330. g = _Lark("""start: /a/
  331. A: /a/ """)
  332. x = g.parse('a')
  333. self.assertEqual(len(x.children), 1, '/a/ should not be considered anonymous')
  334. self.assertEqual(x.children[0].type, "A")
  335. def test_maybe(self):
  336. g = _Lark("""start: ["a"] """)
  337. x = g.parse('a')
  338. x = g.parse('')
  339. def test_start(self):
  340. g = _Lark("""a: "a" a? """, start='a')
  341. x = g.parse('a')
  342. x = g.parse('aa')
  343. x = g.parse('aaa')
  344. def test_alias(self):
  345. g = _Lark("""start: "a" -> b """)
  346. x = g.parse('a')
  347. self.assertEqual(x.data, "b")
  348. def test_token_ebnf(self):
  349. g = _Lark("""start: A
  350. A: "a"* ("b"? "c".."e")+
  351. """)
  352. x = g.parse('abcde')
  353. x = g.parse('dd')
  354. def test_backslash(self):
  355. g = _Lark(r"""start: "\\" "a"
  356. """)
  357. x = g.parse(r'\a')
  358. g = _Lark(r"""start: /\\\\/ /a/
  359. """)
  360. x = g.parse(r'\a')
  361. def test_special_chars(self):
  362. g = _Lark(r"""start: "\n"
  363. """)
  364. x = g.parse('\n')
  365. g = _Lark(r"""start: /\n/
  366. """)
  367. x = g.parse('\n')
  368. def test_backslash2(self):
  369. g = _Lark(r"""start: "\"" "-"
  370. """)
  371. x = g.parse('"-')
  372. g = _Lark(r"""start: /\// /-/
  373. """)
  374. x = g.parse('/-')
  375. # def test_token_recurse(self):
  376. # g = _Lark("""start: A
  377. # A: B
  378. # B: A
  379. # """)
  380. def test_empty(self):
  381. # Fails an Earley implementation without special handling for empty rules,
  382. # or re-processing of already completed rules.
  383. g = _Lark(r"""start: _empty a "B"
  384. a: _empty "A"
  385. _empty:
  386. """)
  387. x = g.parse('AB')
  388. def test_lexer_token_limit(self):
  389. "Python has a stupid limit of 100 groups in a regular expression. Test that we handle this limitation"
  390. tokens = {'A%d'%i:'"%d"'%i for i in range(300)}
  391. g = _Lark("""start: %s
  392. %s""" % (' '.join(tokens), '\n'.join("%s: %s"%x for x in tokens.items())))
  393. def test_float_without_lexer(self):
  394. g = _Lark("""start: ["+"|"-"] float
  395. float: digit* "." digit+ exp?
  396. | digit+ exp
  397. exp: ("e"|"E") ["+"|"-"] digit+
  398. digit: "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
  399. """)
  400. g.parse("1.2")
  401. g.parse("-.2e9")
  402. g.parse("+2e-9")
  403. self.assertRaises(ParseError, g.parse, "+2e-9e")
  404. def test_token_flags(self):
  405. l = _Lark("""!start: "a"i+
  406. """
  407. )
  408. tree = l.parse('aA')
  409. self.assertEqual(tree.children, ['a', 'A'])
  410. l = _Lark("""!start: /a/i+
  411. """
  412. )
  413. tree = l.parse('aA')
  414. self.assertEqual(tree.children, ['a', 'A'])
  415. g = """!start: "a"i "a"
  416. """
  417. self.assertRaises(GrammarError, _Lark, g)
  418. g = """!start: /a/i /a/
  419. """
  420. self.assertRaises(GrammarError, _Lark, g)
  421. g = """start: NAME "," "a"
  422. NAME: /[a-z_]/i /[a-z0-9_]/i*
  423. """
  424. l = _Lark(g)
  425. tree = l.parse('ab,a')
  426. self.assertEqual(tree.children, ['ab'])
  427. tree = l.parse('AB,a')
  428. self.assertEqual(tree.children, ['AB'])
  429. def test_token_flags2(self):
  430. g = """!start: ("a"i | /a/ /b/?)+
  431. """
  432. l = _Lark(g)
  433. tree = l.parse('aA')
  434. self.assertEqual(tree.children, ['a', 'A'])
  435. _NAME = "Test" + PARSER.capitalize() + (LEXER or 'Scanless').capitalize()
  436. _TestParser.__name__ = _NAME
  437. globals()[_NAME] = _TestParser
  438. _TO_TEST = [
  439. ('standard', 'earley'),
  440. ('standard', 'lalr'),
  441. ('contextual', 'lalr'),
  442. (None, 'earley'),
  443. ]
  444. for LEXER, PARSER in _TO_TEST:
  445. _make_parser_test(LEXER, PARSER)
  446. if __name__ == '__main__':
  447. unittest.main()