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.

373 lines
13 KiB

  1. from .exceptions import GrammarError, ConfigurationError
  2. from .lexer import Token
  3. from .tree import Tree
  4. from .visitors import Transformer_InPlace
  5. from .visitors import _vargs_meta, _vargs_meta_inline
  6. ###{standalone
  7. from functools import partial, wraps
  8. from itertools import repeat, product
  9. class ExpandSingleChild:
  10. def __init__(self, node_builder):
  11. self.node_builder = node_builder
  12. def __call__(self, children):
  13. if len(children) == 1:
  14. return children[0]
  15. else:
  16. return self.node_builder(children)
  17. class PropagatePositions:
  18. def __init__(self, node_builder):
  19. self.node_builder = node_builder
  20. def __call__(self, children):
  21. res = self.node_builder(children)
  22. # local reference to Tree.meta reduces number of presence checks
  23. if isinstance(res, Tree):
  24. res_meta = res.meta
  25. src_meta = self._pp_get_meta(children)
  26. if src_meta is not None:
  27. res_meta.line = src_meta.line
  28. res_meta.column = src_meta.column
  29. res_meta.start_pos = src_meta.start_pos
  30. res_meta.empty = False
  31. src_meta = self._pp_get_meta(reversed(children))
  32. if src_meta is not None:
  33. res_meta.end_line = src_meta.end_line
  34. res_meta.end_column = src_meta.end_column
  35. res_meta.end_pos = src_meta.end_pos
  36. res_meta.empty = False
  37. return res
  38. def _pp_get_meta(self, children):
  39. for c in children:
  40. if isinstance(c, Tree):
  41. if not c.meta.empty:
  42. return c.meta
  43. elif isinstance(c, Token):
  44. return c
  45. class PropagatePositions_IgnoreWs(PropagatePositions):
  46. def _pp_get_meta(self, children):
  47. for c in children:
  48. if isinstance(c, Tree):
  49. if not c.meta.empty:
  50. return c.meta
  51. elif isinstance(c, Token):
  52. if c and not c.isspace(): # Disregard whitespace-only tokens
  53. return c
  54. def make_propagate_positions(option):
  55. if option == "ignore_ws":
  56. return PropagatePositions_IgnoreWs
  57. elif option is True:
  58. return PropagatePositions
  59. elif option is False:
  60. return None
  61. raise ConfigurationError('Invalid option for propagate_positions: %r' % option)
  62. class ChildFilter:
  63. def __init__(self, to_include, append_none, node_builder):
  64. self.node_builder = node_builder
  65. self.to_include = to_include
  66. self.append_none = append_none
  67. def __call__(self, children):
  68. filtered = []
  69. for i, to_expand, add_none in self.to_include:
  70. if add_none:
  71. filtered += [None] * add_none
  72. if to_expand:
  73. filtered += children[i].children
  74. else:
  75. filtered.append(children[i])
  76. if self.append_none:
  77. filtered += [None] * self.append_none
  78. return self.node_builder(filtered)
  79. class ChildFilterLALR(ChildFilter):
  80. """Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"""
  81. def __call__(self, children):
  82. filtered = []
  83. for i, to_expand, add_none in self.to_include:
  84. if add_none:
  85. filtered += [None] * add_none
  86. if to_expand:
  87. if filtered:
  88. filtered += children[i].children
  89. else: # Optimize for left-recursion
  90. filtered = children[i].children
  91. else:
  92. filtered.append(children[i])
  93. if self.append_none:
  94. filtered += [None] * self.append_none
  95. return self.node_builder(filtered)
  96. class ChildFilterLALR_NoPlaceholders(ChildFilter):
  97. "Optimized childfilter for LALR (assumes no duplication in parse tree, so it's safe to change it)"
  98. def __init__(self, to_include, node_builder):
  99. self.node_builder = node_builder
  100. self.to_include = to_include
  101. def __call__(self, children):
  102. filtered = []
  103. for i, to_expand in self.to_include:
  104. if to_expand:
  105. if filtered:
  106. filtered += children[i].children
  107. else: # Optimize for left-recursion
  108. filtered = children[i].children
  109. else:
  110. filtered.append(children[i])
  111. return self.node_builder(filtered)
  112. def _should_expand(sym):
  113. return not sym.is_term and sym.name.startswith('_')
  114. def maybe_create_child_filter(expansion, keep_all_tokens, ambiguous, _empty_indices):
  115. # Prepare empty_indices as: How many Nones to insert at each index?
  116. if _empty_indices:
  117. assert _empty_indices.count(False) == len(expansion)
  118. s = ''.join(str(int(b)) for b in _empty_indices)
  119. empty_indices = [len(ones) for ones in s.split('0')]
  120. assert len(empty_indices) == len(expansion)+1, (empty_indices, len(expansion))
  121. else:
  122. empty_indices = [0] * (len(expansion)+1)
  123. to_include = []
  124. nones_to_add = 0
  125. for i, sym in enumerate(expansion):
  126. nones_to_add += empty_indices[i]
  127. if keep_all_tokens or not (sym.is_term and sym.filter_out):
  128. to_include.append((i, _should_expand(sym), nones_to_add))
  129. nones_to_add = 0
  130. nones_to_add += empty_indices[len(expansion)]
  131. if _empty_indices or len(to_include) < len(expansion) or any(to_expand for i, to_expand,_ in to_include):
  132. if _empty_indices or ambiguous:
  133. return partial(ChildFilter if ambiguous else ChildFilterLALR, to_include, nones_to_add)
  134. else:
  135. # LALR without placeholders
  136. return partial(ChildFilterLALR_NoPlaceholders, [(i, x) for i,x,_ in to_include])
  137. class AmbiguousExpander:
  138. """Deal with the case where we're expanding children ('_rule') into a parent but the children
  139. are ambiguous. i.e. (parent->_ambig->_expand_this_rule). In this case, make the parent itself
  140. ambiguous with as many copies as their are ambiguous children, and then copy the ambiguous children
  141. into the right parents in the right places, essentially shifting the ambiguity up the tree."""
  142. def __init__(self, to_expand, tree_class, node_builder):
  143. self.node_builder = node_builder
  144. self.tree_class = tree_class
  145. self.to_expand = to_expand
  146. def __call__(self, children):
  147. def _is_ambig_tree(t):
  148. return hasattr(t, 'data') and t.data == '_ambig'
  149. # -- When we're repeatedly expanding ambiguities we can end up with nested ambiguities.
  150. # All children of an _ambig node should be a derivation of that ambig node, hence
  151. # it is safe to assume that if we see an _ambig node nested within an ambig node
  152. # it is safe to simply expand it into the parent _ambig node as an alternative derivation.
  153. ambiguous = []
  154. for i, child in enumerate(children):
  155. if _is_ambig_tree(child):
  156. if i in self.to_expand:
  157. ambiguous.append(i)
  158. to_expand = [j for j, grandchild in enumerate(child.children) if _is_ambig_tree(grandchild)]
  159. child.expand_kids_by_index(*to_expand)
  160. if not ambiguous:
  161. return self.node_builder(children)
  162. expand = [iter(child.children) if i in ambiguous else repeat(child) for i, child in enumerate(children)]
  163. return self.tree_class('_ambig', [self.node_builder(list(f[0])) for f in product(zip(*expand))])
  164. def maybe_create_ambiguous_expander(tree_class, expansion, keep_all_tokens):
  165. to_expand = [i for i, sym in enumerate(expansion)
  166. if keep_all_tokens or ((not (sym.is_term and sym.filter_out)) and _should_expand(sym))]
  167. if to_expand:
  168. return partial(AmbiguousExpander, to_expand, tree_class)
  169. class AmbiguousIntermediateExpander:
  170. """
  171. Propagate ambiguous intermediate nodes and their derivations up to the
  172. current rule.
  173. In general, converts
  174. rule
  175. _iambig
  176. _inter
  177. someChildren1
  178. ...
  179. _inter
  180. someChildren2
  181. ...
  182. someChildren3
  183. ...
  184. to
  185. _ambig
  186. rule
  187. someChildren1
  188. ...
  189. someChildren3
  190. ...
  191. rule
  192. someChildren2
  193. ...
  194. someChildren3
  195. ...
  196. rule
  197. childrenFromNestedIambigs
  198. ...
  199. someChildren3
  200. ...
  201. ...
  202. propagating up any nested '_iambig' nodes along the way.
  203. """
  204. def __init__(self, tree_class, node_builder):
  205. self.node_builder = node_builder
  206. self.tree_class = tree_class
  207. def __call__(self, children):
  208. def _is_iambig_tree(child):
  209. return hasattr(child, 'data') and child.data == '_iambig'
  210. def _collapse_iambig(children):
  211. """
  212. Recursively flatten the derivations of the parent of an '_iambig'
  213. node. Returns a list of '_inter' nodes guaranteed not
  214. to contain any nested '_iambig' nodes, or None if children does
  215. not contain an '_iambig' node.
  216. """
  217. # Due to the structure of the SPPF,
  218. # an '_iambig' node can only appear as the first child
  219. if children and _is_iambig_tree(children[0]):
  220. iambig_node = children[0]
  221. result = []
  222. for grandchild in iambig_node.children:
  223. collapsed = _collapse_iambig(grandchild.children)
  224. if collapsed:
  225. for child in collapsed:
  226. child.children += children[1:]
  227. result += collapsed
  228. else:
  229. new_tree = self.tree_class('_inter', grandchild.children + children[1:])
  230. result.append(new_tree)
  231. return result
  232. collapsed = _collapse_iambig(children)
  233. if collapsed:
  234. processed_nodes = [self.node_builder(c.children) for c in collapsed]
  235. return self.tree_class('_ambig', processed_nodes)
  236. return self.node_builder(children)
  237. def inplace_transformer(func):
  238. @wraps(func)
  239. def f(children):
  240. # function name in a Transformer is a rule name.
  241. tree = Tree(func.__name__, children)
  242. return func(tree)
  243. return f
  244. def apply_visit_wrapper(func, name, wrapper):
  245. if wrapper is _vargs_meta or wrapper is _vargs_meta_inline:
  246. raise NotImplementedError("Meta args not supported for internal transformer")
  247. @wraps(func)
  248. def f(children):
  249. return wrapper(func, name, children, None)
  250. return f
  251. class ParseTreeBuilder:
  252. def __init__(self, rules, tree_class, propagate_positions=False, ambiguous=False, maybe_placeholders=False):
  253. self.tree_class = tree_class
  254. self.propagate_positions = propagate_positions
  255. self.ambiguous = ambiguous
  256. self.maybe_placeholders = maybe_placeholders
  257. self.rule_builders = list(self._init_builders(rules))
  258. def _init_builders(self, rules):
  259. propagate_positions = make_propagate_positions(self.propagate_positions)
  260. for rule in rules:
  261. options = rule.options
  262. keep_all_tokens = options.keep_all_tokens
  263. expand_single_child = options.expand1
  264. wrapper_chain = list(filter(None, [
  265. (expand_single_child and not rule.alias) and ExpandSingleChild,
  266. maybe_create_child_filter(rule.expansion, keep_all_tokens, self.ambiguous, options.empty_indices if self.maybe_placeholders else None),
  267. propagate_positions,
  268. self.ambiguous and maybe_create_ambiguous_expander(self.tree_class, rule.expansion, keep_all_tokens),
  269. self.ambiguous and partial(AmbiguousIntermediateExpander, self.tree_class)
  270. ]))
  271. yield rule, wrapper_chain
  272. def create_callback(self, transformer=None):
  273. callbacks = {}
  274. for rule, wrapper_chain in self.rule_builders:
  275. user_callback_name = rule.alias or rule.options.template_source or rule.origin.name
  276. try:
  277. f = getattr(transformer, user_callback_name)
  278. wrapper = getattr(f, 'visit_wrapper', None)
  279. if wrapper is not None:
  280. f = apply_visit_wrapper(f, user_callback_name, wrapper)
  281. elif isinstance(transformer, Transformer_InPlace):
  282. f = inplace_transformer(f)
  283. except AttributeError:
  284. f = partial(self.tree_class, user_callback_name)
  285. for w in wrapper_chain:
  286. f = w(f)
  287. if rule in callbacks:
  288. raise GrammarError("Rule '%s' already exists" % (rule,))
  289. callbacks[rule] = f
  290. return callbacks
  291. ###}