Commit f414983c authored by di68kap's avatar di68kap

- work in progress!

parent ce75dc7b
......@@ -56,7 +56,8 @@ import threading
from DHParser.error import line_col
from DHParser.stringview import StringView
from DHParser.syntaxtree import Node, ParserBase
from DHParser.syntaxtree import Node
from DHParser.parse import ParserBase
from DHParser.toolkit import is_filename, escape_control_characters, GLOBALS, typing
from typing import List, Tuple, Union
......
......@@ -37,8 +37,8 @@ from DHParser.error import Error, linebreaks
from DHParser.log import is_logging, HistoryRecord
from DHParser.preprocess import BEGIN_TOKEN, END_TOKEN, RX_TOKEN_NAME
from DHParser.stringview import StringView, EMPTY_STRING_VIEW
from DHParser.syntaxtree import Node, RootNode, ParserBase, WHITESPACE_PTYPE, \
TOKEN_PTYPE, ZOMBIE_PARSER, ZOMBIE, ResultType
from DHParser.syntaxtree import Node, RootNode, WHITESPACE_PTYPE, \
TOKEN_PTYPE, ZOMBIE, ResultType
from DHParser.toolkit import sane_parser_name, escape_control_characters, re, typing
from typing import Callable, cast, List, Tuple, Set, Dict, DefaultDict, Union, Optional, Any
......@@ -147,6 +147,50 @@ ApplyFunc = Callable[['Parser'], None]
FlagFunc = Callable[[ApplyFunc, Set[ApplyFunc]], bool]
class ParserBase:
"""
ParserBase is the base class for all real and mock parser classes.
It is defined here, because Node objects require a parser object
for instantiation.
"""
__slots__ = 'name', 'ptype', 'tag_name'
def __init__(self,): # , pbases=frozenset()):
self.name = '' # type: str
self.ptype = ':' + self.__class__.__name__ # type: str
self.tag_name = self.ptype # type: str
def __repr__(self):
return self.name + self.ptype
def __str__(self):
return self.name + (' = ' if self.name else '') + repr(self)
def __call__(self, text: StringView) -> Tuple[Optional['Node'], StringView]:
return None, text
@property
def repr(self) -> str:
"""Returns the parser's name if it has a name and repr()"""
return self.name if self.name else self.__repr__()
def reset(self):
"""Resets any parser variables. (Should be overridden.)"""
pass
@property
def grammar(self) -> 'Grammar':
"""Returns the Grammar object to which the parser belongs. If not
yet connected to any Grammar object, None is returned."""
raise NotImplementedError
def apply(self, func: Callable):
"""Applies the function `func` recursively to the parser and all
descendant parsers, if any exist."""
pass
class Parser(ParserBase):
"""
(Abstract) Base class for Parser combinator parsers. Any parser
......@@ -2103,3 +2147,59 @@ class Forward(Parser):
self.parser._apply(func, flip)
return True
return False
class MockParser(ParserBase):
"""
MockParser objects can be used to reconstruct syntax trees from a
serialized form like S-expressions or XML. Mock objects can mimic
different parser types by assigning them a `ptype` on initialization.
Mock objects should not be used for anything other than
syntax tree (re-)construction. In all other cases where a parser
object substitute is needed, chose the singleton ZOMBIE_PARSER.
"""
__slots__ = ()
def __init__(self, name='', ptype=''): # , pbases=frozenset()):
assert not ptype or ptype[0] == ':'
super().__init__()
self.name = name
if ptype:
self.ptype = ptype # or ':' + self.__class__.__name__
class ZombieParser(MockParser):
"""
Serves as a substitute for a Parser instance.
``ZombieParser`` is the class of the singelton object
``ZOMBIE_PARSER``. The ``ZOMBIE_PARSER`` has a name and can be
called, but it never matches. It serves as a substitute where only
these (or one of these properties) is needed, but no real Parser-
object is instantiated.
"""
alive = False
__slots__ = ()
def __init__(self):
super(ZombieParser, self).__init__()
assert not self.__class__.alive, "There can be only one!"
assert self.__class__ == ZombieParser, "No derivatives, please!"
self.name = ZOMBIE
self.__class__.alive = True
def __copy__(self):
return self
def __deepcopy__(self, memo):
return self
def __call__(self, text):
"""Better call Saul ;-)"""
return None, text
ZOMBIE_PARSER = ZombieParser()
\ No newline at end of file
......@@ -23,24 +23,18 @@ here, because node-objects refer to parser-objects. All concrete
parser classes are defined in the ``parse`` module.
"""
import collections.abc
from collections import OrderedDict
import copy
from DHParser.error import Error, ErrorCode, linebreaks, line_col
from DHParser.stringview import StringView
from DHParser.toolkit import re, typing
from typing import Callable, cast, Iterator, List, AbstractSet, Set, Dict, Union, Tuple, Optional
from DHParser.toolkit import re
from typing import Callable, cast, Iterator, List, AbstractSet, Set, Union, Tuple, Optional
__all__ = ('ParserBase',
'WHITESPACE_PTYPE',
__all__ = ('WHITESPACE_PTYPE',
'TOKEN_PTYPE',
'MockParser',
'ZombieParser',
'ZOMBIE',
'ZOMBIE_PARSER',
'ZOMBIE_NODE',
'ResultType',
'StrictResultType',
......@@ -61,113 +55,11 @@ __all__ = ('ParserBase',
#######################################################################
class ParserBase:
"""
ParserBase is the base class for all real and mock parser classes.
It is defined here, because Node objects require a parser object
for instantiation.
"""
__slots__ = 'name', 'ptype', 'tag_name'
def __init__(self,): # , pbases=frozenset()):
self.name = '' # type: str
self.ptype = ':' + self.__class__.__name__ # type: str
self.tag_name = self.ptype # type: str
def __repr__(self):
return self.name + self.ptype
def __str__(self):
return self.name + (' = ' if self.name else '') + repr(self)
def __call__(self, text: StringView) -> Tuple[Optional['Node'], StringView]:
return None, text
@property
def repr(self) -> str:
"""Returns the parser's name if it has a name and repr()"""
return self.name if self.name else self.__repr__()
def reset(self):
"""Resets any parser variables. (Should be overridden.)"""
pass
@property
def grammar(self) -> 'Grammar':
"""Returns the Grammar object to which the parser belongs. If not
yet connected to any Grammar object, None is returned."""
raise NotImplementedError
def apply(self, func: Callable):
"""Applies the function `func` recursively to the parser and all
descendant parsers, if any exist."""
pass
WHITESPACE_PTYPE = ':Whitespace'
TOKEN_PTYPE = ':Token'
class MockParser(ParserBase):
"""
MockParser objects can be used to reconstruct syntax trees from a
serialized form like S-expressions or XML. Mock objects can mimic
different parser types by assigning them a `ptype` on initialization.
Mock objects should not be used for anything other than
syntax tree (re-)construction. In all other cases where a parser
object substitute is needed, chose the singleton ZOMBIE_PARSER.
"""
__slots__ = ()
def __init__(self, name='', ptype=''): # , pbases=frozenset()):
assert not ptype or ptype[0] == ':'
super().__init__()
self.name = name
if ptype:
self.ptype = ptype # or ':' + self.__class__.__name__
ZOMBIE = "__ZOMBIE__"
class ZombieParser(MockParser):
"""
Serves as a substitute for a Parser instance.
``ZombieParser`` is the class of the singelton object
``ZOMBIE_PARSER``. The ``ZOMBIE_PARSER`` has a name and can be
called, but it never matches. It serves as a substitute where only
these (or one of these properties) is needed, but no real Parser-
object is instantiated.
"""
alive = False
__slots__ = ()
def __init__(self):
super(ZombieParser, self).__init__()
assert not self.__class__.alive, "There can be only one!"
assert self.__class__ == ZombieParser, "No derivatives, please!"
self.name = ZOMBIE
self.__class__.alive = True
def __copy__(self):
return self
def __deepcopy__(self, memo):
return self
def __call__(self, text):
"""Better call Saul ;-)"""
return None, text
ZOMBIE_PARSER = ZombieParser()
#######################################################################
#
# syntaxtree nodes
......
......@@ -32,8 +32,8 @@ import inspect
from functools import partial, singledispatch
from DHParser.error import Error, ErrorCode
from DHParser.syntaxtree import Node, WHITESPACE_PTYPE, TOKEN_PTYPE, ParserBase, MockParser, \
ZOMBIE_NODE, RootNode, parse_sxpr, flatten_sxpr
from DHParser.syntaxtree import Node, WHITESPACE_PTYPE, TOKEN_PTYPE, ZOMBIE_NODE, RootNode, parse_sxpr, flatten_sxpr
from DHParser.parse import ParserBase, MockParser
from DHParser.toolkit import issubtype, isgenerictype, expand_table, smart_list, re, typing
from typing import AbstractSet, Any, ByteString, Callable, cast, Container, Dict, \
Tuple, List, Sequence, Union, Text, Generic
......
......@@ -32,8 +32,8 @@ from DHParser import logging, is_filename, load_if_file, \
is_empty, is_expendable, collapse, replace_content, WHITESPACE_PTYPE, TOKEN_PTYPE, \
remove_nodes, remove_content, remove_brackets, replace_parser, remove_anonymous_tokens, \
keep_children, is_one_of, has_content, apply_if, remove_first, remove_last, \
remove_anonymous_empty, keep_nodes, traverse_locally, strip, lstrip, rstrip, MockParser, \
ZOMBIE_NODE
remove_anonymous_empty, keep_nodes, traverse_locally, strip, lstrip, rstrip, ZOMBIE_NODE
from DHParser.parse import MockParser
#######################################################################
......@@ -574,7 +574,7 @@ class XMLCompiler(Compiler):
if attributes:
node.attr.update(attributes)
preserve_whitespace |= attributes.get('xml:space', '') == 'preserve'
node.parser = self.get_parser(tag_name)
node.tag_name = tag_name
content = self.compile_children(node.get('content', ZOMBIE_NODE))
if len(content) == 1:
if content[0].tag_name == "CharData":
......
......@@ -18,8 +18,7 @@ try:
import regex as re
except ImportError:
import re
from DHParser import logging, is_filename, load_if_file, MockParser, \
Grammar, Compiler, nil_preprocessor, PreprocessorToken, Whitespace, \
from DHParser import logging, is_filename, load_if_file, Grammar, Compiler, nil_preprocessor, PreprocessorToken, Whitespace, \
Lookbehind, Lookahead, Alternative, Pop, Token, Synonym, AllOf, SomeOf, Unordered, \
Option, NegativeLookbehind, OneOrMore, RegExp, Retrieve, Series, Capture, \
ZeroOrMore, Forward, NegativeLookahead, Required, mixin_comment, compile_source, \
......@@ -34,6 +33,7 @@ from DHParser import logging, is_filename, load_if_file, MockParser, \
remove_anonymous_empty, keep_nodes, traverse_locally, strip, lstrip, rstrip, \
replace_content, replace_content_by, forbid, assert_content, remove_infix_operator, \
error_on, recompile_grammar, GLOBALS
from DHParser.parse import MockParser
#######################################################################
......
......@@ -23,8 +23,8 @@ import copy
import sys
sys.path.extend(['../', './'])
from DHParser.syntaxtree import Node, RootNode, parse_sxpr, parse_xml, flatten_sxpr, flatten_xml, \
MockParser
from DHParser.syntaxtree import Node, RootNode, parse_sxpr, parse_xml, flatten_sxpr, flatten_xml
from DHParser.parse import MockParser
from DHParser.transform import traverse, reduce_single_child, \
replace_by_single_child, flatten, remove_expendables
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, get_ebnf_compiler
......
......@@ -25,7 +25,8 @@ import sys
sys.path.extend(['../', './'])
from DHParser.syntaxtree import Node, parse_sxpr, flatten_sxpr, parse_xml, ZOMBIE_NODE, \
MockParser, TOKEN_PTYPE
TOKEN_PTYPE
from DHParser.parse import MockParser
from DHParser.transform import traverse, reduce_single_child, remove_whitespace, \
traverse_locally, collapse, collapse_if, lstrip, rstrip, remove_content, remove_tokens, \
transformation_factory, has_parent
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment