Commit 09196c53 authored by di68kap's avatar di68kap

- error handling completely refactored

parent ce68124b
......@@ -38,7 +38,7 @@ import os
import re
from DHParser.preprocess import strip_tokens, with_source_mapping, PreprocessorFunc
from DHParser.syntaxtree import Node
from DHParser.syntaxtree import Node, RootNode
from DHParser.transform import TransformationFunc
from DHParser.parse import Grammar
from DHParser.error import adjust_error_locations, is_error, Error
......@@ -94,10 +94,11 @@ class Compiler:
self.set_grammar_name(grammar_name, grammar_source)
def _reset(self):
self.tree = None # type: Optional[RootNode]
self.context = [] # type: List[Node]
self._dirty_flag = False
def __call__(self, node: Node) -> Any:
def __call__(self, root: RootNode) -> Any:
"""
Compiles the abstract syntax tree with the root node `node` and
returns the compiled code. It is up to subclasses implementing
......@@ -108,8 +109,8 @@ class Compiler:
if self._dirty_flag:
self._reset()
self._dirty_flag = True
result = self.compile(node)
self.propagate_error_flags(node, lazy=True)
self.tree = root
result = self.compile(root)
return result
def set_grammar_name(self, grammar_name: str="", grammar_source: str=""):
......@@ -128,18 +129,18 @@ class Compiler:
self.grammar_source = load_if_file(grammar_source)
return self
@staticmethod
def propagate_error_flags(node: Node, lazy: bool = True) -> None:
# See test_parser.TestCompilerClass.test_propagate_error()..
"""Propagates error flags from children to parent nodes to make sure
that the parent's error flag is always greater or equal the maximum
of the children's error flags."""
if not lazy or node.error_flag < Error.HIGHEST:
for child in node.children:
Compiler.propagate_error_flags(child)
node.error_flag = max(node.error_flag, child.error_flag)
if lazy and node.error_flag >= Error.HIGHEST:
return
# @staticmethod
# def propagate_error_flags(node: Node, lazy: bool = True) -> None:
# # See test_parser.TestCompilerClass.test_propagate_error()..
# """Propagates error flags from children to parent nodes to make sure
# that the parent's error flag is always greater or equal the maximum
# of the children's error flags."""
# if not lazy or node.error_flag < Error.HIGHEST:
# for child in node.children:
# Compiler.propagate_error_flags(child)
# node.error_flag = max(node.error_flag, child.error_flag)
# if lazy and node.error_flag >= Error.HIGHEST:
# return
@staticmethod
def method_name(node_name: str) -> str:
......
This diff is collapsed.
......@@ -174,10 +174,10 @@ def line_col(lbreaks: List[int], pos: int) -> Tuple[int, int]:
# """
# Returns the position within a text as (line, column)-tuple.
# """
# if pos < 0 or pos > len(text): # one character behind EOF is still an allowed position!
# if pos < 0 or add_pos > len(text): # one character behind EOF is still an allowed position!
# raise ValueError('Position %i outside text of length %s !' % (pos, len(text)))
# line = text.count("\n", 0, pos) + 1
# column = pos - text.rfind("\n", 0, pos)
# column = pos - text.rfind("\n", 0, add_pos)
# return line, column
......
......@@ -283,9 +283,7 @@ class HistoryRecord:
for cls, item in zip(tpl._fields, tpl)] + ['</tr>'])
def err_msg(self) -> str:
return self.ERROR + ": " + "; ".join(
str(e) for e in (self.node._errors if self.node._errors else
self.node.collect_errors()[:2]))
return self.ERROR + ": " + "; ".join(str(e) for e in (self.node.errors))
@property
def stack(self) -> str:
......@@ -295,7 +293,7 @@ class HistoryRecord:
@property
def status(self) -> str:
return self.FAIL if self.node is None else \
('"%s"' % self.err_msg()) if self.node.error_flag else self.MATCH
('"%s"' % self.err_msg()) if self.node.errors else self.MATCH
# has_errors(self.node._errors)
@property
......@@ -448,7 +446,7 @@ def log_parsing_history(grammar, log_file_name: str = '', html: bool=True) -> No
append_line(full_history, line)
if record.node and record.node.parser.ptype != WHITESPACE_PTYPE:
append_line(match_history, line)
if record.node.error_flag:
if record.node.errors:
append_line(errors_only, line)
write_log(full_history, log_file_name + '_full')
if len(full_history) > LOG_TAIL_THRESHOLD + 10:
......
......@@ -156,7 +156,7 @@ def add_parser_guard(parser_func):
if grammar.history_tracking__:
# don't track returning parsers except in case an error has occurred
# remaining = len(rest)
if grammar.moving_forward__ or (node and node.error_flag): # node._errors
if grammar.moving_forward__ or (node and node._errors):
record = HistoryRecord(grammar.call_stack__, node, text)
grammar.history__.append(record)
# print(record.stack, record.status, rest[:20].replace('\n', '|'))
......@@ -165,7 +165,8 @@ def add_parser_guard(parser_func):
except RecursionError:
node = Node(None, str(text[:min(10, max(1, text.find("\n")))]) + " ...")
grammar.tree__.add_error(location, "maximum recursion depth of parser reached; "
node._pos = location
grammar.tree__.add_error(node, "maximum recursion depth of parser reached; "
"potentially due to too many errors!")
rest = EMPTY_STRING_VIEW
......@@ -727,7 +728,8 @@ class Grammar:
result, _ = parser(rest)
if result is None:
result = Node(None, '').init_pos(0)
result.add_error(0, 'Parser "%s" did not match empty document.' % str(parser))
self.tree__.add_error(result,
'Parser "%s" did not match empty document.' % str(parser))
while rest and len(stitches) < MAX_DROPOUTS:
result, rest = parser(rest)
if rest:
......@@ -747,7 +749,7 @@ class Grammar:
if len(stitches) < MAX_DROPOUTS
else " too often! Terminating parser.")
stitches.append(Node(None, skip).init_pos(tail_pos(stitches)))
stitches[-1].add_error(self.document_length__ - 1, error_msg)
self.tree__.add_error(stitches[-1], error_msg)
if self.history_tracking__:
# # some parsers may have matched and left history records with nodes != None.
# # Because these are not connected to the stitched root node, their pos-
......@@ -773,25 +775,15 @@ class Grammar:
# of the error will be the end of the text. Otherwise, the error
# message above ("...after end of parsing") would appear illogical.
error_node = Node(ZOMBIE_PARSER, '').init_pos(tail_pos(result.children))
error_node.add_error(self.document_length__ - 1, error_str)
self.tree__.add_error(error_node, error_str)
result.result = result.children + (error_node,)
else:
result.add_error(self.document_length__ - 1, error_str)
self.tree__.add_error(result, error_str)
# result.pos = 0 # calculate all positions
# result.collect_errors(self.document__)
self.tree__.swallow(result)
return self.tree__
def location(self, remaining: str) -> int:
"""Returns the location of the `remaining` text within the currently
parsed document.
"""
self.document_length__ - len(remaining)
def add_error(self, location, error_msg, code=Error.ERROR):
"""Adds an error at the location of `text` within the whole document that is
currently being parsed."""
self.tree__.add_error(location, error_msg, code)
def push_rollback__(self, location, func):
"""
......@@ -869,18 +861,20 @@ class PreprocessorToken(Parser):
if text[0:1] == BEGIN_TOKEN:
end = text.find(END_TOKEN, 1)
if end < 0:
self.grammar.add_error(self.grammar.location(text),
node = Node(self, '')
self.grammar.tree__.add_error(node,
'END_TOKEN delimiter missing from preprocessor token. '
'(Most likely due to a preprocessor bug!)') # type: Node
return Node(self, ''), text[1:]
return node, text[1:]
elif end == 0:
self.grammar.add_error(self.grammar.location(text),
node = Node(self, '')
self.grammar.tree__.add_error(node,
'Preprocessor-token cannot have zero length. '
'(Most likely due to a preprocessor bug!)')
return Node(self, ''), text[2:]
return node, text[2:]
elif text.find(BEGIN_TOKEN, 1, end) >= 0:
node = Node(self, text[len(self.name) + 1:end])
self.grammar.add_error(self.grammar.location(text),
self.grammar.tree__.add_error(node,
'Preprocessor-tokens must not be nested or contain '
'BEGIN_TOKEN delimiter as part of their argument. '
'(Most likely due to a preprocessor bug!)')
......@@ -1262,8 +1256,8 @@ class ZeroOrMore(Option):
if not node:
break
if len(text) == n:
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, 'Infinite Loop detected.'))
self.grammar.tree__.add_error(node,
dsl_error_msg(self, 'Infinite Loop encountered.'))
results += (node,)
return Node(self, results), text
......@@ -1307,8 +1301,8 @@ class OneOrMore(UnaryOperator):
if not node:
break
if len(text_) == n:
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, 'Infinite Loop detected.'))
self.grammar.tree__.add_error(node,
dsl_error_msg(self, 'Infinite Loop encountered.'))
results += (node,)
if results == ():
return None, text
......@@ -1368,9 +1362,10 @@ class Series(NaryOperator):
i = max(1, text.index(match.regs[1][0])) if match else 1
node = Node(self, text_[:i]).init_pos(self.grammar.document_length__
- len(text_))
self.grammar.add_error(self.grammar.location(text), '%s expected; "%s" found!'
% (parser.repr, text_[:10].replace('\n', '\\n ')),
code=Error.MANDATORY_CONTINUATION)
self.grammar.tree__.add_error(
node, '%s expected; "%s" found!'
% (parser.repr, text_[:10].replace('\n', '\\n ')),
code=Error.MANDATORY_CONTINUATION)
text_ = text_[i:]
results += (node,)
# if node.error_flag: # break on first error
......@@ -1637,9 +1632,9 @@ def Required(parser: Parser) -> Parser:
# i = max(1, text.index(m.regs[1][0])) if m else 1
# node = Node(self, text[:i])
# text_ = text[i:]
# self.grammar.add_error(self.grammar.location(text),
# '%s expected; "%s" found!' % (str(self.parser),
# text[:10]), code=Error.MANDATORY_CONTINUATION)
# self.grammar.tree__.add_error(node,
# '%s expected; "%s" found!' % (str(self.parser),
# text[:10]), code=Error.MANDATORY_CONTINUATION)
# return node, text_
#
# def __repr__(self):
......@@ -1812,9 +1807,10 @@ class Retrieve(Parser):
stack = self.grammar.variables__[self.symbol.name]
value = self.filter(stack)
except (KeyError, IndexError):
self.grammar.add_error(self.grammar.location(text),
dsl_error_msg(self, "'%s' undefined or exhausted." % self.symbol.name))
return Node(self, ''), text
node = Node(self, '')
self.grammar.tree__.add_error(
node, dsl_error_msg(self, "'%s' undefined or exhausted." % self.symbol.name))
return node, text
if text.startswith(value):
return Node(self, value), text[len(value):]
else:
......@@ -1835,7 +1831,7 @@ class Pop(Retrieve):
def __call__(self, text: StringView) -> Tuple[Optional[Node], StringView]:
node, txt = super().retrieve_and_match(text)
if node and not node.error_flag:
if node and not node.errors:
stack = self.grammar.variables__[self.symbol.name]
value = stack.pop()
location = self.grammar.document_length__ - len(text)
......
......@@ -236,14 +236,14 @@ class Node(collections.abc.Sized):
S-Expression-output.
"""
__slots__ = ['_result', 'children', '_len', '_pos', 'parser', '_errors' '_xml_attr', '_content']
__slots__ = ['_result', 'children', '_len', '_pos', 'parser', '_errors', '_xml_attr', '_content']
def __init__(self, parser, result: ResultType, leafhint: bool = False) -> None:
"""
Initializes the ``Node``-object with the ``Parser``-Instance
that generated the node and the parser's result.
"""
self._errors = [] # type: List[Error]
self._errors = [] # type: List[Error]
self._pos = -1 # type: int
# Assignment to self.result initializes the attributes _result, children and _len
# The following if-clause is merely an optimization, i.e. a fast-path for leaf-Nodes
......@@ -432,7 +432,7 @@ class Node(collections.abc.Sized):
return self._pos
def init_pos(self, pos: int, overwrite: bool = False) -> 'Node':
def init_pos(self, pos: int, overwrite: bool = True) -> 'Node':
"""
(Re-)initialize position value. Usually, the parser guard
(`parsers.add_parser_guard()`) takes care of assigning the
......@@ -460,14 +460,15 @@ class Node(collections.abc.Sized):
def errors(self) -> List[Error]:
"""
Returns the errors that occurred at this Node, not including any
errors from child nodes. Works only, if error propagation has been
enabled when calling `swallow` from the root node.
errors from child nodes.
"""
return self._errors
@property
def attributes(self):
"""Returns a dictionary of XML-Attributes attached to the Node."""
"""
Returns a dictionary of XML-Attributes attached to the Node.
"""
if not hasattr(self, '_xml_attr'):
self._xml_attr = OrderedDict()
return self._xml_attr
......@@ -537,11 +538,11 @@ class Node(collections.abc.Sized):
def opening(node) -> str:
"""Returns the opening string for the representation of `node`."""
txt = [left_bracket, node.tag_name]
# s += " '(pos %i)" % node.pos
# s += " '(pos %i)" % node.add_pos
if hasattr(node, '_xml_attr'):
txt.extend(' `(%s "%s")' % (k, v) for k, v in node.attributes.items())
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.add_pos)))
# if node.error_flag: # just for debugging error collecting
# txt += " HAS ERRORS"
if showerrors and node.errors:
......@@ -688,35 +689,34 @@ class RootNode(Node):
def __init__(self):
super().__init__(ZOMBIE_PARSER, '')
self.all_errors = []
self.err_nodes = []
self.error_flag = 0
self.error_propagation = False
def _propagate_errors(self):
assert self.children
if not self.all_errors or not self.error_propagation:
return
self.all_errors.sort(key=lambda e: e.pos)
i = 0
for leaf in self.select(lambda nd: not nd.children, False):
leaf.errors = []
while i < len(self.all_errors) \
and leaf.pos <= self.all_errors[i].pos < leaf.pos + leaf.len:
leaf._errors.append(self.all_errors[i])
i += 1
if i >= len(self.all_errors):
break
def _propagate_new_error(self, error):
if self.error_propagation:
for leaf in self.select(lambda nd: not nd.children, False):
if leaf.pos <= error.pos < leaf.pos + leaf.len:
leaf._errors.append(error)
break
else:
assert False, "Error %s at pos %i out of bounds" % (str(error), error.pos)
def swallow(self, node, error_propagation=False):
assert not node.errors, "Node has already been swallowed!?"
# def _propagate_errors(self):
# if not self.all_errors or not self.error_propagation:
# return
# self.all_errors.sort(key=lambda e: e.pos)
# i = 0
# for leaf in self.select(lambda nd: not nd.children, False):
# leaf.errors = []
# while i < len(self.all_errors) \
# and leaf.pos <= self.all_errors[i].add_pos < leaf.add_pos + leaf.len:
# leaf._errors.append(self.all_errors[i])
# i += 1
# if i >= len(self.all_errors):
# break
#
# def _propagate_new_error(self, error):
# if self.error_propagation:
# for leaf in self.select(lambda nd: not nd.children, True):
# if leaf.pos <= error.add_pos < leaf.add_pos + leaf.len:
# leaf._errors.append(error)
# break
# else:
# assert False, "Error %s at pos %i out of bounds" % (str(error), error.add_pos)
def swallow(self, node: Node) -> 'RootNode':
self._result = node._result
self.children = node.children
self._len = node._len
......@@ -725,11 +725,10 @@ class RootNode(Node):
if hasattr(node, '_xml_attr'):
self._xml_attr = node._xml_attr
self._content = node._content
self.error_propagation = error_propagation
self._propagate_errors()
return self
def add_error(self,
pos: int,
node: Node,
message: str,
code: int = Error.ERROR) -> 'Node':
"""
......@@ -739,21 +738,26 @@ class RootNode(Node):
message(str): A string with the error message.abs
code(int): An error code to identify the kind of error
"""
error = Error(message, code, pos)
error = Error(message, code)
self.all_errors.append(error)
self.error_flag = max(self.error_flag, code)
if self.children:
self._propagate_new_error(error)
node._errors.append(error)
self.err_nodes.append(node)
return self
def collect_errors(self, clear_errors=False) -> List[Error]:
"""Returns the list of errors, ordered bv their position.
"""
self.errors.sort(key=lambda e: e.pos)
errors = self.errors
for node in self.err_nodes: # lazy evaluation of positions
for err in node.errors:
err.pos = node.pos
self.all_errors.sort(key=lambda e: e.pos)
errors = self.all_errors
if clear_errors:
self.errors = []
self.all_errors = []
self.error_flag = 0
for node in self.select(lambda nd: True, True):
node._errors = []
return errors
......
......@@ -272,7 +272,6 @@ def traverse(root_node: Node,
for child in node.result:
context[-1] = child
traverse_recursive(context) # depth first
node.error_flag = max(node.error_flag, child.error_flag) # propagate error flag
context.pop()
key = key_func(node)
......
......@@ -496,7 +496,7 @@ through AST-transformation. READ ONLY!</p>
<dl class="attribute">
<dt id="syntaxtree.Node.pos">
<code class="descname">pos</code><a class="headerlink" href="#syntaxtree.Node.pos" title="Permalink to this definition"></a></dt>
<code class="descname">pos</code><a class="headerlink" href="#syntaxtree.Node.add_pos" title="Permalink to this definition"></a></dt>
<dd><p><em>int</em> – the position of the node within the parsed text.</p>
<p>The value of <code class="docutils literal notranslate"><span class="pre">pos</span></code> is -1 meaning invalid by default.
Setting this value will set the positions of all child
......
......@@ -613,9 +613,9 @@
<span class="k">if</span> <span class="n">overwrite</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="k">for</span> <span class="n">err</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_errors</span><span class="p">:</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">add_pos</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">==</span> <span class="n">pos</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%i</span><span class="s2"> != </span><span class="si">%i</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pos</span><span class="p">,</span> <span class="n">pos</span><span class="p">))</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">==</span> <span class="n">pos</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%i</span><span class="s2"> != </span><span class="si">%i</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pos</span><span class="p">,</span> <span class="n">add_pos</span><span class="p">))</span>
<span class="c1"># recursively adjust pos-values of all children</span>
<span class="n">offset</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
......@@ -656,7 +656,7 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">errors</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">errors</span>
<span class="k">for</span> <span class="n">err</span> <span class="ow">in</span> <span class="n">errors</span><span class="p">:</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">add_pos</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
<span class="n">errors</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">collect_errors</span><span class="p">(</span><span class="n">clear_errors</span><span class="p">))</span>
......@@ -737,9 +737,9 @@
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the opening string for the representation of `node`.&quot;&quot;&quot;</span>
<span class="n">txt</span> <span class="o">=</span> <span class="n">left_bracket</span> <span class="o">+</span> <span class="n">node</span><span class="o">.</span><span class="n">tag_name</span>
<span class="c1"># s += &quot; &#39;(pos %i)&quot; % node.pos</span>
<span class="c1"># s += &quot; &#39;(pos %i)&quot; % node.add_pos</span>
<span class="k">if</span> <span class="n">src</span><span class="p">:</span>
<span class="n">txt</span> <span class="o">+=</span> <span class="s2">&quot; &#39;(pos </span><span class="si">%i</span><span class="s2"> &quot;</span> <span class="o">%</span> <span class="n">node</span><span class="o">.</span><span class="n">pos</span> <span class="c1"># + &quot; %i %i)&quot; % line_col(src, node.pos)</span>
<span class="n">txt</span> <span class="o">+=</span> <span class="s2">&quot; &#39;(pos </span><span class="si">%i</span><span class="s2"> &quot;</span> <span class="o">%</span> <span class="n">node</span><span class="o">.</span><span class="n">add_pos</span> <span class="c1"># + &quot; %i %i)&quot; % line_col(src, node.add_pos)</span>
<span class="c1"># if node.error_flag: # just for debugging error collecting</span>
<span class="c1"># txt += &quot; HAS ERRORS&quot;</span>
<span class="k">if</span> <span class="n">showerrors</span> <span class="ow">and</span> <span class="n">node</span><span class="o">.</span><span class="n">errors</span><span class="p">:</span>
......@@ -781,7 +781,7 @@
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the opening string for the representation of `node`.&quot;&quot;&quot;</span>
<span class="n">txt</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;&lt;&#39;</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">tag_name</span><span class="p">]</span>
<span class="c1"># s += &#39; pos=&quot;%i&quot;&#39; % node.pos</span>
<span class="c1"># s += &#39; pos=&quot;%i&quot;&#39; % node.add_pos</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="s1">&#39;_xml_attr&#39;</span><span class="p">):</span>
<span class="n">txt</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="s1">&#39; </span><span class="si">%s</span><span class="s1">=&quot;</span><span class="si">%s</span><span class="s1">&quot;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="k">if</span> <span class="n">src</span><span class="p">:</span>
......
......@@ -235,7 +235,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">message</span> <span class="o">=</span> <span class="n">message</span>
<span class="k">assert</span> <span class="n">code</span> <span class="o">&gt;=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">code</span> <span class="o">=</span> <span class="n">code</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">add_pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">orig_pos</span> <span class="o">=</span> <span class="n">orig_pos</span>
<span class="bp">self</span><span class="o">.</span><span class="n">line</span> <span class="o">=</span> <span class="n">line</span>
<span class="bp">self</span><span class="o">.</span><span class="n">column</span> <span class="o">=</span> <span class="n">column</span>
......@@ -319,7 +319,7 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">lbreaks</span> <span class="ow">and</span> <span class="n">pos</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="n">pos</span>
<span class="k">if</span> <span class="n">pos</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">pos</span> <span class="o">&gt;</span> <span class="n">lbreaks</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span> <span class="c1"># one character behind EOF is still an allowed position!</span>
<span class="k">if</span> <span class="n">pos</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">add_pos</span> <span class="o">&gt;</span> <span class="n">lbreaks</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span> <span class="c1"># one character behind EOF is still an allowed position!</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;Position </span><span class="si">%i</span><span class="s1"> outside text of length </span><span class="si">%s</span><span class="s1"> !&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">lbreaks</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">bisect</span><span class="o">.</span><span class="n">bisect_left</span><span class="p">(</span><span class="n">lbreaks</span><span class="p">,</span> <span class="n">pos</span><span class="p">)</span>
<span class="n">column</span> <span class="o">=</span> <span class="n">pos</span> <span class="o">-</span> <span class="n">lbreaks</span><span class="p">[</span><span class="n">line</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
......@@ -330,10 +330,10 @@
<span class="c1"># &quot;&quot;&quot;</span>
<span class="c1"># Returns the position within a text as (line, column)-tuple.</span>
<span class="c1"># &quot;&quot;&quot;</span>
<span class="c1"># if pos &lt; 0 or pos &gt; len(text): # one character behind EOF is still an allowed position!</span>
<span class="c1"># if pos &lt; 0 or add_pos &gt; len(text): # one character behind EOF is still an allowed position!</span>
<span class="c1"># raise ValueError(&#39;Position %i outside text of length %s !&#39; % (pos, len(text)))</span>
<span class="c1"># line = text.count(&quot;\n&quot;, 0, pos) + 1</span>
<span class="c1"># column = pos - text.rfind(&quot;\n&quot;, 0, pos)</span>
<span class="c1"># column = pos - text.rfind(&quot;\n&quot;, 0, add_pos)</span>
<span class="c1"># return line, column</span>
......
......@@ -613,9 +613,9 @@
<span class="k">if</span> <span class="n">overwrite</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="k">for</span> <span class="n">err</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_errors</span><span class="p">:</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">pos</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">add_pos</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">==</span> <span class="n">pos</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%i</span><span class="s2"> != </span><span class="si">%i</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pos</span><span class="p">,</span> <span class="n">pos</span><span class="p">))</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pos</span> <span class="o">==</span> <span class="n">pos</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">%i</span><span class="s2"> != </span><span class="si">%i</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pos</span><span class="p">,</span> <span class="n">add_pos</span><span class="p">))</span>
<span class="c1"># recursively adjust pos-values of all children</span>
<span class="n">offset</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
......@@ -656,7 +656,7 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">errors</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">errors</span>
<span class="k">for</span> <span class="n">err</span> <span class="ow">in</span> <span class="n">errors</span><span class="p">:</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span>
<span class="n">err</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">add_pos</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
<span class="n">errors</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">collect_errors</span><span class="p">(</span><span class="n">clear_errors</span><span class="p">))</span>
......@@ -737,9 +737,9 @@
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the opening string for the representation of `node`.&quot;&quot;&quot;</span>
<span class="n">txt</span> <span class="o">=</span> <span class="n">left_bracket</span> <span class="o">+</span> <span class="n">node</span><span class="o">.</span><span class="n">tag_name</span>
<span class="c1"># s += &quot; &#39;(pos %i)&quot; % node.pos</span>
<span class="c1"># s += &quot; &#39;(pos %i)&quot; % node.add_pos</span>
<span class="k">if</span> <span class="n">src</span><span class="p">:</span>
<span class="n">txt</span> <span class="o">+=</span> <span class="s2">&quot; &#39;(pos </span><span class="si">%i</span><span class="s2"> &quot;</span> <span class="o">%</span> <span class="n">node</span><span class="o">.</span><span class="n">pos</span> <span class="c1"># + &quot; %i %i)&quot; % line_col(src, node.pos)</span>
<span class="n">txt</span> <span class="o">+=</span> <span class="s2">&quot; &#39;(pos </span><span class="si">%i</span><span class="s2"> &quot;</span> <span class="o">%</span> <span class="n">node</span><span class="o">.</span><span class="n">add_pos</span> <span class="c1"># + &quot; %i %i)&quot; % line_col(src, node.add_pos)</span>
<span class="c1"># if node.error_flag: # just for debugging error collecting</span>
<span class="c1"># txt += &quot; HAS ERRORS&quot;</span>
<span class="k">if</span> <span class="n">showerrors</span> <span class="ow">and</span> <span class="n">node</span><span class="o">.</span><span class="n">errors</span><span class="p">:</span>
......@@ -781,7 +781,7 @@
<span class="k">def</span> <span class="nf">opening</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;Returns the opening string for the representation of `node`.&quot;&quot;&quot;</span>
<span class="n">txt</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;&lt;&#39;</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">tag_name</span><span class="p">]</span>
<span class="c1"># s += &#39; pos=&quot;%i&quot;&#39; % node.pos</span>
<span class="c1"># s += &#39; pos=&quot;%i&quot;&#39; % node.add_pos</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="s1">&#39;_xml_attr&#39;</span><span class="p">):</span>
<span class="n">txt</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="s1">&#39; </span><span class="si">%s</span><span class="s1">=&quot;</span><span class="si">%s</span><span class="s1">&quot;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="k">if</span> <span class="n">src</span><span class="p">:</span>
......
......@@ -629,7 +629,7 @@
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="ModuleReference.html#syntaxtree.Node.pos">pos (Node attribute)</a>, <a href="ModuleReference.html#syntaxtree.Node.pos">[1]</a>
<li><a href="ModuleReference.html#syntaxtree.Node.pos">add_pos (Node attribute)</a>, <a href="ModuleReference.html#syntaxtree.Node.add_pos">[1]</a>
</li>
<li><a href="ModuleReference.html#preprocess.SourceMap.positions">positions (SourceMap attribute)</a>
</li>
......
......@@ -130,9 +130,12 @@ class TestNode:
class TestRootNode:
def test_error_handling(self):
tree = parse_sxpr('(A (B D) (C E))')
tree.init_pos(0)
root = RootNode()
root.add_error(4, "error B")