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

- Token nodes and Whitespace nodes are now identified by Parser.ptype rather than by special names

parent cf262cfd
...@@ -79,7 +79,7 @@ from DHParser.parsers import GrammarBase, CompilerBase, nil_scanner, \\ ...@@ -79,7 +79,7 @@ from DHParser.parsers import GrammarBase, CompilerBase, nil_scanner, \\
from DHParser.syntaxtree import Node, traverse, remove_enclosing_delimiters, \\ from DHParser.syntaxtree import Node, traverse, remove_enclosing_delimiters, \\
remove_children_if, reduce_single_child, replace_by_single_child, remove_whitespace, \\ remove_children_if, reduce_single_child, replace_by_single_child, remove_whitespace, \\
no_operation, remove_expendables, remove_tokens, flatten, is_whitespace, is_expendable, \\ no_operation, remove_expendables, remove_tokens, flatten, is_whitespace, is_expendable, \\
WHITESPACE_KEYWORD, TOKEN_KEYWORD WHITESPACE_PTYPE, TOKEN_PTYPE
''' '''
......
...@@ -29,8 +29,8 @@ from .parsers import GrammarBase, mixin_comment, nil_scanner, Forward, RE, Negat ...@@ -29,8 +29,8 @@ from .parsers import GrammarBase, mixin_comment, nil_scanner, Forward, RE, Negat
Alternative, Sequence, Optional, Required, OneOrMore, ZeroOrMore, Token, CompilerBase, \ Alternative, Sequence, Optional, Required, OneOrMore, ZeroOrMore, Token, CompilerBase, \
Capture, Retrieve Capture, Retrieve
from .syntaxtree import Node, traverse, remove_enclosing_delimiters, reduce_single_child, \ from .syntaxtree import Node, traverse, remove_enclosing_delimiters, reduce_single_child, \
replace_by_single_child, TOKEN_KEYWORD, remove_expendables, remove_tokens, flatten, \ replace_by_single_child, TOKEN_PTYPE, remove_expendables, remove_tokens, flatten, \
forbid, assert_content, WHITESPACE_KEYWORD, key_parser_name, key_tag_name forbid, assert_content, WHITESPACE_PTYPE, key_parser_name, key_tag_name
from .versionnumber import __version__ from .versionnumber import __version__
...@@ -207,7 +207,7 @@ EBNF_transformation_table = { ...@@ -207,7 +207,7 @@ EBNF_transformation_table = {
[reduce_single_child, remove_enclosing_delimiters], [reduce_single_child, remove_enclosing_delimiters],
"symbol, literal, regexp": "symbol, literal, regexp":
[remove_expendables, reduce_single_child], [remove_expendables, reduce_single_child],
(TOKEN_KEYWORD, WHITESPACE_KEYWORD): (TOKEN_PTYPE, WHITESPACE_PTYPE):
[remove_expendables, reduce_single_child], [remove_expendables, reduce_single_child],
"list_": "list_":
[flatten, partial(remove_tokens, tokens={','})], [flatten, partial(remove_tokens, tokens={','})],
...@@ -225,7 +225,7 @@ EBNF_validation_table = { ...@@ -225,7 +225,7 @@ EBNF_validation_table = {
def EBNFTransformer(syntax_tree): def EBNFTransformer(syntax_tree):
for processing_table, key_func in [(EBNF_transformation_table, key_parser_name), for processing_table, key_func in [(EBNF_transformation_table, key_tag_name),
(EBNF_validation_table, key_tag_name)]: (EBNF_validation_table, key_tag_name)]:
traverse(syntax_tree, processing_table, key_func) traverse(syntax_tree, processing_table, key_func)
...@@ -290,7 +290,8 @@ class EBNFCompiler(CompilerBase): ...@@ -290,7 +290,8 @@ class EBNFCompiler(CompilerBase):
in EBNF-Notation. in EBNF-Notation.
""" """
COMMENT_KEYWORD = "COMMENT__" COMMENT_KEYWORD = "COMMENT__"
RESERVED_SYMBOLS = {TOKEN_KEYWORD, WHITESPACE_KEYWORD, COMMENT_KEYWORD} WHITESPACE_KEYWORD = "WSP__"
RESERVED_SYMBOLS = {WHITESPACE_KEYWORD, COMMENT_KEYWORD}
AST_ERROR = "Badly structured syntax tree. " \ AST_ERROR = "Badly structured syntax tree. " \
"Potentially due to erroneuos AST transformation." "Potentially due to erroneuos AST transformation."
PREFIX_TABLE = {'§': 'Required', PREFIX_TABLE = {'§': 'Required',
...@@ -377,11 +378,11 @@ class EBNFCompiler(CompilerBase): ...@@ -377,11 +378,11 @@ class EBNFCompiler(CompilerBase):
definitions[i] = (definitions[i][0], 'Capture(%s)' % definitions[1]) definitions[i] = (definitions[i][0], 'Capture(%s)' % definitions[1])
self.definition_names = [defn[0] for defn in definitions] self.definition_names = [defn[0] for defn in definitions]
definitions.append(('wspR__', WHITESPACE_KEYWORD definitions.append(('wspR__', self.WHITESPACE_KEYWORD
if 'right' in self.directives['literalws'] else "''")) if 'right' in self.directives['literalws'] else "''"))
definitions.append(('wspL__', WHITESPACE_KEYWORD definitions.append(('wspL__', self.WHITESPACE_KEYWORD
if 'left' in self.directives['literalws'] else "''")) if 'left' in self.directives['literalws'] else "''"))
definitions.append((WHITESPACE_KEYWORD, definitions.append((self.WHITESPACE_KEYWORD,
("mixin_comment(whitespace=" ("mixin_comment(whitespace="
"r'{whitespace}', comment=r'{comment}')"). "r'{whitespace}', comment=r'{comment}')").
format(**self.directives))) format(**self.directives)))
...@@ -623,13 +624,13 @@ class EBNFCompiler(CompilerBase): ...@@ -623,13 +624,13 @@ class EBNFCompiler(CompilerBase):
name = [] name = []
if rx[:2] == '~/': if rx[:2] == '~/':
if not 'left' in self.directives['literalws']: if not 'left' in self.directives['literalws']:
name = ['wL=' + WHITESPACE_KEYWORD] + name name = ['wL=' + self.WHITESPACE_KEYWORD] + name
rx = rx[1:] rx = rx[1:]
elif 'left' in self.directives['literalws']: elif 'left' in self.directives['literalws']:
name = ["wL=''"] + name name = ["wL=''"] + name
if rx[-2:] == '/~': if rx[-2:] == '/~':
if 'right' not in self.directives['literalws']: if 'right' not in self.directives['literalws']:
name = ['wR=' + WHITESPACE_KEYWORD] + name name = ['wR=' + self.WHITESPACE_KEYWORD] + name
rx = rx[:-1] rx = rx[:-1]
elif 'right' in self.directives['literalws']: elif 'right' in self.directives['literalws']:
name = ["wR=''"] + name name = ["wR=''"] + name
......
...@@ -58,7 +58,7 @@ except ImportError: ...@@ -58,7 +58,7 @@ except ImportError:
from .toolkit import is_logging, log_dir, logfile_basename, escape_re, sane_parser_name, \ from .toolkit import is_logging, log_dir, logfile_basename, escape_re, sane_parser_name, \
compact_sexpr compact_sexpr
from .syntaxtree import WHITESPACE_KEYWORD, TOKEN_KEYWORD, ZOMBIE_PARSER, Node, \ from .syntaxtree import WHITESPACE_PTYPE, TOKEN_PTYPE, ZOMBIE_PARSER, Node, \
mock_syntax_tree mock_syntax_tree
from DHParser.toolkit import load_if_file, error_messages from DHParser.toolkit import load_if_file, error_messages
...@@ -205,16 +205,21 @@ class ParserMetaClass(type): ...@@ -205,16 +205,21 @@ class ParserMetaClass(type):
class Parser(metaclass=ParserMetaClass): class Parser(metaclass=ParserMetaClass):
def __init__(self, name=None): def __init__(self, name=''):
assert name is None or isinstance(name, str), str(name) assert isinstance(name, str), str(name)
self.name = name or '' i = name.find(':')
self.ptype = self.__class__.__name__ if i >= 0:
self.name = name[:i]
self.ptype = name[i:]
else:
self.name = name
self.ptype = ':' + self.__class__.__name__
# self.pbases = {cls.__name__ for cls in inspect.getmro(self.__class__)} # self.pbases = {cls.__name__ for cls in inspect.getmro(self.__class__)}
self._grammar = None # center for global variables etc. self._grammar = None # center for global variables etc.
self.reset() self.reset()
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
return self.__class__(self.name) return self.__class__(self.name + self.ptype)
def reset(self): def reset(self):
self.visited = dict() self.visited = dict()
...@@ -281,27 +286,27 @@ class GrammarBase: ...@@ -281,27 +286,27 @@ class GrammarBase:
cdict = cls.__dict__ cdict = cls.__dict__
for entry, parser in cdict.items(): for entry, parser in cdict.items():
if isinstance(parser, Parser) and sane_parser_name(entry): if isinstance(parser, Parser) and sane_parser_name(entry):
if not parser.name or parser.name == TOKEN_KEYWORD: if not parser.name:
parser.name = entry parser.name = entry
if (isinstance(parser, Forward) and (not parser.parser.name if (isinstance(parser, Forward) and (not parser.parser.name)):
or parser.parser.name == TOKEN_KEYWORD)):
parser.parser.name = entry parser.parser.name = entry
cls.parser_initialization__ = "done" cls.parser_initialization__ = "done"
def __init__(self): def __init__(self):
self.all_parsers = set() self.all_parsers = set()
self.dirty_flag = False self.dirty_flag = False
self.history_tracking = False
self._reset() self._reset()
self._assign_parser_names() self._assign_parser_names()
self.root__ = copy.deepcopy(self.__class__.root__) self.root__ = copy.deepcopy(self.__class__.root__)
if self.wspL__: if self.wspL__:
self.wsp_left_parser__ = RegExp(self.wspL__, WHITESPACE_KEYWORD) self.wsp_left_parser__ = Whitespace(self.wspL__)
self.wsp_left_parser__.grammar = self self.wsp_left_parser__.grammar = self
self.all_parsers.add(self.wsp_left_parser__) # don't you forget about me... self.all_parsers.add(self.wsp_left_parser__) # don't you forget about me...
else: else:
self.wsp_left_parser__ = ZOMBIE_PARSER self.wsp_left_parser__ = ZOMBIE_PARSER
if self.wspR__: if self.wspR__:
self.wsp_right_parser__ = RegExp(self.wspR__, WHITESPACE_KEYWORD) self.wsp_right_parser__ = Whitespace(self.wspR__)
self.wsp_right_parser__.grammar = self self.wsp_right_parser__.grammar = self
self.all_parsers.add(self.wsp_right_parser__) # don't you forget about me... self.all_parsers.add(self.wsp_right_parser__) # don't you forget about me...
else: else:
...@@ -317,7 +322,6 @@ class GrammarBase: ...@@ -317,7 +322,6 @@ class GrammarBase:
self.last_node = None self.last_node = None
self.call_stack = [] # support for call stack tracing self.call_stack = [] # support for call stack tracing
self.history = [] # snapshots of call stacks self.history = [] # snapshots of call stacks
self.history_tracking = is_logging()
self.moving_forward = True # also needed for call stack tracing self.moving_forward = True # also needed for call stack tracing
def _add_parser(self, parser): def _add_parser(self, parser):
...@@ -349,6 +353,7 @@ class GrammarBase: ...@@ -349,6 +353,7 @@ class GrammarBase:
parser.reset() parser.reset()
else: else:
self.dirty_flag = True self.dirty_flag = True
self.history_tracking = is_logging()
self.document = document self.document = document
parser = self[start_parser] parser = self[start_parser]
stitches = [] stitches = []
...@@ -402,7 +407,7 @@ class GrammarBase: ...@@ -402,7 +407,7 @@ class GrammarBase:
for record in self.history: for record in self.history:
line = "; ".join(prepare_line(record)) line = "; ".join(prepare_line(record))
full_history.append(line) full_history.append(line)
if record.node and record.node.parser.name != WHITESPACE_KEYWORD: if record.node and record.node.parser.ptype != WHITESPACE_PTYPE:
match_history.append(line) match_history.append(line)
if record.node.errors: if record.node.errors:
errors_only.append(line) errors_only.append(line)
...@@ -514,7 +519,7 @@ class RegExp(Parser): ...@@ -514,7 +519,7 @@ class RegExp(Parser):
other parsers delegate part of the parsing job to other parsers, other parsers delegate part of the parsing job to other parsers,
but do not match text directly. but do not match text directly.
""" """
def __init__(self, regexp, name=None): def __init__(self, regexp, name=''):
super(RegExp, self).__init__(name) super(RegExp, self).__init__(name)
self.regexp = re.compile(regexp) if isinstance(regexp, str) else regexp self.regexp = re.compile(regexp) if isinstance(regexp, str) else regexp
...@@ -524,7 +529,7 @@ class RegExp(Parser): ...@@ -524,7 +529,7 @@ class RegExp(Parser):
regexp = copy.deepcopy(self.regexp, memo) regexp = copy.deepcopy(self.regexp, memo)
except TypeError: except TypeError:
regexp = self.regexp.pattern regexp = self.regexp.pattern
return RegExp(regexp, self.name) return RegExp(regexp, self.name + self.ptype)
def __call__(self, text): def __call__(self, text):
match = text[0:1] != BEGIN_SCANNER_TOKEN and self.regexp.match(text) # ESC starts a scanner token. match = text[0:1] != BEGIN_SCANNER_TOKEN and self.regexp.match(text) # ESC starts a scanner token.
...@@ -537,6 +542,12 @@ class RegExp(Parser): ...@@ -537,6 +542,12 @@ class RegExp(Parser):
return self.name or self.ptype + ' /%s/' % self.regexp.pattern return self.name or self.ptype + ' /%s/' % self.regexp.pattern
def Whitespace(re_wsp='\s*'):
rx_parser = RegExp(re_wsp)
rx_parser.ptype = WHITESPACE_PTYPE
return rx_parser
class RE(Parser): class RE(Parser):
"""Regular Expressions with optional leading or trailing whitespace. """Regular Expressions with optional leading or trailing whitespace.
...@@ -550,7 +561,7 @@ class RE(Parser): ...@@ -550,7 +561,7 @@ class RE(Parser):
respective parameters in the constructor are set to ``None`` the respective parameters in the constructor are set to ``None`` the
default whitespace expression from the Grammar object will be used. default whitespace expression from the Grammar object will be used.
""" """
def __init__(self, regexp, wL=None, wR=None, name=None): def __init__(self, regexp, wL=None, wR=None, name=''):
"""Constructor for class RE. """Constructor for class RE.
Args: Args:
...@@ -569,8 +580,8 @@ class RE(Parser): ...@@ -569,8 +580,8 @@ class RE(Parser):
super(RE, self).__init__(name) super(RE, self).__init__(name)
self.wL = wL self.wL = wL
self.wR = wR self.wR = wR
self.wspLeft = RegExp(wL, WHITESPACE_KEYWORD) if wL else ZOMBIE_PARSER self.wspLeft = Whitespace(wL) if wL else ZOMBIE_PARSER
self.wspRight = RegExp(wR, WHITESPACE_KEYWORD) if wR else ZOMBIE_PARSER self.wspRight = Whitespace(wR) if wR else ZOMBIE_PARSER
self.main = RegExp(regexp) self.main = RegExp(regexp)
def __deepcopy__(self, memo={}): def __deepcopy__(self, memo={}):
...@@ -578,7 +589,7 @@ class RE(Parser): ...@@ -578,7 +589,7 @@ class RE(Parser):
regexp = copy.deepcopy(self.main.regexp, memo) regexp = copy.deepcopy(self.main.regexp, memo)
except TypeError: except TypeError:
regexp = self.main.regexp.pattern regexp = self.main.regexp.pattern
return self.__class__(regexp, self.wL, self.wR, self.name) return self.__class__(regexp, self.wL, self.wR, self.name + self.ptype)
def __call__(self, text): def __call__(self, text):
# assert self.main.regexp.pattern != "@" # assert self.main.regexp.pattern != "@"
...@@ -593,7 +604,7 @@ class RE(Parser): ...@@ -593,7 +604,7 @@ class RE(Parser):
return None, text return None, text
def __str__(self): def __str__(self):
if self.name == TOKEN_KEYWORD: if self.ptype == TOKEN_PTYPE:
return 'Token "%s"' % self.main.regexp.pattern.replace('\\', '') return 'Token "%s"' % self.main.regexp.pattern.replace('\\', '')
return self.name or ('RE ' + ('~' if self.wL else '') return self.name or ('RE ' + ('~' if self.wL else '')
+ '/%s/' % self.main.regexp.pattern + ('~' if self.wR else '')) + '/%s/' % self.main.regexp.pattern + ('~' if self.wR else ''))
...@@ -615,16 +626,16 @@ class RE(Parser): ...@@ -615,16 +626,16 @@ class RE(Parser):
self.main.apply(func) self.main.apply(func)
def Token(token, wL=None, wR=None, name=None): def Token(token, wL=None, wR=None, name=''):
"""Returns an RE-parser that matches plain strings that are """Returns an RE-parser that matches plain strings that are
considered as 'tokens'. considered as 'tokens'.
If the ``name``-parameter is empty, the parser's name will be set The parser's name will be set to the TOKEN_PTYPE, making it easy to
to the TOKEN_KEYWORD, making it easy to identify tokens in the identify tokens in the abstract syntax tree transformation and
abstract syntax tree transformation and compilation stage. compilation stage.
""" """
parser = RE(escape_re(token), wL, wR, name or TOKEN_KEYWORD) parser = RE(escape_re(token), wL, wR, name)
parser.ptype = "Token" parser.ptype = TOKEN_PTYPE
return parser return parser
...@@ -649,14 +660,14 @@ def mixin_comment(whitespace, comment): ...@@ -649,14 +660,14 @@ def mixin_comment(whitespace, comment):
class UnaryOperator(Parser): class UnaryOperator(Parser):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(UnaryOperator, self).__init__(name) super(UnaryOperator, self).__init__(name)
assert isinstance(parser, Parser) assert isinstance(parser, Parser)
self.parser = parser self.parser = parser
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
parser = copy.deepcopy(self.parser, memo) parser = copy.deepcopy(self.parser, memo)
return self.__class__(parser, self.name) return self.__class__(parser, self.name + self.ptype)
def apply(self, func): def apply(self, func):
if super(UnaryOperator, self).apply(func): if super(UnaryOperator, self).apply(func):
...@@ -664,14 +675,14 @@ class UnaryOperator(Parser): ...@@ -664,14 +675,14 @@ class UnaryOperator(Parser):
class NaryOperator(Parser): class NaryOperator(Parser):
def __init__(self, *parsers, name=None): def __init__(self, *parsers, name=''):
super(NaryOperator, self).__init__(name) super(NaryOperator, self).__init__(name)
assert all([isinstance(parser, Parser) for parser in parsers]), str(parsers) assert all([isinstance(parser, Parser) for parser in parsers]), str(parsers)
self.parsers = parsers self.parsers = parsers
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
parsers = copy.deepcopy(self.parsers, memo) parsers = copy.deepcopy(self.parsers, memo)
return self.__class__(*parsers, name=self.name) return self.__class__(*parsers, name=self.name + self.ptype)
def apply(self, func): def apply(self, func):
if super(NaryOperator, self).apply(func): if super(NaryOperator, self).apply(func):
...@@ -680,7 +691,7 @@ class NaryOperator(Parser): ...@@ -680,7 +691,7 @@ class NaryOperator(Parser):
class Optional(UnaryOperator): class Optional(UnaryOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(Optional, self).__init__(parser, name) super(Optional, self).__init__(parser, name)
assert isinstance(parser, Parser) assert isinstance(parser, Parser)
assert not isinstance(parser, Optional), \ assert not isinstance(parser, Optional), \
...@@ -713,7 +724,7 @@ class ZeroOrMore(Optional): ...@@ -713,7 +724,7 @@ class ZeroOrMore(Optional):
class OneOrMore(UnaryOperator): class OneOrMore(UnaryOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(OneOrMore, self).__init__(parser, name) super(OneOrMore, self).__init__(parser, name)
assert not isinstance(parser, Optional), \ assert not isinstance(parser, Optional), \
"Use ZeroOrMore instead of nesting OneOrMore and Optional: " \ "Use ZeroOrMore instead of nesting OneOrMore and Optional: " \
...@@ -737,7 +748,7 @@ class OneOrMore(UnaryOperator): ...@@ -737,7 +748,7 @@ class OneOrMore(UnaryOperator):
class Sequence(NaryOperator): class Sequence(NaryOperator):
def __init__(self, *parsers, name=None): def __init__(self, *parsers, name=''):
super(Sequence, self).__init__(*parsers, name=name) super(Sequence, self).__init__(*parsers, name=name)
assert len(self.parsers) >= 1 assert len(self.parsers) >= 1
...@@ -757,7 +768,7 @@ class Sequence(NaryOperator): ...@@ -757,7 +768,7 @@ class Sequence(NaryOperator):
class Alternative(NaryOperator): class Alternative(NaryOperator):
def __init__(self, *parsers, name=None): def __init__(self, *parsers, name=''):
super(Alternative, self).__init__(*parsers, name=name) super(Alternative, self).__init__(*parsers, name=name)
assert len(self.parsers) >= 1 assert len(self.parsers) >= 1
assert all(not isinstance(p, Optional) for p in self.parsers) assert all(not isinstance(p, Optional) for p in self.parsers)
...@@ -778,7 +789,7 @@ class Alternative(NaryOperator): ...@@ -778,7 +789,7 @@ class Alternative(NaryOperator):
class FlowOperator(UnaryOperator): class FlowOperator(UnaryOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(FlowOperator, self).__init__(parser, name) super(FlowOperator, self).__init__(parser, name)
...@@ -798,7 +809,7 @@ class Required(FlowOperator): ...@@ -798,7 +809,7 @@ class Required(FlowOperator):
class Lookahead(FlowOperator): class Lookahead(FlowOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(Lookahead, self).__init__(parser, name) super(Lookahead, self).__init__(parser, name)
def __call__(self, text): def __call__(self, text):
...@@ -830,7 +841,7 @@ def iter_right_branch(node): ...@@ -830,7 +841,7 @@ def iter_right_branch(node):
class Lookbehind(FlowOperator): class Lookbehind(FlowOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(Lookbehind, self).__init__(parser, name) super(Lookbehind, self).__init__(parser, name)
print("WARNING: Lookbehind Operator is experimental!") print("WARNING: Lookbehind Operator is experimental!")
...@@ -870,7 +881,7 @@ class NegativeLookbehind(Lookbehind): ...@@ -870,7 +881,7 @@ class NegativeLookbehind(Lookbehind):
class Capture(UnaryOperator): class Capture(UnaryOperator):
def __init__(self, parser, name=None): def __init__(self, parser, name=''):
super(Capture, self).__init__(parser, name) super(Capture, self).__init__(parser, name)
print("WARNING: Capture operator is experimental") print("WARNING: Capture operator is experimental")
...@@ -885,7 +896,7 @@ class Capture(UnaryOperator): ...@@ -885,7 +896,7 @@ class Capture(UnaryOperator):
class Retrieve(Parser): class Retrieve(Parser):
def __init__(self, symbol, counterpart=None, name=None): def __init__(self, symbol, counterpart=None, name=''):
if not name: if not name:
name = symbol.name name = symbol.name
super(Retrieve, self).__init__(name) super(Retrieve, self).__init__(name)
...@@ -894,7 +905,7 @@ class Retrieve(Parser): ...@@ -894,7 +905,7 @@ class Retrieve(Parser):
print("WARNING: Retrieve operator is experimental") print("WARNING: Retrieve operator is experimental")
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
return self.__class__(self.symbol, self.counterpart, self.name) return self.__class__(self.symbol, self.counterpart, self.name + self.ptype)
def __call__(self, text): def __call__(self, text):
stack = self.grammar.variables[self.symbol.name] stack = self.grammar.variables[self.symbol.name]
...@@ -1012,7 +1023,7 @@ class CompilerBase: ...@@ -1012,7 +1023,7 @@ class CompilerBase:
for the parsers of the sub nodes by itself. Rather, this should for the parsers of the sub nodes by itself. Rather, this should
be done within the compilation methods. be done within the compilation methods.
""" """
elem = str(node.parser) elem = node.parser.name or node.parser.ptype[1:]
if not sane_parser_name(elem): if not sane_parser_name(elem):
node.add_error("Reserved name '%s' not allowed as parser " node.add_error("Reserved name '%s' not allowed as parser "
"name! " % elem + "(Any name starting with " "name! " % elem + "(Any name starting with "
...@@ -1114,8 +1125,8 @@ def test_grammar(test_suite, parser_factory, transformer_factory): ...@@ -1114,8 +1125,8 @@ def test_grammar(test_suite, parser_factory, transformer_factory):
errata.append('Abstract syntax tree test "%s" for parser "%s" failed:' errata.append('Abstract syntax tree test "%s" for parser "%s" failed:'
'\n\tExpr.: %s\n\tExpected: %s\n\tReceived: %s' '\n\tExpr.: %s\n\tExpected: %s\n\tReceived: %s'
% (test_name, parser_name, '\n\t'.join(test_code.split('\n')), % (test_name, parser_name, '\n\t'.join(test_code.split('\n')),
compact_sexpr(ast.as_sexpr()), compact_sexpr(compare.as_sexpr()),
compact_sexpr(compare.as_sexpr()))) compact_sexpr(ast.as_sexpr())))
for test_name, test_code in tests['fail'].items(): for test_name, test_code in tests['fail'].items():
cst = parser(test_code, parser_name) cst = parser(test_code, parser_name)
......
...@@ -30,8 +30,8 @@ from typing import NamedTuple ...@@ -30,8 +30,8 @@ from typing import NamedTuple
from .toolkit import is_logging, log_dir, expand_table, line_col, smart_list from .toolkit import is_logging, log_dir, expand_table, line_col, smart_list
__all__ = ['WHITESPACE_KEYWORD', __all__ = ['WHITESPACE_PTYPE',
'TOKEN_KEYWORD', 'TOKEN_PTYPE',
'ZOMBIE_PARSER',