parser.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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:
  305. raise VoltLibError("Invalid substitution type", location)
  306. mapping = dict(zip(tuple(src), tuple(dest)))
  307. if max_src == 1 and max_dest == 1:
  308. # Alternate substitutions are represented by adding multiple
  309. # substitutions for the same glyph, so we detect that here
  310. glyphs = [x.glyphSet() for cov in src for x in cov] # flatten src
  311. if len(set(glyphs)) != len(glyphs): # src has duplicates
  312. sub = ast.SubstitutionAlternateDefinition(mapping, location=location)
  313. else:
  314. if reversal:
  315. # Reversal is valid only for single glyph substitutions
  316. # and VOLT ignores it otherwise.
  317. sub = ast.SubstitutionReverseChainingSingleDefinition(
  318. mapping, location=location
  319. )
  320. else:
  321. sub = ast.SubstitutionSingleDefinition(mapping, location=location)
  322. elif max_src == 1 and max_dest > 1:
  323. sub = ast.SubstitutionMultipleDefinition(mapping, location=location)
  324. elif max_src > 1 and max_dest == 1:
  325. sub = ast.SubstitutionLigatureDefinition(mapping, location=location)
  326. return sub
  327. def parse_position_(self):
  328. assert self.is_cur_keyword_("AS_POSITION")
  329. location = self.cur_token_location_
  330. pos_type = self.expect_name_()
  331. if pos_type not in ("ATTACH", "ATTACH_CURSIVE", "ADJUST_PAIR", "ADJUST_SINGLE"):
  332. raise VoltLibError(
  333. "Expected ATTACH, ATTACH_CURSIVE, ADJUST_PAIR, ADJUST_SINGLE", location
  334. )
  335. if pos_type == "ATTACH":
  336. position = self.parse_attach_()
  337. elif pos_type == "ATTACH_CURSIVE":
  338. position = self.parse_attach_cursive_()
  339. elif pos_type == "ADJUST_PAIR":
  340. position = self.parse_adjust_pair_()
  341. elif pos_type == "ADJUST_SINGLE":
  342. position = self.parse_adjust_single_()
  343. self.expect_keyword_("END_POSITION")
  344. return position
  345. def parse_attach_(self):
  346. assert self.is_cur_keyword_("ATTACH")
  347. location = self.cur_token_location_
  348. coverage = self.parse_coverage_()
  349. coverage_to = []
  350. self.expect_keyword_("TO")
  351. while self.next_token_ != "END_ATTACH":
  352. cov = self.parse_coverage_()
  353. self.expect_keyword_("AT")
  354. self.expect_keyword_("ANCHOR")
  355. anchor_name = self.expect_string_()
  356. coverage_to.append((cov, anchor_name))
  357. self.expect_keyword_("END_ATTACH")
  358. position = ast.PositionAttachDefinition(
  359. coverage, coverage_to, location=location
  360. )
  361. return position
  362. def parse_attach_cursive_(self):
  363. assert self.is_cur_keyword_("ATTACH_CURSIVE")
  364. location = self.cur_token_location_
  365. coverages_exit = []
  366. coverages_enter = []
  367. while self.next_token_ != "ENTER":
  368. self.expect_keyword_("EXIT")
  369. coverages_exit.append(self.parse_coverage_())
  370. while self.next_token_ != "END_ATTACH":
  371. self.expect_keyword_("ENTER")
  372. coverages_enter.append(self.parse_coverage_())
  373. self.expect_keyword_("END_ATTACH")
  374. position = ast.PositionAttachCursiveDefinition(
  375. coverages_exit, coverages_enter, location=location
  376. )
  377. return position
  378. def parse_adjust_pair_(self):
  379. assert self.is_cur_keyword_("ADJUST_PAIR")
  380. location = self.cur_token_location_
  381. coverages_1 = []
  382. coverages_2 = []
  383. adjust_pair = {}
  384. while self.next_token_ == "FIRST":
  385. self.advance_lexer_()
  386. coverage_1 = self.parse_coverage_()
  387. coverages_1.append(coverage_1)
  388. while self.next_token_ == "SECOND":
  389. self.advance_lexer_()
  390. coverage_2 = self.parse_coverage_()
  391. coverages_2.append(coverage_2)
  392. while self.next_token_ != "END_ADJUST":
  393. id_1 = self.expect_number_()
  394. id_2 = self.expect_number_()
  395. self.expect_keyword_("BY")
  396. pos_1 = self.parse_pos_()
  397. pos_2 = self.parse_pos_()
  398. adjust_pair[(id_1, id_2)] = (pos_1, pos_2)
  399. self.expect_keyword_("END_ADJUST")
  400. position = ast.PositionAdjustPairDefinition(
  401. coverages_1, coverages_2, adjust_pair, location=location
  402. )
  403. return position
  404. def parse_adjust_single_(self):
  405. assert self.is_cur_keyword_("ADJUST_SINGLE")
  406. location = self.cur_token_location_
  407. adjust_single = []
  408. while self.next_token_ != "END_ADJUST":
  409. coverages = self.parse_coverage_()
  410. self.expect_keyword_("BY")
  411. pos = self.parse_pos_()
  412. adjust_single.append((coverages, pos))
  413. self.expect_keyword_("END_ADJUST")
  414. position = ast.PositionAdjustSingleDefinition(adjust_single, location=location)
  415. return position
  416. def parse_def_anchor_(self):
  417. assert self.is_cur_keyword_("DEF_ANCHOR")
  418. location = self.cur_token_location_
  419. name = self.expect_string_()
  420. self.expect_keyword_("ON")
  421. gid = self.expect_number_()
  422. self.expect_keyword_("GLYPH")
  423. glyph_name = self.expect_name_()
  424. self.expect_keyword_("COMPONENT")
  425. component = self.expect_number_()
  426. # check for duplicate anchor names on this glyph
  427. if glyph_name in self.anchors_:
  428. anchor = self.anchors_[glyph_name].resolve(name)
  429. if anchor is not None and anchor.component == component:
  430. raise VoltLibError(
  431. 'Anchor "%s" already defined, '
  432. "anchor names are case insensitive" % name,
  433. location,
  434. )
  435. if self.next_token_ == "LOCKED":
  436. locked = True
  437. self.advance_lexer_()
  438. else:
  439. locked = False
  440. self.expect_keyword_("AT")
  441. pos = self.parse_pos_()
  442. self.expect_keyword_("END_ANCHOR")
  443. anchor = ast.AnchorDefinition(
  444. name, gid, glyph_name, component, locked, pos, location=location
  445. )
  446. if glyph_name not in self.anchors_:
  447. self.anchors_[glyph_name] = SymbolTable()
  448. self.anchors_[glyph_name].define(name, anchor)
  449. return anchor
  450. def parse_adjust_by_(self):
  451. self.advance_lexer_()
  452. assert self.is_cur_keyword_("ADJUST_BY")
  453. adjustment = self.expect_number_()
  454. self.expect_keyword_("AT")
  455. size = self.expect_number_()
  456. return adjustment, size
  457. def parse_pos_(self):
  458. # VOLT syntax doesn't seem to take device Y advance
  459. self.advance_lexer_()
  460. location = self.cur_token_location_
  461. assert self.is_cur_keyword_("POS"), location
  462. adv = None
  463. dx = None
  464. dy = None
  465. adv_adjust_by = {}
  466. dx_adjust_by = {}
  467. dy_adjust_by = {}
  468. if self.next_token_ == "ADV":
  469. self.advance_lexer_()
  470. adv = self.expect_number_()
  471. while self.next_token_ == "ADJUST_BY":
  472. adjustment, size = self.parse_adjust_by_()
  473. adv_adjust_by[size] = adjustment
  474. if self.next_token_ == "DX":
  475. self.advance_lexer_()
  476. dx = self.expect_number_()
  477. while self.next_token_ == "ADJUST_BY":
  478. adjustment, size = self.parse_adjust_by_()
  479. dx_adjust_by[size] = adjustment
  480. if self.next_token_ == "DY":
  481. self.advance_lexer_()
  482. dy = self.expect_number_()
  483. while self.next_token_ == "ADJUST_BY":
  484. adjustment, size = self.parse_adjust_by_()
  485. dy_adjust_by[size] = adjustment
  486. self.expect_keyword_("END_POS")
  487. return ast.Pos(adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by)
  488. def parse_unicode_values_(self):
  489. location = self.cur_token_location_
  490. try:
  491. unicode_values = self.expect_string_().split(",")
  492. unicode_values = [int(uni[2:], 16) for uni in unicode_values if uni != ""]
  493. except ValueError as err:
  494. raise VoltLibError(str(err), location)
  495. return unicode_values if unicode_values != [] else None
  496. def parse_enum_(self):
  497. self.expect_keyword_("ENUM")
  498. location = self.cur_token_location_
  499. enum = ast.Enum(self.parse_coverage_(), location=location)
  500. self.expect_keyword_("END_ENUM")
  501. return enum
  502. def parse_coverage_(self):
  503. coverage = []
  504. location = self.cur_token_location_
  505. while self.next_token_ in ("GLYPH", "GROUP", "RANGE", "ENUM"):
  506. if self.next_token_ == "ENUM":
  507. enum = self.parse_enum_()
  508. coverage.append(enum)
  509. elif self.next_token_ == "GLYPH":
  510. self.expect_keyword_("GLYPH")
  511. name = self.expect_string_()
  512. coverage.append(ast.GlyphName(name, location=location))
  513. elif self.next_token_ == "GROUP":
  514. self.expect_keyword_("GROUP")
  515. name = self.expect_string_()
  516. coverage.append(ast.GroupName(name, self, location=location))
  517. elif self.next_token_ == "RANGE":
  518. self.expect_keyword_("RANGE")
  519. start = self.expect_string_()
  520. self.expect_keyword_("TO")
  521. end = self.expect_string_()
  522. coverage.append(ast.Range(start, end, self, location=location))
  523. return tuple(coverage)
  524. def resolve_group(self, group_name):
  525. return self.groups_.resolve(group_name)
  526. def glyph_range(self, start, end):
  527. return self.glyphs_.range(start, end)
  528. def parse_ppem_(self):
  529. location = self.cur_token_location_
  530. ppem_name = self.cur_token_
  531. value = self.expect_number_()
  532. setting = ast.SettingDefinition(ppem_name, value, location=location)
  533. return setting
  534. def parse_noarg_option_(self):
  535. location = self.cur_token_location_
  536. name = self.cur_token_
  537. value = True
  538. setting = ast.SettingDefinition(name, value, location=location)
  539. return setting
  540. def parse_cmap_format(self):
  541. location = self.cur_token_location_
  542. name = self.cur_token_
  543. value = (self.expect_number_(), self.expect_number_(), self.expect_number_())
  544. setting = ast.SettingDefinition(name, value, location=location)
  545. return setting
  546. def is_cur_keyword_(self, k):
  547. return (self.cur_token_type_ is Lexer.NAME) and (self.cur_token_ == k)
  548. def expect_string_(self):
  549. self.advance_lexer_()
  550. if self.cur_token_type_ is not Lexer.STRING:
  551. raise VoltLibError("Expected a string", self.cur_token_location_)
  552. return self.cur_token_
  553. def expect_keyword_(self, keyword):
  554. self.advance_lexer_()
  555. if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
  556. return self.cur_token_
  557. raise VoltLibError('Expected "%s"' % keyword, self.cur_token_location_)
  558. def expect_name_(self):
  559. self.advance_lexer_()
  560. if self.cur_token_type_ is Lexer.NAME:
  561. return self.cur_token_
  562. raise VoltLibError("Expected a name", self.cur_token_location_)
  563. def expect_number_(self):
  564. self.advance_lexer_()
  565. if self.cur_token_type_ is not Lexer.NUMBER:
  566. raise VoltLibError("Expected a number", self.cur_token_location_)
  567. return self.cur_token_
  568. def advance_lexer_(self):
  569. self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
  570. self.next_token_type_,
  571. self.next_token_,
  572. self.next_token_location_,
  573. )
  574. try:
  575. if self.is_cur_keyword_("END"):
  576. raise StopIteration
  577. (
  578. self.next_token_type_,
  579. self.next_token_,
  580. self.next_token_location_,
  581. ) = self.lexer_.next()
  582. except StopIteration:
  583. self.next_token_type_, self.next_token_ = (None, None)
  584. class SymbolTable(object):
  585. def __init__(self):
  586. self.scopes_ = [{}]
  587. def enter_scope(self):
  588. self.scopes_.append({})
  589. def exit_scope(self):
  590. self.scopes_.pop()
  591. def define(self, name, item):
  592. self.scopes_[-1][name] = item
  593. def resolve(self, name, case_insensitive=True):
  594. for scope in reversed(self.scopes_):
  595. item = scope.get(name)
  596. if item:
  597. return item
  598. if case_insensitive:
  599. for key in scope:
  600. if key.lower() == name.lower():
  601. return scope[key]
  602. return None
  603. class OrderedSymbolTable(SymbolTable):
  604. def __init__(self):
  605. self.scopes_ = [{}]
  606. def enter_scope(self):
  607. self.scopes_.append({})
  608. def resolve(self, name, case_insensitive=False):
  609. SymbolTable.resolve(self, name, case_insensitive=case_insensitive)
  610. def range(self, start, end):
  611. for scope in reversed(self.scopes_):
  612. if start in scope and end in scope:
  613. start_idx = list(scope.keys()).index(start)
  614. end_idx = list(scope.keys()).index(end)
  615. return list(scope.keys())[start_idx : end_idx + 1]
  616. return None