summaryrefslogtreecommitdiff
path: root/examples/statemachine/statemachine.py
diff options
context:
space:
mode:
authorptmcg <ptmcg@austin.rr.com>2018-12-28 14:42:07 -0600
committerptmcg <ptmcg@austin.rr.com>2018-12-28 14:42:07 -0600
commit45b78401e8c224619e1b18b9cf43fc02b196676e (patch)
treefd4c4db17624f48d8efb275946240794c7f059cf /examples/statemachine/statemachine.py
parent3288d48409269b404e24888ec90e76ee751251e3 (diff)
downloadpyparsing-git-45b78401e8c224619e1b18b9cf43fc02b196676e.tar.gz
Fix partial named results when And embedded in named MatchFirst or Or
Diffstat (limited to 'examples/statemachine/statemachine.py')
-rw-r--r--examples/statemachine/statemachine.py258
1 files changed, 258 insertions, 0 deletions
diff --git a/examples/statemachine/statemachine.py b/examples/statemachine/statemachine.py
new file mode 100644
index 0000000..7565375
--- /dev/null
+++ b/examples/statemachine/statemachine.py
@@ -0,0 +1,258 @@
+# stateMachine.py
+#
+# module to define .pystate import handler
+#
+# import imputil
+import keyword
+import sys
+import os
+import types
+try:
+ import urllib.parse
+ url_parse = urllib.parse.urlparse
+except ImportError:
+ import urllib
+ url_parse = urllib.parse
+
+
+DEBUG = True #False
+
+from pyparsing import Word, Group, ZeroOrMore, alphas, \
+ alphanums, ParserElement, ParseException, ParseSyntaxException, \
+ Empty, LineEnd, OneOrMore, col, Keyword, pythonStyleComment, \
+ StringEnd, traceParseAction
+
+ident = Word(alphas + "_", alphanums + "_$")
+
+def no_keywords_allowed(s, l, t):
+ wd = t[0]
+ return not keyword.iskeyword(wd)
+
+ident.addCondition(no_keywords_allowed, message="cannot use a Python keyword for state or transition identifier")
+
+stateTransition = ident("fromState") + "->" + ident("toState")
+stateMachine = (Keyword("statemachine") + ident("name") + ":"
+ + OneOrMore(Group(stateTransition))("transitions"))
+
+namedStateTransition = (ident("fromState")
+ + "-(" + ident("transition") + ")->"
+ + ident("toState"))
+namedStateMachine = (Keyword("statemachine") + ident("name") + ":"
+ + OneOrMore(Group(namedStateTransition))("transitions"))
+
+
+def expand_state_definition(source, loc, tokens):
+ indent = " " * (col(loc, source) - 1)
+ statedef = []
+
+ # build list of states
+ states = set()
+ fromTo = {}
+ for tn in tokens.transitions:
+ states.add(tn.fromState)
+ states.add(tn.toState)
+ fromTo[tn.fromState] = tn.toState
+
+ # define base class for state classes
+ baseStateClass = tokens.name + "State"
+ statedef.extend([
+ "class %s(object):" % baseStateClass,
+ " def __str__(self):",
+ " return self.__class__.__name__",
+ " def next_state(self):",
+ " return self._next_state_class()"])
+
+ # define all state classes
+ statedef.extend(
+ "class {}({}): pass".format(s, baseStateClass)
+ for s in states)
+ statedef.extend(
+ "{}._next_state_class = {}".format(s, fromTo[s])
+ for s in states if s in fromTo)
+
+ return indent + ("\n" + indent).join(statedef) + "\n"
+
+
+stateMachine.setParseAction(expand_state_definition)
+
+
+def expand_named_state_definition(source, loc, tokens):
+ indent = " " * (col(loc, source) - 1)
+ statedef = []
+ # build list of states and transitions
+ states = set()
+ transitions = set()
+
+ baseStateClass = tokens.name + "State"
+
+ fromTo = {}
+ for tn in tokens.transitions:
+ states.add(tn.fromState)
+ states.add(tn.toState)
+ transitions.add(tn.transition)
+ if tn.fromState in fromTo:
+ fromTo[tn.fromState][tn.transition] = tn.toState
+ else:
+ fromTo[tn.fromState] = {tn.transition: tn.toState}
+
+ # add entries for terminal states
+ for s in states:
+ if s not in fromTo:
+ fromTo[s] = {}
+
+ # define state transition class
+ statedef.extend([
+ "class %sTransition:" % baseStateClass,
+ " def __str__(self):",
+ " return self.transitionName",
+ ])
+ statedef.extend(
+ "{} = {}Transition()".format(tn, baseStateClass)
+ for tn in transitions)
+ statedef.extend("{}.transitionName = '{}'".format(tn, tn)
+ for tn in transitions)
+
+ # define base class for state classes
+ excmsg = "'" + tokens.name + \
+ '.%s does not support transition "%s"' \
+ "'% (self, tn)"
+ statedef.extend([
+ "class %s(object):" % baseStateClass,
+ " def __str__(self):",
+ " return self.__class__.__name__",
+ " def next_state(self,tn):",
+ " try:",
+ " return self.tnmap[tn]()",
+ " except KeyError:",
+ " raise Exception(%s)" % excmsg,
+ " def __getattr__(self,name):",
+ " raise Exception(%s)" % excmsg,
+ ])
+
+ # define all state classes
+ for s in states:
+ statedef.append("class %s(%s): pass" %
+ (s, baseStateClass))
+
+ # define state transition maps and transition methods
+ for s in states:
+ trns = list(fromTo[s].items())
+ statedef.append("%s.tnmap = {%s}" %
+ (s, ",".join("%s:%s" % tn for tn in trns)))
+ statedef.extend([
+ "%s.%s = staticmethod(lambda : %s())" %
+ (s, tn_, to_)
+ for tn_, to_ in trns
+ ])
+
+ return indent + ("\n" + indent).join(statedef) + "\n"
+
+
+namedStateMachine.setParseAction(
+ expand_named_state_definition)
+
+
+# ======================================================================
+# NEW STUFF - Matt Anderson, 2009-11-26
+# ======================================================================
+class SuffixImporter(object):
+ """An importer designed using the mechanism defined in :pep:`302`. I read
+ the PEP, and also used Doug Hellmann's PyMOTW article `Modules and
+ Imports`_, as a pattern.
+
+ .. _`Modules and Imports`: http://www.doughellmann.com/PyMOTW/sys/imports.html
+
+ Define a subclass that specifies a :attr:`suffix` attribute, and
+ implements a :meth:`process_filedata` method. Then call the classmethod
+ :meth:`register` on your class to actually install it in the appropriate
+ places in :mod:`sys`. """
+
+ scheme = 'suffix'
+ suffix = None
+ path_entry = None
+
+ @classmethod
+ def trigger_url(cls):
+ if cls.suffix is None:
+ raise ValueError('%s.suffix is not set' % cls.__name__)
+ return 'suffix:%s' % cls.suffix
+
+ @classmethod
+ def register(cls):
+ sys.path_hooks.append(cls)
+ sys.path.append(cls.trigger_url())
+
+ def __init__(self, path_entry):
+ pr = url_parse(str(path_entry))
+ if pr.scheme != self.scheme or pr.path != self.suffix:
+ raise ImportError()
+ self.path_entry = path_entry
+ self._found = {}
+
+ def checkpath_iter(self, fullname):
+ for dirpath in sys.path:
+ # if the value in sys.path_importer_cache is None, then this
+ # path *should* be imported by the builtin mechanism, and the
+ # entry is thus a path to a directory on the filesystem;
+ # if it's not None, then some other importer is in charge, and
+ # it probably isn't even a filesystem path
+ if sys.path_importer_cache.get(dirpath, False) is None:
+ checkpath = os.path.join(
+ dirpath, '{}.{}'.format(fullname, self.suffix))
+ yield checkpath
+
+ def find_module(self, fullname, path=None):
+ for checkpath in self.checkpath_iter(fullname):
+ if os.path.isfile(checkpath):
+ self._found[fullname] = checkpath
+ return self
+ return None
+
+ def load_module(self, fullname):
+ assert fullname in self._found
+ if fullname in sys.modules:
+ module = sys.modules[fullname]
+ else:
+ sys.modules[fullname] = module = types.ModuleType(fullname)
+ data = None
+ with open(self._found[fullname]) as f:
+ data = f.read()
+
+ module.__dict__.clear()
+ module.__file__ = self._found[fullname]
+ module.__name__ = fullname
+ module.__loader__ = self
+ self.process_filedata(module, data)
+ return module
+
+ def process_filedata(self, module, data):
+ pass
+
+
+class PystateImporter(SuffixImporter):
+ suffix = 'pystate'
+
+ def process_filedata(self, module, data):
+ # MATT-NOTE: re-worked :func:`get_state_machine`
+
+ # convert any statemachine expressions
+ stateMachineExpr = (stateMachine | namedStateMachine).ignore(pythonStyleComment)
+ generated_code = stateMachineExpr.transformString(data)
+
+ if DEBUG: print(generated_code)
+
+ # compile code object from generated code
+ # (strip trailing spaces and tabs, compile doesn't like
+ # dangling whitespace)
+ COMPILE_MODE = 'exec'
+
+ codeobj = compile(generated_code.rstrip(" \t"),
+ module.__file__,
+ COMPILE_MODE)
+
+ exec(codeobj, module.__dict__)
+
+
+PystateImporter.register()
+
+print("registered {!r} importer".format(PystateImporter.suffix)) \ No newline at end of file