Commit d93e749e authored by Eckhart Arnold's avatar Eckhart Arnold
Browse files

- syntaxtree: mock_syntax_tree renamed to parse_sxpr; ad-hoc xml-parser parse_xml added

parent ae91c19e
...@@ -54,7 +54,7 @@ def last_char(text, begin: int, end: int) -> int: ...@@ -54,7 +54,7 @@ def last_char(text, begin: int, end: int) -> int:
"""Returns the index of the first non-whitespace character in string """Returns the index of the first non-whitespace character in string
`text` within the bounds [begin, end]. `text` within the bounds [begin, end].
""" """
while end > begin and text[end] in ' \n\t': while end > begin and text[end-1] in ' \n\t':
end -= 1 end -= 1
return end return end
...@@ -94,14 +94,17 @@ class StringView(collections.abc.Sized): ...@@ -94,14 +94,17 @@ class StringView(collections.abc.Sized):
copying, i.e. slices are just a view on a section of the sliced copying, i.e. slices are just a view on a section of the sliced
string. string.
""" """
__slots__ = ['text', 'begin', 'end', 'len', 'fullstring_flag'] __slots__ = ['text', 'begin', 'end', 'len', 'fullstring']
def __init__(self, text: str, begin: Optional[int] = 0, end: Optional[int] = None) -> None: def __init__(self, text: str, begin: Optional[int] = 0, end: Optional[int] = None) -> None:
assert isinstance(text, str) # assert isinstance(text, str)
self.text = text # type: str self.text = text # type: str
self.begin, self.end = real_indices(begin, end, len(text)) self.begin, self.end = real_indices(begin, end, len(text))
self.len = max(self.end - self.begin, 0) # type: int self.len = max(self.end - self.begin, 0) # type: int
self.fullstring_flag = (self.begin == 0 and self.len == len(self.text)) # type: bool if (self.begin == 0 and self.len == len(self.text)):
self.fullstring = self.text # type: str
else:
self.fullstring = ''
def __bool__(self): def __bool__(self):
return self.end > self.begin # and bool(self.text) return self.end > self.begin # and bool(self.text)
...@@ -111,16 +114,12 @@ class StringView(collections.abc.Sized): ...@@ -111,16 +114,12 @@ class StringView(collections.abc.Sized):
def __str__(self): def __str__(self):
# PERFORMANCE WARNING: This creates a copy of the string-slice # PERFORMANCE WARNING: This creates a copy of the string-slice
if self.fullstring_flag: # optimization: avoid slicing/copying if self.fullstring: # optimization: avoid slicing/copying
return self.text return self.fullstring
# since the slice is being copyied now, anyway, the copy might # since the slice is being copyied now, anyway, the copy might
# as well be stored in the string view # as well be stored in the string view
self.text = self.text[self.begin:self.end] self.fullstring = self.text[self.begin:self.end]
self.begin = 0 return self.fullstring
self.len = len(self.text)
self.end = self.len
self.fullstring_flag = True
return self.text
def __eq__(self, other): def __eq__(self, other):
# PERFORMANCE WARNING: This creates copies of the strings # PERFORMANCE WARNING: This creates copies of the strings
...@@ -146,16 +145,19 @@ class StringView(collections.abc.Sized): ...@@ -146,16 +145,19 @@ class StringView(collections.abc.Sized):
# assert isinstance(index, slice), "As of now, StringView only allows slicing." # assert isinstance(index, slice), "As of now, StringView only allows slicing."
# assert index.step is None or index.step == 1, \ # assert index.step is None or index.step == 1, \
# "Step sizes other than 1 are not yet supported by StringView" # "Step sizes other than 1 are not yet supported by StringView"
start, stop = real_indices(index.start, index.stop, self.len) try:
return StringView(self.text, self.begin + start, self.begin + stop) start, stop = real_indices(index.start, index.stop, self.len)
return StringView(self.text, self.begin + start, self.begin + stop)
except AttributeError:
return self.text[self.begin + index]
def count(self, sub: str, start=None, end=None) -> int: def count(self, sub: str, start=None, end=None) -> int:
"""Returns the number of non-overlapping occurrences of substring """Returns the number of non-overlapping occurrences of substring
`sub` in StringView S[start:end]. Optional arguments start and end `sub` in StringView S[start:end]. Optional arguments start and end
are interpreted as in slice notation. are interpreted as in slice notation.
""" """
if self.fullstring_flag: if self.fullstring:
return self.text.count(sub, start, end) return self.fullstring.count(sub, start, end)
elif start is None and end is None: elif start is None and end is None:
return self.text.count(sub, self.begin, self.end) return self.text.count(sub, self.begin, self.end)
else: else:
...@@ -168,8 +170,8 @@ class StringView(collections.abc.Sized): ...@@ -168,8 +170,8 @@ class StringView(collections.abc.Sized):
arguments `start` and `end` are interpreted as in slice notation. arguments `start` and `end` are interpreted as in slice notation.
Returns -1 on failure. Returns -1 on failure.
""" """
if self.fullstring_flag: if self.fullstring:
return self.text.find(sub, start, end) return self.fullstring.find(sub, start, end)
elif start is None and end is None: elif start is None and end is None:
return self.text.find(sub, self.begin, self.end) - self.begin return self.text.find(sub, self.begin, self.end) - self.begin
else: else:
...@@ -182,8 +184,8 @@ class StringView(collections.abc.Sized): ...@@ -182,8 +184,8 @@ class StringView(collections.abc.Sized):
arguments `start` and `end` are interpreted as in slice notation. arguments `start` and `end` are interpreted as in slice notation.
Returns -1 on failure. Returns -1 on failure.
""" """
if self.fullstring_flag: if self.fullstring:
return self.text.rfind(sub, start, end) return self.fullstring.rfind(sub, start, end)
if start is None and end is None: if start is None and end is None:
return self.text.rfind(sub, self.begin, self.end) - self.begin return self.text.rfind(sub, self.begin, self.end) - self.begin
else: else:
...@@ -203,9 +205,11 @@ class StringView(collections.abc.Sized): ...@@ -203,9 +205,11 @@ class StringView(collections.abc.Sized):
end = self.end if end is None else self.begin + end end = self.end if end is None else self.begin + end
return self.text.startswith(prefix, start, end) return self.text.startswith(prefix, start, end)
def match(self, regex): def match(self, regex, flags=0):
"""Executes `regex.match` on the StringView object and returns the """Executes `regex.match` on the StringView object and returns the
result, which is either a match-object or None. result, which is either a match-object or None.
WARNING: match.end(), match.span() etc. are mapped to the underlying text,
not the StringView-object!!!
""" """
return regex.match(self.text, pos=self.begin, endpos=self.end) return regex.match(self.text, pos=self.begin, endpos=self.end)
...@@ -232,19 +236,36 @@ class StringView(collections.abc.Sized): ...@@ -232,19 +236,36 @@ class StringView(collections.abc.Sized):
def search(self, regex): def search(self, regex):
"""Executes regex.search on the StringView object and returns the """Executes regex.search on the StringView object and returns the
result, which is either a match-object or None. result, which is either a match-object or None.
WARNING: match.end(), match.span() etc. are mapped to the underlying text,
not the StringView-object!!!
""" """
return regex.search(self.text, pos=self.begin, endpos=self.end) return regex.search(self.text, pos=self.begin, endpos=self.end)
def finditer(self, regex):
"""Executes regex.finditer on the StringView object and returns the
iterator of match objects.
WARNING: match.end(), match.span() etc. are mapped to the underlying text,
not the StringView-object!!!
"""
return regex.finditer(self.text, pos=self.begin, endpos=self.end)
def strip(self): def strip(self):
"""Returns a copy of the StringView `self` with leading and trailing """Returns a copy of the StringView `self` with leading and trailing
whitespace removed. whitespace removed.
""" """
if self.fullstring_flag: begin = first_char(self.text, self.begin, self.end) - self.begin
return self.text.strip() end = last_char(self.text, self.begin, self.end) - self.begin
else: return self if begin == 0 and end == self.len else self[begin:end]
begin = first_char(self.text, self.begin, self.end)
end = last_char(self.text, self.begin, self.end) def lstrip(self):
return self.text[begin:end] """Returns a copy of `self` with leading whitespace removed."""
begin = first_char(self.text, self.begin, self.end) - self.begin
return self if begin == 0 else self[begin:]
def rstrip(self):
"""Returns a copy of `self` with trailing whitespace removed."""
end = last_char(self.text, self.begin, self.end) - self.begin
return self if end == self.len else self[:end]
def split(self, sep=None): def split(self, sep=None):
"""Returns a list of the words in `self`, using `sep` as the """Returns a list of the words in `self`, using `sep` as the
...@@ -252,8 +273,8 @@ class StringView(collections.abc.Sized): ...@@ -252,8 +273,8 @@ class StringView(collections.abc.Sized):
whitespace string is a separator and empty strings are whitespace string is a separator and empty strings are
removed from the result. removed from the result.
""" """
if self.fullstring_flag: if self.fullstring:
return self.text.split(sep) return self.fullstring.split(sep)
else: else:
pieces = [] pieces = []
l = len(sep) l = len(sep)
......
...@@ -42,7 +42,7 @@ __all__ = ('ParserBase', ...@@ -42,7 +42,7 @@ __all__ = ('ParserBase',
'ZOMBIE_PARSER', 'ZOMBIE_PARSER',
'ZOMBIE_NODE', 'ZOMBIE_NODE',
'Node', 'Node',
'mock_syntax_tree', 'parse_sxpr',
'flatten_sxpr') 'flatten_sxpr')
...@@ -318,7 +318,7 @@ class Node(collections.abc.Sized): ...@@ -318,7 +318,7 @@ class Node(collections.abc.Sized):
Returns the child node with the given index if ``index_or_tagname`` is Returns the child node with the given index if ``index_or_tagname`` is
an integer or the first child node with the given tag name. Examples:: an integer or the first child node with the given tag name. Examples::
>>> tree = mock_syntax_tree('(a (b "X") (X (c "d")) (e (X "F")))') >>> tree = parse_sxpr('(a (b "X") (X (c "d")) (e (X "F")))')
>>> flatten_sxpr(tree[0].as_sxpr()) >>> flatten_sxpr(tree[0].as_sxpr())
'(b "X")' '(b "X")'
>>> flatten_sxpr(tree["X"].as_sxpr()) >>> flatten_sxpr(tree["X"].as_sxpr())
...@@ -591,13 +591,13 @@ class Node(collections.abc.Sized): ...@@ -591,13 +591,13 @@ class Node(collections.abc.Sized):
txt = [left_bracket, node.tag_name] txt = [left_bracket, node.tag_name]
# s += " '(pos %i)" % node.pos # s += " '(pos %i)" % node.pos
if hasattr(node, '_xml_attr'): if hasattr(node, '_xml_attr'):
txt.extend(""" `(%s "%s")""" % (k, v) for k, v in node.attributes.items()) txt.extend(' `(%s "%s")' % (k, v) for k, v in node.attributes.items())
if src: if src:
txt.append(" `(pos %i %i %i)" % (node.pos, *line_col(src, node.pos))) txt.append(" `(pos %i %i %i)" % (node.pos, *line_col(src, node.pos)))
# if node.error_flag: # just for debugging error collecting # if node.error_flag: # just for debugging error collecting
# txt += " HAS ERRORS" # txt += " HAS ERRORS"
if showerrors and node.errors: if showerrors and node.errors:
txt.append(" `(err %s)" % ' '.join(str(err) for err in node.errors)) txt.append(" `(err `%s)" % ' '.join(str(err) for err in node.errors))
return "".join(txt) + '\n' return "".join(txt) + '\n'
def closing(node) -> str: def closing(node) -> str:
...@@ -680,7 +680,7 @@ class Node(collections.abc.Sized): ...@@ -680,7 +680,7 @@ class Node(collections.abc.Sized):
Examples:: Examples::
>>> tree = mock_syntax_tree('(a (b "X") (X (c "d")) (e (X "F")))') >>> tree = parse_sxpr('(a (b "X") (X (c "d")) (e (X "F")))')
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag("X", False)) >>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag("X", False))
['(X (c "d"))', '(X "F")'] ['(X (c "d"))', '(X "F")']
>>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag({"X", "b"}, False)) >>> list(flatten_sxpr(item.as_sxpr()) for item in tree.select_by_tag({"X", "b"}, False))
...@@ -730,17 +730,22 @@ class Node(collections.abc.Sized): ...@@ -730,17 +730,22 @@ class Node(collections.abc.Sized):
ZOMBIE_NODE = Node(ZOMBIE_PARSER, '') ZOMBIE_NODE = Node(ZOMBIE_PARSER, '')
def mock_syntax_tree(sxpr: str) -> Node: def parse_sxpr(sxpr: str) -> Node:
""" """
Generates a tree of nodes from an S-expression. The main purpose of this is Generates a tree of nodes from an S-expression.
to generate test data.
This can - among other things - be used for deserialization of trees that
have been serialized with `Node.as_sxpr()` or as a convenient way to
generate test data.
Example: Example:
>>> mock_syntax_tree("(a (b c))").as_sxpr() >>> parse_sxpr("(a (b c))").as_sxpr()
'(a\\n (b\\n "c"\\n )\\n)' '(a\\n (b\\n "c"\\n )\\n)'
""" """
sxpr = StringView(sxpr).strip()
mock_parsers = dict()
def next_block(s): def next_block(s: StringView):
"""Generator that yields all characters until the next closing bracket """Generator that yields all characters until the next closing bracket
that does not match an opening bracket matched earlier within the same that does not match an opening bracket matched earlier within the same
package.""" package."""
...@@ -765,63 +770,146 @@ def mock_syntax_tree(sxpr: str) -> Node: ...@@ -765,63 +770,146 @@ def mock_syntax_tree(sxpr: str) -> Node:
else 'Malformed S-expression. Closing bracket(s) ")" missing.' else 'Malformed S-expression. Closing bracket(s) ")" missing.'
raise AssertionError(errmsg) raise AssertionError(errmsg)
sxpr = StringView(sxpr).strip() def inner_parser(sxpr: StringView) -> Node:
if sxpr[0] != '(': if sxpr[0] != '(':
raise ValueError('"(" expected, not ' + sxpr[:10]) raise ValueError('"(" expected, not ' + sxpr[:10])
# assert sxpr[0] == '(', sxpr # assert sxpr[0] == '(', sxpr
sxpr = sxpr[1:].strip() sxpr = sxpr[1:].strip()
match = re.match(r'[\w:]+', sxpr) match = sxpr.match(re.compile(r'[\w:]+'))
if match is None: if match is None:
raise AssertionError('Malformed S-expression Node-tagname or identifier expected, ' raise AssertionError('Malformed S-expression Node-tagname or identifier expected, '
'not "%s"' % sxpr[:40].replace('\n', '')) 'not "%s"' % sxpr[:40].replace('\n', ''))
name, class_name = (sxpr[:match.end()].split(':') + [''])[:2] end = match.end() - sxpr.begin
sxpr = sxpr[match.end():].strip() tagname = sxpr[:end]
pos = 0 name, class_name = (tagname.split(':') + [''])[:2]
attributes = OrderedDict() sxpr = sxpr[end:].strip()
if sxpr[0] == '(': pos = 0
result = tuple(mock_syntax_tree(block) for block in next_block(sxpr)) attributes = OrderedDict()
for node in result: if sxpr[0] == '(':
node._pos = pos result = tuple(inner_parser(block) for block in next_block(sxpr))
pos += len(node) for node in result:
else: node._pos = pos
lines = [] pos += len(node)
while sxpr and sxpr[0] != ')': else:
# parse attributes lines = []
while sxpr[:2] == "`(": while sxpr and sxpr[0:1] != ')':
i = sxpr.find('"') # parse attributes
k = sxpr.find(')') while sxpr[:2] == "`(":
if sxpr[2:5] == "pos" and (i < 0 or k < i): i = sxpr.find('"')
pos = int(sxpr[5:k].strip().split(' ')[0]) k = sxpr.find(')')
elif sxpr[2:5] == "err": # read very special attribute pos
m = sxpr.find('(', 5) if sxpr[2:5] == "pos" and 0 < i < k:
while m >= 0 and m < k: pos = int(sxpr[5:k].strip().split(' ')[0])
m = sxpr.find('(', k) # ignore very special attribute err
k = max(k, sxpr.find(')', max(m, 0))) elif sxpr[2:5] == "err" and 0 <= sxpr.find('`', 5) < k:
m = sxpr.find('(', 5)
while m >= 0 and m < k:
m = sxpr.find('(', k)
k = max(k, sxpr.find(')', max(m, 0)))
# read attributes
else:
attr = sxpr[2:i].strip()
value = sxpr[i:k].strip()[1:-1]
attributes[attr] = value
sxpr = sxpr[k+1:].strip()
# parse content
for qtmark in ['"""', "'''", '"', "'"]:
match = sxpr.match(re.compile(qtmark + r'.*?' + qtmark, re.DOTALL))
if match:
end = match.end() - sxpr.begin
i = len(qtmark)
lines.append(str(sxpr[i:end - i]))
sxpr = sxpr[end:].strip()
break
else: else:
attr = sxpr[2:i].strip() match = sxpr.match(re.compile(r'(?:(?!\)).)*', re.DOTALL))
value = sxpr[i:k].strip()[1:-1] end = match.end() - sxpr.begin
attributes[attr] = value lines.append(str(sxpr[:end]))
sxpr = sxpr[k+1:].strip() sxpr = sxpr[end:]
# parse content result = "\n".join(lines)
for qtmark in ['"""', "'''", '"', "'"]: node = Node(mock_parsers.setdefault(tagname, MockParser(name, ':' + class_name)), result)
match = re.match(qtmark + r'.*?' + qtmark, sxpr, re.DOTALL) if attributes:
if match: node.attributes.update(attributes)
i = len(qtmark) node._pos = pos
lines.append(sxpr[i:match.end() - i]) return node
sxpr = sxpr[match.end():].strip()
break return inner_parser(sxpr)
else:
match = re.match(r'(?:(?!\)).)*', sxpr, re.DOTALL)
lines.append(sxpr[:match.end()]) def parse_xml(xml: str) -> Node:
sxpr = sxpr[match.end():] """
result = "\n".join(lines) Generates a tree of nodes from a (Pseudo-)XML-source.
node = Node(MockParser(name, ':' + class_name), result) """
if attributes: xml = StringView(xml)
node.attributes.update(attributes) PlainText = MockParser('', 'PlainText')
node._pos = pos mock_parsers = {':PlainText': PlainText}
return node
def parse_attributes(s: StringView) -> Tuple[StringView, OrderedDict]:
"""Parses a sqeuence of XML-Attributes. Returns the string-slice
beginning after the end of the attributes."""
attributes = OrderedDict()
restart = 0
for match in s.finditer(re.compile(r'\s*(?P<attr>\w+)\s*=\s*"(?P<value>.*)"\s*')):
d = match.groupdict()
attributes[d['attr']] = d['value']
restart = match.end() - s.begin
return (s[restart:], attributes)
def parse_opening_tag(s: StringView) -> Tuple[StringView, str, OrderedDict, bool]:
"""Parses an opening tag. Returns the string segment following the
the opening tag, the tag name, a dictionary of attributes and
a flag indicating whether the tag is actually a solitary tag as
indicated by a slash at the end, i.e. <br/>."""
match = s.match(re.compile(r'<\s*(?P<tagname>[\w:]+)\s*'))
assert match
tagname = match.groupdict()['tagname']
s, attributes = parse_attributes(s[match.end() - s.begin:])
i = s.find('>')
assert i >= 0
return s[i+1,], tagname, attributes, s[i-1] == "/"
def parse_closing_tag(s: StringView) -> Tuple[StringView, str]:
"""Parses a closing tag returns the string segment, just after
the closing tag."""
match = s.match(re.compile(r'</\s*(?P<tagname>[\w:]+)>'))
assert match
tagname = match.groupdict()['tagname']
return s[match.end() - s.begin:], tagname
def parse_leaf_content(s: StringView) -> Tuple[StringView, str]:
"""Parses a piece of the content of a tag, just until the next opening,
closing or solitary tag is reached."""
i = 0
while s[i] != "<" or s[max(0, i-1)] == "\\":
i = s.find("<", i)
return s[i:], s[:i]
def parse_full_content(s: StringView) -> Tuple[StringView, Node]:
"""Parses the full content of a tag, starting right at the beginning
of the opening tag and ending right after the closing tag.
"""
result = []
s, tagname, attributes, solitary = parse_opening_tag(s)
name, class_name = (tagname.split(":") + [''])[:2]
if not solitary:
while s and not s[:2] == "</":
s, leaf = parse_leaf_content(s)
if not s.match(re.compile("\s*$")):
result.append(Node(PlainText, leaf))
if s[:1] == "<" and s[:2] != "</":
s, child = parse_full_content(s)
result.append(child)
s, closing_tagname = parse_closing_tag(s)
assert tagname == closing_tagname
if len(result) == 1 and isinstance(result[0].parser == PlainText):
result = result[0].result
else:
result = tuple(result)
return Node(mock_parsers.setdefault(tagname, MockParser(name, ":" + class_name)), result)
return parse_full_content(xml[xml.search(re.compile(r'<(?!\?)')):])
# if __name__ == "__main__": # if __name__ == "__main__":
# st = mock_syntax_tree("(alpha (beta (gamma i\nj\nk) (delta y)) (epsilon z))") # st = parse_sxpr("(alpha (beta (gamma i\nj\nk) (delta y)) (epsilon z))")
# print(st.as_sxpr()) # print(st.as_sxpr())
# print(st.as_xml()) # print(st.as_xml())
...@@ -39,7 +39,7 @@ import sys ...@@ -39,7 +39,7 @@ import sys
from DHParser.error import is_error, adjust_error_locations from DHParser.error import is_error, adjust_error_locations
from DHParser.log import is_logging, clear_logs, log_ST, log_parsing_history from DHParser.log import is_logging, clear_logs, log_ST, log_parsing_history
from DHParser.parse import UnknownParserError from DHParser.parse import UnknownParserError
from DHParser.syntaxtree import Node, mock_syntax_tree, flatten_sxpr, ZOMBIE_PARSER from DHParser.syntaxtree import Node, parse_sxpr, flatten_sxpr, ZOMBIE_PARSER
from DHParser.toolkit import re, typing from DHParser.toolkit import re, typing
from typing import Tuple from typing import Tuple
...@@ -315,12 +315,12 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve ...@@ -315,12 +315,12 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report=True, ve
# write parsing-history log only in case of failure! # write parsing-history log only in case of failure!
if is_logging(): if is_logging():
log_parsing_history(parser, "match_%s_%s.log" % (parser_name, clean_test_name)) log_parsing_history(parser, "match_%s_%s.log" % (parser_name, clean_test_name))
elif "cst" in tests and mock_syntax_tree(tests["cst"][test_name]) != cst: elif "cst" in tests and parse_sxpr(tests["cst"][test_name]) != cst:
errata.append('Concrete syntax tree test "%s" for parser "%s" failed:\n%s' % errata.append('Concrete syntax tree test "%s" for parser "%s" failed:\n%s' %
(test_name, parser_name, cst.as_sxpr())) (test_name, parser_name, cst.as_sxpr()))
elif "ast" in tests: elif "ast" in tests:
try: try:
compare = mock_syntax_tree(tests["ast"][test_name]) compare = parse_sxpr(tests["ast"][test_name])
except KeyError: except KeyError:
pass pass
if compare != ast: if compare != ast:
......
...@@ -728,7 +728,7 @@ node’s parser’s <cite>ptype</cite>.</p> ...@@ -728,7 +728,7 @@ node’s parser’s <cite>ptype</cite>.</p>
<dl class="function"> <dl class="function">
<dt id="syntaxtree.mock_syntax_tree"> <dt id="syntaxtree.mock_syntax_tree">
<code class="descname">mock_syntax_tree</code><span class="sig-paren">(</span><em>sxpr</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/syntaxtree.html#mock_syntax_tree"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#syntaxtree.mock_syntax_tree" title="Permalink to this definition"></a></dt> <code class="descname">mock_syntax_tree</code><span class="sig-paren">(</span><em>sxpr</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/syntaxtree.html#parse_sxpr"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#syntaxtree.parse_sxpr" title="Permalink to this definition"></a></dt>
<dd><p>Generates a tree of nodes from an S-expression. The main purpose of this is <dd><p>Generates a tree of nodes from an S-expression. The main purpose of this is
to generate test data.</p> to generate test data.</p>
<p>Example: <p>Example:
......
...@@ -561,7 +561,7 @@ ...@@ -561,7 +561,7 @@
</li> </li>
<li><a href="ModuleReference.html#parse.mixin_comment">mixin_comment() (in module parse)</a> <li><a href="ModuleReference.html#parse.mixin_comment">mixin_comment() (in module parse)</a>
</li> </li>
<li><a href="ModuleReference.html#syntaxtree.mock_syntax_tree">mock_syntax_tree() (in module syntaxtree)</a> <li><a href="ModuleReference.html#syntaxtree.mock_syntax_tree">parse_sxpr() (in module syntaxtree)</a>
</li> </li>
<li><a href="ModuleReference.html#syntaxtree.MockParser">MockParser (class in syntaxtree)</a> <li><a href="ModuleReference.html#syntaxtree.MockParser">MockParser (class in syntaxtree)</a>
</li> </li>
......
...@@ -20,12 +20,12 @@ limitations under the License. ...@@ -20,12 +20,12 @@ limitations under the License.
""" """
from DHParser import mock_syntax_tree, Compiler from DHParser import parse_sxpr, Compiler
class TestCompilerClass: class TestCompilerClass:
def test_error_propagations(self): def test_error_propagations(self):
tree = mock_syntax_tree('(A (B 1) (C (D (E 2) (F 3))))') tree = parse_sxpr('(A (B 1) (C (D (E 2) (F 3))))')
A = tree