Compare commits

...

2 Commits

Author SHA1 Message Date
  Jason Morgan 2be9a3e519 Make xmlast tostring easier to debug 3 weeks ago
  Jason Morgan 7d3e60fb70 Add more debug print 3 weeks ago
5 changed files with 56 additions and 36 deletions
Split View
  1. +43
    -26
      pypeg2/__init__.py
  2. +3
    -2
      pypeg2/xmlast.py
  3. +4
    -3
      samples/sample1.py
  4. +4
    -4
      samples/sample2.py
  5. +2
    -1
      setup.py

+ 43
- 26
pypeg2/__init__.py View File

@ -10,6 +10,7 @@ This program is under GNU General Public License 2.0.
from __future__ import unicode_literals
import collections
try:
range = xrange
str = unicode
@ -236,12 +237,15 @@ class Literal(object):
else:
return False
class Str:
class Str(collections.UserString):
"""A mutable string like object"""
def __new__(cls, x):
return super().__new__(cls, x)
def __init__(self, value, name=None, **kwargs):
super().__init__(self, )
logger.debug(f"New Str({value})")
self.data = str(value)
if name is not None:
self.name = Symbol(name)
for k, v in kwargs:
@ -261,7 +265,7 @@ class Plain(object):
"""Construct a plain object with an optional name and optional other
attributes
"""
logger.debug(f"New Plain({name})")
logger.debug(f"New Plain({repr(name)})")
if name is not None:
self.name = Symbol(name)
for k, v in kwargs:
@ -461,7 +465,7 @@ class Symbol(str):
TypeError if namespace is given and not a Namespace
"""
logger.debug(f"New Symbol({name})")
logger.debug(f"New Symbol({repr(name)})")
if Symbol.check_keywords and name in Keyword.table:
raise ValueError(repr(name)
+ " is a Keyword, but is used as a Symbol")
@ -656,7 +660,7 @@ def how_many(grammar):
def parse(text, thing, filename=None, whitespace=whitespace, comment=None,
keep_feeble_things=False):
keep_feeble_things=False, name=None):
r"""Parse text following thing as grammar and return the resulting things or
raise an error.
@ -685,7 +689,7 @@ def parse(text, thing, filename=None, whitespace=whitespace, comment=None,
"""
logger.debug(f"parse({repr(text)}, {thing})")
parser = Parser()
parser = Parser(name=name)
parser.whitespace = whitespace
parser.comment = comment
parser.text = text
@ -904,7 +908,9 @@ class Parser(object):
try:
return self._memory[id(thing)][text]
ret = self._memory[id(thing)][text]
logger.debug(f"Parser({self.name})._parse() -> cached ret: {repr(ret)}")
return ret
except:
pass
@ -913,7 +919,13 @@ class Parser(object):
else:
current_pos = None
def syntax_error(msg):
def syntax_error(msg, thing=None):
# Not all error propagate, we log all of them
if thing is not None:
type_of_thing = ""
else:
type_of_thing = type(thing)
logger.error(f"Syntax Error: Parser({self.name})._parse({type_of_thing}): {repr(msg)})")
return self.generate_syntax_error(msg, pos)
try:
@ -943,7 +955,7 @@ class Parser(object):
if thing is None or type(thing) == FunctionType:
result = text, None
#
elif isinstance(thing, Symbol):
m = type(thing).regex.match(text)
if m and m.group(0) == str(thing):
@ -952,8 +964,9 @@ class Parser(object):
result = t, r
update_pos(text, t, pos)
else:
result = text, syntax_error("expecting " + repr(thing) + f" in '{text}'")
err = "expecting " + repr(thing) + f" in '{text}'"
result = text, syntax_error(err, thing)
#
elif isinstance(thing, (RegEx, _RegEx)):
m = thing.match(text)
if m:
@ -962,9 +975,9 @@ class Parser(object):
result = t, r
update_pos(text, t, pos)
else:
result = text, syntax_error("expecting match on "
+ thing.pattern + f" in '{text}'")
err = "expecting match on " + thing.pattern + f" in '{text}'"
result = text, syntax_error(err, thing)
#
elif isinstance(thing, (str, Literal)):
if text.startswith(str(thing)):
t, r = text[len(str(thing)):], None
@ -972,9 +985,10 @@ class Parser(object):
result = t, r
update_pos(text, t, pos)
else:
result = text, syntax_error("expecting " + repr(thing) + f" in '{text}'")
elif _issubclass(thing, Symbol):
err = "expecting " + repr(thing) + f" in '{text}'"
result = text, syntax_error(err, thing)
#
elif _issubclass(thing, (Symbol, Str)):
m = thing.regex.match(text)
if m:
result = None
@ -1000,10 +1014,11 @@ class Parser(object):
result = t, r
update_pos(text, t, pos)
else:
result = text, syntax_error("expecting " + thing.__name__ + f" in '{text}'")
err = "expecting " + thing.__name__ + f" in '{text}'"
result = text, syntax_error(err, thing)
#
# non-terminal constructs
#
elif isinstance(thing, attr.Class):
t, r = self._parse(text, thing.thing, pos)
if type(r) == SyntaxError:
@ -1016,7 +1031,7 @@ class Parser(object):
result = t, attr(thing.name, True)
else:
result = t, attr(thing.name, r)
#
elif isinstance(thing, (tuple, Concat)):
if self.keep_feeble_things:
L = List()
@ -1133,7 +1148,7 @@ class Parser(object):
else:
result = text, r
self._contiguous = contiguous
#
elif isinstance(thing, list):
found = False
for e in thing:
@ -1150,7 +1165,7 @@ class Parser(object):
result = t, r
else:
result = text, syntax_error("expecting one of " + repr(thing))
#
elif _issubclass(thing, Namespace):
t, r = self._parse(text, thing.grammar, pos)
if type(r) != SyntaxError:
@ -1174,7 +1189,7 @@ class Parser(object):
result = t, obj
else:
result = text, r
#
elif _issubclass(thing, list):
try:
g = thing.grammar
@ -1204,7 +1219,7 @@ class Parser(object):
result = t, obj
else:
result = text, r
#
elif _issubclass(thing, object):
try:
g = thing.grammar
@ -1256,10 +1271,12 @@ class Parser(object):
result = t, obj
else:
result = text, r
#
else:
raise GrammarTypeError("in grammar: " + repr(thing))
logger.debug(f"Parser({self.name}).parse() result: {repr(result)}")
if pos:
if type(result[1]) == SyntaxError:
pos[0] = current_pos[0]


+ 3
- 2
pypeg2/xmlast.py View File

@ -120,14 +120,15 @@ def thing2xml(thing, pretty=False, object_names=False):
tree = create_tree(thing, None, object_names)
try:
if lxml:
return etree.tostring(tree, pretty_print=pretty)
txt = etree.tostring(tree, pretty_print=pretty)
except NameError:
if __debug__:
if pretty:
warnings.warn("lxml is needed for pretty printing",
ImportWarning)
etree.indent(tree)
return etree.tostring(tree)
txt = etree.tostring(tree)
return txt
def create_thing(element, symbol_table):


+ 4
- 3
samples/sample1.py View File

@ -58,12 +58,13 @@ pyPEG contains an XML backend, too:
>>> del f[2]
>>> from pypeg2.xmlast import thing2xml
>>> import sys
>>> xml = thing2xml(f, pretty=True)
>>> print(xml.decode())
>>> print(xml.decode(), end="") # doctest: +ELLIPSIS
<Function typing="int" name="f">
<Parameters>
<Parameter typing="int" name="a" />
<Parameter typing="long" name="b" />
<Parameter typing="int" name="a".../>
<Parameter typing="long" name="b".../>
</Parameters>
<Instruction>do_this</Instruction>
<Instruction>do_that</Instruction>


+ 4
- 4
samples/sample2.py View File

@ -53,7 +53,7 @@ twice=goes
pyPEG contains an XML backend, too:
>>> from pypeg2.xmlast import thing2xml
>>> print(thing2xml(ini_file, pretty=True).decode())
>>> print(thing2xml(ini_file, pretty=True).decode()) # doctest: +ELLIPSIS
<IniFile>
<Section name="Number 1">
<Key name="this">something</Key>
@ -63,13 +63,13 @@ pyPEG contains an XML backend, too:
<Key name="once">anything</Key>
<Key name="twice">goes</Key>
</Section>
<Section name="Number 3" />
<Section name="Number 3".../>
</IniFile>
In this sample the tree contains named objects only. Then we can output object
names as tag names. Spaces in names will be translated into underscores.
>>> print(thing2xml(ini_file, pretty=True, object_names=True).decode())
>>> print(thing2xml(ini_file, pretty=True, object_names=True).decode()) # doctest: +ELLIPSIS
<IniFile>
<Number_1>
<this>something</this>
@ -79,7 +79,7 @@ names as tag names. Spaces in names will be translated into underscores.
<once>anything</once>
<twice>goes</twice>
</Number_2>
<Number_3 />
<Number_3.../>
</IniFile>
"""


+ 2
- 1
setup.py View File

@ -13,7 +13,8 @@ setup(
license='LICENSE.txt',
description='An intrinsic PEG Parser-Interpreter for Python',
long_description=open('README.md').read(),
requires=['lxml',],
install_requires=['lxml',],
extras_require={'dev': ['pytest', ], },
provides=['pyPEG2 (' + _version + ')',],
classifiers=[
'Development Status :: 5 - Production/Stable',


Loading…
Cancel
Save