Restructured modules, fixed intra-package imports and applied pep8.

parent d83b7274
from src.python.node import BisonNode
from src.python.bison import BisonParser, BisonSyntaxError
__all__ = ['BisonNode', 'BisonParser', 'BisonSyntaxError']
...@@ -4,10 +4,11 @@ A simple pybison parser program implementing a calculator ...@@ -4,10 +4,11 @@ A simple pybison parser program implementing a calculator
""" """
from bison import BisonParser from bison import BisonParser
class Parser(BisonParser): class Parser(BisonParser):
""" """
Implements the calculator parser. Grammar rules are defined in the method docstrings. Implements the calculator parser. Grammar rules are defined in the method
Scanner rules are in the 'lexscript' attribute. docstrings. Scanner rules are in the 'lexscript' attribute.
""" """
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# lexer tokens - these must match those in your lex script (below) # lexer tokens - these must match those in your lex script (below)
...@@ -105,9 +106,13 @@ class Parser(BisonParser): ...@@ -105,9 +106,13 @@ class Parser(BisonParser):
#define YYSTYPE void * #define YYSTYPE void *
#include "tokens.h" #include "tokens.h"
extern void *py_parser; extern void *py_parser;
extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size); extern void (*py_input)(PyObject *parser, char *buf, int *result,
#define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok); int max_size);
#define YY_INPUT(buf,result,max_size) { (*py_input)(py_parser, buf, &result, max_size); } #define returntoken(tok) \
yylval = PyString_FromString(strdup(yytext)); return (tok);
#define YY_INPUT(buf,result,max_size) { \
(*py_input)(py_parser, buf, &result, max_size); \
}
%} %}
%% %%
...@@ -124,7 +129,8 @@ class Parser(BisonParser): ...@@ -124,7 +129,8 @@ class Parser(BisonParser):
[ \t\v\f] {} [ \t\v\f] {}
[\n] {yylineno++; returntoken(NEWLINE); } [\n] {yylineno++; returntoken(NEWLINE); }
. { printf("unknown char %c ignored, yytext=0x%lx\n", yytext[0], yytext); /* ignore bad chars */} . { printf("unknown char %c ignored, yytext=0x%lx\n", yytext[0],
yytext); /* ignore bad chars */}
%% %%
...@@ -132,5 +138,4 @@ class Parser(BisonParser): ...@@ -132,5 +138,4 @@ class Parser(BisonParser):
""" """
if __name__ == '__main__': if __name__ == '__main__':
p = Parser() Parser().run()
p.run()
...@@ -4,10 +4,11 @@ A simple pybison parser program implementing a calculator ...@@ -4,10 +4,11 @@ A simple pybison parser program implementing a calculator
""" """
from bison import BisonParser from bison import BisonParser
class Parser(BisonParser): class Parser(BisonParser):
""" """
Implements the calculator parser. Grammar rules are defined in the method docstrings. Implements the calculator parser. Grammar rules are defined in the method
Scanner rules are in the 'lexscript' attribute. docstrings. Scanner rules are in the 'lexscript' attribute.
""" """
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# lexer tokens - these must match those in your lex script (below) # lexer tokens - these must match those in your lex script (below)
...@@ -105,9 +106,13 @@ class Parser(BisonParser): ...@@ -105,9 +106,13 @@ class Parser(BisonParser):
#define YYSTYPE void * #define YYSTYPE void *
#include "tokens.h" #include "tokens.h"
extern void *py_parser; extern void *py_parser;
extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size); extern void (*py_input)(PyObject *parser, char *buf, int *result,
#define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok); int max_size);
#define YY_INPUT(buf,result,max_size) { (*py_input)(py_parser, buf, &result, max_size); } #define returntoken(tok) \
yylval = PyString_FromString(strdup(yytext)); return (tok);
#define YY_INPUT(buf,result,max_size) { \
(*py_input)(py_parser, buf, &result, max_size); \
}
%} %}
%% %%
...@@ -124,7 +129,8 @@ class Parser(BisonParser): ...@@ -124,7 +129,8 @@ class Parser(BisonParser):
[ \t\v\f] {} [ \t\v\f] {}
[\n] {yylineno++; returntoken(NEWLINE); } [\n] {yylineno++; returntoken(NEWLINE); }
. { printf("unknown char %c ignored, yytext=0x%lx\n", yytext[0], yytext); /* ignore bad chars */} . { printf("unknown char %c ignored, yytext=0x%lx\n", yytext[0],
yytext); /* ignore bad chars */}
%% %%
......
#@+leo-ver=4
#@+node:@file setup.py
""" """
Builds bison python module Builds bison python module
""" """
version = "0.1" version = '0.1'
from distutils.core import setup from distutils.core import setup
from distutils.extension import Extension from distutils.extension import Extension
...@@ -13,29 +11,28 @@ from Pyrex.Distutils import build_ext ...@@ -13,29 +11,28 @@ from Pyrex.Distutils import build_ext
import sys import sys
if sys.platform == 'win32': if sys.platform == 'win32':
print "Sorry - no windows support at this time - pybison won't work for you" print 'No windows support at this time. PyBison won\'t work for you :('
#sys.exit(1)
libs = [] libs = []
extra_link_args = [] extra_link_args = []
bison2pyscript = 'utils/bison2py.py' bison2pyscript = 'utils/bison2py.py'
bisondynlibModule = "src/c/bisondynlib-win32.c" bisondynlibModule = 'src/c/bisondynlib-win32.c'
elif sys.platform == 'linux2': elif sys.platform == 'linux2':
libs = ['dl'] libs = ['dl']
extra_link_args = [] extra_link_args = []
bison2pyscript = 'utils/bison2py' bison2pyscript = 'utils/bison2py'
bisondynlibModule = "src/c/bisondynlib-linux.c" bisondynlibModule = 'src/c/bisondynlib-linux.c'
else: else:
print "Sorry, your platform is presently unsupported" print 'Sorry, your platform is presently unsupported.'
sys.exit(1) sys.exit(1)
setup( setup(
name = "bison", name='bison',
version = version, version=version,
description='Python bindings for bison/flex parser engine', description='Python bindings for bison/flex parser engine',
author='David McNab <david@freenet.org.nz>', author='David McNab <david@freenet.org.nz>',
url='http://www.freenet.org.nz/python/pybison', url='http://www.freenet.org.nz/python/pybison',
ext_modules=[ ext_modules=[
Extension("bison_", ["src/pyrex/bison_.pyx", bisondynlibModule], Extension('bison_', ['src/pyrex/bison_.pyx', bisondynlibModule],
libraries=libs, libraries=libs,
extra_link_args=extra_link_args, extra_link_args=extra_link_args,
) )
...@@ -43,10 +40,6 @@ setup( ...@@ -43,10 +40,6 @@ setup(
packages=[], packages=[],
package_dir={'': 'src/python'}, package_dir={'': 'src/python'},
py_modules=['bison'], py_modules=['bison'],
cmdclass = {'build_ext': build_ext}, cmdclass={'build_ext': build_ext},
scripts=[bison2pyscript], scripts=[bison2pyscript],
) )
#@-node:@file setup.py
#@-leo
...@@ -18,18 +18,12 @@ for a commercial license. ...@@ -18,18 +18,12 @@ for a commercial license.
""" """
import sys import sys
#import imp
import traceback import traceback
import xml.dom
import xml.dom.minidom
import types
#import distutils.sysconfig
#import distutils.ccompiler
from bison_ import ParserEngine from bison_ import ParserEngine
class ParserSyntaxError(Exception): class BisonSyntaxError(Exception):
pass pass
...@@ -37,173 +31,6 @@ class TimeoutError(Exception): ...@@ -37,173 +31,6 @@ class TimeoutError(Exception):
pass pass
class BisonError(object):
"""
Flags an error to yyparse()
You should return this in your actions to notify a syntax error
"""
_pyBisonError = 1
def __init__(self, error, traceback_info):
self.value = error
self.traceback_info = traceback_info
class BisonException(Exception):
_pyBisonError = 1
def __init__(self, value='syntax error'):
self.value = value
class BisonNode:
"""
Generic class for wrapping parse targets.
Arguments:
- targetname - the name of the parse target being wrapped.
- items - optional - a list of items comprising a clause
in the target rule - typically this will only be used
by the PyBison callback mechanism.
Keywords:
- any keywords you want (except 'items'), with any type of value.
keywords will be stored as attributes in the constructed object.
"""
def __init__(self, **kw):
self.__dict__.update(kw)
# ensure some default attribs
self.target = kw.get('target', 'UnnamedTarget')
self.names = kw.get('names', [])
self.values = kw.get('values', [])
self.option = kw.get('option', 0)
# mirror this dict to simplify dumping
self.kw = kw
def __str__(self):
return '<BisonNode:%s>' % self.target
def __repr__(self):
return str(self)
def __getitem__(self, item):
"""
Retrieves the ith value from this node, or child nodes
If the subscript is a single number, it will be used as an
index into this node's children list.
If the subscript is a list or tuple, we recursively fetch
the item by using the first element as an index into this
node's children, the second element as an index into that
child node's children, and so on
"""
if type(item) in [type(0), type(0L)]:
return self.values[item]
elif type(item) in [type(()), type([])]:
if len(item) == 0:
return self
return self.values[item[0]][item[1:]]
else:
raise TypeError('Can only index %s objects with an int or a'
' list/tuple' % self.__class.__name__)
def __len__(self):
return len(self.values)
def __getslice__(self, fromidx, toidx):
return self.values[fromidx:toidx]
def __iter__(self):
return iter(self.values)
def dump(self, indent=0):
"""
For debugging - prints a recursive dump of a parse tree node and its children
"""
specialAttribs = ['option', 'target', 'names', 'values']
indents = ' ' * indent * 2
#print "%s%s: %s %s" % (indents, self.target, self.option, self.names)
print '%s%s:' % (indents, self.target)
for name, val in self.kw.items() + zip(self.names, self.values):
if name in specialAttribs or name.startswith('_'):
continue
if isinstance(val, BisonNode):
val.dump(indent+1)
else:
print indents + ' %s=%s' % (name, val)
def toxml(self):
"""
Returns an xml serialisation of this node and its children, as a raw string
Called on the toplevel node, the xml is a representation of the
entire parse tree.
"""
return self.toxmldoc().toxml()
def toprettyxml(self, indent=' ', newl='\n', encoding=None):
"""
Returns a human-readable xml serialisation of this node and its
children.
"""
return self.toxmldoc().toprettyxml(indent=indent,
newl=newl,
encoding=encoding)
def toxmldoc(self):
"""
Returns the node and its children as an xml.dom.minidom.Document
object.
"""
d = xml.dom.minidom.Document()
d.appendChild(self.toxmlelem(d))
return d
def toxmlelem(self, docobj):
"""
Returns a DOM Element object of this node and its children.
"""
specialAttribs = ['option', 'target', 'names', 'values']
# generate an xml element obj for this node
x = docobj.createElement(self.target)
# set attribs
for name, val in self.kw.items():
if name in ['names', 'values'] or name.startswith('_'):
continue
x.setAttribute(name, str(val))
#x.setAttribute('target', self.target)
#x.setAttribute('option', self.option)
# and add the children
for name, val in zip(self.names, self.values):
if name in specialAttribs or name.startswith('_'):
continue
if isinstance(val, BisonNode):
x.appendChild(val.toxmlelem(docobj))
else:
sn = docobj.createElement(name)
sn.setAttribute('target', name)
tn = docobj.createTextNode(val)
sn.appendChild(tn)
x.appendChild(sn)
# done
return x
class BisonParser(object): class BisonParser(object):
""" """
Base parser class Base parser class
...@@ -215,43 +42,65 @@ class BisonParser(object): ...@@ -215,43 +42,65 @@ class BisonParser(object):
# --------------------------------------- # ---------------------------------------
# override these if you need to # override these if you need to
# command and options for running yacc/bison, except for filename arg # Command and options for running yacc/bison, except for filename arg
bisonCmd = ['bison', '-d', '-v', '-t'] bisonCmd = ['bison', '-d', '-v', '-t']
bisonFile = 'tmp.y' bisonFile = 'tmp.y'
bisonCFile = 'tmp.tab.c' bisonCFile = 'tmp.tab.c'
bisonHFile = 'tmp.tab.h' # name of header file generated by bison cmd
bisonCFile1 = 'tmp.bison.c' # c output file from bison gets renamed to this # Name of header file generated by bison cmd.
bisonHFile1 = 'tokens.h' # bison-generated header file gets renamed to this bisonHFile = 'tmp.tab.h'
# C output file from bison gets renamed to this.
bisonCFile1 = 'tmp.bison.c'
flexCmd = ['flex', ] # command and options for running [f]lex, except for filename arg # Bison-generated header file gets renamed to this.
bisonHFile1 = 'tokens.h'
# command and options for running [f]lex, except for filename arg.
flexCmd = ['flex', ]
flexFile = 'tmp.l' flexFile = 'tmp.l'
flexCFile = 'lex.yy.c' flexCFile = 'lex.yy.c'
flexCFile1 = 'tmp.lex.c' # c output file from lex gets renamed to this # C output file from flex gets renamed to this.
flexCFile1 = 'tmp.lex.c'
cflags_pre = ['-fPIC'] # = CFLAGS added before all arguments. # CFLAGS added before all command line arguments.
cflags_post = ['-O3','-g'] # = CFLAGS added after all arguments. cflags_pre = ['-fPIC']
buildDirectory = './' # Directory used to store the generated / compiled files. # CFLAGS added after all command line arguments.
debugSymbols = 1 # Add debugging symbols to the binary files. cflags_post = ['-O3', '-g']
verbose = 0 # Directory used to store the generated / compiled files.
buildDirectory = './'
# Add debugging symbols to the binary files.
debugSymbols = 1
timeout = 1 # Timeout in seconds after which a computation is terminated. # Enable verbose debug message sent to stdout.
verbose = 0
file = None # default to sys.stdin # Timeout in seconds after which the parser is terminated.
# TODO: this is currently not implemented.
timeout = 1
last = None # last parsed target, top of parse tree # Default to sys.stdin.
file = None
last_error = None # gets set if there was an error # Last parsed target, top of parse tree.
last = None
keepfiles = 0 # set to 1 to keep temporary engine build files # Enable this to keep all temporary engine build files.
keepfiles = 0
bisonEngineLibName = None # defaults to 'modulename-engine' # Prefix of the shared object / dll file. Defaults to 'modulename-engine'.
# If the module is executed directly, "__main__" will be used (since that
# that is the "module name", in that case).
bisonEngineLibName = None
defaultNodeClass = BisonNode # class to use by default for creating new parse nodes # Class to use by default for creating new parse nodes. If set to None,
# BisonNode will be used.
default_node_class = None
def __init__(self, **kw): def __init__(self, **kw):
""" """
...@@ -313,7 +162,8 @@ class BisonParser(object): ...@@ -313,7 +162,8 @@ class BisonParser(object):
Tries to dispatch to on_TargetName() methods if they exist, Tries to dispatch to on_TargetName() methods if they exist,
otherwise wraps the target in a BisonNode object otherwise wraps the target in a BisonNode object
""" """
handler = getattr(self, 'on_'+targetname, None) handler = getattr(self, 'on_' + targetname, None)
if handler: if handler:
if self.verbose: if self.verbose:
try: try:
...@@ -333,10 +183,13 @@ class BisonParser(object): ...@@ -333,10 +183,13 @@ class BisonParser(object):
else: else:
if self.verbose: if self.verbose:
print 'no handler for %s, using default' % targetname print 'no handler for %s, using default' % targetname
self.last = BisonNode(targetname, option=option, names=names, values=values)
# reset any resulting errors (assume they've been handled) if not self.default_node_class:
#self.last_error = None from .node import BisonNode
self.default_node_class = BisonNode
self.last = self.default_node_class(targetname, option=option,
names=names, values=values)
# assumedly the last thing parsed is at the top of the tree # assumedly the last thing parsed is at the top of the tree
return self.last return self.last
...@@ -435,39 +288,34 @@ class BisonParser(object): ...@@ -435,39 +288,34 @@ class BisonParser(object):
return bytes return bytes
#def _error(self, linenum, msg, tok): def report_last_error(self, filename, error):
# # TODO: should this function be removed? """
# print 'Parser: line %s: syntax error "%s" before "%s"' \ Report a raised exception. Depending on the mode in which the parser is
# % (linenum, msg, tok) running, it will:
#def error(self, exception, traceback_info): - write a verbose message to stderr (verbose=True; interactive=True).
# """ The written error message will include the type, value and traceback
# Return the result of this method from a handler to notify a syntax error of the raised exception.
# """
# # TODO: should this function be removed?
# self.last_error = BisonError(exception, traceback_info)
# return self.last_error - write a minimal message to stderr (verbose=False; interactive=True).
The written error message will only include the type and value of
the raised exception.
#def exception(self, exception): """
# # TODO: should this function be removed?
# self.lastexception = exception
# return BisonException(exception)
def report_last_error(self, filename, error):
if filename != None: if filename != None:
msg = '%s:%d: "%s" near "%s"' \ msg = '%s:%d: "%s" near "%s"' \
% ((filename,) + error) % ((filename,) + error)
if not self.interactive: if not self.interactive:
raise ParserSyntaxError(msg) raise BisonSyntaxError(msg)
print >>sys.stderr, msg print >>sys.stderr, msg
elif hasattr(error, '__getitem__') and isinstance(error[0], int): elif hasattr(error, '__getitem__') and isinstance(error[0], int):
msg = 'Line %d: "%s" near "%s"' % error msg = 'Line %d: "%s" near "%s"' % error
if not self.interactive: if not self.interactive:
raise ParserSyntaxError(msg) raise BisonSyntaxError(msg)
print >>sys.stderr, msg print >>sys.stderr, msg
else: else:
...@@ -478,122 +326,3 @@ class BisonParser(object): ...@@ -478,122 +326,3 @@ class BisonParser(object):
traceback.print_exc() traceback.print_exc()
print 'ERROR:', error print 'ERROR:', error
def toxml(self):
"""
Serialises the parse tree and returns it as a raw xml string
"""
# TODO: should this function be moved to another file?
return self.last.toxml()
def toxmldoc(self):
"""
Returns an xml.dom.minidom.Document object containing the parse tree
"""
# TODO: should this function be moved to another file?
return self.last.toxmldoc()
def toprettyxml(self):
"""
Returns a human-readable xml representation of the parse tree
"""
# TODO: should this function be moved to another file?
return self.last.toprettyxml()
def loadxml(self, raw, namespace=None):
"""
Loads a parse tree from raw xml text
Stores it in the '.last' attribute, which is where the root node
of parsed text gets stored
Arguments:
- raw - string containing the raw xml
- namespace - a dict or module object, where the node classes required for
reconstituting the parse tree, can be found
Returns:
- root node object of reconstituted parse tree
"""
# TODO: should this function be moved to another file?
doc = xml.dom.minidom.parseString(raw)
tree = self.loadxmldoc(doc, namespace)
self.last = tree
return tree
def loadxmldoc(self, xmldoc, namespace=None):
"""
Returns a reconstituted parse tree, loaded from an
xml.dom.minidom.Document instance
Arguments:
- xmldoc - an xml.dom.minidom.Document instance
- namespace - a dict from which to find the classes needed
to translate the document into a tree of parse nodes
"""
# TODO: should this function be moved to another file?
return self.loadxmlobj(xmldoc.childNodes[0], namespace)
def loadxmlobj(self, xmlobj, namespace=None):
"""
Returns a node object, being a parse tree, reconstituted from an
xml.dom.minidom.Element object
Arguments:
- xmlobj - an xml.dom.minidom.Element instance
- namespace - a namespace from which the node classes
needed for reconstituting the tree, can be found
"""
# TODO: should this function be moved to another file?
# check on namespace
if type(namespace) is types.ModuleType:
namespace = namespace.__dict__
elif namespace == None:
namespace = globals()
objname = xmlobj.tagName
classname = objname + '_Node'
classobj = namespace.get(classname, None)
namespacekeys = namespace.keys()
# barf if node is not a known parse node or token
if (not classobj) and objname not in self.tokens:
raise Exception('Cannot reconstitute %s: can\'t find required'
' node class or token %s' % (objname, classname))
if classobj:
nodeobj = classobj()
# add the attribs
for k,v in xmlobj.attributes.items():
setattr(nodeobj, k, v)
else:
nodeobj = None
#print '----------------'
#print 'objname=%s' % repr(objname)
#print 'classname=%s' % repr(classname)
#print 'classobj=%s' % repr(classobj)
#print 'nodeobj=%s' % repr(nodeobj)
# now add the children
for child in xmlobj.childNodes:
#print '%s attributes=%s' % (child, child.attributes.items())
childname = child.attributes['target'].value
#print 'childname=%s' % childname
if childname + '_Node' in namespacekeys:
#print 'we have a node for class %s' % classname
childobj = self.loadxmlobj(child, namespace)
else:
# it's a token
childobj = child.childNodes[0].nodeValue
#print 'got token %s=%s' % (childname, childobj)
nodeobj.names.append(childname)
nodeobj.values.append(childobj)
return nodeobj
def _globals(self):
return globals().keys()
...@@ -187,7 +187,7 @@ def bisonToPython(bisonfileName, lexfileName, pyfileName, generateClasses=0): ...@@ -187,7 +187,7 @@ def bisonToPython(bisonfileName, lexfileName, pyfileName, generateClasses=0):
tmp = map(' '.join, options) tmp = map(' '.join, options)
# totally self-indulgent grammatical pedantry # totally self-indulgent grammatical pedantry
if target[0].lower() in ['a','e','i','o','u']: if target[0].lower() in ['a', 'e', 'i', 'o', 'u']:
plural = 'n' plural = 'n'
else: else:
plural = '' plural = ''
...@@ -205,7 +205,6 @@ def bisonToPython(bisonfileName, lexfileName, pyfileName, generateClasses=0): ...@@ -205,7 +205,6 @@ def bisonToPython(bisonfileName, lexfileName, pyfileName, generateClasses=0):
'\n', '\n',
])) ]))
# start churning out the class dec # start churning out the class dec
pyfile.write('\n'.join([ pyfile.write('\n'.join([
'class Parser(BisonParser):', 'class Parser(BisonParser):',
......
"""
Generic module for wrapping parse targets.
Written April 2004 by David McNab <david@freenet.org.nz>
Copyright (c) 2004 by David McNab, all rights reserved.
Released under the GNU General Public License, a copy of which should appear in
this distribution in the file called 'COPYING'. If this file is missing, then
you can obtain a copy of the GPL license document from the GNU website at
http://www.gnu.org.
This software is released with no warranty whatsoever. Use it at your own
risk.
If you wish to use this software in a commercial application, and wish to
depart from the GPL licensing requirements, please contact the author and apply
for a commercial license.
"""
import xml
class BisonNode:
"""
Generic class for wrapping parse targets.
Arguments:
- targetname - the name of the parse target being wrapped.
- items - optional - a list of items comprising a clause
in the target rule - typically this will only be used
by the PyBison callback mechanism.
Keywords:
- any keywords you want (except 'items'), with any type of value.
keywords will be stored as attributes in the constructed object.
"""
def __init__(self, **kw):
self.__dict__.update(kw)
# ensure some default attribs
self.target = kw.get('target', 'UnnamedTarget')
self.names = kw.get('names', [])
self.values = kw.get('values', [])
self.option = kw.get('option', 0)
# mirror this dict to simplify dumping
self.kw = kw
def __str__(self):
return '<BisonNode:%s>' % self.target
def __repr__(self):
return str(self)
def __getitem__(self, item):
"""
Retrieves the ith value from this node, or child nodes
If the subscript is a single number, it will be used as an
index into this node's children list.
If the subscript is a list or tuple, we recursively fetch
the item by using the first element as an index into this
node's children, the second element as an index into that
child node's children, and so on
"""
if type(item) in [type(0), type(0L)]:
return self.values[item]
elif type(item) in [type(()), type([])]:
if len(item) == 0:
return self
return self.values[item[0]][item[1:]]
else:
raise TypeError('Can only index %s objects with an int or a'
' list/tuple' % self.__class.__name__)
def __len__(self):
return len(self.values)
def __getslice__(self, fromidx, toidx):
return self.values[fromidx:toidx]
def __iter__(self):
return iter(self.values)
def dump(self, indent=0):
"""
For debugging - prints a recursive dump of a parse tree node and its children
"""
specialAttribs = ['option', 'target', 'names', 'values']
indents = ' ' * indent * 2
#print "%s%s: %s %s" % (indents, self.target, self.option, self.names)
print '%s%s:' % (indents, self.target)
for name, val in self.kw.items() + zip(self.names, self.values):
if name in specialAttribs or name.startswith('_'):
continue
if isinstance(val, BisonNode):
val.dump(indent + 1)
else:
print indents + ' %s=%s' % (name, val)
def toxml(self):
"""
Returns an xml serialisation of this node and its children, as a raw string
Called on the toplevel node, the xml is a representation of the
entire parse tree.
"""
return self.toxmldoc().toxml()
def toprettyxml(self, indent=' ', newl='\n', encoding=None):
"""
Returns a human-readable xml serialisation of this node and its
children.
"""
return self.toxmldoc().toprettyxml(indent=indent,
newl=newl,
encoding=encoding)
def toxmldoc(self):
"""
Returns the node and its children as an xml.dom.minidom.Document
object.
"""
d = xml.dom.minidom.Document()
d.appendChild(self.toxmlelem(d))
return d
def toxmlelem(self, docobj):
"""
Returns a DOM Element object of this node and its children.
"""
specialAttribs = ['option', 'target', 'names', 'values']
# generate an xml element obj for this node
x = docobj.createElement(self.target)
# set attribs
for name, val in self.kw.items():
if name in ['names', 'values'] or name.startswith('_'):
continue
x.setAttribute(name, str(val))
#x.setAttribute('target', self.target)
#x.setAttribute('option', self.option)
# and add the children
for name, val in zip(self.names, self.values):
if name in specialAttribs or name.startswith('_'):
continue
if isinstance(val, BisonNode):
x.appendChild(val.toxmlelem(docobj))
else:
sn = docobj.createElement(name)
sn.setAttribute('target', name)
tn = docobj.createTextNode(val)
sn.appendChild(tn)
x.appendChild(sn)
# done
return x
"""
Wrapper module for importing and exporting bison grammar from/to XML.
Written April 2004 by David McNab <david@freenet.org.nz>
Copyright (c) 2004 by David McNab, all rights reserved.
Released under the GNU General Public License, a copy of which should appear in
this distribution in the file called 'COPYING'. If this file is missing, then
you can obtain a copy of the GPL license document from the GNU website at
http://www.gnu.org.
This software is released with no warranty whatsoever. Use it at your own
risk.
If you wish to use this software in a commercial application, and wish to
depart from the GPL licensing requirements, please contact the author and apply
for a commercial license.
"""
# TODO: use cElementTree instead of Python's xml module.
# TODO: test this module, since it is currently only moved to another file.
import xml.dom
import xml.dom.minidom
import types
class XMLifier(object):
def __init__(self, parser):
self.parser = parser
def toxml(self):
"""
Serialises the parse tree and returns it as a raw xml string
"""
return self.parser.last.toxml()
def toxmldoc(self):
"""
Returns an xml.dom.minidom.Document object containing the parse tree
"""
return self.parser.last.toxmldoc()
def toprettyxml(self):
"""
Returns a human-readable xml representation of the parse tree
"""
return self.parser.last.toprettyxml()
def loadxml(self, raw, namespace=None):
"""
Loads a parse tree from raw xml text.
Arguments:
- raw - string containing the raw xml
- namespace - a dict or module object, where the node classes required for
reconstituting the parse tree, can be found
Returns:
- root node object of reconstituted parse tree
"""
doc = xml.dom.minidom.parseString(raw)
tree = self.loadxmldoc(doc, namespace)
return tree
def loadxmldoc(self, xmldoc, namespace=None):
"""
Returns a reconstituted parse tree, loaded from an
xml.dom.minidom.Document instance
Arguments:
- xmldoc - an xml.dom.minidom.Document instance
- namespace - a dict from which to find the classes needed
to translate the document into a tree of parse nodes
"""
return self.loadxmlobj(xmldoc.childNodes[0], namespace)
def loadxmlobj(self, xmlobj, namespace=None):
"""
Returns a node object, being a parse tree, reconstituted from an
xml.dom.minidom.Element object
Arguments:
- xmlobj - an xml.dom.minidom.Element instance
- namespace - a namespace from which the node classes
needed for reconstituting the tree, can be found
"""
# check on namespace
if type(namespace) is types.ModuleType:
namespace = namespace.__dict__
elif namespace == None:
namespace = globals()
objname = xmlobj.tagName
classname = objname + '_Node'
classobj = namespace.get(classname, None)
namespacekeys = namespace.keys()
# barf if node is not a known parse node or token
if (not classobj) and objname not in self.tokens:
raise Exception('Cannot reconstitute %s: can\'t find required'
' node class or token %s' % (objname, classname))
if classobj:
nodeobj = classobj()
# add the attribs
for k, v in xmlobj.attributes.items():
setattr(nodeobj, k, v)
else:
nodeobj = None
#print '----------------'
#print 'objname=%s' % repr(objname)
#print 'classname=%s' % repr(classname)
#print 'classobj=%s' % repr(classobj)
#print 'nodeobj=%s' % repr(nodeobj)
# now add the children
for child in xmlobj.childNodes:
#print '%s attributes=%s' % (child, child.attributes.items())
childname = child.attributes['target'].value
#print 'childname=%s' % childname
if childname + '_Node' in namespacekeys:
#print 'we have a node for class %s' % classname
childobj = self.loadxmlobj(child, namespace)
else:
# it's a token
childobj = child.childNodes[0].nodeValue
#print 'got token %s=%s' % (childname, childobj)
nodeobj.names.append(childname)
nodeobj.values.append(childobj)
return nodeobj
#!/usr/bin/env python #!/usr/bin/env python
#@+leo-ver=4
#@+node:@file utils/bison2py
#@@first
#@@language python
""" """
Utility which creates a boilerplate pybison-compatible Utility which creates a boilerplate pybison-compatible
python file from a yacc file and lex file python file from a yacc file and lex file
...@@ -11,20 +6,17 @@ python file from a yacc file and lex file ...@@ -11,20 +6,17 @@ python file from a yacc file and lex file
Run it with 2 arguments - filename.y and filename.l Run it with 2 arguments - filename.y and filename.l
Output is filename.py Output is filename.py
""" """
#@+others
#@+node:imports import sys, re
import sys, os, re, getopt
from bison import bisonToPython from bison import bisonToPython
#@-node:imports
#@+node:globals
argv = sys.argv argv = sys.argv
argc = len(argv) argc = len(argv)
progname = argv[0] progname = argv[0]
reSpaces = re.compile("\\s+") reSpaces = re.compile("\\s+")
#@-node:globals
#@+node:usage
def usage(s=None): def usage(s=None):
""" """
Display usage info and exit Display usage info and exit
...@@ -47,8 +39,6 @@ def usage(s=None): ...@@ -47,8 +39,6 @@ def usage(s=None):
sys.exit(1) sys.exit(1)
#@-node:usage
#@+node:main
def main(): def main():
""" """
Command-line interface for bison2py Command-line interface for bison2py
...@@ -74,11 +64,5 @@ def main(): ...@@ -74,11 +64,5 @@ def main():
bisonToPython(bisonfile, lexfile, pyfile, generateClasses) bisonToPython(bisonfile, lexfile, pyfile, generateClasses)
#@-node:main
#@+node:MAINLINE
if __name__ == '__main__': if __name__ == '__main__':
main() main()
#@-node:MAINLINE
#@-others
#@-node:@file utils/bison2py
#@-leo
...@@ -18,16 +18,16 @@ def usage(s=None): ...@@ -18,16 +18,16 @@ def usage(s=None):
progname = sys.argv[0] progname = sys.argv[0]
if s: if s:
print progname+": "+s print progname + ': ' + s
print "\n".join([ print '\n'.join([
"Usage: %s [-c] basefilename" % progname, 'Usage: %s [-c] basefilename' % progname,
" or: %s [-c] grammarfile.y lexfile.l pyfile.py" % progname, ' or: %s [-c] grammarfile.y lexfile.l pyfile.py' % progname,
"(generates a boilerplate python file from a grammar and lex file)", '(generates a boilerplate python file from a grammar and lex file)',
"The first form uses 'basefilename' as base for all files, so:", 'The first form uses "basefilename" as base for all files, so:',
" %s fred" % progname, ' %s fred' % progname,
"is equivalent to:", 'is equivalent to:',
" %s fred.y fred.l fred.py" % progname, ' %s fred.y fred.l fred.py' % progname,
'', '',
'The "-c" argument causes the creation of a unique node class', 'The "-c" argument causes the creation of a unique node class',
'for each parse target - highly recommended for complex grammars', 'for each parse target - highly recommended for complex grammars',
...@@ -52,13 +52,13 @@ def main(): ...@@ -52,13 +52,13 @@ def main():
if argc == 2: if argc == 2:
basename = argv[1] basename = argv[1]
bisonfile = basename+".y" bisonfile = basename + '.y'
lexfile = basename+".l" lexfile = basename + '.l'
pyfile = basename+".py" pyfile = basename + '.py'
elif argc == 4: elif argc == 4:
bisonfile, lexfile, pyfile = argv[1:4] bisonfile, lexfile, pyfile = argv[1:4]
else: else:
usage("Bad argument count") usage('Bad argument count')
bisonToPython(bisonfile, lexfile, pyfile, generateClasses) bisonToPython(bisonfile, lexfile, pyfile, generateClasses)
......
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