Hi,
I'm using ply to write a name parsing utility for some software that
manipulates 3D objects and has some strict / easy enough rules to name
them.
However the names are compound / multi parts in nature (a namespace, a
path, attribute names etc), so instead of writing a monolithic parser
I split it in separate elements and I'm using class inheritance to
reuse as much as possible, and parse full names as well as name parts
(the full name or only the namespace...)
For instance namespace and object names follow the same rule so I used
a "Parser" base class to create ply yacc parser clases and define :
class BaseNameParser(Parser) :
""" the tokens and yacc rules that parse a valid most atomic name
"""
(...)
def p_BaseName(self, p):
""" BaseName : some rule here """
(...)
class NameSpaceParser(BaseNameParser) :
""" rules for namespaces, they are a combination of base names
with a separator """
(...)
t_sep = r':'
def p_NameSpace_concat(self, p):
""" NameSpace : NameSpace BaseName sep """
(...)
def p_NameSpace(self, p):
""" NameSpace : BaseName sep """
(...)
class ObjectNameParser(BaseNameParser, NameSpaceParser) :
""" rules for object names, an optionnal namespace followed by a
name """
(...)
def p_ObjectName(self, p):
""" ObjectName : NameSpace BaseName
| BaseName """
if len(p) == 3 :
p[0] = ObjectName(p[1] p[2])
else :
p[0] = ObjectName(p[1])
(...)
It works well and I'm happy with the flexibility it gives. For each
elements of names parsed by these parsers I also generate objects that
correspond to the name rules. So there are classes for "Parsed"
objects as well :
class NameSpace(Parsed) :
""" a valid parsed namespace, some utility functions etc """
and samewise 'BaseName' and 'ObjectName' classes. Parsing a string
returns, if the string represent a valid name, an ObjectName instance,
that contains references to a NameSpace and a BaseName instance :
object = ObjectNameParser.parse(somestring) # an ObjectName
instance
name =
object.name # a
BaseName instance
namespace = object.namespace # a Namespace
instance
With a bit more work I could make the code more dynamic and generate
these "Parsed" classes from the "Parser" classes easily too, as I took
care of matching the names in rules and classes.
Now when I build an 'ObjectName' instance from existing NameSpace and
BaseName instances there should be no need of reparsing the whole
strings, we know the NameSpace and BaseName are valid, we just need to
check whether we can build an ObjectName from a NameSpace and a
BaseName using a valid parsing rule :
object = ObjectName.parse(namespace, name)
I'd like to use yacc to check this as there rule is there already :
def p_ObjectName(self, p):
""" ObjectName : NameSpace BaseName
| BaseName """
So my question is, is there a way to start a yacc parser from a
provided stack / YaccProduction item ? There seem to be dependences on
some global states I'm not sure to understand, being able to call :
ObjectNameParser.p_ObjectName(p) or directly ObjectNameParser.parse(p)
with p being a YaccProduction item with namespace and name in the
stack would mean not having to reparse namespace and name which at
this point I know for being valid anyway. I know this looks like more
the logics of a recursive descent parsing, and maybe it would be
simplier to use one for that task, but it seems ply works in a more
reliable way, and faster than those I tried so far.