mparser.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. # Copyright 2014-2017 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. import re
  12. import codecs
  13. from .mesonlib import MesonException
  14. from . import mlog
  15. # This is the regex for the supported escape sequences of a regular string
  16. # literal, like 'abc\x00'
  17. ESCAPE_SEQUENCE_SINGLE_RE = re.compile(r'''
  18. ( \\U........ # 8-digit hex escapes
  19. | \\u.... # 4-digit hex escapes
  20. | \\x.. # 2-digit hex escapes
  21. | \\[0-7]{1,3} # Octal escapes
  22. | \\N\{[^}]+\} # Unicode characters by name
  23. | \\[\\'abfnrtv] # Single-character escapes
  24. )''', re.UNICODE | re.VERBOSE)
  25. class MesonUnicodeDecodeError(MesonException):
  26. def __init__(self, match):
  27. super().__init__("%s" % match)
  28. self.match = match
  29. def decode_match(match):
  30. try:
  31. return codecs.decode(match.group(0), 'unicode_escape')
  32. except UnicodeDecodeError as err:
  33. raise MesonUnicodeDecodeError(match.group(0))
  34. class ParseException(MesonException):
  35. def __init__(self, text, line, lineno, colno):
  36. # Format as error message, followed by the line with the error, followed by a caret to show the error column.
  37. super().__init__("%s\n%s\n%s" % (text, line, '%s^' % (' ' * colno)))
  38. self.lineno = lineno
  39. self.colno = colno
  40. class BlockParseException(MesonException):
  41. def __init__(self, text, line, lineno, colno, start_line, start_lineno, start_colno):
  42. # This can be formatted in two ways - one if the block start and end are on the same line, and a different way if they are on different lines.
  43. if lineno == start_lineno:
  44. # If block start and end are on the same line, it is formatted as:
  45. # Error message
  46. # Followed by the line with the error
  47. # Followed by a caret to show the block start
  48. # Followed by underscores
  49. # Followed by a caret to show the block end.
  50. super().__init__("%s\n%s\n%s" % (text, line, '%s^%s^' % (' ' * start_colno, '_' * (colno - start_colno - 1))))
  51. else:
  52. # If block start and end are on different lines, it is formatted as:
  53. # Error message
  54. # Followed by the line with the error
  55. # Followed by a caret to show the error column.
  56. # Followed by a message saying where the block started.
  57. # Followed by the line of the block start.
  58. # Followed by a caret for the block start.
  59. super().__init__("%s\n%s\n%s\nFor a block that started at %d,%d\n%s\n%s" % (text, line, '%s^' % (' ' * colno), start_lineno, start_colno, start_line, "%s^" % (' ' * start_colno)))
  60. self.lineno = lineno
  61. self.colno = colno
  62. class Token:
  63. def __init__(self, tid, subdir, line_start, lineno, colno, bytespan, value):
  64. self.tid = tid
  65. self.subdir = subdir
  66. self.line_start = line_start
  67. self.lineno = lineno
  68. self.colno = colno
  69. self.bytespan = bytespan
  70. self.value = value
  71. def __eq__(self, other):
  72. if isinstance(other, str):
  73. return self.tid == other
  74. return self.tid == other.tid
  75. class Lexer:
  76. def __init__(self, code):
  77. self.code = code
  78. self.keywords = {'true', 'false', 'if', 'else', 'elif',
  79. 'endif', 'and', 'or', 'not', 'foreach', 'endforeach'}
  80. self.token_specification = [
  81. # Need to be sorted longest to shortest.
  82. ('ignore', re.compile(r'[ \t]')),
  83. ('id', re.compile('[_a-zA-Z][_0-9a-zA-Z]*')),
  84. ('number', re.compile(r'0[bB][01]+|0[oO][0-7]+|0[xX][0-9a-fA-F]+|0|[1-9]\d*')),
  85. ('eol_cont', re.compile(r'\\\n')),
  86. ('eol', re.compile(r'\n')),
  87. ('multiline_string', re.compile(r"'''(.|\n)*?'''", re.M)),
  88. ('comment', re.compile(r'#.*')),
  89. ('lparen', re.compile(r'\(')),
  90. ('rparen', re.compile(r'\)')),
  91. ('lbracket', re.compile(r'\[')),
  92. ('rbracket', re.compile(r'\]')),
  93. ('lcurl', re.compile(r'\{')),
  94. ('rcurl', re.compile(r'\}')),
  95. ('dblquote', re.compile(r'"')),
  96. ('string', re.compile(r"'([^'\\]|(\\.))*'")),
  97. ('comma', re.compile(r',')),
  98. ('plusassign', re.compile(r'\+=')),
  99. ('dot', re.compile(r'\.')),
  100. ('plus', re.compile(r'\+')),
  101. ('dash', re.compile(r'-')),
  102. ('star', re.compile(r'\*')),
  103. ('percent', re.compile(r'%')),
  104. ('fslash', re.compile(r'/')),
  105. ('colon', re.compile(r':')),
  106. ('equal', re.compile(r'==')),
  107. ('nequal', re.compile(r'!=')),
  108. ('assign', re.compile(r'=')),
  109. ('le', re.compile(r'<=')),
  110. ('lt', re.compile(r'<')),
  111. ('ge', re.compile(r'>=')),
  112. ('gt', re.compile(r'>')),
  113. ('questionmark', re.compile(r'\?')),
  114. ]
  115. def getline(self, line_start):
  116. return self.code[line_start:self.code.find('\n', line_start)]
  117. def lex(self, subdir):
  118. line_start = 0
  119. lineno = 1
  120. loc = 0
  121. par_count = 0
  122. bracket_count = 0
  123. curl_count = 0
  124. col = 0
  125. while loc < len(self.code):
  126. matched = False
  127. value = None
  128. for (tid, reg) in self.token_specification:
  129. mo = reg.match(self.code, loc)
  130. if mo:
  131. curline = lineno
  132. curline_start = line_start
  133. col = mo.start() - line_start
  134. matched = True
  135. span_start = loc
  136. loc = mo.end()
  137. span_end = loc
  138. bytespan = (span_start, span_end)
  139. match_text = mo.group()
  140. if tid == 'ignore' or tid == 'comment':
  141. break
  142. elif tid == 'lparen':
  143. par_count += 1
  144. elif tid == 'rparen':
  145. par_count -= 1
  146. elif tid == 'lbracket':
  147. bracket_count += 1
  148. elif tid == 'rbracket':
  149. bracket_count -= 1
  150. elif tid == 'lcurl':
  151. curl_count += 1
  152. elif tid == 'rcurl':
  153. curl_count -= 1
  154. elif tid == 'dblquote':
  155. raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col)
  156. elif tid == 'string':
  157. # Handle here and not on the regexp to give a better error message.
  158. if match_text.find("\n") != -1:
  159. mlog.warning("""Newline character in a string detected, use ''' (three single quotes) for multiline strings instead.
  160. This will become a hard error in a future Meson release.""", self.getline(line_start), lineno, col)
  161. value = match_text[1:-1]
  162. try:
  163. value = ESCAPE_SEQUENCE_SINGLE_RE.sub(decode_match, value)
  164. except MesonUnicodeDecodeError as err:
  165. raise MesonException("Failed to parse escape sequence: '{}' in string:\n {}".format(err.match, match_text))
  166. elif tid == 'multiline_string':
  167. tid = 'string'
  168. value = match_text[3:-3]
  169. lines = match_text.split('\n')
  170. if len(lines) > 1:
  171. lineno += len(lines) - 1
  172. line_start = mo.end() - len(lines[-1])
  173. elif tid == 'number':
  174. value = int(match_text, base=0)
  175. elif tid == 'eol' or tid == 'eol_cont':
  176. lineno += 1
  177. line_start = loc
  178. if par_count > 0 or bracket_count > 0 or curl_count > 0:
  179. break
  180. elif tid == 'id':
  181. if match_text in self.keywords:
  182. tid = match_text
  183. else:
  184. value = match_text
  185. yield Token(tid, subdir, curline_start, curline, col, bytespan, value)
  186. break
  187. if not matched:
  188. raise ParseException('lexer', self.getline(line_start), lineno, col)
  189. class ElementaryNode:
  190. def __init__(self, token):
  191. self.lineno = token.lineno
  192. self.subdir = token.subdir
  193. self.colno = token.colno
  194. self.value = token.value
  195. self.bytespan = token.bytespan
  196. class BooleanNode(ElementaryNode):
  197. def __init__(self, token, value):
  198. super().__init__(token)
  199. self.value = value
  200. assert(isinstance(self.value, bool))
  201. class IdNode(ElementaryNode):
  202. def __init__(self, token):
  203. super().__init__(token)
  204. assert(isinstance(self.value, str))
  205. def __str__(self):
  206. return "Id node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
  207. class NumberNode(ElementaryNode):
  208. def __init__(self, token):
  209. super().__init__(token)
  210. assert(isinstance(self.value, int))
  211. class StringNode(ElementaryNode):
  212. def __init__(self, token):
  213. super().__init__(token)
  214. assert(isinstance(self.value, str))
  215. def __str__(self):
  216. return "String node: '%s' (%d, %d)." % (self.value, self.lineno, self.colno)
  217. class ArrayNode:
  218. def __init__(self, args):
  219. self.subdir = args.subdir
  220. self.lineno = args.lineno
  221. self.colno = args.colno
  222. self.args = args
  223. class DictNode:
  224. def __init__(self, args):
  225. self.subdir = args.subdir
  226. self.lineno = args.lineno
  227. self.colno = args.colno
  228. self.args = args
  229. class EmptyNode:
  230. def __init__(self, lineno, colno):
  231. self.subdir = ''
  232. self.lineno = lineno
  233. self.colno = colno
  234. self.value = None
  235. class OrNode:
  236. def __init__(self, left, right):
  237. self.subdir = left.subdir
  238. self.lineno = left.lineno
  239. self.colno = left.colno
  240. self.left = left
  241. self.right = right
  242. class AndNode:
  243. def __init__(self, left, right):
  244. self.subdir = left.subdir
  245. self.lineno = left.lineno
  246. self.colno = left.colno
  247. self.left = left
  248. self.right = right
  249. class ComparisonNode:
  250. def __init__(self, ctype, left, right):
  251. self.lineno = left.lineno
  252. self.colno = left.colno
  253. self.subdir = left.subdir
  254. self.left = left
  255. self.right = right
  256. self.ctype = ctype
  257. class ArithmeticNode:
  258. def __init__(self, operation, left, right):
  259. self.subdir = left.subdir
  260. self.lineno = left.lineno
  261. self.colno = left.colno
  262. self.left = left
  263. self.right = right
  264. self.operation = operation
  265. class NotNode:
  266. def __init__(self, location_node, value):
  267. self.subdir = location_node.subdir
  268. self.lineno = location_node.lineno
  269. self.colno = location_node.colno
  270. self.value = value
  271. class CodeBlockNode:
  272. def __init__(self, location_node):
  273. self.subdir = location_node.subdir
  274. self.lineno = location_node.lineno
  275. self.colno = location_node.colno
  276. self.lines = []
  277. class IndexNode:
  278. def __init__(self, iobject, index):
  279. self.iobject = iobject
  280. self.index = index
  281. self.subdir = iobject.subdir
  282. self.lineno = iobject.lineno
  283. self.colno = iobject.colno
  284. class MethodNode:
  285. def __init__(self, subdir, lineno, colno, source_object, name, args):
  286. self.subdir = subdir
  287. self.lineno = lineno
  288. self.colno = colno
  289. self.source_object = source_object
  290. self.name = name
  291. assert(isinstance(self.name, str))
  292. self.args = args
  293. class FunctionNode:
  294. def __init__(self, subdir, lineno, colno, func_name, args):
  295. self.subdir = subdir
  296. self.lineno = lineno
  297. self.colno = colno
  298. self.func_name = func_name
  299. assert(isinstance(func_name, str))
  300. self.args = args
  301. class AssignmentNode:
  302. def __init__(self, lineno, colno, var_name, value):
  303. self.lineno = lineno
  304. self.colno = colno
  305. self.var_name = var_name
  306. assert(isinstance(var_name, str))
  307. self.value = value
  308. class PlusAssignmentNode:
  309. def __init__(self, lineno, colno, var_name, value):
  310. self.lineno = lineno
  311. self.colno = colno
  312. self.var_name = var_name
  313. assert(isinstance(var_name, str))
  314. self.value = value
  315. class ForeachClauseNode:
  316. def __init__(self, lineno, colno, varnames, items, block):
  317. self.lineno = lineno
  318. self.colno = colno
  319. self.varnames = varnames
  320. self.items = items
  321. self.block = block
  322. class IfClauseNode:
  323. def __init__(self, lineno, colno):
  324. self.lineno = lineno
  325. self.colno = colno
  326. self.ifs = []
  327. self.elseblock = EmptyNode(lineno, colno)
  328. class UMinusNode:
  329. def __init__(self, current_location, value):
  330. self.subdir = current_location.subdir
  331. self.lineno = current_location.lineno
  332. self.colno = current_location.colno
  333. self.value = value
  334. class IfNode:
  335. def __init__(self, lineno, colno, condition, block):
  336. self.lineno = lineno
  337. self.colno = colno
  338. self.condition = condition
  339. self.block = block
  340. class TernaryNode:
  341. def __init__(self, lineno, colno, condition, trueblock, falseblock):
  342. self.lineno = lineno
  343. self.colno = colno
  344. self.condition = condition
  345. self.trueblock = trueblock
  346. self.falseblock = falseblock
  347. class ArgumentNode:
  348. def __init__(self, token):
  349. self.lineno = token.lineno
  350. self.colno = token.colno
  351. self.subdir = token.subdir
  352. self.arguments = []
  353. self.commas = []
  354. self.kwargs = {}
  355. self.kwargs_dict = None
  356. self.order_error = False
  357. def prepend(self, statement):
  358. if self.num_kwargs() > 0:
  359. self.order_error = True
  360. if not isinstance(statement, EmptyNode):
  361. self.arguments = [statement] + self.arguments
  362. def append(self, statement):
  363. if self.num_kwargs() > 0:
  364. self.order_error = True
  365. if not isinstance(statement, EmptyNode):
  366. self.arguments += [statement]
  367. def set_kwarg(self, name, value):
  368. if name in self.kwargs:
  369. mlog.warning('Keyword argument "{}" defined multiple times.'.format(name), location=self)
  370. mlog.warning('This will be an error in future Meson releases.')
  371. self.kwargs[name] = value
  372. def num_args(self):
  373. return len(self.arguments)
  374. def num_kwargs(self):
  375. return len(self.kwargs)
  376. def incorrect_order(self):
  377. return self.order_error
  378. def __len__(self):
  379. return self.num_args() # Fixme
  380. comparison_map = {'equal': '==',
  381. 'nequal': '!=',
  382. 'lt': '<',
  383. 'le': '<=',
  384. 'gt': '>',
  385. 'ge': '>='
  386. }
  387. # Recursive descent parser for Meson's definition language.
  388. # Very basic apart from the fact that we have many precedence
  389. # levels so there are not enough words to describe them all.
  390. # Enter numbering:
  391. #
  392. # 1 assignment
  393. # 2 or
  394. # 3 and
  395. # 4 comparison
  396. # 5 arithmetic
  397. # 6 negation
  398. # 7 funcall, method call
  399. # 8 parentheses
  400. # 9 plain token
  401. class Parser:
  402. def __init__(self, code, subdir):
  403. self.lexer = Lexer(code)
  404. self.stream = self.lexer.lex(subdir)
  405. self.current = Token('eof', '', 0, 0, 0, (0, 0), None)
  406. self.getsym()
  407. self.in_ternary = False
  408. def getsym(self):
  409. try:
  410. self.current = next(self.stream)
  411. except StopIteration:
  412. self.current = Token('eof', '', self.current.line_start, self.current.lineno, self.current.colno + self.current.bytespan[1] - self.current.bytespan[0], (0, 0), None)
  413. def getline(self):
  414. return self.lexer.getline(self.current.line_start)
  415. def accept(self, s):
  416. if self.current.tid == s:
  417. self.getsym()
  418. return True
  419. return False
  420. def expect(self, s):
  421. if self.accept(s):
  422. return True
  423. raise ParseException('Expecting %s got %s.' % (s, self.current.tid), self.getline(), self.current.lineno, self.current.colno)
  424. def block_expect(self, s, block_start):
  425. if self.accept(s):
  426. return True
  427. raise BlockParseException('Expecting %s got %s.' % (s, self.current.tid), self.getline(), self.current.lineno, self.current.colno, self.lexer.getline(block_start.line_start), block_start.lineno, block_start.colno)
  428. def parse(self):
  429. block = self.codeblock()
  430. self.expect('eof')
  431. return block
  432. def statement(self):
  433. return self.e1()
  434. def e1(self):
  435. left = self.e2()
  436. if self.accept('plusassign'):
  437. value = self.e1()
  438. if not isinstance(left, IdNode):
  439. raise ParseException('Plusassignment target must be an id.', self.getline(), left.lineno, left.colno)
  440. return PlusAssignmentNode(left.lineno, left.colno, left.value, value)
  441. elif self.accept('assign'):
  442. value = self.e1()
  443. if not isinstance(left, IdNode):
  444. raise ParseException('Assignment target must be an id.',
  445. self.getline(), left.lineno, left.colno)
  446. return AssignmentNode(left.lineno, left.colno, left.value, value)
  447. elif self.accept('questionmark'):
  448. if self.in_ternary:
  449. raise ParseException('Nested ternary operators are not allowed.',
  450. self.getline(), left.lineno, left.colno)
  451. self.in_ternary = True
  452. trueblock = self.e1()
  453. self.expect('colon')
  454. falseblock = self.e1()
  455. self.in_ternary = False
  456. return TernaryNode(left.lineno, left.colno, left, trueblock, falseblock)
  457. return left
  458. def e2(self):
  459. left = self.e3()
  460. while self.accept('or'):
  461. if isinstance(left, EmptyNode):
  462. raise ParseException('Invalid or clause.',
  463. self.getline(), left.lineno, left.colno)
  464. left = OrNode(left, self.e3())
  465. return left
  466. def e3(self):
  467. left = self.e4()
  468. while self.accept('and'):
  469. if isinstance(left, EmptyNode):
  470. raise ParseException('Invalid and clause.',
  471. self.getline(), left.lineno, left.colno)
  472. left = AndNode(left, self.e4())
  473. return left
  474. def e4(self):
  475. left = self.e5()
  476. for nodename, operator_type in comparison_map.items():
  477. if self.accept(nodename):
  478. return ComparisonNode(operator_type, left, self.e5())
  479. return left
  480. def e5(self):
  481. return self.e5add()
  482. def e5add(self):
  483. left = self.e5sub()
  484. if self.accept('plus'):
  485. return ArithmeticNode('add', left, self.e5add())
  486. return left
  487. def e5sub(self):
  488. left = self.e5mod()
  489. if self.accept('dash'):
  490. return ArithmeticNode('sub', left, self.e5sub())
  491. return left
  492. def e5mod(self):
  493. left = self.e5mul()
  494. if self.accept('percent'):
  495. return ArithmeticNode('mod', left, self.e5mod())
  496. return left
  497. def e5mul(self):
  498. left = self.e5div()
  499. if self.accept('star'):
  500. return ArithmeticNode('mul', left, self.e5mul())
  501. return left
  502. def e5div(self):
  503. left = self.e6()
  504. if self.accept('fslash'):
  505. return ArithmeticNode('div', left, self.e5div())
  506. return left
  507. def e6(self):
  508. if self.accept('not'):
  509. return NotNode(self.current, self.e7())
  510. if self.accept('dash'):
  511. return UMinusNode(self.current, self.e7())
  512. return self.e7()
  513. def e7(self):
  514. left = self.e8()
  515. block_start = self.current
  516. if self.accept('lparen'):
  517. args = self.args()
  518. self.block_expect('rparen', block_start)
  519. if not isinstance(left, IdNode):
  520. raise ParseException('Function call must be applied to plain id',
  521. self.getline(), left.lineno, left.colno)
  522. left = FunctionNode(left.subdir, left.lineno, left.colno, left.value, args)
  523. go_again = True
  524. while go_again:
  525. go_again = False
  526. if self.accept('dot'):
  527. go_again = True
  528. left = self.method_call(left)
  529. if self.accept('lbracket'):
  530. go_again = True
  531. left = self.index_call(left)
  532. return left
  533. def e8(self):
  534. block_start = self.current
  535. if self.accept('lparen'):
  536. e = self.statement()
  537. self.block_expect('rparen', block_start)
  538. return e
  539. elif self.accept('lbracket'):
  540. args = self.args()
  541. self.block_expect('rbracket', block_start)
  542. return ArrayNode(args)
  543. elif self.accept('lcurl'):
  544. key_values = self.key_values()
  545. self.block_expect('rcurl', block_start)
  546. return DictNode(key_values)
  547. else:
  548. return self.e9()
  549. def e9(self):
  550. t = self.current
  551. if self.accept('true'):
  552. return BooleanNode(t, True)
  553. if self.accept('false'):
  554. return BooleanNode(t, False)
  555. if self.accept('id'):
  556. return IdNode(t)
  557. if self.accept('number'):
  558. return NumberNode(t)
  559. if self.accept('string'):
  560. return StringNode(t)
  561. return EmptyNode(self.current.lineno, self.current.colno)
  562. def key_values(self):
  563. s = self.statement()
  564. a = ArgumentNode(s)
  565. while not isinstance(s, EmptyNode):
  566. potential = self.current
  567. if self.accept('colon'):
  568. if not isinstance(s, StringNode):
  569. raise ParseException('Key must be a string.',
  570. self.getline(), s.lineno, s.colno)
  571. if s.value in a.kwargs:
  572. # + 1 to colno to point to the actual string, not the opening quote
  573. raise ParseException('Duplicate dictionary key: {}'.format(s.value),
  574. self.getline(), s.lineno, s.colno + 1)
  575. a.set_kwarg(s.value, self.statement())
  576. potential = self.current
  577. if not self.accept('comma'):
  578. return a
  579. a.commas.append(potential)
  580. else:
  581. raise ParseException('Only key:value pairs are valid in dict construction.',
  582. self.getline(), s.lineno, s.colno)
  583. s = self.statement()
  584. return a
  585. def args(self):
  586. s = self.statement()
  587. a = ArgumentNode(s)
  588. while not isinstance(s, EmptyNode):
  589. potential = self.current
  590. if self.accept('comma'):
  591. a.commas.append(potential)
  592. a.append(s)
  593. elif self.accept('colon'):
  594. if not isinstance(s, IdNode):
  595. raise ParseException('Dictionary key must be a plain identifier.',
  596. self.getline(), s.lineno, s.colno)
  597. a.set_kwarg(s.value, self.statement())
  598. potential = self.current
  599. if not self.accept('comma'):
  600. return a
  601. a.commas.append(potential)
  602. else:
  603. a.append(s)
  604. return a
  605. if self.accept('star'):
  606. dict_expr = self.statement()
  607. a.kwargs_dict = dict_expr
  608. return a
  609. s = self.statement()
  610. return a
  611. def method_call(self, source_object):
  612. methodname = self.e9()
  613. if not(isinstance(methodname, IdNode)):
  614. raise ParseException('Method name must be plain id',
  615. self.getline(), self.current.lineno, self.current.colno)
  616. self.expect('lparen')
  617. args = self.args()
  618. self.expect('rparen')
  619. method = MethodNode(methodname.subdir, methodname.lineno, methodname.colno, source_object, methodname.value, args)
  620. if self.accept('dot'):
  621. return self.method_call(method)
  622. return method
  623. def index_call(self, source_object):
  624. index_statement = self.statement()
  625. self.expect('rbracket')
  626. return IndexNode(source_object, index_statement)
  627. def foreachblock(self):
  628. t = self.current
  629. self.expect('id')
  630. varname = t
  631. varnames = [t]
  632. if self.accept('comma'):
  633. t = self.current
  634. self.expect('id')
  635. varnames.append(t)
  636. self.expect('colon')
  637. items = self.statement()
  638. block = self.codeblock()
  639. return ForeachClauseNode(varname.lineno, varname.colno, varnames, items, block)
  640. def ifblock(self):
  641. condition = self.statement()
  642. clause = IfClauseNode(condition.lineno, condition.colno)
  643. self.expect('eol')
  644. block = self.codeblock()
  645. clause.ifs.append(IfNode(clause.lineno, clause.colno, condition, block))
  646. self.elseifblock(clause)
  647. clause.elseblock = self.elseblock()
  648. return clause
  649. def elseifblock(self, clause):
  650. while self.accept('elif'):
  651. s = self.statement()
  652. self.expect('eol')
  653. b = self.codeblock()
  654. clause.ifs.append(IfNode(s.lineno, s.colno, s, b))
  655. def elseblock(self):
  656. if self.accept('else'):
  657. self.expect('eol')
  658. return self.codeblock()
  659. def line(self):
  660. block_start = self.current
  661. if self.current == 'eol':
  662. return EmptyNode(self.current.lineno, self.current.colno)
  663. if self.accept('if'):
  664. block = self.ifblock()
  665. self.block_expect('endif', block_start)
  666. return block
  667. if self.accept('foreach'):
  668. block = self.foreachblock()
  669. self.block_expect('endforeach', block_start)
  670. return block
  671. return self.statement()
  672. def codeblock(self):
  673. block = CodeBlockNode(self.current)
  674. cond = True
  675. while cond:
  676. curline = self.line()
  677. if not isinstance(curline, EmptyNode):
  678. block.lines.append(curline)
  679. cond = self.accept('eol')
  680. return block