parser.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. import fontTools.voltLib.ast as ast
  2. from fontTools.voltLib.lexer import Lexer
  3. from fontTools.voltLib.error import VoltLibError
  4. from io import open
  5. PARSE_FUNCS = {
  6. "DEF_GLYPH": "parse_def_glyph_",
  7. "DEF_GROUP": "parse_def_group_",
  8. "DEF_SCRIPT": "parse_def_script_",
  9. "DEF_LOOKUP": "parse_def_lookup_",
  10. "DEF_ANCHOR": "parse_def_anchor_",
  11. "GRID_PPEM": "parse_ppem_",
  12. "PRESENTATION_PPEM": "parse_ppem_",
  13. "PPOSITIONING_PPEM": "parse_ppem_",
  14. "COMPILER_USEEXTENSIONLOOKUPS": "parse_noarg_option_",
  15. "COMPILER_USEPAIRPOSFORMAT2": "parse_noarg_option_",
  16. "CMAP_FORMAT": "parse_cmap_format",
  17. "DO_NOT_TOUCH_CMAP": "parse_noarg_option_",
  18. }
  19. class Parser(object):
  20. def __init__(self, path):
  21. self.doc_ = ast.VoltFile()
  22. self.glyphs_ = OrderedSymbolTable()
  23. self.groups_ = SymbolTable()
  24. self.anchors_ = {} # dictionary of SymbolTable() keyed by glyph
  25. self.scripts_ = SymbolTable()
  26. self.langs_ = SymbolTable()
  27. self.lookups_ = SymbolTable()
  28. self.next_token_type_, self.next_token_ = (None, None)
  29. self.next_token_location_ = None
  30. self.make_lexer_(path)
  31. self.advance_lexer_()
  32. def make_lexer_(self, file_or_path):
  33. if hasattr(file_or_path, "read"):
  34. filename = getattr(file_or_path, "name", None)
  35. data = file_or_path.read()
  36. else:
  37. filename = file_or_path
  38. with open(file_or_path, "r") as f:
  39. data = f.read()
  40. self.lexer_ = Lexer(data, filename)
  41. def parse(self):
  42. statements = self.doc_.statements
  43. while self.next_token_type_ is not None:
  44. self.advance_lexer_()
  45. if self.cur_token_ in PARSE_FUNCS.keys():
  46. func = getattr(self, PARSE_FUNCS[self.cur_token_])
  47. statements.append(func())
  48. elif self.is_cur_keyword_("END"):
  49. break
  50. else:
  51. raise VoltLibError(
  52. "Expected " + ", ".join(sorted(PARSE_FUNCS.keys())),
  53. self.cur_token_location_,
  54. )
  55. return self.doc_
  56. def parse_def_glyph_(self):
  57. assert self.is_cur_keyword_("DEF_GLYPH")
  58. location = self.cur_token_location_
  59. name = self.expect_string_()
  60. self.expect_keyword_("ID")
  61. gid = self.expect_number_()
  62. if gid < 0:
  63. raise VoltLibError("Invalid glyph ID", self.cur_token_location_)
  64. gunicode = None
  65. if self.next_token_ == "UNICODE":
  66. self.expect_keyword_("UNICODE")
  67. gunicode = [self.expect_number_()]
  68. if gunicode[0] < 0:
  69. raise VoltLibError("Invalid glyph UNICODE", self.cur_token_location_)
  70. elif self.next_token_ == "UNICODEVALUES":
  71. self.expect_keyword_("UNICODEVALUES")
  72. gunicode = self.parse_unicode_values_()
  73. gtype = None
  74. if self.next_token_ == "TYPE":
  75. self.expect_keyword_("TYPE")
  76. gtype = self.expect_name_()
  77. assert gtype in ("BASE", "LIGATURE", "MARK", "COMPONENT")
  78. components = None
  79. if self.next_token_ == "COMPONENTS":
  80. self.expect_keyword_("COMPONENTS")
  81. components = self.expect_number_()
  82. self.expect_keyword_("END_GLYPH")
  83. if self.glyphs_.resolve(name) is not None:
  84. raise VoltLibError(
  85. 'Glyph "%s" (gid %i) already defined' % (name, gid), location
  86. )
  87. def_glyph = ast.GlyphDefinition(
  88. name, gid, gunicode, gtype, components, location=location
  89. )
  90. self.glyphs_.define(name, def_glyph)
  91. return def_glyph
  92. def parse_def_group_(self):
  93. assert self.is_cur_keyword_("DEF_GROUP")
  94. location = self.cur_token_location_
  95. name = self.expect_string_()
  96. enum = None
  97. if self.next_token_ == "ENUM":
  98. enum = self.parse_enum_()
  99. self.expect_keyword_("END_GROUP")
  100. if self.groups_.resolve(name) is not None:
  101. raise VoltLibError(
  102. 'Glyph group "%s" already defined, '
  103. "group names are case insensitive" % name,
  104. location,
  105. )
  106. def_group = ast.GroupDefinition(name, enum, location=location)
  107. self.groups_.define(name, def_group)
  108. return def_group
  109. def parse_def_script_(self):
  110. assert self.is_cur_keyword_("DEF_SCRIPT")
  111. location = self.cur_token_location_
  112. name = None
  113. if self.next_token_ == "NAME":
  114. self.expect_keyword_("NAME")
  115. name = self.expect_string_()
  116. self.expect_keyword_("TAG")
  117. tag = self.expect_string_()
  118. if self.scripts_.resolve(tag) is not None:
  119. raise VoltLibError(
  120. 'Script "%s" already defined, '
  121. "script tags are case insensitive" % tag,
  122. location,
  123. )
  124. self.langs_.enter_scope()
  125. langs = []
  126. while self.next_token_ != "END_SCRIPT":
  127. self.advance_lexer_()
  128. lang = self.parse_langsys_()
  129. self.expect_keyword_("END_LANGSYS")
  130. if self.langs_.resolve(lang.tag) is not None:
  131. raise VoltLibError(
  132. 'Language "%s" already defined in script "%s", '
  133. "language tags are case insensitive" % (lang.tag, tag),
  134. location,
  135. )
  136. self.langs_.define(lang.tag, lang)
  137. langs.append(lang)
  138. self.expect_keyword_("END_SCRIPT")
  139. self.langs_.exit_scope()
  140. def_script = ast.ScriptDefinition(name, tag, langs, location=location)
  141. self.scripts_.define(tag, def_script)
  142. return def_script
  143. def parse_langsys_(self):
  144. assert self.is_cur_keyword_("DEF_LANGSYS")
  145. location = self.cur_token_location_
  146. name = None
  147. if self.next_token_ == "NAME":
  148. self.expect_keyword_("NAME")
  149. name = self.expect_string_()
  150. self.expect_keyword_("TAG")
  151. tag = self.expect_string_()
  152. features = []
  153. while self.next_token_ != "END_LANGSYS":
  154. self.advance_lexer_()
  155. feature = self.parse_feature_()
  156. self.expect_keyword_("END_FEATURE")
  157. features.append(feature)
  158. def_langsys = ast.LangSysDefinition(name, tag, features, location=location)
  159. return def_langsys
  160. def parse_feature_(self):
  161. assert self.is_cur_keyword_("DEF_FEATURE")
  162. location = self.cur_token_location_
  163. self.expect_keyword_("NAME")
  164. name = self.expect_string_()
  165. self.expect_keyword_("TAG")
  166. tag = self.expect_string_()
  167. lookups = []
  168. while self.next_token_ != "END_FEATURE":
  169. # self.advance_lexer_()
  170. self.expect_keyword_("LOOKUP")
  171. lookup = self.expect_string_()
  172. lookups.append(lookup)
  173. feature = ast.FeatureDefinition(name, tag, lookups, location=location)
  174. return feature
  175. def parse_def_lookup_(self):
  176. assert self.is_cur_keyword_("DEF_LOOKUP")
  177. location = self.cur_token_location_
  178. name = self.expect_string_()
  179. if not name[0].isalpha():
  180. raise VoltLibError(
  181. 'Lookup name "%s" must start with a letter' % name, location
  182. )
  183. if self.lookups_.resolve(name) is not None:
  184. raise VoltLibError(
  185. 'Lookup "%s" already defined, '
  186. "lookup names are case insensitive" % name,
  187. location,
  188. )
  189. process_base = True
  190. if self.next_token_ == "PROCESS_BASE":
  191. self.advance_lexer_()
  192. elif self.next_token_ == "SKIP_BASE":
  193. self.advance_lexer_()
  194. process_base = False
  195. process_marks = True
  196. mark_glyph_set = None
  197. if self.next_token_ == "PROCESS_MARKS":
  198. self.advance_lexer_()
  199. if self.next_token_ == "MARK_GLYPH_SET":
  200. self.advance_lexer_()
  201. mark_glyph_set = self.expect_string_()
  202. elif self.next_token_ == "ALL":
  203. self.advance_lexer_()
  204. elif self.next_token_ == "NONE":
  205. self.advance_lexer_()
  206. process_marks = False
  207. elif self.next_token_type_ == Lexer.STRING:
  208. process_marks = self.expect_string_()
  209. else:
  210. raise VoltLibError(
  211. "Expected ALL, NONE, MARK_GLYPH_SET or an ID. "
  212. "Got %s" % (self.next_token_type_),
  213. location,
  214. )
  215. elif self.next_token_ == "SKIP_MARKS":
  216. self.advance_lexer_()
  217. process_marks = False
  218. direction = None
  219. if self.next_token_ == "DIRECTION":
  220. self.expect_keyword_("DIRECTION")
  221. direction = self.expect_name_()
  222. assert direction in ("LTR", "RTL")
  223. reversal = None
  224. if self.next_token_ == "REVERSAL":
  225. self.expect_keyword_("REVERSAL")
  226. reversal = True
  227. comments = None
  228. if self.next_token_ == "COMMENTS":
  229. self.expect_keyword_("COMMENTS")
  230. comments = self.expect_string_().replace(r"\n", "\n")
  231. context = []
  232. while self.next_token_ in ("EXCEPT_CONTEXT", "IN_CONTEXT"):
  233. context = self.parse_context_()
  234. as_pos_or_sub = self.expect_name_()
  235. sub = None
  236. pos = None
  237. if as_pos_or_sub == "AS_SUBSTITUTION":
  238. sub = self.parse_substitution_(reversal)
  239. elif as_pos_or_sub == "AS_POSITION":
  240. pos = self.parse_position_()
  241. else:
  242. raise VoltLibError(
  243. "Expected AS_SUBSTITUTION or AS_POSITION. " "Got %s" % (as_pos_or_sub),
  244. location,
  245. )
  246. def_lookup = ast.LookupDefinition(
  247. name,
  248. process_base,
  249. process_marks,
  250. mark_glyph_set,
  251. direction,
  252. reversal,
  253. comments,
  254. context,
  255. sub,
  256. pos,
  257. location=location,
  258. )
  259. self.lookups_.define(name, def_lookup)
  260. return def_lookup
  261. def parse_context_(self):
  262. location = self.cur_token_location_
  263. contexts = []
  264. while self.next_token_ in ("EXCEPT_CONTEXT", "IN_CONTEXT"):
  265. side = None
  266. coverage = None
  267. ex_or_in = self.expect_name_()
  268. # side_contexts = [] # XXX
  269. if self.next_token_ != "END_CONTEXT":
  270. left = []
  271. right = []
  272. while self.next_token_ in ("LEFT", "RIGHT"):
  273. side = self.expect_name_()
  274. coverage = self.parse_coverage_()
  275. if side == "LEFT":
  276. left.append(coverage)
  277. else:
  278. right.append(coverage)
  279. self.expect_keyword_("END_CONTEXT")
  280. context = ast.ContextDefinition(
  281. ex_or_in, left, right, location=location
  282. )
  283. contexts.append(context)
  284. else:
  285. self.expect_keyword_("END_CONTEXT")
  286. return contexts
  287. def parse_substitution_(self, reversal):
  288. assert self.is_cur_keyword_("AS_SUBSTITUTION")
  289. location = self.cur_token_location_
  290. src = []
  291. dest = []
  292. if self.next_token_ != "SUB":
  293. raise VoltLibError("Expected SUB", location)
  294. while self.next_token_ == "SUB":
  295. self.expect_keyword_("SUB")
  296. src.append(self.parse_coverage_())
  297. self.expect_keyword_("WITH")
  298. dest.append(self.parse_coverage_())
  299. self.expect_keyword_("END_SUB")
  300. self.expect_keyword_("END_SUBSTITUTION")
  301. max_src = max([len(cov) for cov in src])
  302. max_dest = max([len(cov) for cov in dest])
  303. # many to many or mixed is invalid
  304. if (max_src > 1 and max_dest > 1) or (
  305. reversal and (max_src > 1 or max_dest > 1)
  306. ):
  307. raise VoltLibError("Invalid substitution type", location)
  308. mapping = dict(zip(tuple(src), tuple(dest)))
  309. if max_src == 1 and max_dest == 1:
  310. if reversal:
  311. sub = ast.SubstitutionReverseChainingSingleDefinition(
  312. mapping, location=location
  313. )
  314. else:
  315. sub = ast.SubstitutionSingleDefinition(mapping, location=location)
  316. elif max_src == 1 and max_dest > 1:
  317. sub = ast.SubstitutionMultipleDefinition(mapping, location=location)
  318. elif max_src > 1 and max_dest == 1:
  319. sub = ast.SubstitutionLigatureDefinition(mapping, location=location)
  320. return sub
  321. def parse_position_(self):
  322. assert self.is_cur_keyword_("AS_POSITION")
  323. location = self.cur_token_location_
  324. pos_type = self.expect_name_()
  325. if pos_type not in ("ATTACH", "ATTACH_CURSIVE", "ADJUST_PAIR", "ADJUST_SINGLE"):
  326. raise VoltLibError(
  327. "Expected ATTACH, ATTACH_CURSIVE, ADJUST_PAIR, ADJUST_SINGLE", location
  328. )
  329. if pos_type == "ATTACH":
  330. position = self.parse_attach_()
  331. elif pos_type == "ATTACH_CURSIVE":
  332. position = self.parse_attach_cursive_()
  333. elif pos_type == "ADJUST_PAIR":
  334. position = self.parse_adjust_pair_()
  335. elif pos_type == "ADJUST_SINGLE":
  336. position = self.parse_adjust_single_()
  337. self.expect_keyword_("END_POSITION")
  338. return position
  339. def parse_attach_(self):
  340. assert self.is_cur_keyword_("ATTACH")
  341. location = self.cur_token_location_
  342. coverage = self.parse_coverage_()
  343. coverage_to = []
  344. self.expect_keyword_("TO")
  345. while self.next_token_ != "END_ATTACH":
  346. cov = self.parse_coverage_()
  347. self.expect_keyword_("AT")
  348. self.expect_keyword_("ANCHOR")
  349. anchor_name = self.expect_string_()
  350. coverage_to.append((cov, anchor_name))
  351. self.expect_keyword_("END_ATTACH")
  352. position = ast.PositionAttachDefinition(
  353. coverage, coverage_to, location=location
  354. )
  355. return position
  356. def parse_attach_cursive_(self):
  357. assert self.is_cur_keyword_("ATTACH_CURSIVE")
  358. location = self.cur_token_location_
  359. coverages_exit = []
  360. coverages_enter = []
  361. while self.next_token_ != "ENTER":
  362. self.expect_keyword_("EXIT")
  363. coverages_exit.append(self.parse_coverage_())
  364. while self.next_token_ != "END_ATTACH":
  365. self.expect_keyword_("ENTER")
  366. coverages_enter.append(self.parse_coverage_())
  367. self.expect_keyword_("END_ATTACH")
  368. position = ast.PositionAttachCursiveDefinition(
  369. coverages_exit, coverages_enter, location=location
  370. )
  371. return position
  372. def parse_adjust_pair_(self):
  373. assert self.is_cur_keyword_("ADJUST_PAIR")
  374. location = self.cur_token_location_
  375. coverages_1 = []
  376. coverages_2 = []
  377. adjust_pair = {}
  378. while self.next_token_ == "FIRST":
  379. self.advance_lexer_()
  380. coverage_1 = self.parse_coverage_()
  381. coverages_1.append(coverage_1)
  382. while self.next_token_ == "SECOND":
  383. self.advance_lexer_()
  384. coverage_2 = self.parse_coverage_()
  385. coverages_2.append(coverage_2)
  386. while self.next_token_ != "END_ADJUST":
  387. id_1 = self.expect_number_()
  388. id_2 = self.expect_number_()
  389. self.expect_keyword_("BY")
  390. pos_1 = self.parse_pos_()
  391. pos_2 = self.parse_pos_()
  392. adjust_pair[(id_1, id_2)] = (pos_1, pos_2)
  393. self.expect_keyword_("END_ADJUST")
  394. position = ast.PositionAdjustPairDefinition(
  395. coverages_1, coverages_2, adjust_pair, location=location
  396. )
  397. return position
  398. def parse_adjust_single_(self):
  399. assert self.is_cur_keyword_("ADJUST_SINGLE")
  400. location = self.cur_token_location_
  401. adjust_single = []
  402. while self.next_token_ != "END_ADJUST":
  403. coverages = self.parse_coverage_()
  404. self.expect_keyword_("BY")
  405. pos = self.parse_pos_()
  406. adjust_single.append((coverages, pos))
  407. self.expect_keyword_("END_ADJUST")
  408. position = ast.PositionAdjustSingleDefinition(adjust_single, location=location)
  409. return position
  410. def parse_def_anchor_(self):
  411. assert self.is_cur_keyword_("DEF_ANCHOR")
  412. location = self.cur_token_location_
  413. name = self.expect_string_()
  414. self.expect_keyword_("ON")
  415. gid = self.expect_number_()
  416. self.expect_keyword_("GLYPH")
  417. glyph_name = self.expect_name_()
  418. self.expect_keyword_("COMPONENT")
  419. component = self.expect_number_()
  420. # check for duplicate anchor names on this glyph
  421. if glyph_name in self.anchors_:
  422. anchor = self.anchors_[glyph_name].resolve(name)
  423. if anchor is not None and anchor.component == component:
  424. raise VoltLibError(
  425. 'Anchor "%s" already defined, '
  426. "anchor names are case insensitive" % name,
  427. location,
  428. )
  429. if self.next_token_ == "LOCKED":
  430. locked = True
  431. self.advance_lexer_()
  432. else:
  433. locked = False
  434. self.expect_keyword_("AT")
  435. pos = self.parse_pos_()
  436. self.expect_keyword_("END_ANCHOR")
  437. anchor = ast.AnchorDefinition(
  438. name, gid, glyph_name, component, locked, pos, location=location
  439. )
  440. if glyph_name not in self.anchors_:
  441. self.anchors_[glyph_name] = SymbolTable()
  442. self.anchors_[glyph_name].define(name, anchor)
  443. return anchor
  444. def parse_adjust_by_(self):
  445. self.advance_lexer_()
  446. assert self.is_cur_keyword_("ADJUST_BY")
  447. adjustment = self.expect_number_()
  448. self.expect_keyword_("AT")
  449. size = self.expect_number_()
  450. return adjustment, size
  451. def parse_pos_(self):
  452. # VOLT syntax doesn't seem to take device Y advance
  453. self.advance_lexer_()
  454. location = self.cur_token_location_
  455. assert self.is_cur_keyword_("POS"), location
  456. adv = None
  457. dx = None
  458. dy = None
  459. adv_adjust_by = {}
  460. dx_adjust_by = {}
  461. dy_adjust_by = {}
  462. if self.next_token_ == "ADV":
  463. self.advance_lexer_()
  464. adv = self.expect_number_()
  465. while self.next_token_ == "ADJUST_BY":
  466. adjustment, size = self.parse_adjust_by_()
  467. adv_adjust_by[size] = adjustment
  468. if self.next_token_ == "DX":
  469. self.advance_lexer_()
  470. dx = self.expect_number_()
  471. while self.next_token_ == "ADJUST_BY":
  472. adjustment, size = self.parse_adjust_by_()
  473. dx_adjust_by[size] = adjustment
  474. if self.next_token_ == "DY":
  475. self.advance_lexer_()
  476. dy = self.expect_number_()
  477. while self.next_token_ == "ADJUST_BY":
  478. adjustment, size = self.parse_adjust_by_()
  479. dy_adjust_by[size] = adjustment
  480. self.expect_keyword_("END_POS")
  481. return ast.Pos(adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by)
  482. def parse_unicode_values_(self):
  483. location = self.cur_token_location_
  484. try:
  485. unicode_values = self.expect_string_().split(",")
  486. unicode_values = [int(uni[2:], 16) for uni in unicode_values if uni != ""]
  487. except ValueError as err:
  488. raise VoltLibError(str(err), location)
  489. return unicode_values if unicode_values != [] else None
  490. def parse_enum_(self):
  491. self.expect_keyword_("ENUM")
  492. location = self.cur_token_location_
  493. enum = ast.Enum(self.parse_coverage_(), location=location)
  494. self.expect_keyword_("END_ENUM")
  495. return enum
  496. def parse_coverage_(self):
  497. coverage = []
  498. location = self.cur_token_location_
  499. while self.next_token_ in ("GLYPH", "GROUP", "RANGE", "ENUM"):
  500. if self.next_token_ == "ENUM":
  501. enum = self.parse_enum_()
  502. coverage.append(enum)
  503. elif self.next_token_ == "GLYPH":
  504. self.expect_keyword_("GLYPH")
  505. name = self.expect_string_()
  506. coverage.append(ast.GlyphName(name, location=location))
  507. elif self.next_token_ == "GROUP":
  508. self.expect_keyword_("GROUP")
  509. name = self.expect_string_()
  510. coverage.append(ast.GroupName(name, self, location=location))
  511. elif self.next_token_ == "RANGE":
  512. self.expect_keyword_("RANGE")
  513. start = self.expect_string_()
  514. self.expect_keyword_("TO")
  515. end = self.expect_string_()
  516. coverage.append(ast.Range(start, end, self, location=location))
  517. return tuple(coverage)
  518. def resolve_group(self, group_name):
  519. return self.groups_.resolve(group_name)
  520. def glyph_range(self, start, end):
  521. return self.glyphs_.range(start, end)
  522. def parse_ppem_(self):
  523. location = self.cur_token_location_
  524. ppem_name = self.cur_token_
  525. value = self.expect_number_()
  526. setting = ast.SettingDefinition(ppem_name, value, location=location)
  527. return setting
  528. def parse_noarg_option_(self):
  529. location = self.cur_token_location_
  530. name = self.cur_token_
  531. value = True
  532. setting = ast.SettingDefinition(name, value, location=location)
  533. return setting
  534. def parse_cmap_format(self):
  535. location = self.cur_token_location_
  536. name = self.cur_token_
  537. value = (self.expect_number_(), self.expect_number_(), self.expect_number_())
  538. setting = ast.SettingDefinition(name, value, location=location)
  539. return setting
  540. def is_cur_keyword_(self, k):
  541. return (self.cur_token_type_ is Lexer.NAME) and (self.cur_token_ == k)
  542. def expect_string_(self):
  543. self.advance_lexer_()
  544. if self.cur_token_type_ is not Lexer.STRING:
  545. raise VoltLibError("Expected a string", self.cur_token_location_)
  546. return self.cur_token_
  547. def expect_keyword_(self, keyword):
  548. self.advance_lexer_()
  549. if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
  550. return self.cur_token_
  551. raise VoltLibError('Expected "%s"' % keyword, self.cur_token_location_)
  552. def expect_name_(self):
  553. self.advance_lexer_()
  554. if self.cur_token_type_ is Lexer.NAME:
  555. return self.cur_token_
  556. raise VoltLibError("Expected a name", self.cur_token_location_)
  557. def expect_number_(self):
  558. self.advance_lexer_()
  559. if self.cur_token_type_ is not Lexer.NUMBER:
  560. raise VoltLibError("Expected a number", self.cur_token_location_)
  561. return self.cur_token_
  562. def advance_lexer_(self):
  563. self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
  564. self.next_token_type_,
  565. self.next_token_,
  566. self.next_token_location_,
  567. )
  568. try:
  569. if self.is_cur_keyword_("END"):
  570. raise StopIteration
  571. (
  572. self.next_token_type_,
  573. self.next_token_,
  574. self.next_token_location_,
  575. ) = self.lexer_.next()
  576. except StopIteration:
  577. self.next_token_type_, self.next_token_ = (None, None)
  578. class SymbolTable(object):
  579. def __init__(self):
  580. self.scopes_ = [{}]
  581. def enter_scope(self):
  582. self.scopes_.append({})
  583. def exit_scope(self):
  584. self.scopes_.pop()
  585. def define(self, name, item):
  586. self.scopes_[-1][name] = item
  587. def resolve(self, name, case_insensitive=True):
  588. for scope in reversed(self.scopes_):
  589. item = scope.get(name)
  590. if item:
  591. return item
  592. if case_insensitive:
  593. for key in scope:
  594. if key.lower() == name.lower():
  595. return scope[key]
  596. return None
  597. class OrderedSymbolTable(SymbolTable):
  598. def __init__(self):
  599. self.scopes_ = [{}]
  600. def enter_scope(self):
  601. self.scopes_.append({})
  602. def resolve(self, name, case_insensitive=False):
  603. SymbolTable.resolve(self, name, case_insensitive=case_insensitive)
  604. def range(self, start, end):
  605. for scope in reversed(self.scopes_):
  606. if start in scope and end in scope:
  607. start_idx = list(scope.keys()).index(start)
  608. end_idx = list(scope.keys()).index(end)
  609. return list(scope.keys())[start_idx : end_idx + 1]
  610. return None