Franck Pommereau

API doc extraction

Showing 102 changed files with 26081 additions and 0 deletions
1 +syntax: glob
2 +
3 +doc/api/*
4 +dist/*
5 +dput.sh
6 +*.pyc
7 +*~
8 +.gone
9 +,*
10 +*.class
1 +abcd crash on [foo>>(s), foo<<(s+s)]
2 +[foo(x,y,z)] creates a multi-arc instead of a tuple
3 +
4 +abcd substitues for loops variables:
5 +net Foo (x) :
6 + buffer b : int = [x for x in range(4)]
7 + ^ ^
8 + substituted (should not)
9 +
10 +errors during expressions evaluation are directly sent to user, should be wrapper to indicate this is a model error. And should be catched in abcd --simul/check
1 +recursive-include doc *
2 +include *
1 +all:
2 + @echo "Commands:"
3 + @echo " release prepare source for release"
4 + @echo " tgz build a source tarball"
5 + @echo " doc build API documentation"
6 + @echo " dput build and upload Ubuntu packages"
7 + @echo " clean delete some garbage files"
8 + @echo " test run tests through supported Python implementations"
9 + @echo " next-deb increments debian/VERSION"
10 + @echo " next-ppa increments debian/PPA"
11 + @echo " lang build generated files in snakes/lang"
12 + @echo " emacs compile Emacs files"
13 +
14 +committed:
15 + hg summary|grep -q '^commit: (clean)$$'
16 +
17 +next-deb:
18 + echo 1 > debian/PPA
19 + echo $$((1+$$(cat debian/VERSION))) > debian/VERSION
20 +
21 +emacs:
22 + emacs -batch -f batch-byte-compile utils/abcd-mode.el
23 +
24 +next-ppa:
25 + echo $$((1+$$(cat debian/PPA))) > debian/PPA
26 +
27 +release: committed test doc tgz
28 + hg tag version-$$(cat VERSION)
29 + echo 1 > debian/PPA
30 + echo 1 > debian/VERSION
31 + hg commit -m "version $$(cat VERSION)"
32 + hg push
33 +
34 +lang:
35 + python mklang.py
36 +
37 +tgz: committed
38 + hg archive snakes-$$(cat VERSION)-$$(cat debian/VERSION)
39 + cd snakes-$$(cat VERSION)-$$(cat debian/VERSION) && make doc
40 + tar cf snakes-$$(cat VERSION)-$$(cat debian/VERSION).tar snakes-$$(cat VERSION)-$$(cat debian/VERSION)
41 + rm -rf snakes-$$(cat VERSION)-$$(cat debian/VERSION)
42 + gzip -9 snakes-$$(cat VERSION)-$$(cat debian/VERSION).tar
43 + gpg --armor --sign --detach-sig snakes-$$(cat VERSION)-$$(cat debian/VERSION).tar.gz
44 +
45 +doc: snakes/*.py snakes/plugins/*.py snakes/utils/*.py snakes/lang/*.py
46 + make -C doc
47 +
48 +dput.sh: VERSION debian/*
49 + python mkdeb.py
50 +
51 +dput: committed dput.sh
52 + sh dput.sh
53 +
54 +clean:
55 + rm -f $$(find . -name ",*")
56 + rm -f $$(find . -name "*.pyc")
57 + rm -f $$(find . -name "*~")
58 + rm -f $$(find . -name "*.class")
59 + rm -rf $$(find . -type d -name __pycache__)
60 +
61 +test:
62 + python2.5 test.py
63 + python2.6 test.py
64 + python2.7 test.py
65 + python3 test.py
66 + unladen test.py
67 + pypy test.py
68 + spypy test.py
69 + stackless test.py
70 + jython test.py
1 + + emacs mode for ABCD
2 + ! fixex nodes merge in plugin labels
3 + ! fixed nets.MultiArc.flow (thanks to Jan Ciger's report)
4 +
5 +version 0.9.13 (Fri Jul 16 17:01:22 CEST 2010):
6 + + added inhibitor arcs
7 + + added Ubuntu Lucid (10.04) package
8 + ! fixed data.WordSet.fresh when base is used
9 + ! fixed reduce(xor, []) is some __hash__ methods
10 + + added PetriNet.layout method in snakes.plugins.gv
11 +
12 +version 0.9.12 (Thu Apr 1 19:42:33 CEST 2010):
13 + + removed PyGraphviz dependency (layout method supressed temporarily)
14 + + now compatible with PyPy (1.2), Unladen-Swallow and Jython (2.5.1)
15 + ! fixed snakes.plugins.clusters.rename_node
16 + ! fixed snakes.nets.Flush.flow
17 + ! fixed an uncaught exception in snakes.data.MultiSet.__eq__
18 + and snakes.data.Symbol.__eq__
19 + ! hopefully fixed LALR built in ABCD compiler
20 + ! fixed hash-related issues
21 + * moved PLY stuff to snakes.utils.abcd
22 + * snakes.compyler has been completely replaced (PLY dependency removed)
23 +
24 +version 0.9.11 (Thu Mar 25 19:32:31 CET 2010):
25 + ! fixed various doctests
26 + ! fixed issues with attributes locking
27 + ! fixed issues related to missing __hash__ methods
28 + ! fixed renaming a node to itself in ABCD
29 + + added snakes.nets.Evaluator.__contains__
30 + + added base argument to snakes.data.WordSet
31 + + added option --symbols to ABCD compiler
32 + + added snakes.data.Symbol
33 + + added net instances naming in ABCD
34 + + added logo
35 + + added let function to update bindings from expressions
36 + + added snakes.data.Subtitution.__setitem__
37 +
38 +version 0.9.10 (Fri Jun 19 13:32:45 CEST 2009):
39 + ! fixed inconstent hashing on clusters
40 + ! fixed mutability of hashed multisets
41 + ! fixed snakes.nets.Tuple.mode
42 +
43 +version 0.9.9 (Tue, 19 May 2009 15:00:00 +0100):
44 + + added mkdeb.py to build deb packages for multiple distributions
45 + + ported to Python 2.6
46 +
47 +version 0.9.8 (lun, 23 mar 2009 17:30:34 (CET)):
48 + + added graph_attr option to snakes.plugins.gv.StateGraph
49 + * plugin gv now draws partially constructed marking graphs
50 + ! fixed expression compilation in present of net parameters in ABCD
51 + compiler
52 + ! fixed flush arcs binding
53 + * plugin lashdata has been removed because its too experimental
54 + * merged plugin decorators into a single one
55 +
56 +version 0.9.7 (Tue, 20 Jan 2009 13:04:15 +0100):
57 + ! fixed sharing of globals between net components
58 + ! fixed loading of PNML when some plugins fail to load
59 + + added cpp option to abcd
60 + + added some docstrings and doctests
61 + ! fixed sharing of globals between net components
62 +
63 +version 0.9.6 (Fri, 28 Nov 2008 15:22:27 +0100):
64 + + added doc for ABCD
65 + ! fixed False and True handling in ABCD compiler
66 + + added cross product types to ABCD
67 +
68 +version 0.9.5 (Wed, 19 Nov 2008 21:42:05 +0100):
69 + + added distutils setup.py
70 + * strip PNML data before decoding (work around invalid XML)
71 + ! fixed Multiset.__pnmlload__ on iterable values
72 +
73 +version 0.9.4 (mar, 28 oct 2008 12:28:02 (UTC+0100)):
74 + ! fixed nets.Value.__eq__, __ne__ and __hash__
75 + ! fixed nets.Token.__repr__
76 + + added Flush arcs
77 + ! fixed nets.Tuple.bind
78 + ! fixed PNML dump/load of subclasses
79 + ! fixed nets.PetriNet.merge_* in the presence of Tuple arcs
80 + ! fixed plugins.clusters.Cluster.path
81 + ! fixed plugins.status.PetriNet.__pnmlload__
82 + ! fixed ABCD lexer
83 + + improved ABCD compiler with parametric nets, Python declarations
84 + ! fixed black token values in ABCD arcs and flush
85 + + ABCD compiler launches pdb.post_mortem when an error occurs in
86 + + added TCP mode to query plugin debug mode
87 + * removed dependency to epydoc in snakes.typing
88 + + updated PNML doc
89 + + added a producer/consumer ABCD example
90 +
91 +version 0.9.3 (lun, 29 sep 2008 08:04:55 (CEST)):
92 + ! fixed a bug in place pnml loading
93 + ! fixed wrong association of pnml tags to classes
94 + + improved query and associated programs, added documentation
95 + * improved loading of PNML files with plugins
96 + * PNML tag <snakes> can only occur once as a child of <pnml>
97 + + abcd compiler adds many structural information to PNML (including
98 + + test.py checks is snakes.version is correct AST if asked)
99 + + query plugins now has two verbosity levels
100 + + added snakes.compyler.Tree.__pnmldump__()
101 +
102 +version 0.9.2
103 + + added query plugin and demon client/server programs
104 + ! various small bugs fixed
105 + + improved PNML serialisation of standard objects
106 + + snakes.plugins.load puts new module in caller's environment
107 + ! fixed snakes.pnml.Tree.update
108 + * Substitution.image() returns a set
109 + + added apix.py
110 + ! PNML export empty arcs fixed
111 + ! fixed broken abcd.py
112 + * updated abcd.py so it uses gv
113 + * improved clustering efficiency
114 +
115 +version 0.9.1
116 + + added plugin gv to replace graphviz
117 + + added plugin clusters to manage clusters of nodes
118 + + updated plugins ops so it builds clusters
119 + * the plugin posops is deprecated since gv does the work much better
120 +
121 +version 0.9
122 + * dropped compatibilty with Python 2.4
123 + + finished pnml
124 + * the compyler module has been completely replaced
125 +
126 +Changes in 0.8.4 (06.09.2007 11:30:21):
127 + + updated the tutorial
128 +
129 +Changes in 0.8.3 (29.06.2007 11:57:39):
130 + + snakes.plugins.graphivz: added options to control the layout
131 + + snakes.plugins.graphviz: improved the rendering
132 + + updated the tutorial
133 +
134 +Changes in 0.8.2 (22.06.2007 13:09:02):
135 + + snakes.plugins.ops: added a name hiding operator (PetriNet.__div__)
136 + ! fixed several copy/clone problems
137 +
138 +Changes in 0.8.1 (20.06.2007 10:18:17):
139 + * updated tutorial
140 + + snakes.plugins.pos: the position of a node can be redefined when
141 + ! snakes.plugins.pos: accept float positions when loading from pnml
142 +
143 +Changes in 0.8 (19.06.2007 17:19:19): First public release.
1 +SNAKES is the Net Algebra Kit for Editors and Simulators
2 +========================================================
3 +
4 +////////////////////////////////////////////////////////////////
5 +This file is formatted in order to be processed by AsciiDoc
6 +(http://www.methods.co.nz/asciidoc). It will be more comfortable
7 +to render it or to read the HTML version available at:
8 +http://www.univ-paris12.fr/pommereau/soft/snakes/index.html
9 +////////////////////////////////////////////////////////////////
10 +
11 +SNAKES is a Python library that provides all then necessary to define
12 +and execute many sorts of Petri nets, in particular those of the PBC
13 +and M-nets family. Its main aim is to be a general Petri net library,
14 +being able to cope with most Petri nets models, and providing the
15 +researcher with a tool to quickly prototype its new ideas. SNAKES
16 +should be suitable to provide the data model for editors or
17 +simulators; actually, any editor that use SNAKES may also be a
18 +simulator as SNAKES can execute any net.
19 +
20 +A key feature of SNAKES is the ability to use arbitrary Python objects
21 +as tokens and arbitrary Python expressions in many points, for
22 +instance in transitions guards or arcs outgoing of transitions. This
23 +is what makes SNAKES that general. This relies on the capability of
24 +Python to run dynamically provided Python code (the $eval$ function).
25 +This feature may not be efficient enough for model-checking: speed is
26 +the price to pay for the wide generality. However, in the case of a
27 +new model, SNAKES may happen to be the only available tool.
28 +
29 +Another important feature of SNAKES is the plugin system that allows
30 +to extend the features and work with specialised classes of Petri
31 +nets. Currently, the following plugins are provided:
32 +
33 +pos:: adds to nodes the capability of holding their position. Nodes
34 +can be moved or shifted, Petri nets can be shifted globally and their
35 +bounding box can be computed.
36 +
37 +gv:: adds a method to draw a Petri net or a state graph using the tool
38 +http://www.graphviz.org[GraphViz] (through the Python binding
39 +http://networkx.lanl.gov/wiki/pygraphviz[PyGraphViz]). This module
40 +replaces the previous plugin called _graphviz_ and provides more
41 +flexibility and security, _graphviz_ is still provided but deprecated.
42 +
43 +status:: extends the Petri net model by adding status to the nodes.
44 +This is similar to what is used in the models of the PBC or Mnets
45 +family. Nodes can then merged automatically according to their status.
46 +
47 +ops:: this plugins defines control flow operations on Petri nets
48 +usually found in the PBC and Mnets family. Nets can be composed in
49 +parallel, sequence, choice and iteration. These operations rely on the
50 +places status.
51 +
52 +labels:: allows to add arbitrary labels to most objects (places,
53 +transitions, nets, ...)
54 +
55 +posops:: combines the features of pos and ops plugins: the control
56 +flow operations are modified in order to rearrange the nodes position
57 +in order to provide well shaped nets. This plugin is deprecated
58 +because the new _gv_ does the work much better.
59 +
60 +synchro:: it defines the label-based transition synchronisation
61 +defined in the Mnets model.
62 +
63 +// export:: allows to save Petri nets objects in the format of the tools
64 +// http://pep.sourceforge.net[PEP], http://helena.cnam.fr[Helena] and
65 +// http://maria[Maria]. http://pnml[PNML] is also supported as it is
66 +// built-in SNAKES.
67 +
68 +lashdata:: allows to define data that is not handled in the places of
69 +the Petri net but stored instead in the special structures handled by
70 +the http://www.montefiore.ulg.ac.be/~boigelot/research/lash/[library
71 +Lash]. This allows in particular to aggregate possibly infinite states
72 +into one meta-state.
73 +
74 +clusters:: this is an auxiliary plugin that allows to group nodes in a
75 +Petri net. This feature is used by _ops_ in order to record how a net
76 +is constructed, which is exploited by _gv_ in order to build a nice
77 +layout of composed nets.
78 +
79 +
80 +Getting SNAKES and installing it
81 +--------------------------------
82 +
83 +Download http://www.univ-paris12.fr/lacl/pommereau/soft/snakes/snakes-{VERSION}.tar.gz[$snakes-{VERSION}.tar.gz$]
84 +({sys:../stat snakes-{VERSION}.tar.gz})
85 +
86 +To install SNAKES, uncompress the archive and copy the directory
87 +snakes in a location where Python can find it (_i.e._, in a directory
88 +listed in your $PYTHONPATH$ environment variable).
89 +
90 +SNAKES should work with a Python version at least 2.5 but will _not_
91 +work for an older version. Optionally, you may want to install
92 +additional software required by some plugins:
93 +
94 +gv:: depends on http://www.graphviz.org[GraphViz] and its Python
95 + binding http://networkx.lanl.gov/wiki/pygraphviz[PyGraphViz]. The
96 + plugin _graphviz_ depends on GraphViz only but is now deprecated.
97 +
98 +lashdata:: requires
99 + http://www.montefiore.ulg.ac.be/~boigelot/research/lash[Lash] and
100 + the
101 + http://www.univ-paris12.fr/lacl/pommereau/soft/index.html#PyLash[Python
102 + Lash binding].
103 +
104 +[NOTE]
105 +=====================
106 +(C) 2007 Franck Pommereau <pommereau@univ-paris12.fr>
107 +
108 +This library is free software; you can redistribute it and/or modify
109 +it under the terms of the GNU Lesser General Public License as
110 +published by the Free Software Foundation; either version 2.1 of the
111 +License, or (at your option) any later version.
112 +
113 +This library is distributed in the hope that it will be useful, but
114 +WITHOUT ANY WARRANTY; without even the implied warranty of
115 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
116 +Lesser General Public License for more details.
117 +
118 +You should have received a copy of the GNU Lesser General Public
119 +License along with this library; if not, write to the Free Software
120 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
121 +USA
122 +=====================
123 +
124 +
125 +Contact
126 +-------
127 +
128 +Please feel free to send me comments, questions, bug reports or
129 +contributions by mailto:pommereau@univ-paris12.fr[email].
130 +
131 +If you wish to be notified of the new releases of SNAKES, please
132 +register at the http://freshmeat.net/projects/snakes[FreshMeat page].
133 +
134 +You may contribute to SNAKES by either send patches by email, or by
135 +using the https://launchpad.net/snakes[SNAKES Launchpad page].
136 +
137 +
138 +Documentation
139 +-------------
140 +
141 +A good starting point may be the link:tutorial.html[tutorial]. Then,
142 +you may find the link:api/index.html[API reference manual] useful, it
143 +documents all the API and gives number of examples of how to use the
144 +various classes and functions.
145 +
146 +If you do not program Python, you can learn it in a few hours thanks
147 +to the very good http://docs.python.org/tut/tut.html[Python tutorial].
148 +
149 +In order to know more about the PBC and M-nets family or the Petri net
150 +compositions defined in the plugins, you may read papers from
151 +http://www.univ-paris12.fr/lacl/pommereau/publis[my publications page]
152 +(in particular those with _calculus_ in the title).
1 +* add name to transitions, eg, [buff-(x) as name]
2 + make it consistent with instance names?
3 +* zero test/inhibitor arc
4 +! parameter and global buffer with same names => parameter ignored
5 +! accept net instances with too much parameters
1 +0.9.16
1 +#!/usr/bin/env python
2 +import snakes.utils.abcd.main as abcdmain
3 +abcdmain.main()
1 +#!/usr/bin/env python
2 +import socket, sys, readline
3 +from snakes.pnml import dumps, loads
4 +from snakes.plugins.query import Query
5 +
6 +env = {}
7 +
8 +def public (fun) :
9 + env[fun.__name__] = fun
10 + return fun
11 +
12 +@public
13 +def set (*larg, **karg) :
14 + """set(name, value) -> None
15 + assign value (object) to name (str) on the server"""
16 + return Query("set", *larg, **karg)
17 +
18 +@public
19 +def get (*larg, **karg) :
20 + """get(name) -> object
21 + return the last value assigned to name (str)"""
22 + return Query("get", *larg, **karg)
23 +
24 +@public
25 +def delete (*larg, **karg) :
26 + """delete(name) -> None
27 + discard name (str)"""
28 + return Query("del", *larg, **karg)
29 +
30 +@public
31 +def call (*larg, **karg) :
32 + """call(obj, ...) -> object
33 + call obj (str or result from another call) with the additional arguments
34 + return whatever the called object returns"""
35 + return Query("call", *larg, **karg)
36 +
37 +@public
38 +def help (command=None) :
39 + """help(command) -> None
40 + print help about command, if no command is given, list available commands"""
41 + if command is None:
42 + print "commands:", ", ".join(repr(cmd) for cmd in env
43 + if not cmd.startswith("_"))
44 + print " type 'help(cmd)' to ge help about a command"
45 + elif command in env :
46 + print env[command].__doc__
47 + elif command.__name__ in env :
48 + print command.__doc__
49 + else :
50 + print "unknown command %r" % command
51 +
52 +@public
53 +def quit () :
54 + """quit() -> None
55 + terminate the client"""
56 + print "bye"
57 + sys.exit(0)
58 +
59 +@public
60 +def load (path) :
61 + """net(path) -> object
62 + load a PNML file from path (str) and return the object is represents"""
63 + return loads(open(path).read())
64 +
65 +@public
66 +def show (query) :
67 + """show(obj) -> None
68 + show the PNML representation of obj (object), for instance of a query"""
69 + print dumps(query)
70 +
71 +_verbose = False
72 +
73 +@public
74 +def verbose (state=None) :
75 + """verbose(state) -> None
76 + turn on (state=True), off (state=False) or toggle (state not
77 + given) the printing of queries before they are sent to the
78 + server"""
79 + global _verbose
80 + if state is None :
81 + _verbose = not _verbose
82 + else :
83 + _verbose = state
84 + if _verbose :
85 + print "dump of queries enabled"
86 + else :
87 + print "dump of queries disabled"
88 +
89 +try :
90 + if sys.argv[1] in ("-t", "--tcp") :
91 + proto = "TCP"
92 + del sys.argv[1]
93 + else :
94 + proto = "UDP"
95 + host, port = sys.argv[1:]
96 + port = int(port)
97 +except :
98 + print >>sys.stderr, "Usage: snkc [--tcp] HOST PORT"
99 + sys.exit(1)
100 +
101 +sock = None
102 +
103 +def sendto (data, address) :
104 + global sock
105 + if proto == "UDP" :
106 + if sock is None :
107 + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
108 + sock.settimeout(2)
109 + sock.sendto(data, address)
110 + else :
111 + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
112 + sock.settimeout(2)
113 + sock.connect(address)
114 + sock.send(data)
115 +
116 +def recvfrom (size) :
117 + global sock
118 + if proto == "UDP" :
119 + data, address = sock.recvfrom(size)
120 + else :
121 + parts = []
122 + while True :
123 + parts.append(sock.recv(size))
124 + if len(parts[-1]) < size :
125 + break
126 + address = sock.getpeername()
127 + sock.close()
128 + data = "".join(parts)
129 + return data, address
130 +
131 +while True :
132 + try :
133 + data = raw_input("? ")
134 + q = eval(data.strip(), env)
135 + except (EOFError, KeyboardInterrupt) :
136 + quit()
137 + except SystemExit :
138 + raise
139 + except Exception, e :
140 + print "query error:", e
141 + continue
142 + if q is not None :
143 + q = dumps(q)
144 + if _verbose :
145 + print "# query to %s:%u" % (host, port)
146 + print q
147 + sendto(q, (host, port))
148 + try :
149 + data, address = recvfrom(2**20)
150 + if _verbose :
151 + print "# answer from %s:%u" % address
152 + print data.strip()
153 + except socket.timeout :
154 + print "# no answer received (timeout)"
155 + print
1 +#!/usr/bin/env python
2 +import sys
3 +import snakes.plugins
4 +snakes.plugins.load("query", "snakes.nets", "nets")
5 +
6 +port = 1234
7 +size = 2**20
8 +verbose = 0
9 +proto = "UDP"
10 +
11 +def help () :
12 + print "Usage: snkd [OPTION]"
13 + print "Options:"
14 + print " -p PORT, --port PORT listen on port number PORT"
15 + print " -t, --tcp use TCP instead of UDP"
16 + print " -s SIZE, --size SIZE set buffer size for inputs"
17 + print " -v, --verbose display information about queries"
18 + print " (use '-v' twice to dump queries/answers)"
19 + print " -h, --help print this help and exit"
20 +
21 +args = sys.argv[1:]
22 +try :
23 + while len(args) > 0 :
24 + arg = args.pop(0)
25 + if arg in ("-p", "--port") :
26 + port = int(args.pop(0))
27 + elif arg in ("-v", "--verbose") :
28 + verbose += 1
29 + elif arg in ("-t", "--tcp") :
30 + proto = "TCP"
31 + elif arg in ("-s", "--size") :
32 + size = int(args.pop(0))
33 + elif arg in ("-h", "--help") :
34 + help()
35 + sys.exit(0)
36 + else :
37 + print >>sys.stderr("snkd: invalid command %r" % arg)
38 + sys.exit(1)
39 +except SystemExit :
40 + raise
41 +except :
42 + cls, val, tb = sys.exc_info()
43 + print >>sys.stderr, "snkd: %s, %s" % (cls.__name__, val)
44 + sys.exit(1)
45 +
46 +if verbose :
47 + print "# starting"
48 + print "# listen on: %s:%u" % (proto, port)
49 + print "# buffer size: %uMb" % (size/1024)
50 + print "# verbosity:", verbose
51 +
52 +try :
53 + if proto == "UDP" :
54 + nets.UDPServer(port, size=size, verbose=verbose).run()
55 + else :
56 + nets.TCPServer(port, size=size, verbose=verbose).run()
57 +except KeyboardInterrupt :
58 + print "# bye"
59 +except :
60 + cls, val, tb = sys.exc_info()
61 + if verbose > 1 :
62 + raise
63 + elif verbose :
64 + print "# fatal error"
65 + print >>sys.stderr, "snkd: %s, %s" % (cls.__name__, val)
66 + sys.exit(2)
1 +lucid 10.04 LTS
2 +hardy 8.04 LTS
3 +oneiric 11.10
4 +precise 12.04 LTS
1 +python-snakes (0.9.16-1) UNRELEASED; urgency=low
2 +
3 + * see NEWS
4 +
5 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 07 Jun 2011 12:23:33 +0200
6 +
7 +python-snakes (0.9.15-1) UNRELEASED; urgency=low
8 +
9 + * see NEWS
10 +
11 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 10 May 2011 18:22:34 +0200
12 +
13 +python-snakes (0.9.14-1) UNRELEASED; urgency=low
14 +
15 + * see NEWS
16 +
17 + -- Franck Pommereau <pommereau@univ-paris12.fr> Mon, 11 Apr 2011 16:45:50 +0200
18 +
19 +python-snakes (0.9.13-2) UNRELEASED; urgency=low
20 +
21 + * see NEWS
22 +
23 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 23 Jul 2010 20:06:53 +0200
24 +
25 +python-snakes (0.9.13-1) UNRELEASED; urgency=low
26 +
27 + * see NEWS
28 +
29 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 16 Jul 2010 17:09:14 +0200
30 +
31 +python-snakes (0.9.12-2) UNRELEASED; urgency=low
32 +
33 + * see NEWS
34 +
35 + -- Franck Pommereau <pommereau@univ-paris12.fr> Sat, 03 Apr 2010 21:06:50 +0200
36 +
37 +python-snakes (0.9.12-1) UNRELEASED; urgency=low
38 +
39 + * see NEWS
40 +
41 + -- Franck Pommereau <pommereau@univ-paris12.fr> Thu, 01 Apr 2010 19:46:02 +0200
42 +
43 +python-snakes (0.9.11-1) UNRELEASED; urgency=low
44 +
45 + * see NEWS
46 +
47 + -- Franck Pommereau <pommereau@univ-paris12.fr> Thu, 25 Mar 2010 19:39:47 +0100
48 +
49 +python-snakes (0.9.10-1) UNRELEASED; urgency=low
50 +
51 + * see NEWS
52 +
53 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 19 Jun 2009 13:40:36 +0200
54 +
55 +python-snakes (0.9.9-2) UNRELEASED; urgency=low
56 +
57 + * see NEWS
58 +
59 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 19 May 2009 09:29:46 +0200
60 +
61 +python-snakes (0.9.8-2) UNRELEASED; urgency=low
62 +
63 + * see NEWS
64 +
65 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 14 Apr 2009 20:02:32 +0200
66 +
67 +python-snakes (0.9.8-1) UNRELEASED; urgency=low
68 +
69 + * see NEWS
70 +
71 + -- Franck Pommereau <pommereau@univ-paris12.fr> Mon, 23 Mar 2009 17:34:06 +0100
72 +
73 +python-snakes (0.9.7-1) UNRELEASED; urgency=low
74 +
75 + * see NEWS
76 +
77 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 20 Jan 2009 13:07:09 +0100
78 +
79 +python-snakes (0.9.6-2) UNRELEASED; urgency=low
80 +
81 + * see NEWS
82 +
83 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 02 Dec 2008 17:47:43 +0100
84 +
85 +python-snakes (0.9.6-1) UNRELEASED; urgency=low
86 +
87 + * see NEWS
88 +
89 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 28 Nov 2008 15:24:05 +0100
90 +
91 +python-snakes (0.9.5-2) UNRELEASED; urgency=low
92 +
93 + * Fixed doc installation path
94 +
95 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 21 Nov 2008 13:20:55 +0100
96 +
97 +python-snakes (0.9.5-1) UNRELEASED; urgency=low
98 +
99 + * Trying to fix build on Launchpad
100 +
101 + -- Franck Pommereau <pommereau@univ-paris12.fr> Fri, 21 Nov 2008 08:19:04 +0100
102 +
103 +python-snakes (0.9.5) UNRELEASED; urgency=low
104 +
105 + * See NEWS
106 +
107 + -- Franck Pommereau <pommereau@univ-paris12.fr> Wed, 19 Nov 2008 21:47:52 +0100
108 +
109 +python-snakes (0.9.4) UNRELEASED; urgency=low
110 +
111 + * Initial release. (Closes: #XXXXXX)
112 +
113 + -- Franck Pommereau <pommereau@univ-paris12.fr> Tue, 18 Nov 2008 12:09:16 +0100
1 +Source: python-snakes
2 +Section: python
3 +Priority: extra
4 +Maintainer: Franck Pommereau <pommereau@univ-paris12.fr>
5 +Homepage: http://lacl.univ-paris12.fr/pommereau/soft/snakes
6 +Build-Depends: python (>=2.5), cdbs (>=0.4.49), debhelper (>= 5), python-central (>=0.5.6)
7 +XS-Python-Version: >=2.5
8 +Standards-Version: 3.7.2
9 +
10 +Package: python-snakes
11 +Architecture: all
12 +XB-Python-Version: ${python:Versions}
13 +Depends: python (>=2.5), python-central, graphviz, python-tk
14 +Description: SNAKES is the Net Algebra Kit for Editors and Simulators
15 + SNAKES is a general purpose Petri net Python library allowing to
16 + define and execute most classes of Petri nets. It features a plugin
17 + system to allow its extension. In particular, plugins are provided to
18 + implement the operations usually found in the PBC and M-nets family.
1 +This package was debianized by Franck Pommereau <pommereau@univ-paris12.fr> on
2 +DATE
3 +
4 +License:
5 +
6 + This package is free software; you can redistribute it and/or
7 + modify it under the terms of the GNU Lesser General Public License
8 + as published by the Free Software Foundation; either version 2 of
9 + the License, or (at your option) any later version.
10 +
11 + This package is distributed in the hope that it will be useful, but
12 + WITHOUT ANY WARRANTY; without even the implied warranty of
13 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 + Lesser General Public License for more details.
15 +
16 + You should have received a copy of the GNU Lesser General Public
17 + License along with this package; if not, see
18 + `http://www.gnu.org/licenses/lgpl-3.0.txt'.
19 +
20 +On Debian systems, the complete text of the GNU General
21 +Public License can be found in `/usr/share/common-licenses/LGPL'.
22 +
23 +The Debian packaging is (C) 2008, Franck Pommereau
24 +<pommereau@univ-paris12.fr> and is licensed under the LGPL, see above.
1 +#!/usr/bin/make -f
2 +
3 +DEB_PYTHON_SYSTEM=pycentral
4 +
5 +include /usr/share/cdbs/1/rules/debhelper.mk
6 +include /usr/share/cdbs/1/class/python-distutils.mk
1 + GNU LESSER GENERAL PUBLIC LICENSE
2 + Version 3, 29 June 2007
3 +
4 + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 + Everyone is permitted to copy and distribute verbatim copies
6 + of this license document, but changing it is not allowed.
7 +
8 +
9 + This version of the GNU Lesser General Public License incorporates
10 +the terms and conditions of version 3 of the GNU General Public
11 +License, supplemented by the additional permissions listed below.
12 +
13 + 0. Additional Definitions.
14 +
15 + As used herein, "this License" refers to version 3 of the GNU Lesser
16 +General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 +General Public License.
18 +
19 + "The Library" refers to a covered work governed by this License,
20 +other than an Application or a Combined Work as defined below.
21 +
22 + An "Application" is any work that makes use of an interface provided
23 +by the Library, but which is not otherwise based on the Library.
24 +Defining a subclass of a class defined by the Library is deemed a mode
25 +of using an interface provided by the Library.
26 +
27 + A "Combined Work" is a work produced by combining or linking an
28 +Application with the Library. The particular version of the Library
29 +with which the Combined Work was made is also called the "Linked
30 +Version".
31 +
32 + The "Minimal Corresponding Source" for a Combined Work means the
33 +Corresponding Source for the Combined Work, excluding any source code
34 +for portions of the Combined Work that, considered in isolation, are
35 +based on the Application, and not on the Linked Version.
36 +
37 + The "Corresponding Application Code" for a Combined Work means the
38 +object code and/or source code for the Application, including any data
39 +and utility programs needed for reproducing the Combined Work from the
40 +Application, but excluding the System Libraries of the Combined Work.
41 +
42 + 1. Exception to Section 3 of the GNU GPL.
43 +
44 + You may convey a covered work under sections 3 and 4 of this License
45 +without being bound by section 3 of the GNU GPL.
46 +
47 + 2. Conveying Modified Versions.
48 +
49 + If you modify a copy of the Library, and, in your modifications, a
50 +facility refers to a function or data to be supplied by an Application
51 +that uses the facility (other than as an argument passed when the
52 +facility is invoked), then you may convey a copy of the modified
53 +version:
54 +
55 + a) under this License, provided that you make a good faith effort to
56 + ensure that, in the event an Application does not supply the
57 + function or data, the facility still operates, and performs
58 + whatever part of its purpose remains meaningful, or
59 +
60 + b) under the GNU GPL, with none of the additional permissions of
61 + this License applicable to that copy.
62 +
63 + 3. Object Code Incorporating Material from Library Header Files.
64 +
65 + The object code form of an Application may incorporate material from
66 +a header file that is part of the Library. You may convey such object
67 +code under terms of your choice, provided that, if the incorporated
68 +material is not limited to numerical parameters, data structure
69 +layouts and accessors, or small macros, inline functions and templates
70 +(ten or fewer lines in length), you do both of the following:
71 +
72 + a) Give prominent notice with each copy of the object code that the
73 + Library is used in it and that the Library and its use are
74 + covered by this License.
75 +
76 + b) Accompany the object code with a copy of the GNU GPL and this license
77 + document.
78 +
79 + 4. Combined Works.
80 +
81 + You may convey a Combined Work under terms of your choice that,
82 +taken together, effectively do not restrict modification of the
83 +portions of the Library contained in the Combined Work and reverse
84 +engineering for debugging such modifications, if you also do each of
85 +the following:
86 +
87 + a) Give prominent notice with each copy of the Combined Work that
88 + the Library is used in it and that the Library and its use are
89 + covered by this License.
90 +
91 + b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 + document.
93 +
94 + c) For a Combined Work that displays copyright notices during
95 + execution, include the copyright notice for the Library among
96 + these notices, as well as a reference directing the user to the
97 + copies of the GNU GPL and this license document.
98 +
99 + d) Do one of the following:
100 +
101 + 0) Convey the Minimal Corresponding Source under the terms of this
102 + License, and the Corresponding Application Code in a form
103 + suitable for, and under terms that permit, the user to
104 + recombine or relink the Application with a modified version of
105 + the Linked Version to produce a modified Combined Work, in the
106 + manner specified by section 6 of the GNU GPL for conveying
107 + Corresponding Source.
108 +
109 + 1) Use a suitable shared library mechanism for linking with the
110 + Library. A suitable mechanism is one that (a) uses at run time
111 + a copy of the Library already present on the user's computer
112 + system, and (b) will operate properly with a modified version
113 + of the Library that is interface-compatible with the Linked
114 + Version.
115 +
116 + e) Provide Installation Information, but only if you would otherwise
117 + be required to provide such information under section 6 of the
118 + GNU GPL, and only to the extent that such information is
119 + necessary to install and execute a modified version of the
120 + Combined Work produced by recombining or relinking the
121 + Application with a modified version of the Linked Version. (If
122 + you use option 4d0, the Installation Information must accompany
123 + the Minimal Corresponding Source and Corresponding Application
124 + Code. If you use option 4d1, you must provide the Installation
125 + Information in the manner specified by section 6 of the GNU GPL
126 + for conveying Corresponding Source.)
127 +
128 + 5. Combined Libraries.
129 +
130 + You may place library facilities that are a work based on the
131 +Library side by side in a single library together with other library
132 +facilities that are not Applications and are not covered by this
133 +License, and convey such a combined library under terms of your
134 +choice, if you do both of the following:
135 +
136 + a) Accompany the combined library with a copy of the same work based
137 + on the Library, uncombined with any other library facilities,
138 + conveyed under the terms of this License.
139 +
140 + b) Give prominent notice with the combined library that part of it
141 + is a work based on the Library, and explaining where to find the
142 + accompanying uncombined form of the same work.
143 +
144 + 6. Revised Versions of the GNU Lesser General Public License.
145 +
146 + The Free Software Foundation may publish revised and/or new versions
147 +of the GNU Lesser General Public License from time to time. Such new
148 +versions will be similar in spirit to the present version, but may
149 +differ in detail to address new problems or concerns.
150 +
151 + Each version is given a distinguishing version number. If the
152 +Library as you received it specifies that a certain numbered version
153 +of the GNU Lesser General Public License "or any later version"
154 +applies to it, you have the option of following the terms and
155 +conditions either of that published version or of any later version
156 +published by the Free Software Foundation. If the Library as you
157 +received it does not specify a version number of the GNU Lesser
158 +General Public License, you may choose any version of the GNU Lesser
159 +General Public License ever published by the Free Software Foundation.
160 +
161 + If the Library as you received it specifies that a proxy can decide
162 +whether future versions of the GNU Lesser General Public License shall
163 +apply, that proxy's public statement of acceptance of any version is
164 +permanent authorization for you to choose that version for the
165 +Library.
1 +all:
2 + rm -rf api
3 + epydoc --output api --no-frames --graph=all \
4 + --name="SNAKES is the Net Algebra Kit for Editors and Simulators" \
5 + --navlink='<img alt="SNAKES logo" src="snakes-logo.jpg" width="120" height="120"/>' \
6 + --no-private ../snakes
7 + convert ../logo/snakes-logo.png -background white -scale 120x120 ./api/snakes-logo.jpg
1 +ABCD specification language
2 +===========================
3 +
4 +This document presents the ABCD language and compiler that is provided
5 +with SNAKES.
6 +
7 +WARNING: this documentation needs update since ABCD syntax has changed
8 +a bit. A precise (but not user-friendly) syntax can be found in the
9 +source:
10 +
11 + - snakes/lang/abcd/abcd.pgen is the concrete grammar and
12 +
13 + - snakes/lang/abcd/abcd.asdl is the abstract syntax
14 +
15 +
16 +Introduction
17 +------------
18 +
19 +ABCD (Asynchronous Box Calculus with Data) is a specification language
20 +whose semantics is given in terms of coloured Petri net. The formal
21 +semantics will not be defined here, but only an intuition of it. ABCD
22 +can be seen as both an Python based implementation and a variant of
23 +several algebras of Petri nets:
24 +
25 + - with respect to the versatile box calculus
26 + (http://lacl.univ-paris12.fr/pommereau/publis/2007-dasd.html), ABCD
27 + does not provide tasks and abort mechanism, but it allows nested
28 + parallelism;
29 +
30 + - with respect to the box calculus with coloured buffers or the box
31 + calculus with high-level buffers
32 + (http://lacl.univ-paris12.fr/pommereau/publis/2002-bcd.html and
33 + http://lacl.univ-paris12.fr/pommereau/publis/2004-dads.html), ABCD
34 + does not provide synchronous communication operations.
35 +
36 +
37 +Syntax
38 +------
39 +
40 +The syntax of ABCD is a mix between Python and a process algebra. An
41 +ABCD specification is structured as follows:
42 +
43 + 1. a possibly empty list of definitions, each being either
44 + 1. a Python ``def`` statement (function definition)
45 + 2. a Python ``import`` of ``from`` statement
46 + 3. an ABCD communication buffer definition
47 + 4. an ABCD sub-net definition (similar to a sub-program)
48 + 2. an ABCD process (similar to the ``main`` function of a C program)
49 +
50 +Like in Python, block and sub-blocks are defined through indentation,
51 +and comments begin with ``#`` and end with the line. Unlike Python,
52 +scoping is lexical, with name masking as usual.
53 +
54 +
55 +Python definitions
56 +^^^^^^^^^^^^^^^^^^
57 +
58 +Functions definitions and module imports are exactly as in Python.
59 +Classes definition is not allowed, to do so, one must create a
60 +separate Python module and import its content.
61 +
62 +The following is an example of valid ABCD definitions:
63 +
64 + from foo import *
65 + from bar import spam
66 + import math
67 +
68 + def sqrt (x) :
69 + return int(math.sqrt(x))
70 +
71 +
72 +Buffers definitions
73 +^^^^^^^^^^^^^^^^^^^
74 +
75 +An ABCD buffer is implemented in the Petri nets semantics as a
76 +coloured place, so a buffer is:
77 +
78 +typed
79 + values that can be inserted in the buffer must belong to a given
80 + type; using ``object`` allows to put anything in the buffer.
81 +
82 +unbounded
83 + there is no a priori limit to the number of values that can be
84 + inserted in a buffer, not even to the number of copies of a given
85 + value within a buffer.
86 +
87 +unordered
88 + the order in which values are retrieved from a buffer is non
89 + deterministic and is absolutely not related to the order of
90 + insertion.
91 +
92 +In order to contain the combinatorial explosion during the analysis of
93 +the Petri net resulting from an ABCD specification, it is recommended
94 +to take these aspects into account. In particular, it could be good
95 +to:
96 +
97 + - define buffer types as small as possible, allowing just the
98 + expected values and no more;
99 +
100 + - implement some policy in order to limit the number of values
101 + simultaneously stored in a buffer. Anyway, if the buffer is
102 + unbounded, it is likely that the resulting Petri net cannot be
103 + analysed;
104 +
105 + - implement a FIFO policy whenever possible, for instance by storing
106 + numbered pairs ``(num, obj)`` instead of just ``obj`` and by
107 + maintaining a counter for the next value to insert and the next to
108 + get.
109 +
110 +In order to declare a buffer, one has to write:
111 +
112 + buffer NAME : TYPE = INIT
113 +
114 +where ``NAME`` is the name of the buffer (a Python identifier),
115 +``TYPE`` is its type (a Python type name or a more complex type
116 +specification, see below), and ``INIT`` is the initial content of the
117 +buffer: ``()`` if empty, or a comma separated list of values.
118 +
119 +For instance, an empty buffer of integers and a buffer of strings with
120 +two values can be declared as:
121 +
122 + buffer count : int = ()
123 + buffer messages : str = 'hello', 'world'
124 +
125 +Buffer types can be:
126 +
127 +Python classes
128 + for instance ``int``, ``str``, ``float``, ``bool``, ``object``,
129 + etc., including user defined classes.
130 +
131 +Enumerated types
132 + for instance ``enum(1, 3, 'foo', True)`` allows all the value listed
133 + between the parenthesis but no other value.
134 +
135 +Union types
136 + for instance, ``int|float`` allows for integer as well as floating
137 + point numbers. Intersection types, using operator ``&``, are also
138 + allowed even if it hard to find a real usage for them.
139 +
140 +Sets of typed values
141 + for instance, ``{int|float}`` defines sets of numbers (integers or
142 + floating point).
143 +
144 +Lists of typed values
145 + for instance, ``[str]`` defines lists of strings.
146 +
147 +Dictionary types
148 + for instance, ``str:int`` specifies ``dict`` objects whose keys
149 + are strings and values are integers.
150 +
151 +Cross product of types
152 + for instance, ``(int, str)`` specifies tuples of length two whose
153 + first item is an integer and second item is a string. Tuples of
154 + length one *must* use a trailing comma; for instance, ``(int,)``
155 + stands for integer singletons, but ``(int)`` is equivalent to just
156 + ``int`` as usual in Python.
157 +
158 +Parentheses are allowed in order to combine complex types together, as
159 +in ``(int|float):(str|NoneType)``.
160 +
161 +
162 +Sub-nets definitions
163 +^^^^^^^^^^^^^^^^^^^^
164 +
165 +A sub-net is declared as follows:
166 +
167 + net NAME (PARAMS) :
168 + BLOCK
169 +
170 +where ``NAME`` is the name of the sub-net (a Python identifier),
171 +``PARAMS`` is a list of parameters (as in Python with default values
172 +allowed but not ``*`` or ``**`` arguments) and BLOCK is an indented
173 +block that follows the syntax of an ABCD specification (with optional
174 +definitions and a mandatory process term).
175 +
176 +Objects (Python functions or imports, and buffers) defined inside a
177 +sub-net are local to it and cannot be accessed from the outside. But,
178 +objects defined before the sub-net (unless nested in another sub-net)
179 +are visible from within the sub-net and can be used.
180 +
181 +
182 +Process terms
183 +^^^^^^^^^^^^^
184 +
185 +An ABCD process is defined as a term on a process algebra whose
186 +operators are control flow operators:
187 +
188 +sequential composition
189 + the execution of ``A ; B`` starts with the execution of ``A``,
190 + followed by the execution of ``B``
191 +
192 +choice composition
193 + the execution of ``A + B`` is either the execution of ``A`` or
194 + that of ``B``, which is chosen non-deterministically.
195 +
196 +loop composition
197 +
198 + the execution of ``A * B`` starts by an arbitrary number of
199 + executions of ``A``, followed by exactly one execution of ``B``,
200 + the choice to loop or terminate is non-deterministic. So, ``A *
201 + B`` is equivalent to ``B + (A ; B) + (A ; A ; B) + ...``.
202 +
203 +parallel composition
204 + the execution of ``A | B`` is that of both ``A`` and ``B``
205 + concurrently.
206 +
207 +Base terms of the algebra are either atomic processes or sub-net
208 +instantiations. An atomic process, also called an action, is described
209 +by a term enclosed in square brackets ``[...]``. The semantics of an
210 +action is a Petri net transition. We distinguish:
211 +
212 +``[True]``
213 + the silent action that can always be executed and performs no
214 + buffer access.
215 +
216 +``[False]``
217 + the deadlock action that can never be executed.
218 +
219 +complex actions
220 + such an action involve buffer accesses and an optional condition.
221 + If ``expr`` denotes a Python expression, ``obj`` a Python constant
222 + and ``var`` a Python identifier, buffer accesses may be:
223 +
224 + - ``buffer+(expr)`` evaluated ``expr`` and adds the resulting value
225 + to the buffer; this results in the semantics as an arc from the
226 + transition to the buffer place, labeled by ``expr``.
227 +
228 + - ``buffer-(obj)`` consumes the value ``obj`` from the buffer; this
229 + results in the semantics as an arc from the buffer place to the
230 + transition, labeled by ``obj``.
231 +
232 + - ``buffer-(var)`` binds the variable ``var`` to a value present in
233 + the buffer and consumes it; this results in the semantics as an arc
234 + from the buffer place to the transition, labeled by ``var``.
235 +
236 + - ``buffer?(obj)`` or ``buffer?(var)`` are similar except that they
237 + just test the presence of a value but do not consume it; this is
238 + semantically a read arc.
239 +
240 + - ``buffer>>(var)`` consumes all the values in the buffer and bind
241 + the resulting multiset to the variable ``var``; this is
242 + semantically a flush arc.
243 +
244 + - ``buffer<<(expr)`` evaluates the expression ``expr`` (the result
245 + must be iterable) and adds all its values to the buffer; this is
246 + semantically a fill arc.
247 +
248 +For instance:
249 +
250 + [count-(x), count+(x+1), shift?(j), buf+(j+x) if x<10]
251 +
252 +This action can be execution if the following hold:
253 +
254 + - buffer ``count`` must hold a value that it is bound to ``x``
255 +
256 + - buffer ``shift`` mush hold a value that is bound to ``j``
257 +
258 + - the type of buffer ``count`` must allow the value resulting from
259 + the evaluation of ``x+1``
260 +
261 + - the type of buffer ``buf`` must allow the value resulting from the
262 + evaluation of ``j+x``
263 +
264 + - expression ``x<10`` must evaluate to ``True``
265 +
266 +If all these condition hold, the action can be executed, which results in:
267 +
268 + - the chosen value for ``x`` is removed from buffer ``count``
269 +
270 + - a new value corresponding to the evaluation of ``x+1` is added to
271 + ``count``
272 +
273 + - a new value corresponding to the evaluation of ``j+x`` is added to
274 + ``buf``
275 +
276 +This execution is atomic: it can be considered that all buffers accesses
277 +and conditions evaluation are performed simultaneously and
278 +instantaneously.
279 +
280 +If ``count`` or ``shift`` contain more than one value, only those that
281 +allow to fulfill the conditions listed above are considered. Among
282 +those valuations, one is chosen non deterministically in order to
283 +execute the action.
284 +
285 +Note that the variables (like ``var``) used in an action do not need
286 +to be declared and are local to this action. These are variables
287 +exactly like in mathematics. Moreover, if a variable is used more than
288 +once in an action, the execution gives it a single consistent value.
289 +For instance, ``[count-(x), shit?(x) if x != 0]`` is executable only
290 +if a same non-zero value can be found both in ``count`` and ``shift``.
291 +
292 +
293 +Example
294 +-------
295 +
296 +Let's consider a simple railroad crossing involving:
297 +
298 + - one track where trains can arrive repeatedly;
299 +
300 + - one road crossing the track;
301 +
302 + - a pair of gates that prevent cars to go on the track when a train
303 + is approaching;
304 +
305 + - a red light that prevent the train to cross the road before the
306 + gates completely close.
307 +
308 +When a train is approaching, the light is turned red and the gates are
309 +asked to go down. When they arrive down, the light is reset to green.
310 +When the train leaves the gates, they are asked to go up.
311 +
312 +This system can be specified in ABCD as follows. We first specify
313 +global buffers to model the red light and a communication channel
314 +between trains and the gates:
315 +
316 + # light is initially 'green'
317 + buffer light : in('red', 'green') = 'green'
318 + # no command is available initially
319 + buffer command : in('up', 'down') = ()
320 +
321 +Then we specify the behavior of the gates. We provide for it an
322 +internal buffer allowing to easily observe its current state.
323 +
324 + net gate () :
325 + # gates are initially 'open'
326 + buffer state : in('open', 'moving', 'closed') = 'open'
327 + # a sequence of actions
328 + # receive the command 'down' and start moving
329 + ([command-('down'), state-('open'), state+('moving')] ;
330 + # finish to close and reset the light to 'green'
331 + [state-('moving'), state+('closed'), light-('red'), light+('green')] ;
332 + # receive the command 'up' and start moving
333 + [command-('up'), state-('closed'), state+('moving')] ;
334 + # finish to open
335 + [state-('moving'), state+('open')])
336 + # this sequence is infinitely repeated because the loop exit
337 + # cannot be executed
338 + * [False]
339 +
340 +Then we specify the track on which trains can repeatedly arrive
341 +
342 + net track () :
343 + # we also need to observe trains position
344 + buffer crossing : bool = False
345 + # here also an sequence is infinitely repeated
346 + # a train is approaching so the light is turned red and the
347 + # gates are asked to close
348 + ([command+('down'), light-('green'), light+('red')] ;
349 + # the train must wait for green light before to go further and
350 + # cross the road
351 + [light?('green'), crossing-(False), crossing+(True)] ;
352 + # when the train leaves, gates are asked to open
353 + [crossing-(True), crossing+(False), command+('up')])
354 + * [False]
355 +
356 +The full system is specified by running in parallel one instance of
357 +the gates and one of the track.
358 +
359 + gate() | track()
360 +
361 +The Petri net from this specification can be drawn and saved to PNML
362 +by invoking:
363 +
364 + abcd --pnml railroad.pnml --dot railroad.png railroad.abcd
365 +
366 +This creates both ``railroad.png`` and ``railroad.pnml``, the former
367 +can be viewed in order to check how is the Petri net semantics, and
368 +the latter can be used to verify the system. On such a small system,
369 +SNAKES performs quickly enough for the verification. So we can use it
370 +to iterate the marking graph and search for an insecure state, ie, in
371 +which gates are open and train. The following program does the job:
372 +
373 + from snakes.nets import *
374 +
375 + n = loads(",railroad.pnml")
376 + g = StateGraph(n)
377 + for s in g :
378 + m = g.net.get_marking()
379 + if ("train().crossing" in m
380 + and True in m["train().crossing"]
381 + and "closed" not in m["gate().state"]) :
382 + print s, m
383 + print "checked", len(g), "states"
384 +
385 +Here, no insecure marking is found. This would not be the case if we
386 +would remove the red light since a train could always arrive on the
387 +road faster than the gate could close.
1 +prod-cons.abcd
2 + a simple producer/consumer
3 +
4 +railroad.abcd
5 + a (not so) simple railroad crossing system
6 +railroad.py
7 + checks basic property of railroad.abcd
8 +
9 +ns/ns.abcd
10 + Needham-Schroeder public key authentication protocol
11 +ns/ns.py
12 + checks mutual authentication
1 +class Nonce (object) :
2 + def __init__ (self, agent) :
3 + self._agent = agent
4 + def __eq__ (self, other) :
5 + try :
6 + return self._agent == other._agent
7 + except :
8 + return False
9 + def __ne__ (self, other) :
10 + return not self.__eq__(other)
11 + def __str__ (self) :
12 + return self.__repr__()
13 + def __repr__ (self) :
14 + return "Nonce(%x)" % self._agent
15 +
16 +def _cross (sets) :
17 + if len(sets) == 0 :
18 + pass
19 + elif len(sets) == 1 :
20 + for item in sets[0] :
21 + yield (item,)
22 + else :
23 + for item in sets[0] :
24 + for others in _cross(sets[1:]) :
25 + yield (item,) + others
26 +
27 +
28 +class Spy (object) :
29 + keywords = set(["crypt", "pub", "priv", "secret", "hash"])
30 + def __init__ (self, *types) :
31 + """
32 + >>> s = Spy(str, int, (str, int, (float, object)))
33 + >>> s
34 + <Spy>
35 + >>> s._subtypes == set([str, int, float, object,
36 + ... (float, object),
37 + ... (str, int, (float, object))])
38 + True
39 + """
40 + self._types = set(types)
41 + self._subtypes = set()
42 + todo = set(self._types)
43 + while len(todo) > 0 :
44 + t = todo.pop()
45 + self._subtypes.add(t)
46 + if isinstance(t, tuple) :
47 + todo.update(t)
48 + def __str__ (self) :
49 + """
50 + >>> str(Spy(str, int))
51 + '<Spy>'
52 + """
53 + return "<%s>" % self.__class__.__name__
54 + def __repr__ (self) :
55 + """
56 + >>> str(Spy(str, int))
57 + '<Spy>'
58 + """
59 + return self.__str__()
60 + def __eq__ (self, other) :
61 + """
62 + >>> Spy(str, int) == Spy(int, str)
63 + True
64 + >>> Spy(str, int) == Spy(int, str, float)
65 + False
66 + """
67 + return self._types == other._types
68 + def __ne__ (self, other) :
69 + """
70 + >>> Spy(str, int) != Spy(int, str, float)
71 + True
72 + >>> Spy(str, int) != Spy(int, str)
73 + False
74 + """
75 + return not self.__eq__(other)
76 + def __hash__ (self) :
77 + """
78 + >>> hash(Spy(str, int)) == hash(Spy(int, str))
79 + True
80 + >>> hash(Spy(str, int)) == hash(Spy(int, str, float))
81 + False
82 + """
83 + return hash(tuple(sorted(self._types)))
84 + @classmethod
85 + def get_type (cls, obj) :
86 + """
87 + >>> Spy().get_type(('hello', ('foo', 4), 5))
88 + (<type 'str'>, (<type 'str'>, <type 'int'>), <type 'int'>)
89 + """
90 + t = type(obj)
91 + if t is tuple :
92 + if len(obj) > 0 and obj[0] in cls.keywords :
93 + return (obj[0],) + tuple(cls.get_type(o) for o in obj[1:])
94 + else :
95 + return tuple(cls.get_type(o) for o in obj)
96 + else :
97 + return t
98 + @classmethod
99 + def match (cls, obj, pattern) :
100 + """
101 + >>> Spy.match(('hello', 42, (1, 2, 3.4)), (str, int, (1, 2, float)))
102 + True
103 + >>> Spy.match(('hello', 42, (1, 2, 3.4)), ('hello', int, (1, 2, float)))
104 + True
105 + >>> Spy.match(('hello', 42, (1, 2, 3)), (str, int, (1, 2, float)))
106 + False
107 + >>> Spy.match(('hello', 42, (1, 2, 3, 4)), (str, int, (1, 2, float)))
108 + False
109 + >>> Spy.match(('hello', 42, (1, 2, 3.4)), ('foo', int, (1, 2, float)))
110 + False
111 + """
112 + if type(obj) == tuple == type(pattern) :
113 + if len(obj) != len(pattern) :
114 + return False
115 + for o, p in zip(obj, pattern) :
116 + if not cls.match(o, p) :
117 + return False
118 + return True
119 + elif type(pattern) is type :
120 + return isinstance(obj, pattern)
121 + else :
122 + return obj == pattern
123 + def message (self, obj) :
124 + """
125 + >>> Spy(str, int).message('hello')
126 + True
127 + >>> Spy(str, int).message(42)
128 + True
129 + >>> Spy(str, int).message((1, 2))
130 + False
131 + >>> Spy(str, int).message(3.14)
132 + False
133 + """
134 + return self.get_type(obj) in self._types
135 + def fragment (self, obj) :
136 + """
137 + >>> s = Spy(str, int, (str, int, (float, list)))
138 + >>> s.fragment('hello')
139 + True
140 + >>> s.fragment(3.14)
141 + True
142 + >>> s.fragment((3.14, []))
143 + True
144 + >>> s.fragment(('hello', 1, (3.14, [])))
145 + True
146 + >>> s.fragment((1, 2))
147 + False
148 + >>> s.fragment({})
149 + False
150 + """
151 + return self.get_type(obj) in self._subtypes
152 + def can_decrypt (self, message, knowledge) :
153 + try :
154 + if message[0] != "crypt" :
155 + return False
156 + key = message[1]
157 + if key[0] == "pub" :
158 + return ("priv", key[1]) in knowledge
159 + elif key[0] == "priv" :
160 + return ("pub", key[1]) in knowledge
161 + elif key[0] == "secret" :
162 + return key in knowledge
163 + except :
164 + pass
165 + return False
166 + def can_decompose (self, message) :
167 + try :
168 + if not isinstance(message, tuple) :
169 + return False
170 + elif message[0] not in self.keywords :
171 + return True
172 + except :
173 + pass
174 + return False
175 + def learn (self, msg, knowledge) :
176 + """
177 + >>> s = Spy((int, int, str), (int, int, (str, str)))
178 + >>> k = set()
179 + >>> k = s.learn((1, 2, 'hello'), k)
180 + >>> for m in sorted(k) :
181 + ... print m
182 + 1
183 + 2
184 + hello
185 + (1, 1, 'hello')
186 + (1, 1, ('hash', 'hello'))
187 + (1, 1, ('hello', 'hello'))
188 + (1, 2, 'hello')
189 + (1, 2, ('hash', 'hello'))
190 + (1, 2, ('hello', 'hello'))
191 + (2, 1, 'hello')
192 + (2, 1, ('hash', 'hello'))
193 + (2, 1, ('hello', 'hello'))
194 + (2, 2, 'hello')
195 + (2, 2, ('hash', 'hello'))
196 + (2, 2, ('hello', 'hello'))
197 + ('hash', 'hello')
198 + ('hello', 'hello')
199 + >>> k = s.learn((2, 3, ('hello', 'world')), k)
200 + >>> for m in sorted(k) :
201 + ... print m
202 + 1
203 + 2
204 + 3
205 + hello
206 + world
207 + (1, 1, 'hello')
208 + (1, 1, 'world')
209 + (1, 1, ('hash', 'hello'))
210 + (1, 1, ('hash', 'world'))
211 + (1, 1, ('hello', 'hello'))
212 + (1, 1, ('hello', 'world'))
213 + (1, 1, ('world', 'hello'))
214 + (1, 1, ('world', 'world'))
215 + (1, 2, 'hello')
216 + (1, 2, 'world')
217 + (1, 2, ('hash', 'hello'))
218 + (1, 2, ('hash', 'world'))
219 + (1, 2, ('hello', 'hello'))
220 + (1, 2, ('hello', 'world'))
221 + (1, 2, ('world', 'hello'))
222 + (1, 2, ('world', 'world'))
223 + (1, 3, 'hello')
224 + (1, 3, 'world')
225 + (1, 3, ('hash', 'hello'))
226 + (1, 3, ('hash', 'world'))
227 + (1, 3, ('hello', 'hello'))
228 + (1, 3, ('hello', 'world'))
229 + (1, 3, ('world', 'hello'))
230 + (1, 3, ('world', 'world'))
231 + (2, 1, 'hello')
232 + (2, 1, 'world')
233 + (2, 1, ('hash', 'hello'))
234 + (2, 1, ('hash', 'world'))
235 + (2, 1, ('hello', 'hello'))
236 + (2, 1, ('hello', 'world'))
237 + (2, 1, ('world', 'hello'))
238 + (2, 1, ('world', 'world'))
239 + (2, 2, 'hello')
240 + (2, 2, 'world')
241 + (2, 2, ('hash', 'hello'))
242 + (2, 2, ('hash', 'world'))
243 + (2, 2, ('hello', 'hello'))
244 + (2, 2, ('hello', 'world'))
245 + (2, 2, ('world', 'hello'))
246 + (2, 2, ('world', 'world'))
247 + (2, 3, 'hello')
248 + (2, 3, 'world')
249 + (2, 3, ('hash', 'hello'))
250 + (2, 3, ('hash', 'world'))
251 + (2, 3, ('hello', 'hello'))
252 + (2, 3, ('hello', 'world'))
253 + (2, 3, ('world', 'hello'))
254 + (2, 3, ('world', 'world'))
255 + (3, 1, 'hello')
256 + (3, 1, 'world')
257 + (3, 1, ('hash', 'hello'))
258 + (3, 1, ('hash', 'world'))
259 + (3, 1, ('hello', 'hello'))
260 + (3, 1, ('hello', 'world'))
261 + (3, 1, ('world', 'hello'))
262 + (3, 1, ('world', 'world'))
263 + (3, 2, 'hello')
264 + (3, 2, 'world')
265 + (3, 2, ('hash', 'hello'))
266 + (3, 2, ('hash', 'world'))
267 + (3, 2, ('hello', 'hello'))
268 + (3, 2, ('hello', 'world'))
269 + (3, 2, ('world', 'hello'))
270 + (3, 2, ('world', 'world'))
271 + (3, 3, 'hello')
272 + (3, 3, 'world')
273 + (3, 3, ('hash', 'hello'))
274 + (3, 3, ('hash', 'world'))
275 + (3, 3, ('hello', 'hello'))
276 + (3, 3, ('hello', 'world'))
277 + (3, 3, ('world', 'hello'))
278 + (3, 3, ('world', 'world'))
279 + ('hash', 'hello')
280 + ('hash', 'world')
281 + ('hello', 'hello')
282 + ('hello', 'world')
283 + ('world', 'hello')
284 + ('world', 'world')
285 +
286 + >>> s = Spy(('crypt', ('pub', int), str))
287 + >>> pub, priv = ('pub', 1), ('priv', 1)
288 + >>> k = set([pub])
289 + >>> k = s.learn(('crypt', priv, 'hello'), k)
290 + >>> 'hello' in k
291 + True
292 + >>> k = s.learn(('crypt', pub, 'secret message'), k)
293 + >>> 'secret message' in k
294 + False
295 + >>> k.add(priv)
296 + >>> k = s.learn(('crypt', pub, 'secret message'), k)
297 + >>> 'secret message' in k
298 + True
299 + """
300 + k = set(knowledge)
301 + # learn from new message
302 + # add new message to knowledge
303 + k.add(msg)
304 + # hash new message if useful
305 + h = ("hash", msg)
306 + if self.fragment(h) :
307 + k.add(h)
308 + # try to decrypt new message
309 + if self.can_decrypt(msg, k) :
310 + for m in msg[2:] :
311 + if m not in k :
312 + k.update(self.learn(m, k))
313 + # try to decompose new message
314 + elif self.can_decompose(msg) :
315 + for m in msg :
316 + if m not in k :
317 + k.update(self.learn(m, k))
318 + self._learn_(msg, k)
319 + # compose new messages from fragments
320 + for sub in (s for s in sorted(self._subtypes, key=self._size)
321 + if type(s) is tuple) :
322 + sets = []
323 + for t in sub :
324 + sets.append([x for x in k|self.keywords if self.match(x, t)])
325 + k.update(_cross(sets))
326 + return k
327 + @classmethod
328 + def _size (cls, obj) :
329 + if isinstance(obj, tuple) :
330 + return (len(obj),) + tuple(cls._size(o) for o in obj)
331 + else :
332 + return 1
333 + def _learn_ (self, m, k) :
334 + for attr in (a for a in dir(self) if a.startswith("learn_")) :
335 + getattr(self, attr)(m, k)
336 +
337 +class SpyKS (Spy) :
338 + def can_decrypt (self, message, knowledge) :
339 + try :
340 + if message[1][0] == "priv" :
341 + knowledge.add(("pub", message[1][1]))
342 + except :
343 + pass
344 + return Spy.can_decrypt(self, message, knowledge)
345 +
346 +if __name__ == "__main__" :
347 + import doctest
348 + doctest.testmod(optionflags=doctest.ELLIPSIS)
1 +# communication network
2 +buffer nw : object = ()
3 +# implementation of nonces and Dolev-Yao attacker
4 +from dolev_yao import *
5 +
6 +net Alice (this, who: buffer) :
7 + # protocol initiater
8 + buffer peer : int = ()
9 + buffer peer_nonce : Nonce = ()
10 + [who?(B), peer+(B), nw+("crypt", ("pub", B), this, Nonce(this))]
11 + ; [nw-("crypt", ("pub", this), Na, Nb), peer_nonce+(Nb) if Na == Nonce(this)]
12 + ; [peer?(B), peer_nonce?(Nb), nw+("crypt", ("pub", B), Nb)]
13 +
14 +net Bob (this) :
15 + # protocol responder
16 + buffer peer : int = ()
17 + buffer peer_nonce : Nonce = ()
18 + [nw-("crypt", ("pub", this), A, Na), peer+(A), peer_nonce+(Na)]
19 + ; [peer?(A), peer_nonce?(Na), nw+("crypt", ("pub", A), Na, Nonce(this))]
20 + ; [nw-("crypt", ("pub", this), Nb) if Nb == Nonce(this)]
21 +
22 +net Mallory (this, init) :
23 + # attacker
24 + buffer knowledge : object = (this, Nonce(this), ("priv", this)) + init
25 + # Dolev-Yao attacker, bound by protocol signature
26 + buffer spy : object = Spy(("crypt", ("pub", int), int, Nonce),
27 + ("crypt", ("pub", int), Nonce, Nonce),
28 + ("crypt", ("pub", int), Nonce))
29 + # capture on message and learn from it
30 + ([spy?(s), nw-(m), knowledge>>(k), knowledge<<(s.learn(m, k))]
31 + # loose message or inject another one (may be the same)
32 + ; ([True] + [spy?(s), knowledge?(x), nw+(x) if s.message(x)]))
33 + * [False]
34 +
35 +# Alice will contact one of these agents
36 +buffer agents : int = 2, 3
37 +# main processes, with friendly names
38 +alice::Alice(1, agents)
39 +| bob::Bob(2)
40 +| spy::Mallory(3, (1, ("pub", 1), 2, ("pub", 2)))
1 +import snakes.plugins
2 +snakes.plugins.load("status", "snakes.nets", "nets")
3 +from nets import *
4 +from dolev_yao import Nonce
5 +
6 +ns = loads(",ns.pnml")
7 +states = StateGraph(ns)
8 +
9 +for s in states :
10 + m = states.net.get_marking()
11 + # skip non final markings
12 + if "bob.x" not in m or "alice.x" not in m :
13 + continue
14 + # get Alice's and Bob's peers ids
15 + bp = list(m["bob.peer"])[0]
16 + ap = list(m["alice.peer"])[0]
17 + # violation of mutual authentication
18 + if bp == 1 and ap != 2 :
19 + print(s, "A(1) <=> %s ; B(2) <=> %s" % (ap, bp))
20 + print(m)
21 +
22 +print(len(states), "states")
1 +# get BlackToken
2 +#from snakes.nets import *
3 +
4 +buffer fork1 : BlackToken = dot
5 +buffer fork2 : BlackToken = dot
6 +buffer fork3 : BlackToken = dot
7 +
8 +# buffer parameters have to be declared as such
9 +net philo (left: buffer, right: buffer):
10 + buffer eating : BlackToken = ()
11 + ([left-(dot), right-(dot), eating+(dot)]
12 + ; [left+(dot), right+(dot), eating-(dot)])
13 + * [False]
14 +
15 +philo(fork1, fork2)
16 +| philo(fork2, fork3)
17 +| philo(fork3, fork1)
1 +# shared buffer between producers and consumers
2 +buffer bag : int = ()
3 +
4 +net prod () :
5 + # produces 10 tokens: 1..9 in bag
6 + buffer count : int = 0
7 + [count-(x), count+(x+1), bag+(x) if x < 10] * [count-(x) if x == 10]
8 +
9 +net odd () :
10 + # consumes odd tokens in bag
11 + [bag-(x) if (x % 2) == 1] * [False]
12 +
13 +net even () :
14 + # consumes even tokens un bag
15 + [bag-(x) if (x % 2) == 0] * [False]
16 +
17 +# main process with one instance of each net
18 +odd() | even() | prod()
1 +# symbols
2 +symbol RED, GREEN, UP, DOWN, OPEN, MOVING, CLOSED
3 +# states of the gate
4 +typedef gatestate : enum(OPEN, MOVING, CLOSED)
5 +
6 +# stores green light state
7 +buffer light : enum(RED, GREEN) = GREEN
8 +# commands send by the track to the gate
9 +buffer command : enum(UP, DOWN) = ()
10 +
11 +net gate () :
12 + # a pair of gates
13 + buffer state : gatestate = OPEN
14 + ([command-(DOWN), state-(OPEN), state+(MOVING)] ;
15 + [state-(MOVING), state+(CLOSED), light-(RED), light+(GREEN)] ;
16 + [command-(UP), state-(CLOSED), state+(MOVING)] ;
17 + [state-(MOVING), state+(OPEN)])
18 + * [False]
19 +
20 +net track () :
21 + # a track with trains passing on it
22 + buffer crossing : bool = False
23 + ([command+(DOWN), light-(GREEN), light+(RED)] ;
24 + [light?(GREEN), crossing-(False), crossing+(True)] ;
25 + [crossing-(True), crossing+(False), command+(UP)])
26 + * [False]
27 +
28 +# main process
29 +gate() | track()
1 +import sys
2 +
3 +import snakes.plugins
4 +snakes.plugins.load("gv", "snakes.nets", "snk")
5 +from snk import *
6 +
7 +n = loads(sys.argv[1])
8 +g = StateGraph(n)
9 +for s in g :
10 + m = g.net.get_marking()
11 + # safety property: train present => gates closed
12 + if ("train().crossing" in m
13 + and True in m["train().crossing"]
14 + and "closed" not in m["gate().state"]) :
15 + print("%s %s" % (s, m))
16 +print("checked %s states" % len(g))
17 +
18 +g.draw(sys.argv[1].rsplit(".", 1)[0] + "-states.png")
1 +The plugin 'queries' introduces two new classes:
2 +
3 + - Query allows to describe and execute various kind of queries, that
4 + can be serialized to PNML in order to be exchanged with another
5 + program. SNAKES uses an extension of PNML, for a complete list of
6 + SNAKES' PNML tags, see 'snakes-pnml.txt'.
7 +
8 + - UDPServer is a sample server over UDP that handles a limited number
9 + of simple queries. However, these queries can be nested, in the
10 + client program as well as in the communication with the server, so
11 + the range of possibilities is unlimited and very complex queries
12 + may be constructed from those simple ones.
13 +
14 +A server program is provides in 'utils/query/snkd.py' and is general
15 +an robust enough to be used in real cases (but considering it only
16 +implements the limited set of queries exposed above). As it works in a
17 +disconnected mode (UDP), this server does not try to make any
18 +difference between possibly several clients. This may result in data
19 +sharing or conflicts, which makes this server more suitable to a local
20 +use only. Start the server with option '-h' to have details about its
21 +command line.
22 +
23 +A sample client is provided also, in order to allow to experiment with
24 +the server. (Notice that since the client is programmed in Python, it
25 +could use SNAKES directly, which explains why it will never be made
26 +more sophisticated.) It takes the form a shell-like program that
27 +accepts commands of the form "? command(param, ...)" where "? " is the
28 +prompt.
29 +
30 +First come local commands, these are commands that do not send any
31 +data to the server but are handled locally:
32 +
33 + ? help()
34 +
35 + list available commands
36 +
37 + ? help(command)
38 +
39 + print help about the given command
40 +
41 + ? quit()
42 +
43 + exits the client, end-of-file or ^C may be used equivalently
44 +
45 + ? load(path)
46 +
47 + loads a PNML file and return the object it represents. This is
48 + useful for instance to load a Petri net on the server as in:
49 + "? set('net', load('mynet.pnml'))"
50 +
51 + ? show(obj)
52 +
53 + prints the PNML representation of 'obj', this may be useful for
54 + instance to see how a query is translated, as in:
55 + "? show(set('x', [1, 2, 'hello']))"
56 +
57 + ? verbose(mode)
58 +
59 + turns on (mode=True), off (mode=False) or toggle (no mode given)
60 + the printing of queries before they are sent to the server
61 +
62 +Then come commands that actually generate queries, there is only 4 of
63 +them:
64 +
65 + ? set('name', value)
66 +
67 + assign value to 'name', which is equivalent to the Python statement
68 + "name=value". 'value' may be any Python expression. It is important
69 + to notice that 'name' is assigned on the server side, in an
70 + environment that initially contains Python's builtins, the content
71 + of 'operator' and of 'snakes.net' modules (the later being extended
72 + by all the plugins loaded before 'query').
73 +
74 + ? get('name')
75 +
76 + returns the value previously assigned to 'name'.
77 +
78 + ? delete('name')
79 +
80 + Equivalent to the Python statement "del name".
81 +
82 + ? call(obj, ...)
83 +
84 + equivalent to the Python statement "obj(...)". 'obj' may be a name
85 + or an access to an object like 'x' or 'x.method', or even the
86 + result from a nested query (see examples below).
87 +
88 +##
89 +## Answers
90 +##
91 +
92 +In case of a success with no return value, the answer is:
93 +
94 +<?xml version="1.0" encoding="utf-8"?>
95 +<pnml>
96 + <answer status="ok"/>
97 +</pnml>
98 +
99 +If there is a return value, it is given as a PNML sub-tree of tag
100 +<answer>. For instance:
101 +
102 +<?xml version="1.0" encoding="utf-8"?>
103 +<pnml>
104 + <answer status="ok">
105 + <object type="str">hello world!</object>
106 + </answer>
107 +</pnml>
108 +
109 +If an errors occurs during the handling of the query, an answer with
110 +status "error" is returned. The data in tag <answer> is the error
111 +message and the tag has an attribute 'error' that is the name of the
112 +caught exception. For instance:
113 +
114 +<?xml version="1.0" encoding="utf-8"?>
115 +<pnml>
116 + <answer error="ExceptionName" status="error">Exception message</answer>
117 +</pnml>
118 +
119 +##
120 +## Queries
121 +##
122 +
123 +Query arguments are passed as tags <argument> nested in <query>, the
124 +value of each argument is encoded in PNML using the tags presented in
125 +'snakes-pnml.txt'. The following is a copy/paste from a snkc session,
126 +interleaved with comments.
127 +
128 +First, we turn on the verbose mode.
129 +
130 +? verbose()
131 +dump of queries enabled
132 +
133 +The first query assigns to 'x' a list composed of an integer, a float
134 +and a string. This allows to illustrate how these types are encoded.
135 +
136 +? set('x', [1, 3.14, 'hello'])
137 +# query to localhost:1234
138 +<?xml version="1.0" encoding="utf-8"?>
139 +<pnml>
140 + <query name="set">
141 + <argument>
142 + <object type="str">x</object>
143 + </argument>
144 + <argument>
145 + <object type="list">
146 + <object type="int">1</object>
147 + <object type="float">3.14</object>
148 + <object type="str">hello</object>
149 + </object>
150 + </argument>
151 + </query>
152 +</pnml>
153 +# answer from 127.0.0.1:1234
154 +<?xml version="1.0" encoding="utf-8"?>
155 +<pnml>
156 + <answer status="ok"/>
157 +</pnml>
158 +
159 +If we get 'x' back, the same encoding is used again but in the other
160 +direction.
161 +
162 +? get('x')
163 +# query to localhost:1234
164 +<?xml version="1.0" encoding="utf-8"?>
165 +<pnml>
166 + <query name="get">
167 + <argument>
168 + <object type="str">
169 + x
170 + </object>
171 + </argument>
172 + </query>
173 +</pnml>
174 +# answer from 127.0.0.1:1234
175 +<?xml version="1.0" encoding="utf-8"?>
176 +<pnml>
177 + <answer status="ok">
178 + <object type="list">
179 + <object type="int">1</object>
180 + <object type="float">3.14</object>
181 + <object type="str">hello</object>
182 + </object>
183 + </answer>
184 +</pnml>
185 +
186 +'x' can be removed, after which trying to get it again results in an
187 +error.
188 +
189 +? delete('x')
190 +# query to localhost:1234
191 +<?xml version="1.0" encoding="utf-8"?>
192 +<pnml>
193 + <query name="del">
194 + <argument>
195 + <object type="str">x</object>
196 + </argument>
197 + </query>
198 +</pnml>
199 +# answer from 127.0.0.1:1234
200 +<?xml version="1.0" encoding="utf-8"?>
201 +<pnml>
202 + <answer status="ok"/>
203 +</pnml>
204 +
205 +? get('x')
206 +# query to localhost:1234
207 +<?xml version="1.0" encoding="utf-8"?>
208 +<pnml>
209 + <query name="get">
210 + <argument>
211 + <object type="str">x</object>
212 + </argument>
213 + </query>
214 +</pnml>
215 +# answer from 127.0.0.1:1234
216 +<?xml version="1.0" encoding="utf-8"?>
217 +<pnml>
218 + <answer error="AttributeError" status="error">
219 + 'module' object has no attribute 'x'
220 + </answer>
221 +</pnml>
222 +
223 +We set a new name 's' that is a string in order to illustrate method
224 +calls.
225 +
226 +? set('s', 'hello world!')
227 +# query to localhost:1234
228 +<?xml version="1.0" encoding="utf-8"?>
229 +<pnml>
230 + <query name="set">
231 + <argument>
232 + <object type="str">s</object>
233 + </argument>
234 + <argument>
235 + <object type="str">hello world!</object>
236 + </argument>
237 + </query>
238 +</pnml>
239 +# answer from 127.0.0.1:1234
240 +<?xml version="1.0" encoding="utf-8"?>
241 +<pnml>
242 + <answer status="ok"/>
243 +</pnml>
244 +
245 +The following calls the method 'replace' of 's' passing it two string
246 +arguments.
247 +
248 +? call('s.replace', 'o', '_')
249 +# query to localhost:1234
250 +<?xml version="1.0" encoding="utf-8"?>
251 +<pnml>
252 + <query name="call">
253 + <argument>
254 + <object type="str">s.replace</object>
255 + </argument>
256 + <argument>
257 + <object type="str">o</object>
258 + </argument>
259 + <argument>
260 + <object type="str">_</object>
261 + </argument>
262 + </query>
263 +</pnml>
264 +# answer from 127.0.0.1:1234
265 +<?xml version="1.0" encoding="utf-8"?>
266 +<pnml>
267 + <answer status="ok">
268 + <object type="str">hell_ w_rld!</object>
269 + </answer>
270 +</pnml>
271 +
272 +In order to cascade calls, that is call a method of the object
273 +returned by the first call, we can use the function 'getattr' that
274 +returns a named attribute of an object. Here, we get and call the
275 +'split' method from the string returned by 's.replace'. This is
276 +exactly the same as "s.replace('o', '_').split()" in Python.
277 +
278 +? call(call('getattr', call('s.replace', 'o', '_'), 'split'))
279 +# query to localhost:1234
280 +<?xml version="1.0" encoding="utf-8"?>
281 +<pnml>
282 + <query name="call">
283 + <argument>
284 + <query name="call">
285 + <argument>
286 + <object type="str">getattr</object>
287 + </argument>
288 + <argument>
289 + <query name="call">
290 + <argument>
291 + <object type="str">s.replace</object>
292 + </argument>
293 + <argument>
294 + <object type="str">o</object>
295 + </argument>
296 + <argument>
297 + <object type="str">_</object>
298 + </argument>
299 + </query>
300 + </argument>
301 + <argument>
302 + <object type="str">split</object>
303 + </argument>
304 + </query>
305 + </argument>
306 + </query>
307 +</pnml>
308 +# answer from 127.0.0.1:1234
309 +<?xml version="1.0" encoding="utf-8"?>
310 +<pnml>
311 + <answer status="ok">
312 + <object type="list">
313 + <object type="str">hell_</object>
314 + <object type="str">w_rld!</object>
315 + </object>
316 + </answer>
317 +</pnml>
318 +
319 +Let's now apply these techniques to work with Petri nets. First we
320 +load a net from PNML file. In practical cases, the command 'load' from
321 +snkc is not available. But it is enough to read the PNML file, extract
322 +its '<net>...</net>' part and paste it in the middle of the 'set'
323 +request.
324 +
325 +? set('n', load('simple-coloured.pnml'))
326 +# query to localhost:1234
327 +<?xml version="1.0" encoding="utf-8"?>
328 +<pnml>
329 + <query name="set">
330 + <argument>
331 + <object type="str">n</object>
332 + </argument>
333 + <argument>
334 + <net id="mynet">
335 + <place id="p2">
336 + <type domain="universal"/>
337 + <initialMarking>
338 + <multiset/>
339 + </initialMarking>
340 + </place>
341 + <place id="p1">
342 + <type domain="universal"/>
343 + <initialMarking>
344 + <multiset>
345 + <item>
346 + <value>
347 + <object type="int">1</object>
348 + </value>
349 + <multiplicity>1</multiplicity>
350 + </item>
351 + <item>
352 + <value>
353 + <object type="int">2</object>
354 + </value>
355 + <multiplicity>1</multiplicity>
356 + </item>
357 + </multiset>
358 + </initialMarking>
359 + </place>
360 + <transition id="t"/>
361 + <arc id="p1:t" source="p1" target="t">
362 + <inscription>
363 + <variable>x</variable>
364 + </inscription>
365 + </arc>
366 + <arc id="t:p2" source="t" target="p2">
367 + <inscription>
368 + <expression>x+1</expression>
369 + </inscription>
370 + </arc>
371 + </net>
372 + </argument>
373 + </query>
374 +</pnml>
375 +# answer from 127.0.0.1:1234
376 +<?xml version="1.0" encoding="utf-8"?>
377 +<pnml>
378 + <answer status="ok"/>
379 +</pnml>
380 +
381 +The marking of the net can then be retrieved. Only place 'p1' is
382 +marked by the two integer-valued tokens 1 and 2.
383 +
384 +? call('n.get_marking')
385 +# query to localhost:1234
386 +<?xml version="1.0" encoding="utf-8"?>
387 +<pnml>
388 + <query name="call">
389 + <argument>
390 + <object type="str">n.get_marking</object>
391 + </argument>
392 + </query>
393 +</pnml>
394 +# answer from 127.0.0.1:1234
395 +<?xml version="1.0" encoding="utf-8"?>
396 +<pnml>
397 + <answer status="ok">
398 + <marking>
399 + <place id="p1">
400 + <tokens>
401 + <multiset>
402 + <item>
403 + <value>
404 + <object type="int">1</object>
405 + </value>
406 + <multiplicity>1</multiplicity>
407 + </item>
408 + <item>
409 + <value>
410 + <object type="int">2</object>
411 + </value>
412 + <multiplicity>1</multiplicity>
413 + </item>
414 + </multiset>
415 + </tokens>
416 + </place>
417 + </marking>
418 + </answer>
419 +</pnml>
420 +
421 +Using the same techniques as for emulating "s.replace().split()"
422 +above, we can query the modes of transition 't' in net 'n'. We get in
423 +return a list of two substitutions that allow to fire 't'.
424 +
425 +? call(call('getattr', call('n.transition', 't'), 'modes'))
426 +# query to localhost:1234
427 +<?xml version="1.0" encoding="utf-8"?>
428 +<pnml>
429 + <query name="call">
430 + <argument>
431 + <query name="call">
432 + <argument>
433 + <object type="str">getattr</object>
434 + </argument>
435 + <argument>
436 + <query name="call">
437 + <argument>
438 + <object type="str">n.transition</object>
439 + </argument>
440 + <argument>
441 + <object type="str">t</object>
442 + </argument>
443 + </query>
444 + </argument>
445 + <argument>
446 + <object type="str">modes</object>
447 + </argument>
448 + </query>
449 + </argument>
450 + </query>
451 +</pnml>
452 +# answer from 127.0.0.1:1234
453 +<?xml version="1.0" encoding="utf-8"?>
454 +<pnml>
455 + <answer status="ok">
456 + <object type="list">
457 + <substitution>
458 + <item>
459 + <name>x</name>
460 + <value>
461 + <object type="int">1</object>
462 + </value>
463 + </item>
464 + </substitution>
465 + <substitution>
466 + <item>
467 + <name>x</name>
468 + <value>
469 + <object type="int">2</object>
470 + </value>
471 + </item>
472 + </substitution>
473 + </object>
474 + </answer>
475 +</pnml>
476 +
477 +Instead of getting this list of modes, we could have saved it to a
478 +name 's'. We just need to nest the above query in a 'set' query.
479 +
480 +? set('s', call(call('getattr', call('n.transition', 't'), 'modes')))
481 +# query to localhost:1234
482 +<?xml version="1.0" encoding="utf-8"?>
483 +<pnml>
484 + <query name="set">
485 + <argument>
486 + <object type="str">s</object>
487 + </argument>
488 + <argument>
489 + <query name="call">
490 + <argument>
491 + <query name="call">
492 + <argument>
493 + <object type="str">getattr</object>
494 + </argument>
495 + <argument>
496 + <query name="call">
497 + <argument>
498 + <object type="str">n.transition</object>
499 + </argument>
500 + <argument>
501 + <object type="str">t</object>
502 + </argument>
503 + </query>
504 + </argument>
505 + <argument>
506 + <object type="str">modes</object>
507 + </argument>
508 + </query>
509 + </argument>
510 + </query>
511 + </argument>
512 + </query>
513 +</pnml>
514 +# answer from 127.0.0.1:1234
515 +<?xml version="1.0" encoding="utf-8"?>
516 +<pnml>
517 + <answer status="ok"/>
518 +</pnml>
519 +
520 +In order to fire 't', we will call its method 'fire' and pass it one
521 +of the modes stored in 's'. Here, we use function 'getitem' to
522 +retrieve the first item in 's' (numbered 0). In a realistic example,
523 +it could be simpler to parse and store on the client the list of
524 +substitutions, instead of storing it on the server.
525 +
526 +? call(call('getattr', call('n.transition', 't'), 'fire'), call('getitem', get('s'), 0))
527 +# query to localhost:1234
528 +<?xml version="1.0" encoding="utf-8"?>
529 +<pnml>
530 + <query name="call">
531 + <argument>
532 + <query name="call">
533 + <argument>
534 + <object type="str">getattr</object>
535 + </argument>
536 + <argument>
537 + <query name="call">
538 + <argument>
539 + <object type="str">n.transition</object>
540 + </argument>
541 + <argument>
542 + <object type="str">t</object>
543 + </argument>
544 + </query>
545 + </argument>
546 + <argument>
547 + <object type="str">fire</object>
548 + </argument>
549 + </query>
550 + </argument>
551 + <argument>
552 + <query name="call">
553 + <argument>
554 + <object type="str">getitem</object>
555 + </argument>
556 + <argument>
557 + <query name="get">
558 + <argument>
559 + <object type="str">s</object>
560 + </argument>
561 + </query>
562 + </argument>
563 + <argument>
564 + <object type="int">0</object>
565 + </argument>
566 + </query>
567 + </argument>
568 + </query>
569 +</pnml>
570 +# answer from 127.0.0.1:1234
571 +<?xml version="1.0" encoding="utf-8"?>
572 +<pnml>
573 + <answer status="ok"/>
574 +</pnml>
575 +
576 +As we can see now, both places are marked.
577 +
578 +? call('n.get_marking')
579 +# query to localhost:1234
580 +<?xml version="1.0" encoding="utf-8"?>
581 +<pnml>
582 + <query name="call">
583 + <argument>
584 + <object type="str">n.get_marking</object>
585 + </argument>
586 + </query>
587 +</pnml>
588 +# answer from 127.0.0.1:1234
589 +<?xml version="1.0" encoding="utf-8"?>
590 +<pnml>
591 + <answer status="ok">
592 + <marking>
593 + <place id="p2">
594 + <tokens>
595 + <multiset>
596 + <item>
597 + <value>
598 + <object type="int">2</object>
599 + </value>
600 + <multiplicity>1</multiplicity>
601 + </item>
602 + </multiset>
603 + </tokens>
604 + </place>
605 + <place id="p1">
606 + <tokens>
607 + <multiset>
608 + <item>
609 + <value>
610 + <object type="int">2</object>
611 + </value>
612 + <multiplicity>1</multiplicity>
613 + </item>
614 + </multiset>
615 + </tokens>
616 + </place>
617 + </marking>
618 + </answer>
619 +</pnml>
620 +
621 +Then we can call again 'fire', with the second available mode.
622 +
623 +? call(call('getattr', call('n.transition', 't'), 'fire'), call('getitem', get('s'), 1))
624 +# query to localhost:1234
625 +<?xml version="1.0" encoding="utf-8"?>
626 +<pnml>
627 + <query name="call">
628 + <argument>
629 + <query name="call">
630 + <argument>
631 + <object type="str">getattr</object>
632 + </argument>
633 + <argument>
634 + <query name="call">
635 + <argument>
636 + <object type="str">n.transition</object>
637 + </argument>
638 + <argument>
639 + <object type="str">t</object>
640 + </argument>
641 + </query>
642 + </argument>
643 + <argument>
644 + <object type="str">fire</object>
645 + </argument>
646 + </query>
647 + </argument>
648 + <argument>
649 + <query name="call">
650 + <argument>
651 + <object type="str">getitem</object>
652 + </argument>
653 + <argument>
654 + <query name="get">
655 + <argument>
656 + <object type="str">s</object>
657 + </argument>
658 + </query>
659 + </argument>
660 + <argument>
661 + <object type="int">1</object>
662 + </argument>
663 + </query>
664 + </argument>
665 + </query>
666 +</pnml>
667 +# answer from 127.0.0.1:1234
668 +<?xml version="1.0" encoding="utf-8"?>
669 +<pnml>
670 + <answer status="ok"/>
671 +</pnml>
672 +
673 +And now only 'p2' is marked.
674 +
675 +? call('n.get_marking')
676 +# query to localhost:1234
677 +<?xml version="1.0" encoding="utf-8"?>
678 +<pnml>
679 + <query name="call">
680 + <argument>
681 + <object type="str">n.get_marking</object>
682 + </argument>
683 + </query>
684 +</pnml>
685 +# answer from 127.0.0.1:1234
686 +<?xml version="1.0" encoding="utf-8"?>
687 +<pnml>
688 + <answer status="ok">
689 + <marking>
690 + <place id="p2">
691 + <tokens>
692 + <multiset>
693 + <item>
694 + <value>
695 + <object type="int">2</object>
696 + </value>
697 + <multiplicity>1</multiplicity>
698 + </item>
699 + <item>
700 + <value>
701 + <object type="int">3</object>
702 + </value>
703 + <multiplicity>1</multiplicity>
704 + </item>
705 + </multiset>
706 + </tokens>
707 + </place>
708 + </marking>
709 + </answer>
710 +</pnml>
711 +
712 +Querying the modes of 't' now results in an empty list because there
713 +is no more tokens in the input place of 't'.
714 +
715 +? call(call('getattr', call('n.transition', 't'), 'modes'))
716 +# query to localhost:1234
717 +<?xml version="1.0" encoding="utf-8"?>
718 +<pnml>
719 + <query name="call">
720 + <argument>
721 + <query name="call">
722 + <argument>
723 + <object type="str">getattr</object>
724 + </argument>
725 + <argument>
726 + <query name="call">
727 + <argument>
728 + <object type="str">n.transition</object>
729 + </argument>
730 + <argument>
731 + <object type="str">t</object>
732 + </argument>
733 + </query>
734 + </argument>
735 + <argument>
736 + <object type="str">modes</object>
737 + </argument>
738 + </query>
739 + </argument>
740 + </query>
741 +</pnml>
742 +# answer from 127.0.0.1:1234
743 +<?xml version="1.0" encoding="utf-8"?>
744 +<pnml>
745 + <answer status="ok">
746 + <object type="list"/>
747 + </answer>
748 +</pnml>
749 +
750 +These examples are intended to illustrate what _can_ be made, but not
751 +necessarily what _should_ be made. Here, we use snkc at the client
752 +side, so we have no way to store locally information, nor to parse the
753 +PNML we get from the server. So, when we need to record some data, we
754 +put it at the server side using a 'set' query. Doing so, we will need
755 +to use quite complicated queries in order to extract the bits of data
756 +that we will want to use. See for instance how complicated was the
757 +firing of a transition.
758 +
759 +In a realistic client, it is possible to store an manage locally some
760 +information and so avoid complex queries. It is even possible to
761 +completely parse and interpret PNML data received from the server.
762 +Both extremities have pros and cons:
763 +
764 + - Storing everything on the server simplifies processing for the
765 + client; but it increases (a lot) the complexity of queries.
766 +
767 + - Storing everything on the client requires to parse and interpret
768 + PNML data, and to manage stored information; but it simplifies
769 + queries and may give more control on the amount of exchanged.
770 +
771 +Any intermediary position may be adopted: it is possible for a client
772 +to partially interpret PNML and to store fragments of uninterpreted
773 +PNML text as symbolic values. These fragments can then be inserted
774 +into queries where they are required.
775 +
776 +For instance, a client could parse the list of modes of a transition
777 +up to the tag <substitution>, which is quite a simple task. Each mode
778 +could then be saved locally as a fragment of PNML text
779 +"<substitution>...</substitution>". Then, firing a transition would
780 +simply require to insert such a fragment at the right position in a
781 +template query. For instance, let's run:
782 +
783 +? show(call(call('getattr', call('n.transition', 't'), 'fire'), 'SUBST'))
784 +<?xml version="1.0" encoding="utf-8"?>
785 +<pnml>
786 + <query name="call">
787 + <argument>
788 + <query name="call">
789 + <argument>
790 + <object type="str">getattr</object>
791 + </argument>
792 + <argument>
793 + <query name="call">
794 + <argument>
795 + <object type="str">n.transition</object>
796 + </argument>
797 + <argument>
798 + <object type="str">t</object>
799 + </argument>
800 + </query>
801 + </argument>
802 + <argument>
803 + <object type="str">fire</object>
804 + </argument>
805 + </query>
806 + </argument>
807 + <argument>
808 + <object type="str">SUBST</object>
809 + </argument>
810 + </query>
811 +</pnml>
812 +
813 +Then, we just need to replace '<object type="str">SUBST</object>' with
814 +the saved fragment '<substitution>...</substitution>' in order to fire
815 +the transition with the chosen mode. A similar techniques can be
816 +applied to many situations. A lazy (but clever) approach would be to
817 +prepare a series of template queries where placeholders could be
818 +substituted with fragments of PNML text retrieved from the server.
819 +
820 +##
821 +## Keyword arguments
822 +##
823 +
824 +A <query> may also accepts keyword arguments like functions in Python.
825 +But currently no query expects that. The syntax to add keyword
826 +arguments is to use a tag <keyword> for each such argument, with an
827 +attribute 'name' that store the keyword name and with a child tag that
828 +stores the keyword value. For instance, a Python call "example('x', 1,
829 +foo=5, bar='hello')" would translate to a query:
830 +
831 +<?xml version="1.0" encoding="utf-8"?>
832 +<pnml>
833 + <query name="example">
834 + <argument>
835 + <object type="str">
836 + x
837 + </object>
838 + </argument>
839 + <argument>
840 + <object type="int">
841 + 1
842 + </object>
843 + </argument>
844 + <keyword name="foo">
845 + <object type="int">
846 + 5
847 + </object>
848 + </keyword>
849 + <keyword name="bar">
850 + <object type="str">
851 + hello
852 + </object>
853 + </keyword>
854 + </query>
855 +</pnml>
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<pnml>
3 + <net id="mynet">
4 + <place id="p2">
5 + <type domain="universal"/>
6 + <initialMarking>
7 + <multiset/>
8 + </initialMarking>
9 + </place>
10 + <place id="p1">
11 + <type domain="universal"/>
12 + <initialMarking>
13 + <multiset>
14 + <item>
15 + <value>
16 + <object type="int">
17 + 1
18 + </object>
19 + </value>
20 + <multiplicity>
21 + 1
22 + </multiplicity>
23 + </item>
24 + <item>
25 + <value>
26 + <object type="int">
27 + 2
28 + </object>
29 + </value>
30 + <multiplicity>
31 + 1
32 + </multiplicity>
33 + </item>
34 + </multiset>
35 + </initialMarking>
36 + </place>
37 + <transition id="t"/>
38 + <arc id="p1:t" source="p1" target="t">
39 + <inscription>
40 + <variable>
41 + x
42 + </variable>
43 + </inscription>
44 + </arc>
45 + <arc id="t:p2" source="t" target="p2">
46 + <inscription>
47 + <expression>
48 + x+1
49 + </expression>
50 + </inscription>
51 + </arc>
52 + </net>
53 +</pnml>
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<pnml>
3 + <net id="Simple P/T net">
4 + <place id="p2">
5 + <initialMarking>
6 + <text>
7 + 0
8 + </text>
9 + </initialMarking>
10 + </place>
11 + <place id="p1">
12 + <initialMarking>
13 + <text>
14 + 1
15 + </text>
16 + </initialMarking>
17 + </place>
18 + <transition id="t2"/>
19 + <transition id="t1"/>
20 + <arc id="p2:t2" source="p2" target="t2">
21 + <inscription>
22 + <text>
23 + 1
24 + </text>
25 + </inscription>
26 + </arc>
27 + <arc id="t2:p1" source="t2" target="p1">
28 + <inscription>
29 + <text>
30 + 1
31 + </text>
32 + </inscription>
33 + </arc>
34 + <arc id="p1:t1" source="p1" target="t1">
35 + <inscription>
36 + <text>
37 + 1
38 + </text>
39 + </inscription>
40 + </arc>
41 + <arc id="t1:p2" source="t1" target="p2">
42 + <inscription>
43 + <text>
44 + 1
45 + </text>
46 + </inscription>
47 + </arc>
48 + </net>
49 +</pnml>
1 +This document describes the PNML extensions used bu SNAKES. See
2 +http://www.pnml.org for the definition of the standard PNML. In the
3 +following, we use a simplified RELAX NG Compact Syntax (see
4 +http://relaxng.org). For each element, we provide the Python class
5 +that implements it (see API reference for details), a list of its
6 +attributes and children elements.
7 +
8 +##
9 +## Petri net elements
10 +##
11 +
12 +element pnml {
13 + element net { ... }*
14 +}
15 +# Several nets may be provided in one PNML file.
16 +
17 +element net { # class snakes.nets.PetriNet
18 + attribute id { text } # identity of the net
19 + element place { ... }* # places in the net
20 + element transition { ... }* # transitions in the net
21 + element arc { ... }* # arcs in the net
22 +}
23 +
24 +element place { # class snakes.nets.Place
25 + attribute id { text } # identity of the place
26 + element type { ... }? # for a coloured place
27 + element initialMarking { # marking
28 + ( element text { num:integer } # for a P/T place
29 + | element multiset { ... } ) # for a coloured place
30 + }?
31 +}
32 +# A P/T place is identified in SNAKES by the fact it has a typing
33 +# constraint "tBlackToken". In such a case, its initial marking is
34 +# given as the number of black tokens held by the place, which
35 +# respects the PNML standard. In the other cases, the place is
36 +# considered to be coloured and its type constraint is given by an
37 +# element <type> and its marking is given by an element <multiset>.
38 +
39 +element transition { # class snakes.nets.Transition
40 + attribute id { text } # identity of the transition
41 + element guard {
42 + element expression { ... } # guard if not True
43 + }?
44 +}
45 +# When the guard is True, like in P/T nets, it is not saved in the
46 +# PNML so that the result respects PNML standard. Otherwise, the guard
47 +# is saved inside a tag <guard>.
48 +
49 +element declare {
50 + statement
51 +}
52 +# One <declare> tag is added to <net> for each Python statement run
53 +# using PetriNet.declare() for this net.
54 +
55 +element global {
56 + attribute name { name:string } # object's name
57 + element * { ... } # value
58 +}
59 +# One <global> is added to <net> for each entry in net.globals that
60 +# is not obtained through the use of PetriNet.declare().
61 +
62 +##
63 +## Arcs
64 +##
65 +
66 +element arc {
67 + attribute id { text } # identity of the arc
68 + attribute source { text } # identity of source node
69 + attribute target { text } # identity of target node
70 + element inscription {
71 + element text { # for a P/T net
72 + weight:int
73 + }?
74 + element * { ... }? # for a coloured net
75 + }
76 +}
77 +# When the net is a P/T net, a weight is given for the arcs, which
78 +# respects the PNML standard. Otherwise, the inscription is one of the
79 +# possible inscriptions given below.
80 +
81 +element value { # class snakes.nets.Value
82 + element object { ... } # value transported on an arc
83 +}
84 +
85 +element variable { # class snakes.nets.Variable
86 + name:string # name of the variable
87 +}
88 +
89 +element expression { # class snakes.nets.Expression
90 + expr:string # text of the expression (Python code)
91 +}
92 +
93 +element test { # class snakes.nets.Test
94 + ( element value { ... }
95 + | element variable { ... }
96 + | element expression { ... }
97 + | element multiarc { ... }
98 + | element tuple { ... } ) # tested annotation
99 +}
100 +
101 +element multiarc { # class snakes.nets.MultiArc
102 + ( element value { ... }
103 + | element variable { ... }
104 + | element expression { ... }
105 + | element multiarc { ... }
106 + | element tuple { ... } )* # list of encapsulated annotations
107 +}
108 +
109 +element tuple { # class snakes.nets.Tuple
110 + ( element value { ... }
111 + | element variable { ... }
112 + | element expression { ... }
113 + | element multiarc { ... }
114 + | element tuple { ... } )* # list of encapsulated annotations
115 +}
116 +
117 +
118 +##
119 +## Auxiliary tags
120 +##
121 +
122 +element token { # class snakes.nets.BlackToken
123 +} # a standard black token
124 +
125 +element marking { # class snakes.nets.Marking
126 + element place { # one for each marked place
127 + attribute id { text } # identity of the place
128 + element tokens {
129 + element multiset { ... } # marking of the place
130 + }
131 + }*
132 +}
133 +
134 +element multiset { # class snakes.data.MultiSet
135 + element item { # items in the multiset
136 + element value {
137 + element object { ... } # value of one item
138 + }
139 + element multiplicity { # number of time it is repeated
140 + num:integer
141 + }
142 + }*
143 +}
144 +
145 +element substitution { # class snakes.data.Substitution
146 + element item { # mapped variables
147 + element name {
148 + name:string # variable name
149 + }
150 + element value {
151 + element object { ... } # associated value
152 + }
153 + }*
154 +}
155 +
156 +##
157 +## Python objects
158 +##
159 +
160 +element object {
161 + attribute type { ... } # type of the object
162 + ... # depends of attribute type
163 +}
164 +
165 +element object {
166 + attribute type { "int" } # object is an integer
167 + value:integer
168 +}
169 +
170 +element object {
171 + attribute type { "float" } # object is a float
172 + value:float
173 +}
174 +
175 +element object {
176 + attribute type { "str" } # object is a string
177 + value:string
178 +}
179 +
180 +element object {
181 + attribute type { "bool" } # object is a Boolean
182 + ( "True" | "False" )
183 +}
184 +
185 +element object {
186 + attribute type { "list" } # object is a list
187 + element object { ... }* # list items
188 +}
189 +
190 +element object {
191 + attribute type { "tuple" } # object is a tuple
192 + element object { ... }* # tuple items
193 +}
194 +
195 +element object {
196 + attribute type { "set" } # object is a set
197 + element object { ... }* # set items
198 +}
199 +
200 +element object {
201 + attribute type { "method" } # object is a class method
202 + attribute name { path:string} # path to method, including module name
203 +}
204 +
205 +element object {
206 + attribute type { "function" } # object is a function
207 + attribute name { path:string} # path to function, including module name
208 +}
209 +
210 +element object {
211 + attribute type { "class" } # object is a class
212 + attribute name { path:string} # path to class, including module name
213 +}
214 +
215 +element object {
216 + attribute type { "module" } # object is a module
217 + attribute name { path:string} # path to module
218 +}
219 +
220 +element object {
221 + attribute type { "pickle" } # object that cannot be handled symbolically
222 + data:string # pickled object
223 +}
224 +
225 +element object {
226 + attribute type { "NoneType" } # object is the value None
227 +}
228 +
229 +##
230 +## Typing constraints
231 +##
232 +
233 +element type {
234 + attribute domain { text } # kind of typing constraint
235 + ... # depending on domain
236 +}
237 +# Module snakes.typing defines a full algebra of types, all are saved
238 +# to an element <type>. The attribute "domain" is then the key to
239 +# decompose correctly a type. Below is a list of the different
240 +# domains, with the structure of the corresponding type
241 +
242 +element type { # class snakes.typing._And
243 + attribute domain { "intersection" } # intersection of two types
244 + element left { # left operand type
245 + element type { ... }
246 + }
247 + element right { # right operand type
248 + element type { ... }
249 + }
250 +}
251 +
252 +element type { # class snakes.typing._Or
253 + attribute domain { "union" } # union of two types
254 + element left { # left operand type
255 + element type { ... }
256 + }
257 + element right { # right operand type
258 + element type { ... }
259 + }
260 +}
261 +
262 +element type { # class snakes.typing._Sub
263 + attribute domain { "difference" } # difference of two types
264 + element left { # left operand type
265 + element type { ... }
266 + }
267 + element right { # right operand type
268 + element type { ... }
269 + }
270 +}
271 +
272 +element type { # class snakes.typing._Xor
273 + attribute domain { "xor" } # exclusive union of two types
274 + element left { # left operand type
275 + element type { ... }
276 + }
277 + element right { # right operand type
278 + element type { ... }
279 + }
280 +}
281 +
282 +element type { # class snakes.typing._Invert
283 + attribute domain { "complement" } # complement of a type
284 + element type { ... } # complemented type
285 +}
286 +
287 +element type { # class snakes.typing._All
288 + attribute domain { "universal" } # type with all possible values
289 +}
290 +
291 +element type { # class snakes.typing._Nothing
292 + attribute domain { "empty" } # type with no value
293 +}
294 +
295 +element type { # class snakes.typing.Instance
296 + attribute domain { "instance" } # type whose values are instances
297 + element object { ... } # class of the instances
298 +}
299 +
300 +element type { # class snakes.typing.TypeCheck
301 + attribute domain { "checker" } # type defined by a Boolean function
302 + element checker {
303 + element object { ... } # the Boolean function
304 + }
305 + element iterator {
306 + element object { ... } # a function to enumerate the values
307 + }
308 +}
309 +
310 +element type { # class snakes.typing.OneOf
311 + attribute domain { "enum" } # enumerated type
312 + element object { ... }* # values in the type
313 +}
314 +
315 +element type { # class snakes.typing.Collection
316 + attribute domain { "collection" } # flat container type (list, set, ...)
317 + element container {
318 + element type { ... } # type of the container
319 + }
320 + element items {
321 + element type { ... } # type of the contained items
322 + }
323 + element min {
324 + element object { ... } # smallest allowed number of elements
325 + }
326 + element max {
327 + element object { ... } # biggest allowed number of elements
328 + }
329 +}
330 +
331 +element type { # class snakes.typing.Mapping
332 + attribute domain { "mapping" } # dictionary-like container type
333 + element container {
334 + element type { ... } # type of the container
335 + }
336 + element keys {
337 + element type { ... } # type of the contained keys
338 + }
339 + element items {
340 + element type { ... } # type of the contained items
341 + }
342 +}
343 +
344 +element type { # class snakes.typing.Range
345 + attribute domain { "range" } # values in a range
346 + element min {
347 + element object { ... } # smallest allowed value
348 + }
349 + element min {
350 + element object { ... } # smallest excluded value
351 + }
352 + element min {
353 + element object { ... } # step between consecutive values
354 + }
355 +}
356 +
357 +element type { # class snakes.typing.Greater
358 + attribute domain { "greater" } # values bigger than a given one
359 + element object { ... } # biggest excluded value
360 +}
361 +
362 +element type { # class snakes.typing.GreaterOrEqual
363 + attribute domain { "greatereq" } # values not smaller than a given one
364 + element object { ... } # smallest allowed value
365 +}
366 +
367 +element type { # class snakes.typing.Less
368 + attribute domain { "less" } # values smaller than a given one
369 + element object { ... } # smallest excluded value
370 +}
371 +
372 +element type { # class snakes.typing.LessOrEqual
373 + attribute domain { "lesseq" } # values not bigger than a given one
374 + element object { ... } # biggest allowed value
375 +}
376 +
377 +element type { # class snakes.typing.CrossProduct
378 + attribute domain { "crossproduct" } # cross product of types
379 + element type { ... }* # crossed types
380 +}
381 +
382 +##
383 +## Additional elements from plugins
384 +##
385 +
386 +element snakes {
387 + attribute version { ... } # SNAKES' version that produced this PNML
388 + element plugins {
389 + element object {
390 + attribute type { "tuple" }
391 + element object { # the base module 'snakes.nets' is listed also
392 + attribute type { "str" }
393 + "snakes.nets"
394 + }
395 + element object { # list of plugins
396 + attribute type { "str" }
397 + plugin:string
398 + }*
399 + }
400 + }
401 +}
402 +# Added to another element in order to specify the plugins necessary
403 +# to load properly the element. <snakes><plugins> tags can be added at
404 +# any position in the PNML, but they are then used globally for a
405 +# whole <pnml> tree.
406 +
407 +element status { # class snakes.plugins.status.Status
408 + element name {
409 + name:string # status name
410 + }
411 + element value {
412 + element object { ... } # attached value
413 + }
414 +}
415 +# Added to <place> and <transition> when plugin 'status' is loaded.
416 +
417 +element multiaction { # class snakes.plugins.synchro.MultiAction
418 + element action { ... }* # actions in the multi-action
419 +}
420 +# Added to <transition> when plugin 'synchro' is loaded.
421 +
422 +element action { # class snakes.plugins.synchro.Action
423 + attribute name { name:string } # action name
424 + attribute send { send:boolean } # send/receive action
425 + element * { ... }* # action parameters
426 +}
427 +
428 +element clusters { # class snakes.plugins.clusters.Cluster
429 + element node { id:string }* # nodes at this level
430 + element clusters { ... }* # children clusters
431 +}
432 +# Added to <net> when plugin 'clusters' is loaded.
433 +
434 +element label { # class snakes.plugins.labels
435 + attribute name { name:string} # label name
436 + element object { ... } # label content
437 +}
438 +# Added to <net>, <place> and <transition> when plugin 'labels' is
439 +# loaded.
440 +
441 +element graphics {
442 + element position { # node position
443 + attribute x { xpos:(integer|float) } # x coordinate
444 + attribute y { ypos:(integer|float) } # y coordinate
445 + }
446 +}
447 +# Added to <place> and <transition> when plugin 'pos' is loaded.
448 +
449 +element query { # snakes.plugins.query.Query
450 + attribute name { name:text} # name of the query
451 + element argument { ... }* # arguments of the query
452 + element keyword { # keyword arguments
453 + attribute name { text } # keyword name
454 + element * { ... } # associated value
455 + }*
456 +}
457 +# See file 'queries.txt'
458 +
459 +element answer {
460 + attribute status { "ok" | "error" } # status of the answer
461 + message:string? # error message if status=error
462 + element * { ... }? # returned value if status=ok
463 +}
464 +# See file 'queries.txt'
465 +
466 +##
467 +## Abstract syntax tree
468 +##
469 +
470 +# The following specifies the PNML translation of abstract syntax
471 +# trees for ABCD programs, as inserted into <net> but the ABCD
472 +# compiler.
473 +
474 +element ast { # class snakes.compyler.Tree
475 + attribute name { text } # node nature
476 + attribute lineno { num:integer }? # line number in source code
477 + attribute * { ... }* # depending on name
478 + element * { ... }* # depending on name
479 +}
480 +# <ast> trees are obtained by direct translation of
481 +# snakes.compyler.Tree instances: tree name is mapped to an attribute
482 +# 'name', sub-trees are mapped to child tags, and attributes are
483 +# mapped either to attributes or to child tags <attribute> when they
484 +# cannot be represented as a simple string. The following lists the
485 +# more current patterns. The element 'lineno' will be omitted in the
486 +# following.
487 +
488 +element ast {
489 + attribute name { "abcd" } # start symbol
490 + element ast { # buffer declarations
491 + attribute name { "buffer" }
492 + ...
493 + }*
494 + element ast { # net declarations
495 + attribute name { "net" }
496 + ...
497 + }*
498 + element attribute { # ABCD expression
499 + attribute name { "expr" }
500 + ...
501 + }
502 +}
503 +# An ABCD program is composed of optional buffer and net declarations,
504 +# followed by an ABCD expression.
505 +
506 +element ast {
507 + attribute name { "buffer" } # buffer declaration
508 + attribute ident { text:string } # buffer's name
509 + element attribute {
510 + attribute name { "type" } # buffer's type
511 + ...
512 + }
513 + element attribute { # buffer's initial value
514 + attribute name { "init" }
515 + ...
516 + }
517 +}
518 +
519 +element net {
520 + attribute name { "net" } # net declaration
521 + attribute ident { text:string } # net's name
522 + element ast { # net's body
523 + attribute name { "abcd" }
524 + ...
525 + }
526 +}
527 +
528 +element ast {
529 + attribute name { "expr" }
530 + element ast {
531 + attribute name { ( "parallel" # binary composition
532 + | "loop"
533 + | "sequence"
534 + | "choice" ) }
535 + element ast { ... } # first operand
536 + element ast { ... } # second operand
537 + }
538 +}
539 +
540 +element ast {
541 + attribute name { "expr" }
542 + element ast {
543 + attribute name { "scope" } # name hiding
544 + element ast { ... } # first operand
545 + element object {
546 + attribute type { "str" } # hidden name
547 + name:string
548 + }
549 + }
550 +}
551 +
552 +element ast {
553 + attribute name { "action" } # basic action
554 + attribute net { ( name:string | "None" ) } # reference to a net
555 + attribute test { ( "False" | "True" ) }? # trivial condition
556 + element ast {
557 + attribute name { "access" } # buffer access
558 + attribute buffer { name:string } # buffer's name
559 + attribute mode { ("?" | "+" | "-") } # test, put or get
560 + element attribute {
561 + attribute name { "param" } # access' parameter
562 + element python { ... } # Python's AST of the parameter
563 + }
564 + }*
565 + element attribute { # non-trivial condition
566 + attribute name { "test" }
567 + element python { ... } # Python's AST of the condition
568 + }?
569 +}
570 +# A basic action can be the name of a net or a trivial action
571 +# '[False]'. Otherwise, it is composed of a possibly empty list of
572 +# buffer accesses and a condition. If the condition is 'True', it is
573 +# stored as attribute 'test=True", but more complex conditions are
574 +# stored as a Python AST in a child tag <attribute name="test">. Each
575 +# access is composed of a buffer name, an access mode and a parameter.
576 +
577 +# Next elements are children of a tag <attribute name="type"> and
578 +# defined the type of a buffer.
579 +
580 +element ast {
581 + attribute name { "name } # Python built-in type
582 + attribute value { type:string } # name of the type
583 +}
584 +
585 +element ast {
586 + attribute name { "enum" } # enumerated type
587 + element ast {
588 + attribute name { "values" }
589 + element python { # Python's AST node in this case
590 + attribute class { "Tuple" } # is a Tuple of Const
591 + element attribute {
592 + attribute name { "nodes" }
593 + values # eg, "[Const('a'), Const('b')]"
594 + }
595 + }
596 + }
597 +}
598 +
599 +element ast {
600 + attribute name { "union" } # union of two types
601 + element ast { ... } # first type
602 + element ast { ... } # second type
603 +}
604 +
605 +element ast {
606 + attribute name { "intersection" } # intersection of two types
607 + element ast { ... } # first type
608 + element ast { ... } # second type
609 +}
610 +
611 +element ast {
612 + attribute name { "list" } # list type
613 + element ast { ... } # items' type
614 +}
615 +
616 +element ast {
617 + attribute name { "dict" } # dict type
618 + element ast { ... } # keys' type
619 + element ast { ... } # values' type
620 +}
621 +
622 +element ast {
623 + attribute name { "set" } # set type
624 + element ast { ... } # items' type
625 +}
626 +
627 +# A Python AST is serialized as a <python> tag, see the section 31.3.1
628 +# (AST Nodes) of the Python Library Reference for a list of AST nodes.
629 +
630 +element python {
631 + attribute * { text }* # direct mapping of simple attributes
632 + element attribute { # complex attributes are serialized
633 + attribute name { text } # in a tag <object>
634 + element object { ... }
635 + }*
636 + data? # when none of the above method works,
637 + # 'repr' is used to convert the AST to text
638 +}
1 +SNAKES: a tutorial
2 +==================
3 +
4 +////////////////////////////////////////////////////////////////
5 +This file is formatted in order to be processed by AsciiDoc
6 +(http://www.methods.co.nz/asciidoc). It will be more comfortable
7 +to render it or to read the HTML version available at:
8 +http://www.univ-paris12.fr/pommereau/soft/snakes/tutorial.html
9 +////////////////////////////////////////////////////////////////
10 +
11 +The first example is a simple coloured Petri net with a single
12 +transition that increments an integer valued (so 0 is the _value_ of
13 +the token, not a number of tokens) token held by a single place, the
14 +incrementation stops when the value is $5$ thanks to a guard on the
15 +transition.
16 +
17 +image:tutorial.png[]
18 +
19 +To define this net, we must load SNAKES, define a Petri net (lets call
20 +it 'First net'), add the place (called 'p'), add the transition
21 +(called 't') and then connect them with arcs.
22 +
23 +[python]
24 +^^^^^^^^^^^^^^^^^^^^^^
25 +>>> from snakes.nets import *
26 +>>> n1 = PetriNet('First net')
27 +>>> n1.add_place(Place('p', [0]))
28 +>>> n1.add_transition(Transition('t', Expression('x<5')))
29 +>>> n1.add_input('p', 't', Variable('x'))
30 +>>> n1.add_output('p', 't', Expression('x+1'))
31 +^^^^^^^^^^^^^^^^^^^^^^
32 +
33 +On the third line, the net is added a place, which could equivalently
34 +be written as:
35 +
36 +//skip
37 +[python]
38 +^^^^^^^^^^^^^^^^^^^^^^
39 +>>> p = Place('p', [0])
40 +>>> n1.add_place(p)
41 +^^^^^^^^^^^^^^^^^^^^^^
42 +
43 +However, having a variable for the place is not necessary as it can be
44 +retrieved from $n1$ using its name with $n1.place('p')$.
45 +
46 +The instruction $Place('p', [0])$ is the construction of a new
47 +instance of the class $Place$ that expects the name of the place as
48 +its first argument. The second argument is optional and is the initial
49 +marking of the place, that can be given as a list, set, tuple or
50 +multiset of tokens (the class for multisets is defined in the module
51 +$snakes.data$). A third optional argument is a constraint for the
52 +tokens that the place can hold (also known as its type); the default
53 +value allows any token to mark the place. The typing of the places
54 +will be detailed later on.
55 +
56 +In order to build the transition, we create an instance of the class
57 +$Transition$ whose constructor expects first the name of the
58 +transition and the, optionally, the guard of it that is true by
59 +default. A guard is otherwise specified as $Expression('...')$ where
60 +$...$ is an arbitrary Python expression, like $Expression('x<5')$ in
61 +our example. We will details latter on how this expression is
62 +evaluated.
63 +
64 +Arcs are added using one of the methods $add_input$ or $add_output$ of
65 +$PetriNet$; both expect a place name, a transition name and the arc
66 +annotation as arguments (always in this order). An input arc is
67 +directed from a place toward a transition, an output arc is outgoing a
68 +transition; so arcs are considered from the point of view of the
69 +transition to which they are connected. Valid arc annotations are:
70 +
71 +values:: They are instances of the class $Value$ whose constructor
72 +simply expects the value that can be any Python object. For instances,
73 +$Value(1)$ is the integer $1$.
74 +
75 +variables:: These are names that are bound to token values when a
76 +transition if executed. A variable is created by instantiating the
77 +class $Variable$ whose constructor expects the name of the variable as
78 +a Python string (valid names are those matching the Python regexp:
79 +$'[a-zA-Z]\w*'$). For instance, $Variable('x')$, $Variable('count')
80 +and $Variable('x_1')$ are valid but $Variable('x-1')$ and
81 +$Variable('1x') are not.
82 +
83 +expressions:: They are used to compute new values. An expression is an
84 +instance of the class $Expression$ whose constructor expects any
85 +Python expression as a string. How this expression is evaluated is
86 +explained in the next section. In our example, $Expression('x+1')$ as
87 +been used on the output arc.
88 +
89 +tests:: The class $Test$ is used to implement a test arc: it
90 +encapsulates another arc annotation and behaves exactly like it,
91 +except that no token is transported by the arc when the attached
92 +transition fires. The constructor expects another arc annotation as
93 +its sole argument, for instance: $Test(Variable('x'))$ on an input arc
94 +allows to test for a token in a place, its value being usable as $x$.
95 +On a output arc, $Test(Expression('x+1'))$ may be used to test that
96 +the value of $x+1$ is accepted by the connected place, without
97 +actually producing it.
98 +
99 +multi-arcs:: When an arc needs to transport several values, the class
100 +$MultiArc$ may be used. Its constructor expects a list (or tuple) of
101 +other annotations that are simultaneously transported on the arc. For
102 +instance, $MultiArc([Variable('x'), Variable('y')])$ on an input arcs
103 +allows to consume two tokens, binding them to the variables $x$ and
104 +$y$.
105 +
106 +
107 +Executing transitions
108 +---------------------
109 +
110 +The first step to execute a transition is to bind the variables
111 +labelling the arcs to actual token values. This is possible by calling
112 +the method $modes()$ of a transition. It returns a list of
113 +$Substitution$ instances (this class is defined in $snakes.data$). A
114 +$Substitution$ is a $dict$-like object that maps variable names to
115 +other variables names or to values. The method $modes$ returns the
116 +list of substitutions that are acceptable in order to fire the
117 +transition, _i.e._, those that respect the usual following conditions:
118 +
119 + * each input arc, evaluated through the substitution, corresponds to
120 + a multiset of tokens that is less or equal to the current marking
121 + of the connected place;
122 +
123 + * each output arc, evaluated through the substitution, results in a
124 + multiset of tokens that respects the type constraint of the
125 + connected place;
126 +
127 + * the guard of the transition, evaluates to $True$ through the
128 + substitution.
129 +
130 +For instance, with our net:
131 +
132 +[python]
133 +^^^^^^^^^^^^^^^^^^
134 +>>> n1.transition('t').modes()
135 +[Substitution(x=0)]
136 +^^^^^^^^^^^^^^^^^^
137 +
138 +The only way to fire the transition is to bind $x$ to $0$. Other may
139 +have been tried, but do not respect at least one of the above
140 +conditions. For instance, choosing $x=1$ respects the guard and place
141 +types but not the marking (the token $1$ is missing):
142 +
143 +[python]
144 +^^^^^^^^^^^^^^^^^^
145 +>>> from snakes.data import Substitution
146 +>>> s = Substitution(x=1)
147 +>>> n1.transition('t').enabled(s)
148 +False
149 +^^^^^^^^^^^^^^^^^^
150 +
151 +In order to fire a transition, we have to call its method $fire$ with
152 +an enabling substitution as argument (_i.e._, one of those returned by
153 +$modes()$). In our example, we could run:
154 +
155 +[python]
156 +^^^^^^^^^^^^^^^^^^
157 +>>> n1.transition('t').fire(Substitution(x=0))
158 +>>> n1.place('p').tokens
159 +MultiSet([1])
160 +^^^^^^^^^^^^^^^^^^
161 +
162 +It is important to understand how the firing is performed: the
163 +substitution is used as a Python environment to evaluate the
164 +annotations on the arcs and the guard of the transition in order to
165 +check that the conditions above are respected. For instance, the guard
166 +$Expression('x<5')$ can be evaluated to $true$ because $x$ is bound to
167 +$0$ through the substitution. Similarly, the output arc
168 +$Expression('x+1')$ is evaluated to $1$. The environment used to
169 +evaluate the guard and output arcs is built using the input arcs, this
170 +means that all the variables used during the firing must have been
171 +bound through one of these input arcs. For instance, we could use
172 +$Expression('x<5 and y==x+1')$ for the guard and $Variable('y')$ for the
173 +output arc, and $Substitution(x=0, y=1)$ for the firing:
174 +
175 +[python]
176 +^^^^^^^^^^^^^^^^^^
177 +>>> n2 = PetriNet('Second net')
178 +>>> n2.add_place(Place('p', [0]))
179 +>>> n2.add_transition(Transition('t', Expression('x<5 and y==x+1')))
180 +>>> n2.add_input('p', 't', Variable('x'))
181 +>>> n2.add_output('p', 't', Variable('y'))
182 +>>> n2.transition('t').fire(Substitution(x=0, y=1))
183 +>>> n2.place('p').tokens
184 +MultiSet([1])
185 +^^^^^^^^^^^^^^^^^^
186 +
187 +This example is correct as long as the substitution is provided by the
188 +user. But the method $modes$ is unable to deduce the value for $y$, it
189 +would require to solve the equation $x<5 and y==x+1$ in the guard.
190 +This is easy here but impossible in general, so, the modes are
191 +computed only with respect to the input arcs. This is why $Expression$
192 +instances are not allowed on input arcs, neither directly nor when
193 +encapsulated in $Test$ or $MultiArc$ instances. So, in this second
194 +example, if we call $modes()$, we get an error since $y$ in cannot be
195 +evaluated:
196 +
197 +[python]
198 +^^^^^^^^^^^^^^^^^^
199 +>>> n2.transition('t').modes()
200 +Traceback (most recent call last):
201 + ...
202 +NameError: name 'y' is not defined
203 +^^^^^^^^^^^^^^^^^^
204 +
205 +
206 +Declarations
207 +------------
208 +
209 +There is one more aspect about the evaluation of the expression that
210 +should be known: it is possible to declare names that are global to a
211 +Petri net, for instance constants or functions. To do so, we shall use
212 +the method $declare$ of a $PetriNet$ instance, that expects as its
213 +argument a Python statement in a string. This statement is executed
214 +and its effect is remembered in order to be used as a global execution
215 +environment when expressions are evaluated. For instance, lets
216 +construct a Petri net that generates random tokens using the standard
217 +Python function $random.randint$:
218 +
219 +[python]
220 +^^^^^^^^^^^^^^^^^^
221 +>>> n3 = PetriNet('Thirs net')
222 +>>> n3.add_place(Place('p', [0]))
223 +>>> n3.add_transition(Transition('t'))
224 +>>> n3.add_input('p', 't', Variable('x'))
225 +>>> n3.add_output('p', 't', Expression('random.randint(0, 100)'))
226 +>>> n3.transition('t').fire(Substitution(x=0))
227 +Traceback (most recent call last):
228 + ...
229 +NameError: name 'random' is not defined
230 +^^^^^^^^^^^^^^^^^^
231 +
232 +This result is not surprising as the module as not been imported. This
233 +must be made with the statements (the second line is to initialise the
234 +random generator):
235 +
236 +[python]
237 +^^^^^^^^^^^^^^^^^^
238 +>>> n3.declare('import random')
239 +>>> n3.declare('random.seed()')
240 +^^^^^^^^^^^^^^^^^^
241 +
242 +Then, the transition can fire; of course, the resulting marking will
243 +be different from one execution to another since it is random:
244 +
245 +//hide 93
246 +[python]
247 +^^^^^^^^^^^^^^^^^^
248 +>>> n3.transition('t').fire(Substitution(x=0))
249 +>>> n3.place('p').tokens
250 +MultiSet([93])
251 +^^^^^^^^^^^^^^^^^^
252 +
253 +The effect of the $n3.declare(statement)$ method is to call $exec
254 +statement in n3.globals$, where $n3.globals$ is a dictionary shared by
255 +all the expression embedded in the net (in guards or arcs). So,
256 +another way to influence the evaluation of these expressions is to
257 +directly assign values to the dictionary $n3.globals$, for instance:
258 +
259 +//doc >>> n3.place('p').reset([93])
260 +//hide 18
261 +[python]
262 +^^^^^^^^^^^^^^^^^^
263 +>>> n3.add_place(Place('x'))
264 +>>> n3.add_output('x', 't', Expression('y+1'))
265 +>>> n3.transition('t').fire(Substitution(x=93))
266 +Traceback (most recent call last):
267 + ...
268 +NameError: name 'y' is not defined
269 +>>> n3.globals['y'] = 42
270 +>>> n3.transition('t').fire(Substitution(x=93))
271 +>>> n3.get_marking()
272 +Marking({'p': MultiSet([18]), 'x': MultiSet([43])})
273 +^^^^^^^^^^^^^^^^^^
274 +
275 +The first error is expected as $y$ as not been declared and is not
276 +bound to any token value through an input arc. After assigning it to
277 +$n3.globals$, it becomes defined so $Expression('y+1')$ can be
278 +evaluated. The same effect could have been achieved using
279 +$n3.declare('y=42')$.
280 +
281 +The last instruction gets the marking of the net, lets detail now what
282 +we can do with marking objects.
283 +
284 +
285 +Markings, marking graph
286 +-----------------------
287 +
288 +An instance of $PetriNet$ has methods to get and set its marking,
289 +which are respectively $get_marking$ and $set_marking$. There are also
290 +the methods $add_marking$ and $remove_marking$ to increase or decrease
291 +the current marking.
292 +
293 +[NOTE]
294 +========================
295 +The marking of an individual place can also be directly manipulated,
296 +either though its attribute $tokens$ (we used it above) that is a
297 +multiset, or through the following methods:
298 +
299 +$add(toks)$:: adds the tokens in $toks$ to the place ($toks$ can be
300 +any collection of values: $set$, $list$, $tuple$, $MultiSet$, ...)
301 +
302 +$remove(toks)$:: does just the contrary
303 +
304 +$reset(toks)$:: replaces the marking with the tokens in $toks$
305 +
306 +$empty()$:: removes all the tokens from the place
307 +
308 +$is_empty()$:: returns a Boolean indicating whether the place is empty
309 +or not
310 +========================
311 +
312 +A marking is an instance of the class $snakes.nets.Marking$ and is
313 +basically a mapping from place names to multisets of values. It has
314 +been chosen that a marking is independent of any particular Petri net,
315 +so, empty places are not listed in a marking and when assigning a
316 +marking to a net, places that are present in the marking but absent
317 +from the net are simply ignored. Markings can be added with the $+$
318 +operator, subtracted with $-$ or compared with the usual operators
319 +$>$, $<=$, $==$, etc. In order to know the marking of a particular
320 +place, we can use it as a function taking the place name as argument:
321 +
322 +[python]
323 +^^^^^^^^^^^^^^^^
324 +>>> m = Marking(p1=MultiSet([1, 2, 3]), p2=MultiSet([5]))
325 +>>> m('p1')
326 +MultiSet([1, 2, 3])
327 +>>> m('p')
328 +MultiSet([])
329 +^^^^^^^^^^^^^^^^
330 +
331 +The last result shows that a place that is not listed in a marking is
332 +considered empty, which is consistent with the fact that empty places
333 +are not listed in a marking extracted from a net. If we use the
334 +marking as a $dict$ instead of a function, we will get errors on non
335 +existing places:
336 +
337 +[python]
338 +^^^^^^^^^^^^^^^^
339 +>>> m['p1']
340 +MultiSet([1, 2, 3])
341 +>>> m['p']
342 +Traceback (most recent call last):
343 + ...
344 +KeyError: 'p'
345 +^^^^^^^^^^^^^^^^
346 +
347 +The marking graph of a net can be manipulated using the class
348 +$StateGraph$. Lets take an example to see how it works:
349 +
350 +[python]
351 +^^^^^^^^^^^^^^^^^^^^^^
352 +>>> n4 = PetriNet('Fourth net')
353 +>>> n4.add_place(Place('p', [-1]))
354 +>>> n4.add_transition(Transition('t'))
355 +>>> n4.add_input('p', 't', Variable('x'))
356 +>>> n4.add_output('p', 't', Expression('(x+1) % 5'))
357 +^^^^^^^^^^^^^^^^^^^^^^
358 +
359 +This example runs infinitely, incrementing modulo 5 the token in the
360 +place $'p'$. Its markings can be computed using a simple loop:
361 +
362 +//skip
363 +[python]
364 +^^^^^^^^^^^^^^^^^^^^^^
365 +>>> while True:
366 +... print n4.get_marking()
367 +... modes = n4.transition('t').modes()
368 +... if len(modes) == 0 :
369 +... break
370 +... n4.transition('t').fire(modes[0])
371 +...
372 +Marking({'p': MultiSet([-1])})
373 +Marking({'p': MultiSet([0])})
374 +Marking({'p': MultiSet([1])})
375 +Marking({'p': MultiSet([2])})
376 +Marking({'p': MultiSet([3])})
377 +Marking({'p': MultiSet([4])})
378 +Marking({'p': MultiSet([0])})
379 +Marking({'p': MultiSet([1])})
380 +^^^^^^^^^^^^^^^^^^^^^^
381 +
382 +Unfortunately, this loop runs forever as the net has no deadlock.
383 +Moreover, if in a state there exist several modes, only the first one
384 +will be used in the exploration. To avoid building ourselves the
385 +marking graph, we can use instead:
386 +
387 +[python]
388 +^^^^^^^^^^^^^^^^^^^^^^
389 +>>> s = StateGraph(n4)
390 +>>> s.build()
391 +>>> for state in s :
392 +... print state, s.net.get_marking()
393 +... print " =>", s.successors()
394 +... print " <=", s.predecessors()
395 +...
396 +0 Marking({'p': MultiSet([-1])})
397 + => {1: (Transition('t', Expression('True')), Substitution(x=-1))}
398 + <= {}
399 +1 Marking({'p': MultiSet([0])})
400 + => {2: (Transition('t', Expression('True')), Substitution(x=0))}
401 + <= {0: (Transition('t', Expression('True')), Substitution(x=-1)), 5: (Transition('t', Expression('True')), Substitution(x=4))}
402 +2 Marking({'p': MultiSet([1])})
403 + => {3: (Transition('t', Expression('True')), Substitution(x=1))}
404 + <= {1: (Transition('t', Expression('True')), Substitution(x=0))}
405 +3 Marking({'p': MultiSet([2])})
406 + => {4: (Transition('t', Expression('True')), Substitution(x=2))}
407 + <= {2: (Transition('t', Expression('True')), Substitution(x=1))}
408 +4 Marking({'p': MultiSet([3])})
409 + => {5: (Transition('t', Expression('True')), Substitution(x=3))}
410 + <= {3: (Transition('t', Expression('True')), Substitution(x=2))}
411 +5 Marking({'p': MultiSet([4])})
412 + => {1: (Transition('t', Expression('True')), Substitution(x=4))}
413 + <= {4: (Transition('t', Expression('True')), Substitution(x=3))}
414 +^^^^^^^^^^^^^^^^^^^^^^
415 +
416 +The second statement computes the graph, then, for each state, we
417 +print its number, the corresponding marking and successors and
418 +predecessors states that include the state number as well as the
419 +transition (and its mode) that changed the marking. For instance, the
420 +state $1$ can be reached from $0$ or $5$ by firing $'t'$ with $x=-1$
421 +in and $x=4$ respectively.
422 +
423 +A marking graph is computed (an thus iterated) in a breadth first
424 +search. It may be iterated before it has been built completely; in
425 +this case, successors states are computed on the fly as each state is
426 +yield by the iteration. However, this results in having wrong
427 +predecessors states as a state not yet explored may lead to one
428 +already visited. This is the case here for the state $5$ that can lead
429 +to $1$ but this is not yet known when we are visiting $1$. This error
430 +is noticeable when the state $1$ is displayed while running:
431 +
432 +[python]
433 +^^^^^^^^^^^^^^^^^^^^^^
434 +>>> s2 = StateGraph(n4)
435 +>>> for state in s2 :
436 +... print state, s2.net.get_marking()
437 +... print " =>", s2.successors()
438 +... print " <=", s2.predecessors()
439 +...
440 +0 Marking({'p': MultiSet([-1])})
441 + => {1: (Transition('t', Expression('True')), Substitution(x=-1))}
442 + <= {}
443 +1 Marking({'p': MultiSet([0])})
444 + => {2: (Transition('t', Expression('True')), Substitution(x=0))}
445 + <= {0: (Transition('t', Expression('True')), Substitution(x=-1))}
446 +2 Marking({'p': MultiSet([1])})
447 + => {3: (Transition('t', Expression('True')), Substitution(x=1))}
448 + <= {1: (Transition('t', Expression('True')), Substitution(x=0))}
449 +3 Marking({'p': MultiSet([2])})
450 + => {4: (Transition('t', Expression('True')), Substitution(x=2))}
451 + <= {2: (Transition('t', Expression('True')), Substitution(x=1))}
452 +4 Marking({'p': MultiSet([3])})
453 + => {5: (Transition('t', Expression('True')), Substitution(x=3))}
454 + <= {3: (Transition('t', Expression('True')), Substitution(x=2))}
455 +5 Marking({'p': MultiSet([4])})
456 + => {1: (Transition('t', Expression('True')), Substitution(x=4))}
457 + <= {4: (Transition('t', Expression('True')), Substitution(x=3))}
458 +^^^^^^^^^^^^^^^^^^^^^^
459 +
460 +Finally, it is worth noting that no check is performed in order to
461 +ensure that a marking graph is finite. The method $build$ may run
462 +forever until it fills the computer memory and crash the program.
463 +Moreover, if we use strange constructs like random numbers generators,
464 +the state graph obtained may be partial as the program cannot known
465 +when all the possibilities have been explored.
466 +
467 +
468 +Places types
469 +------------
470 +
471 +The places defined until now did accept any token value in their
472 +marking. It is possible to restrict that by providing a type to place
473 +when it is constructed. A typing system is defined in the module
474 +$typing$ to provide the flexibility for defining any place type. In
475 +this context, a type is understood as a set of values that can be
476 +infinite. Types can be combined to by the usual set operation
477 +(examples are given below). Several basic types are already defined:
478 +
479 +$tAll$:: allows any value, this is the type assigned to a constructed
480 +place when no other type is given.
481 +
482 +$tNothing$:: is the type with no value.
483 +
484 +$tInteger$:: is the type of integer values.
485 +
486 +$tNatural$:: is a restriction of $tInteger$ to non-negative values.
487 +
488 +$tPositive$:: is a restriction of $tInteger$ to strictly positive
489 +values.
490 +
491 +$tFloat$:: is the type of floating point numbers.
492 +
493 +$tNumber$:: is the union of $tFloat$ and $tInteger$.
494 +
495 +$tString$:: is the type for Python $str$ instances.
496 +
497 +$tBoolean$:: is the set holding the two values $False$ and $True$.
498 +
499 +$tNone$:: holds the only value $None$.
500 +
501 +$tBlackToken$:: holds the only value $dot$ that stands for the usual
502 +Petri net black token.
503 +
504 +$tList$:: allows for values that are instances of the Python class
505 +$list$.
506 +
507 +$tDict$:: allows for values that are instances of the Python class
508 +$dict$.
509 +
510 +$tTuple$:: allows for values that are instances of the Python class
511 +$tuple$.
512 +
513 +$tPair$:: is a restriction of $tTuple$ to tuples of length 2.
514 +
515 +The module typing also provides with type constructors, allowing to
516 +create new types. Moreover, types can be combined using various
517 +operators, for instance, the module $typing$ makes the following
518 +definitions:
519 +
520 +[python]
521 +^^^^^^^^^^^^^^^^^
522 +tInteger = Instance(int)
523 +# an instance of int
524 +tNatural = tInteger & GreaterOrEqual(0)
525 +# an instance of int and a value greater than or equal to zero
526 +tPositive = tInteger & Greater(0)
527 +# an instance of int and a value strictly positive
528 +tFloat = Instance(float)
529 +tNumber = tInteger|tFloat
530 +# an instance of int or of float
531 +tBoolean = OneOf(False, True)
532 +# one value of False and True
533 +tNone = OneOf(None)
534 +# only the value None
535 +tBlackToken = OneOf(dot)
536 +# only the value dot
537 +tTuple = Tuple(tAll)
538 +# an instance of tuple holding any value
539 +tPair = Tuple(tAll, min=2, max=2)
540 +# an instance of tuple holding exactly two items of any type
541 +^^^^^^^^^^^^^^^^^
542 +
543 +A type can be called as a function in which case it returns $True$ if
544 +all its argument belong to the type and $False$ otherwise, for
545 +instance:
546 +
547 +[python]
548 +^^^^^^^^^^^^^^^^^
549 +>>> from snakes.typing import *
550 +>>> tNatural(2, 3, 4, 0)
551 +True
552 +>>> tNatural(-1)
553 +False
554 +^^^^^^^^^^^^^^^^^
555 +
556 +When a place is constructed a third argument can be given to define
557 +its type. If later on a token that does not respect the type is added
558 +to the place, an exception is raised.
559 +
560 +[python]
561 +^^^^^^^^^^^^^^^^^
562 +>>> from snakes.typing import *
563 +>>> tNatural(2)
564 +True
565 +>>> tNatural(-1)
566 +False
567 +>>> p = Place('p', [0, 1, 2, 3], tNatural)
568 +>>> p.add(3)
569 +>>> p.tokens
570 +MultiSet([0, 1, 2, 3, 3])
571 +>>> p.add(-1)
572 +Traceback (most recent call last):
573 + ...
574 +ValueError: forbidden token '-1'
575 +^^^^^^^^^^^^^^^^^
576 +
577 +When no type is given at construction time, $tAll$ is actually used,
578 +which explains why a place accepts any token by default.
579 +
580 +
581 +Plugins
582 +-------
583 +
584 +A system a plugins allow to extend SNAKES. In order to plug the module
585 +$foo$ into the module $snakes.nets$, one as to use:
586 +
587 +//skip
588 +[python]
589 +^^^^^^^^^^^^^^^^^^^^^^^
590 +import snakes.plugins
591 +snakes.plugins.load('snakes.plugins.foo', 'snakes.nets', 'my_nets')
592 +^^^^^^^^^^^^^^^^^^^^^^^
593 +
594 +Intuitively, this (correct) statements have the effect of the
595 +(incorrect) statement:
596 +
597 +//skip
598 +[python]
599 +^^^^^^^^^^^^^^^^^^^^^^^
600 +import 'snakes.nets extended by snakes.plugins.foo' as my_nets
601 +^^^^^^^^^^^^^^^^^^^^^^^
602 +
603 +So, one could now use:
604 +
605 +//skip
606 +[python]
607 +^^^^^^^^^^^^^^^^^^^^^^^
608 +from my_nets import *
609 +^^^^^^^^^^^^^^^^^^^^^^^
610 +
611 +Several plugins may be loaded at the same time:
612 +
613 +//skip
614 +[python]
615 +^^^^^^^^^^^^^^^^^^^^^^^
616 +snakes.plugins.load(['snakes.plugins.foo, 'snakes.plugins.bar],
617 + 'snakes.nets', 'my_nets')
618 +^^^^^^^^^^^^^^^^^^^^^^^
619 +
620 +This has the effect to load the plugin $foo$ on the top of
621 +$snakes.nets$ resulting in a module on the top of which $bar$ is then
622 +loaded.
623 +
624 +=== Giving positions to the nodes
625 +
626 +The plugin $snakes.plugins.pos$ is a very simple one that allows to
627 +give _x_/_y_ positions to the nodes of a Petri net.
628 +
629 +First we load the plugin:
630 +
631 +[python]
632 +^^^^^^^^^^^^^^^^^^^^^^
633 +>>> import snakes.plugins
634 +>>> snakes.plugins.load('snakes.plugins.pos', 'snakes.nets', 'nets')
635 +<module 'nets' from '...'>
636 +>>> from nets import PetriNet, Place, Transition
637 +^^^^^^^^^^^^^^^^^^^^^^
638 +
639 +A place can be added without specifying a position for it, in which
640 +case it will be positioned at $(0,0)$. The position is stored in an
641 +attribute $pos$ of the place as well as in $pos.x$ and $pos.y$:
642 +
643 +[python]
644 +^^^^^^^^^^^^^^^^^^^^^^
645 +>>> n = PetriNet('N')
646 +>>> p = Place('p00')
647 +>>> n.add_place(p)
648 +>>> p.pos
649 +Position(0, 0)
650 +>>> p.pos.x, p.pos.y
651 +(0, 0)
652 +^^^^^^^^^^^^^^^^^^^^^^
653 +
654 +The position can be defined when the node is created or when it is
655 +added to a net.
656 +
657 +[python]
658 +^^^^^^^^^^^^^^^^^^^^^^
659 +>>> t10 = Transition('t10', pos=(1, 0))
660 +>>> n.add_transition(t10)
661 +>>> t10.pos
662 +Position(1, 0)
663 +>>> t = Transition('t01')
664 +>>> t.pos
665 +Position(0, 0)
666 +>>> n.add_transition(t, pos=(0, 1))
667 +>>> t.pos
668 +Position(0, 1)
669 +^^^^^^^^^^^^^^^^^^^^^^
670 +
671 +The last statement above shows that the node is copied when added to a
672 +net and thus $t$ keeps its position $(0,0)$ but its copy in $n$ as
673 +been positioned at $(0,1)$.
674 +
675 +Nodes can be moved using the $shift$ or $moveto$ method, but not by
676 +directly assigning their attributes $pos.x$ of $pos.y$:
677 +
678 +[python]
679 +^^^^^^^^^^^^^^^^^^^^^^
680 +>>> t = n.transition('t01')
681 +>>> t.pos
682 +Position(0, 1)
683 +>>> t.pos.moveto(1, 2)
684 +>>> t.pos
685 +Position(1, 2)
686 +>>> t.pos.shift(1, -1)
687 +>>> t.pos
688 +Position(2, 1)
689 +>>> t.pos.x = 0
690 +Traceback (most recent call last):
691 + ...
692 +AttributeError: readonly attribute
693 +^^^^^^^^^^^^^^^^^^^^^^
694 +
695 +A net extended by $snakes.plugins.pos$ has a method to compute its
696 +bounding box that is a tuple $((xmin, ymin), (xmax, ymax))$ where
697 +$xmin$ is the $x$ coordinate of the left-most node, and so on. A
698 +method $shift$ also allows to shift all the nodes of the net and a
699 +method $transpose$ rotates a whole net by 90° (_i.e._, the top-down
700 +direction becomes the left-right).
701 +
702 +[python]
703 +^^^^^^^^^^^^^^^^^^^^^^
704 +>>> n.bbox()
705 +((0, 0), (2, 1))
706 +>>> n.shift(10, 10)
707 +>>> n.bbox()
708 +((10, 10), (12, 11))
709 +>>> t.pos
710 +Position(12, 11)
711 +>>> n.transpose()
712 +>>> t.pos
713 +Position(-11, 12)
714 +>>> n.bbox()
715 +((-11, 10), (-10, 12))
716 +^^^^^^^^^^^^^^^^^^^^^^
717 +
718 +Finally, notice that when nodes are merged, the position of the result
719 +can be defined by giving an argument $pos$ to the $merge_transitions$
720 +or $merge_places$ method. If no such argument is given, the position
721 +of the new node is computed as the barycentre of the positions of the
722 +merged nodes.
723 +
724 +[python]
725 +^^^^^^^^^^^^^^^^^^^^^^
726 +>>> n.merge_transitions('t11', ['t01', 't10'], pos=(1,1))
727 +>>> n.transition('t11').pos
728 +Position(1, 1)
729 +>>> n.transition('t01').pos
730 +Position(-11, 12)
731 +>>> n.transition('t10').pos
732 +Position(-10, 11)
733 +>>> n.merge_transitions('t', ['t01', 't10'])
734 +>>> n.transition('t').pos
735 +Position(-10.5, 11.5)
736 +^^^^^^^^^^^^^^^^^^^^^^
737 +
738 +=== Drawing nets and marking graphs
739 +
740 +The plugin $graphviz$ can be used in order to produce a graphical
741 +rendering of $PetriNet$ and $StateGraph$ objects. It add to them a
742 +method $draw$ that saves a picture in various formats (those supported
743 +by http://www.graphviz.org[GraphViz]), in particular: PNG, JPEG, EPS,
744 +DOT. For a Petri net, the positions of the nodes is fixed using the
745 +plugin $pos$ (that is automatically loaded). For a stage graph, the
746 +nodes are automatically positioned by GraphViz.
747 +
748 +[WARNING]
749 +===========================
750 +In order to produce a graphical rendering in a format other than DOT,
751 +the plugin $graphviz$ calls the program $dot$ or $neato$. It is
752 +possible that a specially crafted file name will result in executing
753 +arbitrary commands on the system. So, take care when you run a SNAKES
754 +script that you did not program yourself. (In general, take care when
755 +your run any script from an unsafe source.)
756 +===========================
757 +
758 +Let's consider a simple example:
759 +
760 +[python]
761 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^
762 +>>> import snakes.plugins
763 +>>> snakes.plugins.load('snakes.plugins.graphviz', 'snakes.nets', 'nets')
764 +<module 'nets' from '...'>
765 +>>> from nets import *
766 +>>> n = PetriNet('N')
767 +>>> n.add_place(Place('p00', [0]))
768 +>>> n.add_transition(Transition('t10', pos=(1, 0)))
769 +>>> n.add_place(Place('p11', pos=(1, 1)))
770 +>>> n.add_transition(Transition('t01', pos=(0, 1)))
771 +>>> n.add_input('p00', 't10', Variable('x'))
772 +>>> n.add_output('p11', 't10', Expression('(x+1) % 3'))
773 +>>> n.add_input('p11', 't01', Variable('y'))
774 +>>> n.add_output('p00', 't01', Expression('(y+2) % 4'))
775 +>>> n.draw('graphviz-net.png')
776 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^
777 +
778 +This produces the following picture:
779 +
780 +image:graphviz-net.png[GraphViz net]
781 +
782 +The rendering is not very beautiful but should be useful in many
783 +cases. Transitions are labelled with their name and guard, places with
784 +their marking (inside), name and type (top-right), arcs are labelled
785 +with their inscription. The picture format is chosen using the
786 +extension of the created file: $.png$, $.jpg$, $.eps$, $.dot$...
787 +
788 +The marking graph can then be built and drawn also:
789 +
790 +[python]
791 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^
792 +>>> s = StateGraph(n)
793 +>>> s.draw('graphviz-graph.png', landscape=False)
794 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^
795 +
796 +Which produces the picture:
797 +
798 +image:graphviz-graph.png[GraphViz stage graph]
799 +
800 +Each state is labelled with its number and the corresponding marking,
801 +arcs are labelled by the transition and binding that produce one
802 +marking from another.
803 +
804 +Notice that the option $landscape$ has been used in order to ask
805 +GraphViz to produce a vertical layout (it is horizontal by default).
806 +Other options exist, given here with their default value:
807 +
808 +$scale=72.0$:: a scale factor for the whole picture. The greater it is,
809 +the larger will be the space between the nodes. This option applies to
810 +both $PetriNet.draw$ and $StateGraoh.draw$.
811 +
812 +$nodesize=0.5$:: the size of the nodes (places, transitions or
813 +states). The greater it is, the wider the nodes are. This option
814 +applies to both $PetriNet.draw$ and $StateGraoh.draw$.
815 +
816 +$engine$:: the rendering program to use (one of $"neato"$, $dot$,
817 +$"circo"$, $"twopi"$, $"fdp"$). The default for $PetriNet$ objects is
818 +$"neato"$, the default for $StateGraph$ objects is $dot$.
819 +
820 +$layout=False$:: for $PetriNet.draw$ only, controls whether the
821 +rendering program is allowed to move nodes or not. This only works for
822 +$"neato"$, other programs are always allowed to move nodes.
823 +
824 +$print_state=None$:: for $StateGraph.draw$ only, defines the text
825 +printed inside each state. If $None$, the number of the state is
826 +printed with the corresponding marking. Otherwise, a function should
827 +be provided, taking the state number as its first argument and the
828 +state graph as its second argument, and returning the string to print.
829 +
830 +$print_arc=None$:: for $StateGraph.draw$, similarly to $print_state$,
831 +defines the text printed on the arcs. The function to provide must
832 +take five arguments: the source state, the target state, the name of
833 +the transition fired, its mode (_i.e._ the substitution used), and the
834 +state graph. For $PetriNet.draw$, the function must take five
835 +arguments: the label of the arc, the place connected to it, the
836 +transition connected to it, a Boolean indicating whether this is an
837 +input (if $True$) or output arc (if $False$) and the Petri net.
838 +
839 +$print_trans=None$:: for $PetriNet.draw$ only, the function should
840 +take as arguments the transition and the net.
841 +
842 +$print_place=None$:: for $PetriNet.draw$ only, the function should
843 +take as arguments the place and the net. This corresponds to the label
844 +that is drawn outside of the place.
845 +
846 +$print_tokens=None$:: for $PetriNet.draw$ only, the function should
847 +take as arguments the multiset of tokens, the place and the net. This
848 +corresponds to the label that is drawn inside the place.
849 +
850 +=== Merging nodes using name-based rules
851 +
852 +The plugin $status$ extends Petri nets nodes with an attribute called
853 +$status$ that is composed of a _name_ and _value_. The former
854 +corresponds to a class of similar statuses (_e.g._, $entry$,
855 +$internal$, $exit$, $buffer$ or $tick$) and the latter to a particular
856 +subset of this class. The idea is to be able to merge nodes that have
857 +the same status (name and value). Each status uses its own merge rule.
858 +
859 +[NOTE]
860 +====================
861 +The principle of places status is well known in PBC and M-nets, see
862 +for instance the paper
863 +http://www.univ-paris12.fr/lacl/pommereau/publis/2003-fi.html[Asynchronous
864 +Box Calculus] where they are used in order to perform compositions
865 +of Petri net. The plugin extends this notion to transitions.
866 +====================
867 +
868 +For instance, the plugin defines a function $buffer(id)$ that creates
869 +a status $('buffer', id)$. If several places with the status $buffer$
870 +and the same $id$ are present in the net, they can be automatically
871 +merged. Concretely, let's define a net wit three buffer places:
872 +
873 +[python]
874 +^^^^^^^^^^^^^^^^^^^^^^^^
875 +>>> import snakes.plugins
876 +>>> snakes.plugins.load('snakes.plugins.status', 'snakes.nets', 'nets')
877 +<module 'nets' from ...>
878 +>>> from nets import *
879 +>>> import snakes.plugins.status as status
880 +>>> n = PetriNet('N')
881 +>>> n.add_place(Place('p1', [1], status=status.buffer('foo')))
882 +>>> n.add_place(Place('p2', [2]), status=status.buffer('foo'))
883 +>>> n.add_place(Place('p3', [3]), status=status.buffer('bar'))
884 +>>> n.add_place(Place('p4', [4]))
885 +^^^^^^^^^^^^^^^^^^^^^^^^
886 +
887 +Notice that the status can be assigned when the place is created (as
888 +for $p1$) or when it is added to the net (as for $p2$ and $p3$). A
889 +status has not been specified for $p4$ that thus receives the empty
890 +status $(None, None)$.
891 +
892 +It is now possible to list all the places that have the same status:
893 +
894 +[python]
895 +^^^^^^^^^^^^^^^^^^^^^^^^
896 +>>> n.status(status.buffer('foo'))
897 +('p2', 'p1')
898 +>>> n.status(status.buffer('bar'))
899 +('p3',)
900 +^^^^^^^^^^^^^^^^^^^^^^^^
901 +
902 +Moreover, it is possible to merge the places that have the same
903 +status:
904 +
905 +[python]
906 +^^^^^^^^^^^^^^^^^^^^^^^^
907 +>>> n.status.merge(status.buffer('foo'))
908 +>>> n.place()
909 +[Place('p3', MultiSet([3]), tAll, status=Buffer('buffer','bar')),
910 + Place('(p1+p2)', MultiSet([1, 2]), tAll, status=Buffer('buffer','foo')),
911 + Place('p4', MultiSet([4]), tAll)]
912 +^^^^^^^^^^^^^^^^^^^^^^^^
913 +
914 +The places $p1$ and $p2$ has been merged (and then removed), yielding
915 +a place $p1+p2$ whose marking is the sum of the marking of $p1$ and
916 +$p2$. This treatment of the marking is specific to buffer status,
917 +other status may use other methods.
918 +
919 +A Petri net is also enriched with a method in order to change the
920 +status of a node:
921 +
922 +[python]
923 +^^^^^^^^^^^^^^^^^^^^^^^^
924 +>>> n.set_status('(p1+p2)', status.buffer(None))
925 +>>> n.place()
926 +[Place('p3', MultiSet([3]), tAll, status=Buffer('buffer','bar')),
927 + Place('(p1+p2)', MultiSet([1, 2]), tAll, status=Buffer('buffer')),
928 + Place('p4', MultiSet([4]), tAll)]
929 +^^^^^^^^^^^^^^^^^^^^^^^^
930 +
931 +A buffer status with a value $None$ is particular in that it will be
932 +ignored by $PetriNet.status.merge$ ans thus not merged (it is a
933 +private buffer).
934 +
935 +One may have noticed that buffer status are actually instances of the
936 +class $Buffer$ that is itself a subclass of $Status$. In order to
937 +create a status, one just has to extend the class $Status$ and
938 +redefine the method $merge$ (with its arguments as below), for example
939 +the $Buffer$ class is defined as:
940 +
941 +//skip
942 +[python]
943 +^^^^^^^^^^^^^^^^^^^^^^^^
944 +class Buffer (Status) :
945 + def merge (self, net, nodes, name=None) :
946 + # net: the net in which the merge occurs
947 + # nodes: the nodes to merge
948 + # name: the name of the resulting node
949 + if self._value is None :
950 + return # private buffers are ignored
951 + if name is None : # create a name if none has been given
952 + name = "(%s)" % "+".join(sorted(nodes))
953 + net.merge_places(name, nodes, status=self) # merge the places
954 + for src in nodes : # remove the merged places
955 + net.remove_place(src)
956 +^^^^^^^^^^^^^^^^^^^^^^^^
957 +
958 +[NOTE]
959 +==========================
960 +Using this class $Buffer$, a helper function $buffer$ is defined as:
961 +
962 +//skip
963 +[python]
964 +^^^^^^^^^^^^^^^^^^^^^^^^^^
965 +def buffer (name) :
966 + return Buffer('buffer', name)
967 +^^^^^^^^^^^^^^^^^^^^^^^^^^
968 +
969 +==========================
970 +
971 +The plugin defines other statuses:
972 +
973 +$variable(id)$:: is similar to $buffer$ except that when places are
974 +merged, they must all have the same marking. So, $variable$ places are
975 +like variables in a program that store a single value (possibly
976 +structured).
977 +
978 +$tick(id)$:: is a transition status. When $tick$ transitions are
979 +merged, their guards are and-ed.
980 +
981 +$entry$, $internal$ and $exit$:: are the traditional place status
982 +allowing to define control flow operations between Petri nets in PBC
983 +and its successors.
984 +
985 +=== PBC/M-nets control flow operations
986 +
987 +The plugin $ops$ defines the control flow operations usually defined
988 +for PBC and M-nets. To do so, it relies on places status (the plugin
989 +$status$ is automatically loaded) and in particular on the $entry$,
990 +$internal$ and $exit$ statuses. Indeed, it is expected that a Petri
991 +net starts its execution with one token in each entry place (entry
992 +marking) and evolves until it reaches a marking with one token in each
993 +exit place (exit marking). In order to produce the expected control
994 +flow, the operations use combinations of the entry and exit places of
995 +the composed nets. All the details about these operations can be found
996 +in the paper
997 +http://www.univ-paris12.fr/lacl/pommereau/publis/2003-fi.html[Asynchronous
998 +Box Calculus].
999 +
1000 +[NOTE]
1001 +======================
1002 +There are basically two approaches for such combinations. The PBC
1003 +approach relies on cross-products of sets of places with simple types
1004 +(low-level places). This has the advantage to be simple but may
1005 +produce a large number of places. On the other hand, the M-nets
1006 +approach relies on building fewer high-level places whose type are
1007 +cross-products of the types of the combined places (this is a
1008 +simplification). This has the advantage to produce less places than
1009 +in PBC but their types are very complicated and the resulting transition
1010 +rule is hard to implement (it is necessary to match tree-structured
1011 +tokens against tree-structures annotations). Because of this
1012 +complexity, we have chosen to implement to PBC approach which is also
1013 +what is used in the most recent models of the family.
1014 +======================
1015 +
1016 +The plugin defines four control flow operations. Let $n1$ and $n2$ be
1017 +two nets:
1018 +
1019 + * the _sequence_ $n1 & n2$ is obtained by combining the exit places
1020 + of $n1$ with the entry places of $n2$ so that when $n1$ reaches its
1021 + exit marking, it corresponds to the entry marking of $n2$. As a
1022 + result, $n1$ is executed first and is followed by the execution of
1023 + $n2$;
1024 +
1025 + * the _choice_ $n1 + n2$ combines the entry places of $n1$ and $n2$
1026 + on the one hand, and their exit places on the other hand. As a
1027 + result, either $n1$ or $n2$ is executed and they have the same exit
1028 + marking;
1029 +
1030 + * the _iteration_ $n1 * n2$ combines the entry and exit places of
1031 + $n1$ with the $entry$ places of $n2$. As a result, $n1$ can be
1032 + executed an arbitrary number of time (including zero) and is
1033 + followed by one execution of $n2$;
1034 +
1035 + * the _parallel_ composition $n1 | n2$ does not combine any place so
1036 + that $n1$ and $n2$ can evolve concurrently.
1037 +
1038 +When two nets are combined using one of these operators, their nodes
1039 +are automatically merged according to their status, in particular:
1040 +buffer or variables places, and tick transitions are merged using the
1041 +method that is defined by each status.
1042 +
1043 +Two operations that are not related to control flow are also defined:
1044 +
1045 + * the node hiding $n1.hide(old)$ gives the empty status to all the
1046 + nodes in $n1$ that used to have the status $old$ before. It may be
1047 + called with a second argument in order to choose the new status.
1048 + For instance $n1.hide(buffer('foo'), buffer(None))$ makes private
1049 + all the buffer places with the status $('buffer', 'foo')$;
1050 +
1051 + * a variant of the node hiding is $n1 / val$. Its right argument must
1052 + be a status value, the result is a copy of $n1$ in which all the
1053 + nodes with a status $(x,val)$ are given the status $(x,None)$.
1054 +
1055 +This has the effect to disable further merges of, _e.g._, buffer
1056 +places if the net is later combined with another one.
1057 +
1058 +The plugin $posops$ is a combination of $pos$ and $ops$ that tries to
1059 +take into account the positions of the nodes when nets are composed.
1060 +It avoids overlapping the composed nets and tries to distribute evenly
1061 +the combined places in order to have a acceptable result (as far as
1062 +possible). Let's see it in action:
1063 +
1064 +[python]
1065 +^^^^^^^^^^^^^^^^^^^^^^
1066 +>>> import snakes.plugins
1067 +>>> snakes.plugins.load(['snakes.plugins.posops', 'snakes.plugins.graphviz'], 'snakes.nets', 'nets')
1068 +<module 'nets' from ...>
1069 +>>> from nets import *
1070 +>>> from snakes.plugins.status import entry, internal, exit, safebuffer
1071 +>>> n = PetriNet('basic')
1072 +>>> n.add_place(Place('e', status=entry, pos=(0, 2)))
1073 +>>> n.add_place(Place('x', status=exit, pos=(0, 0)))
1074 +>>> n.add_transition(Transition('t', pos=(0, 1)))
1075 +>>> n.add_input('e', 't', Value(dot))
1076 +>>> n.add_output('x', 't', Value(dot))
1077 +>>> n.add_place(Place('v', [0], status=safebuffer('var'), pos=(1,1)))
1078 +>>> n.add_input('v', 't', Variable('x'))
1079 +>>> n.add_output('v', 't', Expression('x+1'))
1080 +>>> n.draw('basic.png')
1081 +^^^^^^^^^^^^^^^^^^^^^^
1082 +
1083 +This basic net is as follows:
1084 +
1085 +image:basic.png[Basic net]
1086 +
1087 +It can be composed with itself in order to produce a more complex net,
1088 +for instance:
1089 +
1090 +[python]
1091 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1092 +>>> complex = n & (n * (n + n))
1093 +>>> var = complex.status(safebuffer('var'))[0]
1094 +>>> complex.place(var).pos.moveto(-1,-1)
1095 +>>> complex.draw('complex.png', scale=60)
1096 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1097 +
1098 +On the second line, the new name of the variable place is retrieved
1099 +from its status. Then the place is moved in order to have a prettier
1100 +result:
1101 +
1102 +image:complex.png[Complex net]
1103 +
1104 +=== PBC/M-nets synchronisation
1105 +
1106 +Another important operation featured by the models in the PBC and
1107 +M-nets family is the synchronization. This operation is similar to the
1108 +CCS synchronization but operates on multi-actions (_i.e_, several
1109 +synchronizations may take place at the same transition). In PBC,
1110 +actions have no parameters while the have in M-nets (which implies the
1111 +unification of these parameters). With respect to CCS, the
1112 +synchronization is here a static operation that builds all the
1113 +possible transitions corresponding to synchronized actions. The plugin
1114 +$synchro$ implements a generalisation of the M-nets synchronisation.
1115 +It is generalised in that it does not impose a fixed arity associated
1116 +to each action name. For more information about M-nets
1117 +synchronisation, see for instance the section 4 of the paper
1118 +http://www.univ-paris12.fr/lacl/pommereau/publis/2002-mtcs.html[Petri
1119 +nets with causal time for system verification].
1120 +
1121 +Let's consider a simple example: three transitions have to
1122 +synchronize, doing so, one transition can receive a value from each
1123 +other transition. To do this with SNAKES, one may run the following
1124 +code:
1125 +
1126 +[python]
1127 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1128 +>>> import snakes.plugins
1129 +>>> snakes.plugins.load(['snakes.plugins.synchro', 'snakes.plugins.graphviz'], 'snakes.nets', 'nets')
1130 +<module 'nets' from ...>
1131 +>>> from nets import *
1132 +>>> from snakes.plugins.synchro import Action
1133 +>>> n = PetriNet('N')
1134 +>>> n.add_place(Place('e1', [dot], pos=(0,2)))
1135 +>>> n.add_place(Place('e2', [dot], pos=(1,2)))
1136 +>>> n.add_place(Place('e3', [dot], pos=(2,2)))
1137 +>>> n.add_place(Place('x1', [], pos=(0,0)))
1138 +>>> n.add_place(Place('x2', [], pos=(1,0)))
1139 +>>> n.add_place(Place('x3', [], pos=(2,0)))
1140 +>>> n.add_transition(Transition('t1', pos=(0,1),
1141 +... actions=[Action('a', True, [Value(2)])]))
1142 +>>> n.add_transition(Transition('t2', pos=(1,1),
1143 +... actions=[Action('a', False, [Variable('x')]),
1144 +... Action('a', False, [Variable('y')])]))
1145 +>>> n.add_transition(Transition('t3', pos=(2,1),
1146 +... actions=[Action('a', True, [Value(3)])]))
1147 +>>> n.add_input("e1", "t1", Value(dot))
1148 +>>> n.add_input("e2", "t2", Value(dot))
1149 +>>> n.add_input("e3", "t3", Value(dot))
1150 +>>> n.add_output("x1", "t1", Value(dot))
1151 +>>> n.add_output("x2", "t2", Value(dot))
1152 +>>> n.add_output("x3", "t3", Value(dot))
1153 +>>> def pt (trans, net) :
1154 +... return "%s\\n%s" % (trans.name, str(trans.actions))
1155 +>>> n.draw('synchro-1.png', print_trans=pt)
1156 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1157 +
1158 +The resulting picture is the following:
1159 +
1160 +image:synchro-1.png[Before synchronisation]
1161 +
1162 +Applying the synchronisation is possible by calling the method
1163 +$synchronise$ that expects an action name as parameter. The code below
1164 +performs the synchronisation then draws the net with a new layout in
1165 +order to make it more readable (by default, a synchronised transition
1166 +is place in the middle of the transition that participated to the
1167 +synchronisation).
1168 +
1169 +[python]
1170 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1171 +>>> n.synchronise('a')
1172 +>>> n.draw('synchro-2.png', print_trans=pt, layout=True, engine='circo')
1173 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1174 +
1175 +image:synchro-2.png[After synchronisation]
1176 +
1177 +The result is not really readable, but let's see what happened:
1178 +
1179 + - $t1$ has two ways to synchronise with $t2$, which results in
1180 + building two transitions, that still hold one receive action $a$;
1181 +
1182 + - this is the same for $t3$ and $t2$, we have so far created 4 new
1183 + transitions;
1184 +
1185 + - then, both $t1$ and $t3$ can synchronise with the 4 new
1186 + transitions, which results in 8 new transitions.
1187 +
1188 +So their are now 15 transitions in the net. The names of the new
1189 +transitions correspond to how they were obtained. For instance, the
1190 +one on the left of $x1$ (on the top) has the name
1191 +$(t1{x->2}+t2{x->2}[a(2)]$, which means that is was obtained from the
1192 +synchronisation of $t1$ and $t2$ for which the variable $x$ was bound
1193 +to $2$ (the two substitutions are the way to unify the synchronised
1194 +actions) that communicated the value $2$ on the action $a$.
1195 +
1196 +It may be observed that only the last 8 transitions correspond to the
1197 +full synchronisation of $t2$ with both $t1$ and $t3$ (and each of
1198 +these 8 transition corresponds to one way of achieving the
1199 +synchronisation, some being equivalent). But if the net is executed,
1200 +it is possible to fire $t1$, $t2$, $t3$ or any of the partially
1201 +synchronised transitions. In order to force the execution of the full
1202 +synchronisation, one can call the method $restrict$ that remove all
1203 +the transitions that still hold an action $a$. Most of time, a
1204 +restriction always follows a synchronisation; so, there is also a
1205 +method $scope$ that perform both in turn.
1206 +
1207 +[python]
1208 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1209 +>>> n.restrict('a')
1210 +>>> n.draw('synchro-3.png', print_trans=pt, layout=True, engine='circo')
1211 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1212 +
1213 +This results in the following picture where only 8 transitions are
1214 +remaining:
1215 +
1216 +image:synchro-3.png[After restriction]
1217 +
1218 +On this last picture, one may notice that some transitions consume or
1219 +produce two tokens. This is the case for instance for the top-left
1220 +transition that results from the synchronisation of $t1$ with $t2$
1221 +which was then synchronised again with $t1$. In a model of safe or
1222 +colour-safe Petri nets like PBC or M-nets, this is a dead transition
1223 +that could be removed. This can be made easily with the following
1224 +code, resulting in the picture below:
1225 +
1226 +[python]
1227 +^^^^^^^^^^^^^^^^^^^^^^^
1228 +>>> for trans in n.transition() :
1229 +... for place, label in trans.pre.items() :
1230 +... if label == MultiArc([Value(dot), Value(dot)]) :
1231 +... n.remove_transition(trans.name)
1232 +... break
1233 +>>> n.draw('synchro-4.png', print_trans=pt, layout=True, engine='circo')
1234 +^^^^^^^^^^^^^^^^^^^^^^^
1235 +
1236 +image:synchro-4.png[After removing the dead transitions]
1237 +
1238 +It may be interesting also to remove the duplicated transitions, but
1239 +this is beyond the scope of this tutorial.
1240 +
1241 +
1242 +=== Representing infinite data domains
1243 +
1244 +[NOTE]
1245 +=======================
1246 +This section describes the implementation that corresponds to the
1247 +paper
1248 +http://www.univ-paris12.fr/lacl/pommereau/publis/2007-infinity.html[Efficient
1249 +reachability graph representation of Petri nets with unbounded
1250 +counters]. The only difference with respect to the paper is that
1251 +SNAKES is not limited to P/T nets but happily handles high-level nets
1252 +together with Lash data. The rest of the section uses the example
1253 +developed in the paper, taking $omega=8$.
1254 +=======================
1255 +
1256 +The first thing to do is to load the plugin $lashdata$ do that we are
1257 +able to store data in Lash, we also import load $graphviz$ in order to
1258 +draw the resulting graphs:
1259 +
1260 +[python]
1261 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1262 +>>> import snakes.plugins
1263 +>>> nets = snakes.plugins.load(["snakes.plugins.graphviz",
1264 +... "snakes.plugins.lashdata"],
1265 +... "snakes.nets", "nets")
1266 +>>> from nets import *
1267 +>>> from snakes.typing import *
1268 +>>> from snakes.data import *
1269 +>>> from snakes.plugins.lashdata import Data
1270 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1271 +
1272 +$Data$ is a class that allows to store integer variables into Lash.
1273 +This library actually store data under the form of sets of vectors of
1274 +integers but the class $Data$ hides this under the usual concept of
1275 +variables. When we build a Petri net, we must give a $lash$ argument
1276 +that is one instance of $Data$. Its constructor simply takes a list of
1277 +variables with their initial values:
1278 +
1279 +[python]
1280 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1281 +>>> n = PetriNet("N", lash=Data(x=0))
1282 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1283 +
1284 +Then we add the places:
1285 +
1286 +[python]
1287 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1288 +>>> n.add_place(Place("s_1", [dot], tBlackToken, pos=(0, 0)))
1289 +>>> n.add_place(Place("s_2", [], tBlackToken, pos=(2, 0)))
1290 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1291 +
1292 +And last the transitions. Each transition is given a $condition$ under
1293 +the form of a linear Python expression ($or$, $not$ nor $=!$ allowed).
1294 +An $update$ is also provided in order to modify the variables, this a
1295 +single assignment of one variable with a linear expression (several
1296 +assignment may be combined using $;$).
1297 +
1298 +[python]
1299 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1300 +>>> n.add_transition(Transition("t_3", pos=(1, -1)),
1301 +... condition="x<8",
1302 +... update="x=x+1")
1303 +>>> n.add_input("s_2", "t_3", Value(dot))
1304 +>>> n.add_output("s_1", "t_3", Value(dot))
1305 +>>> n.add_transition(Transition("t_2", pos=(1, 1)),
1306 +... condition="x>0", update="x=x-1")
1307 +>>> n.add_input("s_1", "t_2", Value(dot))
1308 +>>> n.add_output("s_2", "t_2", Value(dot))
1309 +>>> n.add_transition(Transition("t_5", pos=(3, 1)),
1310 +... condition="x>0", update="x=x-1")
1311 +>>> n.add_input("s_2", "t_5", Value(dot))
1312 +>>> n.add_output("s_2", "t_5", Value(dot))
1313 +>>> n.add_transition(Transition("t_4", pos=(3, -1)),
1314 +... condition="x<8",
1315 +... update="x=x+1")
1316 +>>> n.add_input("s_2", "t_4", Value(dot))
1317 +>>> n.add_output("s_2", "t_4", Value(dot))
1318 +>>> n.add_transition(Transition("t_1", pos=(-1, 0)),
1319 +... condition="x<4",
1320 +... update="x=x+1")
1321 +>>> n.add_input("s_1", "t_1", Value(dot))
1322 +>>> n.add_output("s_1", "t_1", Value(dot))
1323 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1324 +
1325 +Then, we can build four different marking graph. The first one is the
1326 +full, usual, marking graph:
1327 +
1328 +[python]
1329 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1330 +>>> m = StateGraph(n)
1331 +>>> m.build()
1332 +>>> def ps (state, graph) :
1333 +... return str(state)
1334 +>>> def pa (source, target, trans, mode, graph) :
1335 +... return trans
1336 +>>> m.draw("lash-full.png", landscape=True, print_state=ps, print_arc=pa)
1337 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1338 +
1339 +That results in the following picture:
1340 +
1341 +image:lash-full.png[Full marking graph]
1342 +
1343 +Then come the compact graphs. First we can ask SNAKES to add a meta
1344 +transition for each detected side-loop (a transition that does not
1345 +change the marking).
1346 +
1347 +[python]
1348 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1349 +>>> m = StateGraph(n, loops=True)
1350 +>>> m.build()
1351 +>>> m.draw("lash-loops.png", landscape=True, print_state=ps, print_arc=pa)
1352 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1353 +
1354 +That results in:
1355 +
1356 +image:lash-loops.png[First compact marking graph]
1357 +
1358 +We can then ask to add a meta transition when cycles are detected (a
1359 +new state that covers an existing one). We can further ask to remove
1360 +states that are covered when cycles are detected.
1361 +
1362 +[python]
1363 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1364 +>>> m = StateGraph(n, cycles=True)
1365 +>>> m.build()
1366 +>>> m.draw("lash-cycles.png", landscape=True, print_state=ps, print_arc=pa)
1367 +>>> m = StateGraph(n, remove=True)
1368 +>>> m.build()
1369 +>>> m.draw("lash-remove.png", landscape=True, print_state=ps, print_arc=pa)
1370 +^^^^^^^^^^^^^^^^^^^^^^^^^^
1371 +
1372 +In this particular example, exploiting cycles results in
1373 +link:lash-cycles.png[the same graph as above], but with the $remove$
1374 +option, we get a more compact graph:
1375 +
1376 +image:lash-remove.png[The most compact graph]
1377 +
1378 +Finally, note that using the $cycles$ option automatically turns on
1379 +the $loops$ option, and using the $remove$ option turns on the
1380 +$cycles$ option (and thus also the $loops$ one).
1381 +
1382 +
1383 +Exporting to PNML and other formats
1384 +-----------------------------------
1385 +
1386 +To do...
1387 +
1388 +
1389 +Writing a plugin
1390 +----------------
1391 +
1392 +To do...
1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
3 +<svg
4 + xmlns:dc="http://purl.org/dc/elements/1.1/"
5 + xmlns:cc="http://creativecommons.org/ns#"
6 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7 + xmlns:svg="http://www.w3.org/2000/svg"
8 + xmlns="http://www.w3.org/2000/svg"
9 + xmlns:xlink="http://www.w3.org/1999/xlink"
10 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 + width="744.09448819"
13 + height="1052.3622047"
14 + id="svg4049"
15 + sodipodi:version="0.32"
16 + inkscape:version="0.46"
17 + sodipodi:docname="snakes-logo.svg"
18 + inkscape:output_extension="org.inkscape.output.svg.inkscape">
19 + <defs
20 + id="defs4051">
21 + <linearGradient
22 + id="linearGradient4671">
23 + <stop
24 + style="stop-color:#ffd43b;stop-opacity:1;"
25 + offset="0"
26 + id="stop4673" />
27 + <stop
28 + style="stop-color:#ffe873;stop-opacity:1"
29 + offset="1"
30 + id="stop4675" />
31 + </linearGradient>
32 + <linearGradient
33 + id="linearGradient4689">
34 + <stop
35 + style="stop-color:#5a9fd4;stop-opacity:1;"
36 + offset="0"
37 + id="stop4691" />
38 + <stop
39 + style="stop-color:#306998;stop-opacity:1;"
40 + offset="1"
41 + id="stop4693" />
42 + </linearGradient>
43 + <linearGradient
44 + id="linearGradient3275">
45 + <stop
46 + style="stop-color:#5a9fd4;stop-opacity:1;"
47 + offset="0"
48 + id="stop3277" />
49 + <stop
50 + style="stop-color:#ffd43b;stop-opacity:1;"
51 + offset="1"
52 + id="stop3279" />
53 + </linearGradient>
54 + <marker
55 + inkscape:stockid="Arrow2Mend"
56 + orient="auto"
57 + refY="0.0"
58 + refX="0.0"
59 + id="Arrow2Mend"
60 + style="overflow:visible;">
61 + <path
62 + id="path4724"
63 + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
64 + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
65 + transform="scale(0.6) rotate(180) translate(0,0)" />
66 + </marker>
67 + <marker
68 + inkscape:stockid="Arrow1Mend"
69 + orient="auto"
70 + refY="0.0"
71 + refX="0.0"
72 + id="Arrow1Mend"
73 + style="overflow:visible;">
74 + <path
75 + id="path4706"
76 + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
77 + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
78 + transform="scale(0.4) rotate(180) translate(10,0)" />
79 + </marker>
80 + <marker
81 + inkscape:stockid="Arrow2Lend"
82 + orient="auto"
83 + refY="0.0"
84 + refX="0.0"
85 + id="Arrow2Lend"
86 + style="overflow:visible;">
87 + <path
88 + id="path4718"
89 + style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
90 + d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
91 + transform="scale(1.1) rotate(180) translate(1,0)" />
92 + </marker>
93 + <linearGradient
94 + id="linearGradient4671-685">
95 + <stop
96 + id="stop3989"
97 + offset="0"
98 + style="stop-color:#9d9d9d;stop-opacity:1;" />
99 + <stop
100 + id="stop3991"
101 + offset="1"
102 + style="stop-color:#b9b9b9;stop-opacity:1" />
103 + </linearGradient>
104 + <linearGradient
105 + id="linearGradient4689-776">
106 + <stop
107 + id="stop3983"
108 + offset="0"
109 + style="stop-color:#979797;stop-opacity:1;" />
110 + <stop
111 + id="stop3985"
112 + offset="1"
113 + style="stop-color:#646464;stop-opacity:1;" />
114 + </linearGradient>
115 + <inkscape:perspective
116 + sodipodi:type="inkscape:persp3d"
117 + inkscape:vp_x="0 : 526.18109 : 1"
118 + inkscape:vp_y="0 : 1000 : 0"
119 + inkscape:vp_z="744.09448 : 526.18109 : 1"
120 + inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
121 + id="perspective4057" />
122 + <linearGradient
123 + inkscape:collect="always"
124 + xlink:href="#linearGradient4689-776"
125 + id="linearGradient4656"
126 + gradientUnits="userSpaceOnUse"
127 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
128 + x1="26.648937"
129 + y1="20.603781"
130 + x2="135.66525"
131 + y2="114.39767" />
132 + <linearGradient
133 + inkscape:collect="always"
134 + xlink:href="#linearGradient4671-685"
135 + id="linearGradient4658"
136 + gradientUnits="userSpaceOnUse"
137 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
138 + x1="150.96111"
139 + y1="192.35176"
140 + x2="112.03144"
141 + y2="137.27299" />
142 + <linearGradient
143 + inkscape:collect="always"
144 + xlink:href="#linearGradient4689-776"
145 + id="linearGradient7767"
146 + gradientUnits="userSpaceOnUse"
147 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
148 + x1="26.648937"
149 + y1="20.603781"
150 + x2="135.66525"
151 + y2="114.39767" />
152 + <linearGradient
153 + inkscape:collect="always"
154 + xlink:href="#linearGradient4671-685"
155 + id="linearGradient7769"
156 + gradientUnits="userSpaceOnUse"
157 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
158 + x1="150.96111"
159 + y1="192.35176"
160 + x2="112.03144"
161 + y2="137.27299" />
162 + <linearGradient
163 + inkscape:collect="always"
164 + xlink:href="#linearGradient4689-776"
165 + id="linearGradient7775"
166 + gradientUnits="userSpaceOnUse"
167 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
168 + x1="26.648937"
169 + y1="20.603781"
170 + x2="135.66525"
171 + y2="114.39767" />
172 + <linearGradient
173 + inkscape:collect="always"
174 + xlink:href="#linearGradient4671-685"
175 + id="linearGradient7777"
176 + gradientUnits="userSpaceOnUse"
177 + gradientTransform="matrix(0.562541,0,0,0.567972,78.198395,2.6157917)"
178 + x1="150.96111"
179 + y1="192.35176"
180 + x2="112.03144"
181 + y2="137.27299" />
182 + <linearGradient
183 + inkscape:collect="always"
184 + xlink:href="#linearGradient4671-685"
185 + id="linearGradient7780"
186 + gradientUnits="userSpaceOnUse"
187 + gradientTransform="matrix(0.2881307,0,0,0.2909125,159.73956,131.18265)"
188 + x1="150.96111"
189 + y1="192.35176"
190 + x2="112.03144"
191 + y2="137.27299" />
192 + <linearGradient
193 + inkscape:collect="always"
194 + xlink:href="#linearGradient4689-776"
195 + id="linearGradient7783"
196 + gradientUnits="userSpaceOnUse"
197 + gradientTransform="matrix(0.2881307,0,0,0.2909125,159.73956,131.18265)"
198 + x1="26.648937"
199 + y1="20.603781"
200 + x2="135.66525"
201 + y2="114.39767" />
202 + <linearGradient
203 + inkscape:collect="always"
204 + xlink:href="#linearGradient3275"
205 + id="linearGradient3281"
206 + x1="28.148954"
207 + y1="-295.26871"
208 + x2="296.6087"
209 + y2="-295.26871"
210 + gradientUnits="userSpaceOnUse"
211 + gradientTransform="matrix(0,1,-1,0,-2,0)" />
212 + <linearGradient
213 + inkscape:collect="always"
214 + xlink:href="#linearGradient4689"
215 + id="linearGradient3978"
216 + gradientUnits="userSpaceOnUse"
217 + gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)"
218 + x1="26.648937"
219 + y1="20.603781"
220 + x2="135.66525"
221 + y2="114.39767" />
222 + <linearGradient
223 + inkscape:collect="always"
224 + xlink:href="#linearGradient4671"
225 + id="linearGradient3980"
226 + gradientUnits="userSpaceOnUse"
227 + gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)"
228 + x1="150.96111"
229 + y1="192.35176"
230 + x2="112.03144"
231 + y2="137.27299" />
232 + </defs>
233 + <sodipodi:namedview
234 + id="base"
235 + pagecolor="#ffffff"
236 + bordercolor="#666666"
237 + borderopacity="1.0"
238 + gridtolerance="10000"
239 + guidetolerance="10"
240 + objecttolerance="10"
241 + inkscape:pageopacity="0.0"
242 + inkscape:pageshadow="2"
243 + inkscape:zoom="0.98994949"
244 + inkscape:cx="322.98744"
245 + inkscape:cy="827.15881"
246 + inkscape:document-units="px"
247 + inkscape:current-layer="layer1"
248 + showgrid="false"
249 + inkscape:window-width="1440"
250 + inkscape:window-height="878"
251 + inkscape:window-x="0"
252 + inkscape:window-y="0"
253 + showguides="true"
254 + inkscape:guide-bbox="true" />
255 + <metadata
256 + id="metadata4054">
257 + <rdf:RDF>
258 + <cc:Work
259 + rdf:about="">
260 + <dc:format>image/svg+xml</dc:format>
261 + <dc:type
262 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
263 + </cc:Work>
264 + </rdf:RDF>
265 + </metadata>
266 + <g
267 + inkscape:label="Layer 1"
268 + inkscape:groupmode="layer"
269 + id="layer1">
270 + <g
271 + id="g4096">
272 + <path
273 + id="rect4073"
274 + d="M 53.21875 28.4375 L 53.21875 296.3125 L 131.5625 296.3125 L 131.5625 254.96875 C 127.40006 255.10395 123.06445 255.05613 122.53125 254.25 C 121.71364 253.01388 127.57742 239.04558 128.15625 237.59375 C 128.35581 237.09321 129.06594 235.3019 129.9375 233.09375 C 86.808636 220.67326 55.21875 180.88942 55.21875 133.78125 C 55.218752 76.74507 101.52631 30.4375 158.5625 30.4375 C 215.59868 30.4375 261.875 76.745063 261.875 133.78125 C 261.87501 181.48268 229.49465 221.65529 185.53125 233.53125 L 185.53125 277.28125 C 189.44172 277.51593 194.36954 277.98504 194.625 278.96875 C 195.04299 280.57833 189.62262 291.82522 187.4375 296.3125 L 321.0625 296.3125 L 321.0625 28.4375 L 53.21875 28.4375 z M 173.125 236.0625 C 168.3641 236.73542 163.50817 237.09375 158.5625 237.09375 C 154.43899 237.09375 150.37275 236.84547 146.375 236.375 C 148.86246 241.49268 153.47528 251.18709 153.09375 252.65625 C 152.83829 253.63995 147.91047 254.10906 144 254.34375 L 144 296.3125 L 170.59375 296.3125 C 170.24478 295.4181 169.79764 294.30751 169.6875 294.03125 C 169.10867 292.57942 163.2449 278.61112 164.0625 277.375 C 164.59703 276.56685 168.95311 276.51976 173.125 276.65625 L 173.125 236.0625 z "
275 + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
276 + <path
277 + id="text4087"
278 + d="M 284.16123,61.264954 C 279.36122,61.264918 275.47323,59.896919 272.49723,57.160954 C 269.52123,54.424924 268.00923,50.680928 267.96123,45.928954 C 267.91323,41.272938 269.49723,37.360941 272.71323,34.192954 C 275.68923,31.264948 279.57722,29.416949 284.37723,28.648954 L 288.55323,40.600954 C 285.57722,40.600938 283.58522,40.792938 282.57723,41.176954 C 280.70522,41.848937 279.76922,43.408935 279.76923,45.856954 C 279.81722,48.64093 280.87322,50.032929 282.93723,50.032954 C 284.95322,50.032929 286.89722,48.40093 288.76923,45.136954 C 291.93721,39.616939 293.73721,36.592942 294.16923,36.064954 C 296.80921,32.800946 300.0972,31.168948 304.03323,31.168954 C 308.06519,31.168948 311.40119,32.608946 314.04123,35.488954 C 316.63319,38.224941 317.92918,41.632937 317.92923,45.712954 C 317.92918,49.552929 316.60919,52.744926 313.96923,55.288954 C 312.14519,57.064922 309.09719,58.91292 304.82523,60.832954 L 300.50523,50.104954 C 302.5692,49.09693 303.7212,48.54493 303.96123,48.448954 C 305.4492,47.536931 306.1932,46.600932 306.19323,45.640954 C 306.1932,44.872934 305.9772,44.176935 305.54523,43.552954 C 305.1132,42.928936 304.5132,42.592936 303.74523,42.544954 C 303.6972,42.640936 302.0412,45.328934 298.77723,50.608954 C 296.71321,53.968925 294.76921,56.440922 292.94523,58.024954 C 290.40121,60.184919 287.52121,61.264918 284.30523,61.264954 L 284.16123,61.264954 M 284.23323,60.904954 C 288.07321,60.904918 291.19321,59.680919 293.59323,57.232954 C 294.98521,55.840923 296.68921,53.200926 298.70523,49.312954 C 300.5772,45.760933 302.2572,43.384935 303.74523,42.184954 L 303.88923,42.184954 C 304.7532,42.184937 305.4012,42.520936 305.83323,43.192954 C 306.3132,43.912935 306.5532,44.704934 306.55323,45.568954 C 306.5052,47.056932 304.6572,48.61693 301.00923,50.248954 L 305.04123,60.328954 C 309.12119,58.45692 312.04919,56.656922 313.82523,54.928954 C 316.36919,52.432926 317.64118,49.33693 317.64123,45.640954 C 317.64118,41.656937 316.36919,38.296941 313.82523,35.560954 C 311.32919,32.824946 308.08919,31.456947 304.10523,31.456954 C 300.2652,31.456947 297.0492,33.088946 294.45723,36.352954 C 294.21721,36.640942 292.41721,39.688939 289.05723,45.496954 C 287.18521,48.76093 285.19322,50.392928 283.08123,50.392954 C 281.88122,50.392928 280.96922,49.936929 280.34523,49.024954 C 279.72122,48.160931 279.40922,47.104932 279.40923,45.856954 C 279.40922,43.408935 280.32122,41.776937 282.14523,40.960954 C 283.24922,40.480938 285.26522,40.240939 288.19323,40.240954 L 284.16123,29.080954 C 279.60122,29.848949 275.88123,31.672947 273.00123,34.552954 C 269.92923,37.624941 268.39323,41.392937 268.39323,45.856954 C 268.39323,50.560928 269.83323,54.232925 272.71323,56.872954 C 275.64123,59.560919 279.48122,60.904918 284.23323,60.904954 M 305.47323,59.392954 L 304.53723,57.160954 L 306.33723,58.960954 L 305.47323,59.392954 M 308.85723,57.664954 C 304.2492,53.056926 301.9452,50.728928 301.94523,50.680954 C 302.9532,50.200929 303.8652,49.696929 304.68123,49.168954 L 311.52123,55.936954 C 310.60919,56.608922 309.72119,57.184922 308.85723,57.664954 M 313.46523,54.280954 L 306.62523,47.512954 C 307.00919,46.936932 307.22519,46.312933 307.27323,45.640954 C 307.27319,45.016934 307.05719,44.176935 306.62523,43.120954 L 315.33723,51.832954 C 314.90519,52.648926 314.28119,53.464925 313.46523,54.280954 M 316.34523,49.096954 L 300.14523,32.824954 C 301.4412,32.440946 302.6892,32.248947 303.88923,32.248954 L 316.70523,45.136954 L 316.70523,45.712954 C 316.70519,46.768932 316.58519,47.896931 316.34523,49.096954 M 315.91323,40.528954 L 308.42523,32.968954 C 311.83319,34.408944 314.32919,36.928942 315.91323,40.528954 M 292.00923,57.376954 L 285.31323,50.752954 C 286.03322,50.320929 286.82522,49.624929 287.68923,48.664954 L 294.31323,55.216954 C 293.49721,56.128923 292.72921,56.848922 292.00923,57.376954 M 295.96923,53.200954 L 289.20123,46.432954 C 289.58521,45.856933 290.16121,44.944934 290.92923,43.696954 L 297.76923,50.608954 C 297.0012,51.760927 296.40121,52.624926 295.96923,53.200954 M 299.06523,48.232954 L 292.22523,41.320954 L 293.73723,38.512954 L 300.64923,45.424954 L 299.06523,48.232954 M 302.08923,43.048954 L 295.32123,36.352954 C 296.13721,35.440943 296.90521,34.720944 297.62523,34.192954 L 305.40123,42.040954 C 304.6332,41.752937 304.0572,41.608937 303.67323,41.608954 C 303.2412,41.608937 302.7132,42.088937 302.08923,43.048954 M 286.60923,60.112954 L 269.32923,42.832954 C 269.52123,41.728937 269.88123,40.624938 270.40923,39.520954 L 278.90523,48.016954 C 279.24122,49.600929 280.20122,50.560928 281.78523,50.896954 L 289.77723,58.888954 C 288.67321,59.416919 287.61721,59.824919 286.60923,60.112954 M 282.93723,60.328954 C 279.76922,60.040919 277.96922,59.680919 277.53723,59.248954 L 269.90523,51.616954 C 269.56923,51.280928 269.25723,49.528929 268.96923,46.360954 L 282.93723,60.328954 M 282.86523,40.096954 L 275.88123,33.112954 C 276.50523,32.728946 277.44122,32.200947 278.68923,31.528954 L 286.75323,39.664954 C 285.21722,39.664939 283.92122,39.808939 282.86523,40.096954 M 278.76123,44.200954 L 271.70523,37.144954 C 272.18523,36.424942 272.85723,35.632943 273.72123,34.768954 L 280.27323,41.392954 C 279.55322,42.016937 279.04922,42.952936 278.76123,44.200954 M 285.02523,34.192954 L 281.35323,30.520954 C 282.31322,30.232949 283.05722,30.064949 283.58523,30.016954 L 285.02523,34.192954 M 305.61723,58.888954 L 305.76123,58.888954 L 305.54523,58.672954 L 305.61723,58.888954 M 308.85723,57.304954 C 309.81719,56.728922 310.51319,56.272923 310.94523,55.936954 L 304.60923,49.600954 C 304.1292,49.888929 303.4092,50.272929 302.44923,50.752954 L 308.92923,57.232954 L 308.85723,57.304954 M 313.39323,53.704954 C 313.92119,53.224926 314.42519,52.624926 314.90523,51.904954 L 307.48923,44.416954 C 307.58519,44.992934 307.60919,45.400933 307.56123,45.640954 C 307.60919,46.264933 307.46519,46.864932 307.12923,47.440954 L 313.39323,53.704954 M 316.20123,48.376954 C 316.34519,47.512931 316.41719,46.648932 316.41723,45.784954 L 316.34523,45.280954 L 303.74523,32.608954 C 302.7372,32.608946 301.7532,32.752946 300.79323,33.040954 L 316.20123,48.376954 M 314.61723,38.656954 C 313.46519,36.688942 311.95319,35.176944 310.08123,34.120954 L 314.61723,38.656954 M 292.08123,56.944954 C 292.46521,56.608922 293.04121,56.056923 293.80923,55.288954 L 287.68923,49.168954 C 287.06521,49.840929 286.48922,50.368928 285.96123,50.752954 L 292.08123,56.944954 M 295.89723,52.552954 C 296.37721,51.976927 296.83321,51.328928 297.26523,50.608954 L 291.00123,44.272954 L 289.70523,46.432954 L 295.89723,52.552954 M 298.99323,47.656954 L 300.21723,45.496954 L 293.88123,39.088954 L 292.58523,41.248954 L 298.99323,47.656954 M 302.01723,42.472954 C 302.6892,41.560937 303.4092,41.176938 304.17723,41.320954 L 297.55323,34.624954 C 296.9772,35.056944 296.40121,35.608943 295.82523,36.280954 L 302.01723,42.472954 M 286.68123,59.680954 C 287.59321,59.440919 288.43321,59.15292 289.20123,58.816954 L 281.64123,51.256954 C 279.91322,50.824928 278.88122,49.792929 278.54523,48.160954 L 270.55323,40.096954 C 270.16923,41.056938 269.90523,41.944937 269.76123,42.760954 L 286.68123,59.680954 M 282.07323,59.896954 L 269.47323,47.296954 C 269.80923,49.840929 270.07323,51.208928 270.26523,51.400954 L 277.82523,58.888954 C 278.20922,59.27292 279.62522,59.608919 282.07323,59.896954 M 283.00923,39.664954 C 283.68122,39.520939 284.66522,39.400939 285.96123,39.304954 L 278.61723,32.032954 C 277.65722,32.512946 276.93722,32.896946 276.45723,33.184954 L 283.00923,39.664954 M 278.54523,43.480954 C 278.73722,42.712936 279.14522,41.992937 279.76923,41.320954 L 273.72123,35.272954 C 273.19323,35.848943 272.66523,36.448942 272.13723,37.072954 L 278.54523,43.480954 M 284.16123,32.824954 L 283.36923,30.448954 L 282.07323,30.664954 L 284.16123,32.824954 M 268.17723,113.0622 L 268.10523,105.5022 L 295.75323,82.318204 L 268.03323,82.318204 L 267.96123,70.150204 L 318.21723,70.078204 L 318.28923,79.870204 L 292.44123,101.1102 L 318.43323,100.9662 L 318.50523,113.0622 L 268.17723,113.0622 M 268.60923,112.7022 L 318.14523,112.7022 L 318.21723,101.3262 L 291.50523,101.4702 L 317.92923,79.726204 L 318.00123,70.438204 L 268.46523,70.510204 L 268.39323,81.958204 L 296.83323,81.958204 L 268.53723,105.6462 L 268.60923,112.7022 M 317.49723,110.7582 L 308.85723,102.1182 L 313.60923,102.1182 L 317.42523,105.9342 L 317.49723,110.7582 M 310.51323,111.9822 L 300.64923,102.1182 L 305.40123,102.1182 L 315.26523,111.9822 L 310.51323,111.9822 M 302.37723,111.9822 L 292.58523,102.1902 L 297.33723,102.1902 L 307.12923,111.9822 L 302.37723,111.9822 M 294.24123,111.9822 L 279.55323,97.294204 L 282.14523,95.134204 L 298.99323,111.9822 L 294.24123,111.9822 M 286.10523,111.9822 L 275.08923,101.0382 L 277.68123,98.878204 L 290.85723,111.9822 L 286.10523,111.9822 M 317.20923,77.926204 L 310.44123,71.158204 L 315.19323,71.158204 L 317.13723,73.174204 L 317.20923,77.926204 M 300.00123,93.406204 L 292.80123,86.206204 L 295.39323,83.974204 L 302.66523,91.246204 L 300.00123,93.406204 M 313.39323,82.390204 L 302.23323,71.230204 L 306.98523,71.158204 L 316.05723,80.230204 L 313.39323,82.390204 M 295.53723,97.078204 L 288.33723,89.878204 L 290.92923,87.718204 L 298.20123,94.918204 L 295.53723,97.078204 M 291.07323,100.7502 L 283.87323,93.622204 L 286.53723,91.462204 L 293.66523,98.590204 L 291.07323,100.7502 M 277.82523,111.9822 L 270.62523,104.7822 L 273.21723,102.5502 L 282.64923,111.9822 L 277.82523,111.9822 M 308.92923,86.062204 L 294.09723,71.230204 L 298.84923,71.230204 L 311.59323,83.902204 L 308.92923,86.062204 M 269.68923,111.9822 L 269.18523,111.4062 L 269.11323,106.6542 L 274.51323,111.9822 L 269.68923,111.9822 M 304.46523,89.734204 L 297.19323,82.462204 L 298.63323,81.238204 L 295.96923,81.238204 L 285.96123,71.230204 L 290.71323,71.230204 L 307.12923,87.574204 L 304.46523,89.734204 M 287.83323,81.238204 L 277.82523,71.230204 L 282.57723,71.230204 L 292.65723,81.238204 L 287.83323,81.238204 M 279.69723,81.238204 L 269.68923,71.230204 L 274.44123,71.230204 L 284.44923,81.238204 L 279.69723,81.238204 M 271.56123,81.238204 L 269.04123,78.718204 L 268.96923,73.894204 L 276.31323,81.238204 L 271.56123,81.238204 M 317.06523,109.8942 L 317.13723,106.0782 L 313.46523,102.4782 L 309.72123,102.4782 L 317.06523,109.8942 M 310.65723,111.6222 L 314.40123,111.6222 L 305.25723,102.4782 L 301.51323,102.4782 L 310.65723,111.6222 M 302.52123,111.6222 L 306.26523,111.6222 L 297.19323,102.5502 L 293.44923,102.5502 L 302.52123,111.6222 M 294.38523,111.6222 L 298.12923,111.6222 L 289.05723,102.5502 L 288.40923,102.5502 L 288.76923,102.2622 L 282.14523,95.638204 L 280.05723,97.366204 L 294.38523,111.6222 M 316.77723,77.062204 L 316.84923,73.318204 L 315.04923,71.518204 L 311.30523,71.518204 L 316.77723,77.062204 M 286.24923,111.6222 L 289.99323,111.6222 L 277.68123,99.310204 L 275.66523,101.0382 L 286.24923,111.6222 M 300.00123,92.974204 L 302.08923,91.246204 L 295.32123,84.478204 L 293.30523,86.206204 L 300.00123,92.974204 M 313.46523,81.886204 L 315.48123,80.230204 L 306.84123,71.518204 L 303.09723,71.590204 L 313.46523,81.886204 M 295.53723,96.646204 L 297.62523,94.918204 L 290.92923,88.222204 L 288.84123,89.950204 L 295.53723,96.646204 M 291.07323,100.3182 L 293.16123,98.590204 L 286.46523,91.894204 L 284.44923,93.622204 L 291.07323,100.3182 M 277.96923,111.6222 L 281.78523,111.6222 L 273.21723,103.0542 L 271.12923,104.7822 L 277.96923,111.6222 M 309.00123,85.558204 L 311.01723,83.902204 L 298.70523,71.590204 L 294.96123,71.590204 L 309.00123,85.558204 M 269.83323,111.6222 L 273.64923,111.6222 L 269.54523,107.5182 L 269.47323,111.2622 L 269.83323,111.6222 M 304.53723,89.230204 L 306.55323,87.574204 L 290.56923,71.590204 L 286.82523,71.590204 L 296.11323,80.878204 L 299.64123,80.878204 L 297.69723,82.462204 L 304.53723,89.230204 M 287.97723,80.878204 L 291.79323,80.878204 L 282.43323,71.590204 L 278.68923,71.590204 L 287.97723,80.878204 M 279.84123,80.878204 L 283.58523,80.878204 L 274.29723,71.590204 L 270.55323,71.590204 L 279.84123,80.878204 M 271.70523,80.878204 L 275.44923,80.878204 L 269.40123,74.830204 L 269.32923,78.574204 L 271.70523,80.878204 M 267.96123,168.68333 L 268.03323,168.68333 L 267.96123,155.79533 L 280.92123,151.33133 L 280.99323,138.87533 L 268.03323,134.26733 L 267.96123,121.45133 L 318.14523,139.95533 L 318.21723,150.03533 L 268.03323,168.68333 L 268.03323,168.68333 L 267.96123,168.68333 M 268.46523,168.17933 L 317.85723,149.74733 L 317.92923,140.24333 L 268.46523,121.95533 L 268.39323,133.97933 L 281.35323,138.58733 L 281.42523,151.61933 L 268.39323,156.08333 L 268.46523,168.17933 M 317.20923,143.91533 L 312.16923,138.87533 L 317.13723,140.74733 L 317.20923,143.91533 M 303.16923,154.42733 L 295.60923,146.86733 L 298.77723,145.64333 L 306.40923,153.27533 L 303.16923,154.42733 M 315.04923,150.03533 L 299.13723,134.12333 C 303.5052,135.75531 305.8332,136.57131 306.12123,136.57133 C 307.36919,137.53131 309.21719,139.30731 311.66523,141.89933 C 313.48919,143.8193 315.28919,145.7393 317.06523,147.65933 L 317.13723,149.24333 L 315.04923,150.03533 M 293.01723,147.08333 L 293.08923,147.08333 L 293.01723,143.05133 L 298.34523,145.06733 L 293.08923,147.08333 L 293.08923,147.08333 L 293.01723,147.08333 M 291.21723,158.81933 L 282.00123,149.60333 L 281.92923,145.21133 L 294.45723,157.66733 L 291.21723,158.81933 M 309.07323,152.26733 L 286.10523,129.29933 C 288.40921,130.11532 290.76121,130.97932 293.16123,131.89133 C 295.75321,134.05131 298.9932,137.17131 302.88123,141.25133 C 309.02519,147.6833 312.14519,150.9473 312.24123,151.04333 L 309.07323,152.26733 M 285.31323,161.05133 L 277.82523,153.56333 L 281.06523,152.41133 L 288.48123,159.89933 L 285.31323,161.05133 M 279.33723,163.28333 L 271.70523,155.65133 L 275.01723,154.49933 L 282.57723,162.05933 L 279.33723,163.28333 M 268.96923,167.09933 L 269.04123,167.09933 L 268.96923,164.79533 L 270.69723,166.52333 L 269.04123,167.09933 L 269.04123,167.09933 L 268.96923,167.09933 M 273.43323,165.44333 L 269.04123,161.05133 L 268.96923,156.65933 L 276.60123,164.29133 L 273.43323,165.44333 M 297.19323,156.65933 L 281.92923,141.39533 L 282.00123,138.15533 C 280.12922,138.05931 277.72922,136.78731 274.80123,134.33933 C 272.40123,132.37131 270.48123,130.40332 269.04123,128.43533 L 268.96923,124.11533 L 292.29723,147.37133 L 292.22523,148.09133 L 292.80123,147.87533 L 300.36123,155.43533 L 297.19323,156.65933 M 296.76123,143.69933 L 292.22523,141.97133 L 292.29723,143.55533 L 273.21723,124.47533 C 275.52123,125.33932 277.84922,126.22732 280.20123,127.13933 C 282.26522,128.81932 285.07322,131.50732 288.62523,135.20333 C 291.31321,138.03531 294.02521,140.86731 296.76123,143.69933 M 270.98523,134.26733 L 269.04123,133.54733 L 268.96923,132.32333 L 270.98523,134.26733 M 316.77723,143.05133 L 316.84923,140.96333 L 313.53723,139.81133 L 316.77723,143.05133 M 303.31323,153.99533 L 305.76123,153.13133 L 298.70523,146.07533 L 296.25723,147.01133 L 303.31323,153.99533 M 315.19323,149.60333 L 316.70523,149.02733 L 316.77723,147.80333 C 314.23319,145.0673 310.60919,141.44331 305.90523,136.93133 C 305.7612,136.78731 303.9852,136.13931 300.57723,134.98733 L 315.19323,149.60333 M 309.14523,151.83533 L 311.59323,150.89933 L 292.87323,132.17933 C 292.72921,132.03532 290.95321,131.38732 287.54523,130.23533 L 309.14523,151.83533 M 293.44923,146.50733 L 297.33723,145.06733 L 293.37723,143.55533 L 293.44923,146.50733 M 291.36123,158.45933 L 293.80923,157.52333 L 282.36123,146.07533 L 282.28923,149.38733 L 291.36123,158.45933 M 285.38523,160.61933 L 287.83323,159.75533 L 280.99323,152.84333 L 278.47323,153.70733 L 285.38523,160.61933 M 297.26523,156.22733 L 299.71323,155.29133 L 292.72923,148.30733 L 291.86523,148.59533 L 291.93723,147.51533 L 282.36123,137.93933 L 282.28923,141.25133 L 297.26523,156.22733 M 279.48123,162.85133 L 281.92923,161.91533 L 274.87323,154.93133 L 272.42523,155.79533 L 279.48123,162.85133 M 269.47323,166.59533 L 270.12123,166.37933 L 269.40123,165.65933 L 269.47323,166.59533 M 273.50523,165.08333 L 275.95323,164.14733 L 269.40123,157.52333 L 269.32923,160.90733 L 273.50523,165.08333 M 295.17723,142.69133 C 291.81721,139.04331 286.75322,133.93131 279.98523,127.35533 C 279.79322,127.16332 278.01722,126.51532 274.65723,125.41133 L 291.93723,142.69133 L 291.86523,141.46733 L 295.17723,142.69133 M 282.21723,137.79533 L 269.40123,124.97933 L 269.32923,128.36333 C 271.20123,130.37932 273.76923,132.89931 277.03323,135.92333 C 277.17722,136.06731 278.90522,136.69131 282.21723,137.79533 M 269.61723,133.40333 L 269.40123,133.18733 L 269.32923,133.33133 L 269.61723,133.40333 M 318.21723,218.70983 L 295.10523,193.79783 L 268.03323,218.34983 L 267.96123,201.78983 L 282.21723,189.04583 L 268.03323,189.04583 L 267.96123,176.08583 L 318.14523,176.08583 L 318.21723,189.04583 L 307.05723,189.04583 L 318.14523,201.14183 L 318.21723,218.70983 M 317.78523,217.77383 L 317.85723,201.28583 L 306.19323,188.68583 L 317.78523,188.68583 L 317.85723,176.44583 L 268.39323,176.44583 L 268.32123,188.68583 L 283.15323,188.68583 L 268.39323,202.00583 L 268.32123,217.48583 L 295.10523,193.29383 L 317.78523,217.77383 M 316.99323,180.90983 L 316.99323,180.83783 L 313.24923,177.16583 L 317.06523,177.16583 L 316.99323,180.83783 L 317.06523,180.90983 L 316.99323,180.90983 M 316.99323,205.38983 L 288.76923,177.16583 L 293.16123,177.09383 C 299.7852,183.62182 303.8412,187.62981 305.32923,189.11783 C 309.88919,193.72581 313.75319,197.8778 316.92123,201.57383 L 316.99323,205.38983 M 316.92123,213.52583 L 280.48923,177.16583 L 284.88123,177.16583 L 316.84923,209.13383 L 316.92123,213.52583 M 315.69723,187.96583 L 304.89723,177.16583 L 309.28923,177.16583 L 316.77723,184.72583 L 316.84923,187.96583 L 315.69723,187.96583 M 307.56123,187.96583 L 296.76123,177.16583 L 301.15323,177.16583 L 311.95323,187.96583 L 307.56123,187.96583 M 269.61723,215.18183 L 268.82523,214.38983 L 268.75323,209.99783 L 271.92123,213.09383 L 269.61723,215.18183 M 286.68123,199.70183 L 279.62523,192.57383 L 281.92923,190.48583 L 288.98523,197.61383 L 286.68123,199.70183 M 273.86523,211.29383 L 268.82523,206.18183 L 268.75323,202.29383 L 269.04123,202.07783 L 276.16923,209.20583 L 273.86523,211.29383 M 282.43323,203.51783 L 275.30523,196.46183 L 277.60923,194.37383 L 284.73723,201.50183 L 282.43323,203.51783 M 278.11323,207.40583 L 270.98523,200.27783 L 273.36123,198.26183 L 280.41723,205.31783 L 278.11323,207.40583 M 291.00123,195.81383 L 283.87323,188.75783 L 284.73723,187.96583 L 283.08123,187.96583 L 272.28123,177.16583 L 276.67323,177.16583 L 293.23323,193.72583 L 291.00123,195.81383 M 274.94523,187.96583 L 268.82523,181.77383 L 268.75323,177.38183 L 279.33723,187.96583 L 274.94523,187.96583 M 268.82523,187.96583 L 268.75323,185.51783 L 271.20123,187.96583 L 268.82523,187.96583 M 316.63323,180.04583 L 316.70523,177.52583 L 314.11323,177.52583 L 316.63323,180.04583 M 316.56123,204.52583 L 316.63323,201.71783 C 311.97719,196.29381 304.0812,188.22981 292.94523,177.52583 L 289.63323,177.52583 L 316.56123,204.52583 M 316.48923,212.66183 L 316.56123,209.27783 L 284.73723,177.52583 L 281.42523,177.52583 L 316.48923,212.66183 M 315.84123,187.60583 L 316.41723,187.60583 L 316.48923,184.86983 L 309.14523,177.52583 L 305.76123,177.52583 L 315.84123,187.60583 M 307.70523,187.60583 L 311.08923,187.60583 L 301.00923,177.52583 L 297.62523,177.52583 L 307.70523,187.60583 M 269.61723,214.67783 L 271.34523,213.09383 L 269.18523,210.86183 L 269.11323,214.24583 L 269.61723,214.67783 M 286.68123,199.19783 L 288.48123,197.61383 L 281.92923,190.98983 L 280.12923,192.57383 L 286.68123,199.19783 M 273.86523,210.78983 L 275.66523,209.20583 L 269.18523,202.72583 L 269.11323,206.03783 L 273.86523,210.78983 M 282.43323,203.08583 L 284.16123,201.42983 L 277.60923,194.87783 L 275.80923,196.46183 L 282.43323,203.08583 M 278.18523,206.90183 L 279.91323,205.31783 L 273.28923,198.69383 L 271.56123,200.34983 L 278.18523,206.90183 M 291.00123,195.30983 L 292.72923,193.72583 L 276.52923,177.52583 L 273.14523,177.52583 L 283.22523,187.60583 L 285.74523,187.60583 L 284.44923,188.75783 L 291.00123,195.30983 M 269.25723,187.60583 L 270.40923,187.60583 L 269.18523,186.38183 L 269.25723,187.60583 M 275.08923,187.60583 L 278.47323,187.60583 L 269.18523,178.24583 L 269.11323,181.62983 L 275.08923,187.60583 M 268.03323,258.31433 L 267.96123,224.47433 L 318.50523,224.47433 L 318.57723,258.31433 L 306.62523,258.31433 L 306.69723,236.57033 L 297.91323,236.57033 L 297.98523,253.99433 L 286.03323,253.99433 L 286.10523,236.57033 L 279.84123,236.49833 L 279.91323,258.31433 L 268.03323,258.31433 M 268.46523,257.95433 L 279.62523,257.95433 L 279.55323,236.13833 L 286.53723,236.21033 L 286.46523,253.63433 L 297.69723,253.63433 L 297.62523,236.21033 L 307.12923,236.21033 L 307.05723,257.95433 L 318.21723,257.95433 L 318.28923,224.83433 L 268.39323,224.83433 L 268.39323,225.55433 L 268.96923,225.55433 L 278.83323,235.41833 L 278.04123,235.41833 L 278.11323,239.01833 L 268.39323,229.29833 L 268.39323,233.04233 L 278.11323,242.76233 L 278.18523,247.15433 L 268.46523,237.43433 L 268.39323,234.26633 L 268.46523,257.95433 M 277.75323,246.29033 L 277.82523,242.90633 L 268.82523,233.90633 L 268.75323,237.29033 L 277.75323,246.29033 M 277.75323,238.15433 L 277.68123,235.05833 L 277.96923,235.05833 L 268.75323,225.91433 L 268.68123,229.15433 L 277.75323,238.15433 M 313.39323,257.23433 L 307.84923,251.69033 L 307.77723,247.29833 L 317.71323,257.23433 L 313.39323,257.23433 M 307.84923,257.23433 L 307.77723,255.43433 L 309.57723,257.23433 L 307.84923,257.23433 M 317.49723,253.27433 L 307.77723,243.55433 L 307.70523,239.16233 L 317.42523,248.88233 L 317.49723,253.27433 M 317.42523,228.79433 L 314.11323,225.55433 L 317.35323,225.55433 L 317.42523,228.79433 M 317.35323,236.93033 L 305.90523,225.55433 L 310.29723,225.55433 L 317.28123,232.53833 L 317.35323,236.93033 M 317.20923,245.06633 C 315.09719,242.95431 311.92919,239.76231 307.70523,235.49033 L 307.63323,235.49033 L 297.69723,225.55433 L 302.08923,225.55433 L 317.20923,240.74633 L 317.28123,245.06633 L 317.20923,245.06633 M 292.29723,252.91433 L 286.82523,247.37033 L 286.75323,242.97833 L 296.68923,252.91433 L 292.29723,252.91433 M 286.82523,252.91433 L 286.75323,251.11433 L 288.55323,252.91433 L 286.82523,252.91433 M 299.28123,235.49033 L 289.34523,225.55433 L 293.66523,225.55433 L 303.60123,235.49033 L 299.28123,235.49033 M 272.13723,257.23433 L 268.68123,253.70633 L 268.60923,249.38633 L 276.52923,257.23433 L 272.13723,257.23433 M 296.47323,248.95433 L 286.68123,239.23433 L 286.75323,235.49033 L 282.93723,235.41833 L 273.00123,225.55433 L 277.39323,225.55433 L 296.40123,244.56233 L 296.47323,248.95433 M 296.40123,240.81833 L 281.06523,225.55433 L 285.45723,225.55433 L 296.32923,236.42633 L 296.40123,240.81833 M 278.18523,255.29033 L 278.18523,255.21833 L 268.53723,245.57033 L 268.46523,241.17833 L 278.25723,250.89833 L 278.18523,255.21833 L 278.25723,255.29033 L 278.18523,255.29033 M 313.53723,256.87433 L 316.84923,256.87433 L 308.20923,248.16233 L 308.13723,251.54633 L 313.53723,256.87433 M 308.20923,256.87433 L 308.71323,256.87433 L 308.13723,256.29833 L 308.20923,256.87433 M 317.06523,252.33833 L 317.13723,249.02633 L 308.13723,240.02633 L 308.06523,243.33833 L 317.06523,252.33833 M 316.99323,227.93033 L 317.06523,225.91433 L 314.97723,225.91433 L 316.99323,227.93033 M 316.92123,236.06633 L 316.99323,232.75433 L 310.15323,225.91433 L 306.76923,225.91433 L 316.92123,236.06633 M 316.77723,244.20233 L 316.84923,240.89033 L 301.87323,225.91433 L 298.48923,225.91433 L 316.77723,244.20233 M 292.44123,252.55433 L 295.82523,252.55433 L 287.18523,243.84233 L 287.11323,247.22633 L 292.44123,252.55433 M 287.18523,252.55433 L 287.68923,252.55433 L 287.11323,251.97833 L 287.18523,252.55433 M 296.04123,248.09033 L 296.11323,244.70633 L 287.11323,235.70633 L 287.04123,239.09033 L 296.04123,248.09033 M 299.42523,235.13033 L 302.73723,235.13033 L 293.52123,225.91433 L 290.20923,225.91433 L 299.42523,235.13033 M 272.28123,256.87433 L 275.66523,256.87433 L 269.04123,250.25033 L 268.96923,253.56233 L 272.28123,256.87433 M 295.96923,239.95433 L 296.04123,236.57033 L 285.31323,225.91433 L 282.00123,225.91433 L 295.96923,239.95433 M 277.82523,254.42633 L 277.89723,251.04233 L 268.89723,242.04233 L 268.82523,245.42633 L 277.82523,254.42633 M 286.39323,235.13033 L 277.17723,225.91433 L 273.79323,225.91433 L 283.00923,235.05833 L 286.39323,235.13033 M 284.16123,296.1087 C 279.36122,296.10867 275.47323,294.74067 272.49723,292.0047 C 269.52123,289.26867 268.00923,285.52468 267.96123,280.7727 C 267.91323,276.11669 269.49723,272.20469 272.71323,269.0367 C 275.68923,266.1087 279.57722,264.2607 284.37723,263.4927 L 288.55323,275.4447 C 285.57722,275.44469 283.58522,275.63669 282.57723,276.0207 C 280.70522,276.69269 279.76922,278.25269 279.76923,280.7007 C 279.81722,283.48468 280.87322,284.87668 282.93723,284.8767 C 284.95322,284.87668 286.89722,283.24468 288.76923,279.9807 C 291.93721,274.46069 293.73721,271.43669 294.16923,270.9087 C 296.80921,267.6447 300.0972,266.0127 304.03323,266.0127 C 308.06519,266.0127 311.40119,267.4527 314.04123,270.3327 C 316.63319,273.06869 317.92918,276.47669 317.92923,280.5567 C 317.92918,284.39668 316.60919,287.58868 313.96923,290.1327 C 312.14519,291.90867 309.09719,293.75667 304.82523,295.6767 L 300.50523,284.9487 C 302.5692,283.94068 303.7212,283.38868 303.96123,283.2927 C 305.4492,282.38068 306.1932,281.44468 306.19323,280.4847 C 306.1932,279.71668 305.9772,279.02068 305.54523,278.3967 C 305.1132,277.77269 304.5132,277.43669 303.74523,277.3887 C 303.6972,277.48469 302.0412,280.17268 298.77723,285.4527 C 296.71321,288.81267 294.76921,291.28467 292.94523,292.8687 C 290.40121,295.02867 287.52121,296.10867 284.30523,296.1087 L 284.16123,296.1087 M 284.23323,295.7487 C 288.07321,295.74867 291.19321,294.52467 293.59323,292.0767 C 294.98521,290.68467 296.68921,288.04468 298.70523,284.1567 C 300.5772,280.60468 302.2572,278.22869 303.74523,277.0287 L 303.88923,277.0287 C 304.7532,277.02869 305.4012,277.36469 305.83323,278.0367 C 306.3132,278.75668 306.5532,279.54868 306.55323,280.4127 C 306.5052,281.90068 304.6572,283.46068 301.00923,285.0927 L 305.04123,295.1727 C 309.12119,293.30067 312.04919,291.50067 313.82523,289.7727 C 316.36919,287.27668 317.64118,284.18068 317.64123,280.4847 C 317.64118,276.50069 316.36919,273.14069 313.82523,270.4047 C 311.32919,267.6687 308.08919,266.3007 304.10523,266.3007 C 300.2652,266.3007 297.0492,267.9327 294.45723,271.1967 C 294.21721,271.48469 292.41721,274.53269 289.05723,280.3407 C 287.18521,283.60468 285.19322,285.23668 283.08123,285.2367 C 281.88122,285.23668 280.96922,284.78068 280.34523,283.8687 C 279.72122,283.00468 279.40922,281.94868 279.40923,280.7007 C 279.40922,278.25269 280.32122,276.62069 282.14523,275.8047 C 283.24922,275.32469 285.26522,275.08469 288.19323,275.0847 L 284.16123,263.9247 C 279.60122,264.6927 275.88123,266.5167 273.00123,269.3967 C 269.92923,272.46869 268.39323,276.23669 268.39323,280.7007 C 268.39323,285.40468 269.83323,289.07667 272.71323,291.7167 C 275.64123,294.40467 279.48122,295.74867 284.23323,295.7487 M 305.47323,294.2367 L 304.53723,292.0047 L 306.33723,293.8047 L 305.47323,294.2367 M 308.85723,292.5087 C 304.2492,287.90068 301.9452,285.57268 301.94523,285.5247 C 302.9532,285.04468 303.8652,284.54068 304.68123,284.0127 L 311.52123,290.7807 C 310.60919,291.45267 309.72119,292.02867 308.85723,292.5087 M 313.46523,289.1247 L 306.62523,282.3567 C 307.00919,281.78068 307.22519,281.15668 307.27323,280.4847 C 307.27319,279.86068 307.05719,279.02068 306.62523,277.9647 L 315.33723,286.6767 C 314.90519,287.49268 314.28119,288.30868 313.46523,289.1247 M 316.34523,283.9407 L 300.14523,267.6687 C 301.4412,267.2847 302.6892,267.0927 303.88923,267.0927 L 316.70523,279.9807 L 316.70523,280.5567 C 316.70519,281.61268 316.58519,282.74068 316.34523,283.9407 M 315.91323,275.3727 L 308.42523,267.8127 C 311.83319,269.25269 314.32919,271.77269 315.91323,275.3727 M 292.00923,292.2207 L 285.31323,285.5967 C 286.03322,285.16468 286.82522,284.46868 287.68923,283.5087 L 294.31323,290.0607 C 293.49721,290.97267 292.72921,291.69267 292.00923,292.2207 M 295.96923,288.0447 L 289.20123,281.2767 C 289.58521,280.70068 290.16121,279.78868 290.92923,278.5407 L 297.76923,285.4527 C 297.0012,286.60468 296.40121,287.46868 295.96923,288.0447 M 299.06523,283.0767 L 292.22523,276.1647 L 293.73723,273.3567 L 300.64923,280.2687 L 299.06523,283.0767 M 302.08923,277.8927 L 295.32123,271.1967 C 296.13721,270.28469 296.90521,269.56469 297.62523,269.0367 L 305.40123,276.8847 C 304.6332,276.59669 304.0572,276.45269 303.67323,276.4527 C 303.2412,276.45269 302.7132,276.93269 302.08923,277.8927 M 286.60923,294.9567 L 269.32923,277.6767 C 269.52123,276.57269 269.88123,275.46869 270.40923,274.3647 L 278.90523,282.8607 C 279.24122,284.44468 280.20122,285.40468 281.78523,285.7407 L 289.77723,293.7327 C 288.67321,294.26067 287.61721,294.66867 286.60923,294.9567 M 282.93723,295.1727 C 279.76922,294.88467 277.96922,294.52467 277.53723,294.0927 L 269.90523,286.4607 C 269.56923,286.12468 269.25723,284.37268 268.96923,281.2047 L 282.93723,295.1727 M 282.86523,274.9407 L 275.88123,267.9567 C 276.50523,267.5727 277.44122,267.0447 278.68923,266.3727 L 286.75323,274.5087 C 285.21722,274.50869 283.92122,274.65269 282.86523,274.9407 M 278.76123,279.0447 L 271.70523,271.9887 C 272.18523,271.26869 272.85723,270.47669 273.72123,269.6127 L 280.27323,276.2367 C 279.55322,276.86069 279.04922,277.79669 278.76123,279.0447 M 285.02523,269.0367 L 281.35323,265.3647 C 282.31322,265.0767 283.05722,264.9087 283.58523,264.8607 L 285.02523,269.0367 M 305.61723,293.7327 L 305.76123,293.7327 L 305.54523,293.5167 L 305.61723,293.7327 M 308.85723,292.1487 C 309.81719,291.57267 310.51319,291.11667 310.94523,290.7807 L 304.60923,284.4447 C 304.1292,284.73268 303.4092,285.11668 302.44923,285.5967 L 308.92923,292.0767 L 308.85723,292.1487 M 313.39323,288.5487 C 313.92119,288.06868 314.42519,287.46868 314.90523,286.7487 L 307.48923,279.2607 C 307.58519,279.83668 307.60919,280.24468 307.56123,280.4847 C 307.60919,281.10868 307.46519,281.70868 307.12923,282.2847 L 313.39323,288.5487 M 316.20123,283.2207 C 316.34519,282.35668 316.41719,281.49268 316.41723,280.6287 L 316.34523,280.1247 L 303.74523,267.4527 C 302.7372,267.4527 301.7532,267.5967 300.79323,267.8847 L 316.20123,283.2207 M 314.61723,273.5007 C 313.46519,271.53269 311.95319,270.02069 310.08123,268.9647 L 314.61723,273.5007 M 292.08123,291.7887 C 292.46521,291.45267 293.04121,290.90067 293.80923,290.1327 L 287.68923,284.0127 C 287.06521,284.68468 286.48922,285.21268 285.96123,285.5967 L 292.08123,291.7887 M 295.89723,287.3967 C 296.37721,286.82068 296.83321,286.17268 297.26523,285.4527 L 291.00123,279.1167 L 289.70523,281.2767 L 295.89723,287.3967 M 298.99323,282.5007 L 300.21723,280.3407 L 293.88123,273.9327 L 292.58523,276.0927 L 298.99323,282.5007 M 302.01723,277.3167 C 302.6892,276.40469 303.4092,276.02069 304.17723,276.1647 L 297.55323,269.4687 C 296.9772,269.90069 296.40121,270.45269 295.82523,271.1247 L 302.01723,277.3167 M 286.68123,294.5247 C 287.59321,294.28467 288.43321,293.99667 289.20123,293.6607 L 281.64123,286.1007 C 279.91322,285.66868 278.88122,284.63668 278.54523,283.0047 L 270.55323,274.9407 C 270.16923,275.90069 269.90523,276.78869 269.76123,277.6047 L 286.68123,294.5247 M 282.07323,294.7407 L 269.47323,282.1407 C 269.80923,284.68468 270.07323,286.05268 270.26523,286.2447 L 277.82523,293.7327 C 278.20922,294.11667 279.62522,294.45267 282.07323,294.7407 M 283.00923,274.5087 C 283.68122,274.36469 284.66522,274.24469 285.96123,274.1487 L 278.61723,266.8767 C 277.65722,267.3567 276.93722,267.7407 276.45723,268.0287 L 283.00923,274.5087 M 278.54523,278.3247 C 278.73722,277.55669 279.14522,276.83669 279.76923,276.1647 L 273.72123,270.1167 C 273.19323,270.69269 272.66523,271.29269 272.13723,271.9167 L 278.54523,278.3247 M 284.16123,267.6687 L 283.36923,265.2927 L 282.07323,265.5087 L 284.16123,267.6687"
279 + style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:none;fill-opacity:1;stroke:url(#linearGradient3281);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;font-family:Malabars Tryout;-inkscape-font-specification:Malabars Tryout" />
280 + <g
281 + transform="translate(-4.7251541,72.197657)"
282 + id="g3972">
283 + <path
284 + transform="matrix(0.5473468,0,0,0.5565847,134.61044,56.187104)"
285 + d="M 160.75191,61.069302 A 56.146683,55.214787 0 1 1 48.458549,61.069302 A 56.146683,55.214787 0 1 1 160.75191,61.069302 z"
286 + sodipodi:ry="55.214787"
287 + sodipodi:rx="56.146683"
288 + sodipodi:cy="61.069302"
289 + sodipodi:cx="104.60523"
290 + id="path3915"
291 + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.78393936;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
292 + sodipodi:type="arc" />
293 + <g
294 + id="g3930"
295 + transform="matrix(0.4966719,0,0,0.4966719,161.48334,59.089877)">
296 + <path
297 + id="path3943"
298 + d="M 60.510156,6.3979729 C 55.926503,6.4192712 51.549217,6.8101906 47.697656,7.4917229 C 36.35144,9.4962267 34.291407,13.691825 34.291406,21.429223 L 34.291406,31.647973 L 61.103906,31.647973 L 61.103906,35.054223 L 34.291406,35.054223 L 24.228906,35.054223 C 16.436447,35.054223 9.6131468,39.73794 7.4789058,48.647973 C 5.0170858,58.860939 4.9078907,65.233996 7.4789058,75.897973 C 9.3848341,83.835825 13.936449,89.491721 21.728906,89.491723 L 30.947656,89.491723 L 30.947656,77.241723 C 30.947656,68.391821 38.6048,60.585475 47.697656,60.585473 L 74.478906,60.585473 C 81.933857,60.585473 87.885159,54.447309 87.885156,46.960473 L 87.885156,21.429223 C 87.885156,14.162884 81.755176,8.7044455 74.478906,7.4917229 C 69.872919,6.7249976 65.093809,6.3766746 60.510156,6.3979729 z M 46.010156,14.616723 C 48.779703,14.616723 51.041406,16.915369 51.041406,19.741723 C 51.041404,22.558059 48.779703,24.835473 46.010156,24.835473 C 43.23068,24.835472 40.978906,22.558058 40.978906,19.741723 C 40.978905,16.91537 43.23068,14.616723 46.010156,14.616723 z"
299 + style="fill:url(#linearGradient3978);fill-opacity:1" />
300 + <path
301 + id="path3945"
302 + d="M 91.228906,35.054223 L 91.228906,46.960473 C 91.228906,56.191228 83.403011,63.960472 74.478906,63.960473 L 47.697656,63.960473 C 40.361823,63.960473 34.291407,70.238956 34.291406,77.585473 L 34.291406,103.11672 C 34.291406,110.38306 40.609994,114.65704 47.697656,116.74172 C 56.184987,119.23733 64.323893,119.68835 74.478906,116.74172 C 81.229061,114.78733 87.885159,110.85411 87.885156,103.11672 L 87.885156,92.897973 L 61.103906,92.897973 L 61.103906,89.491723 L 87.885156,89.491723 L 101.29141,89.491723 C 109.08387,89.491723 111.98766,84.056315 114.69765,75.897973 C 117.49698,67.499087 117.37787,59.422197 114.69765,48.647973 C 112.77187,40.890532 109.09378,35.054223 101.29141,35.054223 L 91.228906,35.054223 z M 76.166406,99.710473 C 78.945884,99.710476 81.197656,101.98789 81.197656,104.80422 C 81.197654,107.63057 78.945881,109.92922 76.166406,109.92922 C 73.396856,109.92922 71.135156,107.63057 71.135156,104.80422 C 71.135158,101.98789 73.396853,99.710473 76.166406,99.710473 z"
303 + style="fill:url(#linearGradient3980);fill-opacity:1" />
304 + </g>
305 + </g>
306 + </g>
307 + </g>
308 +</svg>
1 +import os, os.path
2 +
3 +snakes_version = open("VERSION").readline().strip()
4 +package_version = open("debian/VERSION").readline().strip()
5 +ppa_version = open("debian/PPA").readline().strip()
6 +changelog_version = open("debian/changelog").readline().split()[1].strip("()")
7 +distribs = [l.strip().split()[0] for l in open("debian/DISTRIB")]
8 +base_dir = os.getcwd()
9 +base_dist_dir = "dist"
10 +dput_sh = open("dput.sh", "w")
11 +
12 +def system (command) :
13 + print("*** %s" % command)
14 + retcode = os.system(command)
15 + if retcode != 0 :
16 + print("*** error return status (%s)" % retcode)
17 + sys.exit(retcode)
18 +
19 +def chdir (path) :
20 + print("*** cd %s" % path)
21 + os.chdir(path)
22 +
23 +def changelog (path, dist) :
24 + full_version = "%s-%s~ppa%s~%s1" % (snakes_version, package_version,
25 + ppa_version, dist)
26 + chdir(path)
27 + system("debchange -b --newversion %s --distribution %s 'see NEWS'"
28 + % (full_version, dist))
29 + chdir(base_dir)
30 +
31 +def build_package (dist_dir, dist) :
32 + full_version = "%s-%s~ppa%s~%s1" % (snakes_version, package_version,
33 + ppa_version, dist)
34 + deb_dir = os.path.join(dist_dir, "python-snakes_%s" % full_version)
35 + if not os.path.isdir(dist_dir) :
36 + print("*** make dir %r" % dist_dir)
37 + os.makedirs(dist_dir)
38 + if os.path.isdir(deb_dir) :
39 + system("rm -rf %s" % deb_dir)
40 + system("hg archive %s" % deb_dir)
41 + changelog(deb_dir, dist)
42 + system("sed -i -e 's/DATE/$(date -R)/' %s/debian/copyright" % deb_dir)
43 + system("sed -i -e 's/UNRELEASED/%s/' %s/debian/changelog" % (dist, deb_dir))
44 + chdir(deb_dir)
45 + system("make doc")
46 + system("dpkg-buildpackage")
47 + system("dpkg-buildpackage -S -sa")
48 + chdir(base_dir)
49 + dput_sh.write("dput lp %s_source.changes\n" % deb_dir)
50 +
51 +main_version = "%s-%s" % (snakes_version, package_version)
52 +if main_version != changelog_version :
53 + system("debchange --newversion %s --distribution UNRELEASED 'see NEWS'"
54 + % main_version)
55 + system("hg commit -m 'updated debian/changelog' debian/changelog")
56 +
57 +for dist in distribs :
58 + build_package(base_dist_dir, dist)
59 +dput_sh.close()
1 +import glob, os, os.path
2 +
3 +for src in glob.glob("snakes/lang/*/*.pgen") :
4 + tgt = os.path.join(os.path.dirname(src), "pgen.py")
5 + if not os.path.isfile(tgt) or os.path.getmtime(src) > os.path.getmtime(tgt) :
6 + print("python snakes/lang/pgen.py --output=%s %s" % (tgt, src))
7 + os.system("python snakes/lang/pgen.py --output=%s %s" % (tgt, src))
8 +
9 +for src in glob.glob("snakes/lang/*/*.asdl") :
10 + tgt = os.path.join(os.path.dirname(src), "asdl.py")
11 + if not os.path.isfile(tgt) or os.path.getmtime(src) > os.path.getmtime(tgt) :
12 + print("python snakes/lang/asdl.py --output=%s %s" % (tgt, src))
13 + os.system("python snakes/lang/asdl.py --output=%s %s" % (tgt, src))
1 +#!/usr/bin/env python
2 +
3 +import sys, os
4 +from distutils.core import setup
5 +
6 +def doc_files() :
7 + import os, os.path
8 + result = {}
9 + for root, dirs, files in os.walk("doc") :
10 + target_dir = os.path.join("share/doc/python-snakes",
11 + *root.split(os.sep)[1:])
12 + for name in files :
13 + if target_dir not in result :
14 + result[target_dir] = []
15 + result[target_dir].append(os.path.join(root, name))
16 + return list(result.items())
17 +
18 +if __name__ == "__main__" :
19 + print("Compiling Emacs files...")
20 + os.system("emacs -batch -f batch-byte-compile utils/abcd-mode.el")
21 + #
22 + setup(name="SNAKES",
23 + version=open("VERSION").read().strip(),
24 + description="SNAKES is the Net Algebra Kit for Editors and Simulators",
25 + long_description="""SNAKES is a general purpose Petri net Python
26 + library allowing to define and execute most classes of Petri
27 + nets. It features a plugin system to allow its extension. In
28 + particular, plugins are provided to implement the operations
29 + usually found in the PBC and M-nets family.""",
30 + author="Franck Pommereau",
31 + author_email="pommereau@univ-paris12.fr",
32 + maintainer="Franck Pommereau",
33 + maintainer_email="pommereau@univ-paris12.fr",
34 + url="http://lacl.univ-paris12.fr/pommereau/soft/snakes",
35 + scripts=["bin/abcd",
36 + "bin/snkc",
37 + "bin/snkd",
38 + ],
39 + packages=["snakes",
40 + "snakes.lang",
41 + "snakes.lang.pylib",
42 + "snakes.lang.python",
43 + "snakes.lang.abcd",
44 + "snakes.lang.ctlstar",
45 + "snakes.plugins",
46 + "snakes.utils",
47 + "snakes.utils.abcd",
48 + "snakes.utils.ctlstar",
49 + ],
50 + data_files=(doc_files()
51 + + [("share/emacs/site-lisp", ["utils/abcd-mode.el",
52 + "utils/abcd-mode.elc"])]),
53 + )
1 +"""SNAKES is the Net Algebra Kit for Editors and Simulators
2 +
3 +@author: Franck Pommereau
4 +@organization: University of Paris 12
5 +@copyright: (C) 2005 Franck Pommereau
6 +@license: GNU Lesser General Public Licence (aka. GNU LGPL),
7 + see the file C{doc/COPYING} in the distribution or visit U{the GNU
8 + web site<http://www.gnu.org/licenses/licenses.html#LGPL>}
9 +@contact: pommereau@univ-paris12.fr
10 +
11 +SNAKES is a Python library allowing to model all sorts of Petri nets
12 +and to execute them. It is very general as most Petri nets annotations
13 +can be arbitrary Python expressions while most values can be arbitrary
14 +Python objects.
15 +
16 +SNAKES can be further extended with plugins, several ones being
17 +already provided, in particular two plugins implement the Petri nets
18 +compositions defined for the Petri Box Calculus and its successors.
19 +"""
20 +
21 +version = "0.9.16"
22 +defaultencoding = "utf-8"
23 +
24 +class SnakesError (Exception) :
25 + "An error in SNAKES"
26 + pass
27 +
28 +class ConstraintError (SnakesError) :
29 + "Violation of a constraint"
30 + pass
31 +
32 +class NodeError (SnakesError) :
33 + "Error related to a place or a transition"
34 + pass
35 +
36 +class DomainError (SnakesError) :
37 + "Function applied out of its domain"
38 + pass
39 +
40 +class ModeError (SnakesError) :
41 + "The modes of a transition cannot be found"
42 + pass
43 +
44 +class PluginError (SnakesError) :
45 + "Error when adding a plugin"
46 + pass
47 +
48 +class UnificationError (SnakesError) :
49 + "Error while unifying parameters"
50 + pass
1 +"""Python 2 and 3 compatibility layer
2 +"""
3 +
4 +import sys
5 +
6 +try :
7 + xrange
8 +except NameError :
9 + xrange = range
10 +
11 +try :
12 + reduce
13 +except NameError :
14 + from functools import reduce
15 +
16 +try :
17 + import StringIO as io
18 +except ImportError :
19 + import io
20 +
21 +try :
22 + next
23 +except NameError :
24 + def next (obj) :
25 + return obj.next()
26 +
27 +PY3 = sys.version > "3"
1 +"""Basic data types and functions used in SNAKES"""
2 +
3 +import operator, inspect
4 +from snakes.compat import *
5 +from snakes import DomainError
6 +from snakes.hashables import hdict
7 +from snakes.pnml import Tree
8 +
9 +def cross (sets) :
10 + """Cross-product.
11 +
12 + >>> list(cross([[1, 2], [3, 4, 5]]))
13 + [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
14 + >>> list(cross([[1, 2], [3, 4, 5], [6, 7, 8, 9]]))
15 + [(1, 3, 6), (1, 3, 7), (1, 3, 8), (1, 3, 9), (1, 4, 6), (1, 4, 7),
16 + (1, 4, 8), (1, 4, 9), (1, 5, 6), (1, 5, 7), (1, 5, 8), (1, 5, 9),
17 + (2, 3, 6), (2, 3, 7), (2, 3, 8), (2, 3, 9), (2, 4, 6), (2, 4, 7),
18 + (2, 4, 8), (2, 4, 9), (2, 5, 6), (2, 5, 7), (2, 5, 8), (2, 5, 9)]
19 + >>> list(cross([[], [1]]))
20 + []
21 +
22 + @param sets: the sets of values to use
23 + @type sets: C{iterable(iterable(object))}
24 + @return: the C{list} of obtained tuples (lists are used to allow
25 + unhashable objects)
26 + @rtype: C{generator(tuple(object))}
27 + """
28 + if len(sets) == 0 :
29 + pass
30 + elif len(sets) == 1 :
31 + for item in sets[0] :
32 + yield (item,)
33 + else :
34 + for item in sets[0] :
35 + for others in cross(sets[1:]) :
36 + yield (item,) + others
37 +
38 +def iterate (value) :
39 + """Like Python's builtin C{iter} but consider strings as atomic.
40 +
41 + >>> list(iter([1, 2, 3]))
42 + [1, 2, 3]
43 + >>> list(iterate([1, 2, 3]))
44 + [1, 2, 3]
45 + >>> list(iter('foo'))
46 + ['f', 'o', 'o']
47 + >>> list(iterate('foo'))
48 + ['foo']
49 +
50 + @param value: any object
51 + @type value: C{object}
52 + @return: an iterator on the elements of C{value} if is is iterable
53 + and is not string, an iterator on the sole C{value} otherwise
54 + @rtype: C{generator}
55 + """
56 + if isinstance(value, str) :
57 + return iter([value])
58 + else :
59 + try :
60 + return iter(value)
61 + except TypeError :
62 + return iter([value])
63 +
64 +class WordSet (set) :
65 + """A set of words being able to generate fresh words."""
66 + def fresh (self, add=False, min=1, allowed="abcdefghijklmnopqrstuvwxyz",
67 + base="") :
68 + """Create a fresh word (ie, which is not in the set).
69 +
70 + >>> w = WordSet(['foo', 'bar'])
71 + >>> list(sorted(w))
72 + ['bar', 'foo']
73 + >>> w.fresh(True, 3)
74 + 'aaa'
75 + >>> list(sorted(w))
76 + ['aaa', 'bar', 'foo']
77 + >>> w.fresh(True, 3)
78 + 'baa'
79 + >>> list(sorted(w))
80 + ['aaa', 'baa', 'bar', 'foo']
81 +
82 + @param add: add the created word to the set if C{add=True}
83 + @type add: C{bool}
84 + @param min: minimal length of the new word
85 + @type min: C{int}
86 + @param allowed: characters allowed in the new word
87 + @type allowed: C{str}
88 + """
89 + if base :
90 + result = [base] + [allowed[0]] * max(0, min - len(base))
91 + if base in self :
92 + result.append(allowed[0])
93 + pos = len(result) - 1
94 + elif len(base) < min :
95 + pos = 1
96 + else :
97 + pos = 0
98 + else :
99 + result = [allowed[0]] * min
100 + pos = 0
101 + while "".join(result) in self :
102 + for c in allowed :
103 + try :
104 + result[pos] = c
105 + except IndexError :
106 + result.append(c)
107 + if "".join(result) not in self :
108 + break
109 + pos += 1
110 + if add :
111 + self.add("".join(result))
112 + return "".join(result)
113 +
114 +class MultiSet (hdict) :
115 + """Set with repetitions, ie, function from values to integers.
116 +
117 + MultiSets support various operations, in particular: addition
118 + (C{+}), substraction (C{-}), multiplication by a non negative
119 + integer (C{*k}), comparisons (C{<}, C{>}, etc.), length (C{len})"""
120 + def __init__ (self, values=[]) :
121 + """Initialise the multiset, adding values to it.
122 +
123 + >>> MultiSet([1, 2, 3, 1, 2])
124 + MultiSet([...])
125 + >>> MultiSet()
126 + MultiSet([])
127 +
128 + @param values: a single value or an iterable object holding
129 + values (strings are not iterated)
130 + @type values: any atomic object (C{str} included) or an
131 + iterable object
132 + """
133 + self.add(values)
134 + def copy (self) :
135 + """Copy a C{MultiSet}
136 +
137 + >>> MultiSet([1, 2, 3, 1, 2]).copy()
138 + MultiSet([...])
139 +
140 + @return: a copy of the multiset
141 + @rtype: C{MultiSet}
142 + """
143 + result = MultiSet()
144 + result.update(self)
145 + return result
146 + __pnmltag__ = "multiset"
147 + def __pnmldump__ (self) :
148 + """
149 + >>> MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__()
150 + <?xml version="1.0" encoding="utf-8"?>
151 + <pnml>
152 + <multiset>
153 + <item>
154 + <value>
155 + <object type="int">
156 + ...
157 + </object>
158 + </value>
159 + <multiplicity>
160 + ...
161 + </multiplicity>
162 + </item>
163 + <item>
164 + <value>
165 + <object type="int">
166 + ...
167 + </object>
168 + </value>
169 + <multiplicity>
170 + ...
171 + </multiplicity>
172 + </item>
173 + <item>
174 + <value>
175 + <object type="int">
176 + ...
177 + </object>
178 + </value>
179 + <multiplicity>
180 + ...
181 + </multiplicity>
182 + </item>
183 + <item>
184 + <value>
185 + <object type="int">
186 + ...
187 + </object>
188 + </value>
189 + <multiplicity>
190 + ...
191 + </multiplicity>
192 + </item>
193 + </multiset>
194 + </pnml>
195 + """
196 + nodes = []
197 + for value in hdict.__iter__(self) :
198 + nodes.append(Tree("item", None,
199 + Tree("value", None, Tree.from_obj(value)),
200 + Tree("multiplicity", str(self[value]))))
201 + return Tree(self.__pnmltag__, None, *nodes)
202 + @classmethod
203 + def __pnmlload__ (cls, tree) :
204 + """Load a multiset from its PNML representation
205 +
206 + >>> t = MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__()
207 + >>> MultiSet.__pnmlload__(t)
208 + MultiSet([...])
209 + """
210 + result = cls()
211 + for item in tree :
212 + times = int(item.child("multiplicity").data)
213 + value = item.child("value").child().to_obj()
214 + result._add(value, times)
215 + return result
216 + def _add (self, value, times=1) :
217 + """Add a single value C{times} times.
218 +
219 + @param value: the value to add
220 + @type value: any object
221 + @param times: the number of times that C{value} has to be
222 + added
223 + @type times: non negative C{int}
224 + """
225 + if times < 0 :
226 + raise ValueError("negative values are forbidden")
227 + try :
228 + self[value] += times
229 + except KeyError :
230 + self[value] = times
231 + def add (self, values, times=1) :
232 + """Add values to the multiset.
233 +
234 + >>> m = MultiSet()
235 + >>> m.add([1, 2, 2, 3], 2)
236 + >>> list(sorted(m.items()))
237 + [1, 1, 2, 2, 2, 2, 3, 3]
238 + >>> m.add(5, 3)
239 + >>> list(sorted(m.items()))
240 + [1, 1, 2, 2, 2, 2, 3, 3, 5, 5, 5]
241 +
242 + @param values: the values to add or a single value to add
243 + @type values: any atomic object (C{str} included) or an
244 + iterable object
245 + @param times: the number of times each value should be added
246 + @type times: non negative C{int}"""
247 + self.__mutable__()
248 + for value in iterate(values) :
249 + self._add(value, times)
250 + def _remove (self, value, times=1) :
251 + """Remove a single value C{times} times.
252 +
253 + @param value: the value to remove
254 + @type value: any object
255 + @param times: the number of times that C{value} has to be
256 + removed
257 + @type times: non negative C{int}
258 + """
259 + if times < 0 :
260 + raise ValueError("negative values are forbidden")
261 + if times > self.get(value, 0) :
262 + raise ValueError("not enough occurrences")
263 + self[value] -= times
264 + if self[value] == 0 :
265 + del self[value]
266 + def remove (self, values, times=1) :
267 + """Remove values to the multiset.
268 +
269 + >>> m = MultiSet()
270 + >>> m.add([1, 2, 2, 3], 2)
271 + >>> list(sorted(m.items()))
272 + [1, 1, 2, 2, 2, 2, 3, 3]
273 + >>> m.remove(2, 3)
274 + >>> list(sorted(m.items()))
275 + [1, 1, 2, 3, 3]
276 + >>> m.remove([1, 3], 2)
277 + >>> list(sorted(m.items()))
278 + [2]
279 +
280 + @param values: the values to remove or a single value to remove
281 + @type values: any atomic object (C{str} included) or an
282 + iterable object
283 + @param times: the number of times each value should be removed
284 + @type times: non negative C{int}"""
285 + self.__mutable__()
286 + for value in iterate(values) :
287 + self._remove(value, times)
288 + def __call__ (self, value) :
289 + """Number of occurrences of C{value}.
290 +
291 + >>> m = MultiSet([1, 1, 2, 3, 3, 3])
292 + >>> m(1), m(2), m(3), m(4)
293 + (2, 1, 3, 0)
294 +
295 + @param value: the value the count
296 + @type value: C{object}
297 + @rtype: C{int}
298 + """
299 + return self.get(value, 0)
300 + def __iter__ (self) :
301 + """Iterate over the values (with repetitions).
302 +
303 + Use C{MultiSet.keys} to ignore repetitions.
304 +
305 + >>> list(sorted(iter(MultiSet([1, 2, 3, 1, 2]))))
306 + [1, 1, 2, 2, 3]
307 +
308 + @return: an iterator on the elements
309 + @rtype: C{iterator}"""
310 + for value in dict.__iter__(self) :
311 + for count in range(self[value]) :
312 + yield value
313 + def items (self) :
314 + """
315 + Return the list of items with repetitions. The list without
316 + repetitions can be retrieved with the C{key} method.
317 +
318 + >>> m = MultiSet([1, 2, 2, 3])
319 + >>> list(sorted(m.items()))
320 + [1, 2, 2, 3]
321 + >>> list(sorted(m.keys()))
322 + [1, 2, 3]
323 +
324 + @return: list of items with repetitions
325 + @rtype: C{list}"""
326 + return list(iter(self))
327 + def __str__ (self) :
328 + """Return a simple string representation of the multiset
329 +
330 + >>> str(MultiSet([1, 2, 2, 3]))
331 + '{...}'
332 +
333 + @return: simple string representation of the multiset
334 + @rtype: C{str}
335 + """
336 + return "{%s}" % ", ".join(repr(x) for x in self)
337 + def __repr__ (self) :
338 + """Return a string representation of the multiset that is
339 + suitable for C{eval}
340 +
341 + >>> repr(MultiSet([1, 2, 2, 3]))
342 + 'MultiSet([...])'
343 +
344 + @return: precise string representation of the multiset
345 + @rtype: C{str}
346 + """
347 + return "MultiSet([%s])" % ", ".join(repr(x) for x in self)
348 + def __len__ (self) :
349 + """Return the number of elements, including repetitions.
350 +
351 + >>> len(MultiSet([1, 2] * 3))
352 + 6
353 +
354 + @rtype: C{int}
355 + """
356 + if self.size() == 0 :
357 + return 0
358 + else :
359 + return reduce(operator.add, self.values())
360 + def size (self) :
361 + """Return the number of elements, excluding repetitions.
362 +
363 + >>> MultiSet([1, 2] * 3).size()
364 + 2
365 +
366 + @rtype: C{int}
367 + """
368 + return dict.__len__(self)
369 + def __add__ (self, other) :
370 + """Adds two multisets.
371 +
372 + >>> MultiSet([1, 2, 3]) + MultiSet([2, 3, 4])
373 + MultiSet([...])
374 +
375 + @param other: the multiset to add
376 + @type other: C{MultiSet}
377 + @rtype: C{MultiSet}
378 + """
379 + result = self.copy()
380 + for value, times in dict.items(other) :
381 + result._add(value, times)
382 + return result
383 + def __sub__ (self, other) :
384 + """Substract two multisets.
385 +
386 + >>> MultiSet([1, 2, 3]) - MultiSet([2, 3])
387 + MultiSet([1])
388 + >>> MultiSet([1, 2, 3]) - MultiSet([2, 3, 4])
389 + Traceback (most recent call last):
390 + ...
391 + ValueError: not enough occurrences
392 +
393 + @param other: the multiset to substract
394 + @type other: C{MultiSet}
395 + @rtype: C{MultiSet}
396 + """
397 + result = self.copy()
398 + for value, times in dict.items(other) :
399 + result._remove(value, times)
400 + return result
401 + def __mul__ (self, other) :
402 + """Multiplication by a non-negative integer.
403 +
404 + >>> MultiSet([1, 2]) * 3
405 + MultiSet([...])
406 +
407 + @param other: the integer to multiply
408 + @type other: non-negative C{int}
409 + @rtype: C{MultiSet}
410 + """
411 + if other < 0 :
412 + raise ValueError("negative values are forbidden")
413 + elif other == 0 :
414 + return MultiSet()
415 + else :
416 + result = self.copy()
417 + for value in self.keys() :
418 + result[value] *= other
419 + return result
420 + __hash__ = hdict.__hash__
421 + def __eq__ (self, other) :
422 + """Test for equality.
423 +
424 + >>> MultiSet([1, 2, 3]*2) == MultiSet([1, 2, 3]*2)
425 + True
426 + >>> MultiSet([1, 2, 3]) == MultiSet([1, 2, 3, 3])
427 + False
428 +
429 + @param other: the multiset to compare with
430 + @type other: C{MultiSet}
431 + @rtype: C{bool}
432 + """
433 + if len(self) != len(other) :
434 + return False
435 + else :
436 + for val in self :
437 + try :
438 + if self[val] != other[val] :
439 + return False
440 + except (KeyError, TypeError) :
441 + return False
442 + return True
443 + def __ne__ (self, other) :
444 + """Test for difference.
445 +
446 + >>> MultiSet([1, 2, 3]*2) != MultiSet([1, 2, 3]*2)
447 + False
448 + >>> MultiSet([1, 2, 3]) != MultiSet([1, 2, 3, 3])
449 + True
450 +
451 + @param other: the multiset to compare with
452 + @type other: C{MultiSet}
453 + @rtype: C{bool}
454 + """
455 + return not(self == other)
456 + def __lt__ (self, other) :
457 + """Test for strict inclusion.
458 +
459 + >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 4])
460 + True
461 + >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 3])
462 + True
463 + >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3])
464 + False
465 + >>> MultiSet([1, 2, 3]) < MultiSet([1, 2])
466 + False
467 + >>> MultiSet([1, 2, 2]) < MultiSet([1, 2, 3, 4])
468 + False
469 +
470 + @param other: the multiset to compare with
471 + @type other: C{MultiSet}
472 + @rtype: C{bool}
473 + """
474 + if not set(self.keys()) <= set(other.keys()) :
475 + return False
476 + result = False
477 + for value, times in dict.items(self) :
478 + count = other(value)
479 + if times > count :
480 + return False
481 + elif times < count :
482 + result = True
483 + return result or (dict.__len__(self) < dict.__len__(other))
484 + def __le__ (self, other) :
485 + """Test for inclusion inclusion.
486 +
487 + >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 4])
488 + True
489 + >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 3])
490 + True
491 + >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3])
492 + True
493 + >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2])
494 + False
495 + >>> MultiSet([1, 2, 2]) <= MultiSet([1, 2, 3, 4])
496 + False
497 +
498 + @param other: the multiset to compare with
499 + @type other: C{MultiSet}
500 + @rtype: C{bool}
501 + """
502 + if not set(self.keys()) <= set(other.keys()) :
503 + return False
504 + for value, times in dict.items(self) :
505 + count = other(value)
506 + if times > count :
507 + return False
508 + return True
509 + def __gt__ (self, other) :
510 + """Test for strict inclusion.
511 +
512 + >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 3])
513 + True
514 + >>> MultiSet([1, 2, 3, 3]) > MultiSet([1, 2, 3])
515 + True
516 + >>> MultiSet([1, 2, 3]) > MultiSet([1, 2, 3])
517 + False
518 + >>> MultiSet([1, 2]) > MultiSet([1, 2, 3])
519 + False
520 + >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 2])
521 + False
522 +
523 + @param other: the multiset to compare with
524 + @type other: C{MultiSet}
525 + @rtype: C{bool}
526 + """
527 + return other.__lt__(self)
528 + def __ge__ (self, other) :
529 + """Test for inclusion.
530 +
531 + >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 3])
532 + True
533 + >>> MultiSet([1, 2, 3, 3]) >= MultiSet([1, 2, 3])
534 + True
535 + >>> MultiSet([1, 2, 3]) >= MultiSet([1, 2, 3])
536 + True
537 + >>> MultiSet([1, 2]) >= MultiSet([1, 2, 3])
538 + False
539 + >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 2])
540 + False
541 +
542 + @param other: the multiset to compare with
543 + @type other: C{MultiSet}
544 + @rtype: C{bool}
545 + """
546 + return other.__le__(self)
547 + def domain (self) :
548 + """Return the domain of the multiset
549 +
550 + >>> list(sorted((MultiSet([1, 2, 3, 4]) + MultiSet([1, 2, 3])).domain()))
551 + [1, 2, 3, 4]
552 +
553 + @return: the set of values in the domain
554 + @rtype: C{set}
555 + """
556 + return set(self.keys())
557 +
558 +class Substitution (object) :
559 + """Map names to values or names, equals the identity where not defined.
560 +
561 + Substitutions support the C{+} operation (union with consistency
562 + check between the two operands) and the C{*} operation which is
563 + the composition of functions (C{(f*g)(x)} is C{f(g(x))}).
564 +
565 + Several methods (eg, C{image}) return lists instead of sets, this
566 + avoids the restriction of having only hashable values in a
567 + substitution image.
568 + """
569 + def __init__ (self, *largs, **dargs) :
570 + """Initialise using a dictionnary as a mapping.
571 +
572 + The expected arguments are any ones acceptables for
573 + initializing a dictionnary.
574 +
575 + >>> Substitution()
576 + Substitution()
577 + >>> Substitution(x=1, y=2)
578 + Substitution(...)
579 + >>> Substitution([('x', 1), ('y', 2)])
580 + Substitution(...)
581 + >>> Substitution({'x': 1, 'y': 2})
582 + Substitution(...)
583 + """
584 + self._dict = dict(*largs, **dargs)
585 + def __hash__ (self) :
586 + """
587 + >>> hash(Substitution(x=1, y=2)) == hash(Substitution(y=2, x=1))
588 + True
589 + """
590 + # 153913524 = hash('snakes.data.Substitution')
591 + return reduce(operator.xor,
592 + (hash(i) for i in self._dict.items()),
593 + 153913524)
594 + def __eq__ (self, other) :
595 + """
596 + >>> Substitution(x=1, y=2) == Substitution(y=2, x=1)
597 + True
598 + >>> Substitution(x=1, y=2) == Substitution(y=1, x=1)
599 + False
600 + """
601 + try :
602 + return self._dict == other._dict
603 + except :
604 + return False
605 + def __ne__ (self, other) :
606 + """
607 + >>> Substitution(x=1, y=2) != Substitution(y=2, x=1)
608 + False
609 + >>> Substitution(x=1, y=2) != Substitution(y=1, x=1)
610 + True
611 + """
612 + return not self.__eq__(other)
613 + __pnmltag__ = "substitution"
614 + def __pnmldump__ (self) :
615 + """Dumps a substitution to a PNML tree
616 +
617 + >>> Substitution(x=1, y=2).__pnmldump__()
618 + <?xml version="1.0" encoding="utf-8"?>
619 + <pnml>
620 + <substitution>
621 + <item>
622 + <name>
623 + ...
624 + </name>
625 + <value>
626 + <object type="int">
627 + ...
628 + </object>
629 + </value>
630 + </item>
631 + <item>
632 + <name>
633 + ...
634 + </name>
635 + <value>
636 + <object type="int">
637 + ...
638 + </object>
639 + </value>
640 + </item>
641 + </substitution>
642 + </pnml>
643 +
644 + @return: PNML representation
645 + @rtype: C{snakes.pnml.Tree}
646 + """
647 + nodes = []
648 + for name, value in self._dict.items() :
649 + nodes.append(Tree("item", None,
650 + Tree("name", name),
651 + Tree("value", None,
652 + Tree.from_obj(value))))
653 + return Tree(self.__pnmltag__, None, *nodes)
654 + @classmethod
655 + def __pnmlload__ (cls, tree) :
656 + """Load a substitution from its PNML representation
657 +
658 + >>> t = Substitution(x=1, y=2).__pnmldump__()
659 + >>> Substitution.__pnmlload__(t)
660 + Substitution(...)
661 +
662 + @param tree: the PNML tree to load
663 + @type tree: C{snakes.pnml.Tree}
664 + @return: the substitution loaded
665 + @rtype: C{Substitution}
666 + """
667 + result = cls()
668 + for item in tree :
669 + name = item.child("name").data
670 + value = item.child("value").child().to_obj()
671 + result._dict[name] = value
672 + return result
673 + def items (self) :
674 + """Return the list of pairs (name, value).
675 +
676 + >>> Substitution(x=1, y=2).items()
677 + [('...', ...), ('...', ...)]
678 +
679 + @return: a list of pairs (name, value) for each mapped name
680 + @rtype: C{list}
681 + """
682 + return list(self._dict.items())
683 + def domain (self) :
684 + """Return the set of mapped names.
685 +
686 + >>> list(sorted(Substitution(x=1, y=2).domain()))
687 + ['x', 'y']
688 +
689 + @return: the set of mapped names
690 + @rtype: C{set}
691 + """
692 + return set(self._dict.keys())
693 + def image (self) :
694 + """Return the set of values associated to the names.
695 +
696 + >>> list(sorted(Substitution(x=1, y=2).image()))
697 + [1, 2]
698 +
699 + @return: the set of values associated to names
700 + @rtype: C{set}
701 + """
702 + return set(self._dict.values())
703 + def __contains__ (self, name) :
704 + """Test if a name is mapped.
705 +
706 + >>> 'x' in Substitution(x=1, y=2)
707 + True
708 + >>> 'z' in Substitution(x=1, y=2)
709 + False
710 +
711 + @param name: the name to test
712 + @type name: C{str} (usually)
713 + @return: a Boolean indicating whether this name is in the
714 + domain or not
715 + @rtype: C{bool}
716 + """
717 + return name in self._dict
718 + def __iter__ (self) :
719 + """Iterate over the mapped names.
720 +
721 + >>> list(sorted(iter(Substitution(x=1, y=2))))
722 + ['x', 'y']
723 +
724 + @return: an iterator over the domain of the substitution
725 + @rtype: C{generator}
726 + """
727 + return iter(self._dict)
728 + def __str__ (self) :
729 + """Return a compact string representation.
730 +
731 + >>> str(Substitution(x=1, y=2))
732 + '{... -> ..., ... -> ...}'
733 +
734 + @return: a simple string representation
735 + @rtype: C{str}
736 + """
737 + return "{%s}" % ", ".join(["%s -> %r" % (str(var), val)
738 + for var, val in self.items()])
739 + def __repr__ (self) :
740 + """Return a string representation suitable for C{eval}.
741 +
742 + >>> repr(Substitution(x=1, y=2))
743 + 'Substitution(...)'
744 +
745 + @return: a precise string representation
746 + @rtype: C{str}
747 + """
748 + return "%s(%s)" % (self.__class__.__name__,
749 + ", ".join(("%s=%s" % (str(var), repr(val))
750 + for var, val in self.items())))
751 + def dict (self) :
752 + """Return the mapping as a dictionnary.
753 +
754 + >>> Substitution(x=1, y=2).dict()
755 + {'...': ..., '...': ...}
756 +
757 + @return: a dictionnary that does the same mapping as the
758 + substitution
759 + @rtype: C{dict}
760 + """
761 + return self._dict.copy()
762 + def copy (self) :
763 + """Copy the mapping.
764 +
765 + >>> Substitution(x=1, y=2).copy()
766 + Substitution(...)
767 +
768 + @return: a copy of the substitution
769 + @rtype: C{Substitution}
770 + """
771 + return Substitution(self.dict())
772 + def __setitem__ (self, var, value) :
773 + """Assign an entry to the substitution
774 +
775 + >>> s = Substitution()
776 + >>> s['x'] = 42
777 + >>> s
778 + Substitution(x=42)
779 +
780 + @param var: the name of the variable
781 + @type var: C{str}
782 + @param value: the value to which C{var} is bound
783 + @type value: C{object}
784 + """
785 + self._dict[var] = value
786 + def __getitem__ (self, var) :
787 + """Return the mapped value.
788 +
789 + Fails with C{DomainError} if C{var} is not mapped.
790 +
791 + >>> s = Substitution(x=1, y=2)
792 + >>> s['x']
793 + 1
794 + >>> try : s['z']
795 + ... except DomainError : print(sys.exc_info()[1])
796 + unbound variable 'z'
797 +
798 + @param var: the name of the variable
799 + @type var: C{str} (usually)
800 + @return: the value that C{var} maps to
801 + @rtype: C{object}
802 + @raise DomainError: if C{var} does not belong to the domain
803 + """
804 + try :
805 + return self._dict[var]
806 + except KeyError :
807 + raise DomainError("unbound variable '%s'" % var)
808 + def __call__ (self, var) :
809 + """Return the mapped value.
810 +
811 + Never fails but return C{var} if it is not mapped.
812 +
813 + >>> s = Substitution(x=1, y=2)
814 + >>> s('x')
815 + 1
816 + >>> s('z')
817 + 'z'
818 +
819 + @param var: the name of the variable
820 + @type var: C{str} (usually)
821 + @return: the value that C{var} maps to or C{var} itself if it
822 + does not belong to the domain
823 + @rtype: C{object}
824 + """
825 + try :
826 + return self._dict[var]
827 + except KeyError :
828 + return var
829 + def __add__ (self, other) :
830 + """Add two substitution.
831 +
832 + Fails with C{DomainError} if the two substitutions map a same
833 + name to different values.
834 +
835 + >>> s = Substitution(x=1, y=2) + Substitution(y=2, z=3)
836 + >>> s('x'), s('y'), s('z')
837 + (1, 2, 3)
838 + >>> try : Substitution(x=1, y=2) + Substitution(y=4, z=3)
839 + ... except DomainError : print(sys.exc_info()[1])
840 + conflict on 'y'
841 +
842 + @param other: another substitution
843 + @type other: C{Substitution}
844 + @return: the union of the substitutions
845 + @rtype: C{Substitution}
846 + @raise DomainError: when one name is mapped to distinct values
847 + """
848 + for var in self :
849 + if var in other and (self[var] != other[var]) :
850 + raise DomainError("conflict on '%s'" % var)
851 + s = self.__class__(self.dict())
852 + s._dict.update(other.dict())
853 + return s
854 + def __mul__ (self, other) :
855 + """Compose two substitutions.
856 +
857 + The composition of f and g is such that (f*g)(x) = f(g(x)).
858 +
859 + >>> f = Substitution(a=1, d=3, y=5)
860 + >>> g = Substitution(b='d', c=2, e=4, y=6)
861 + >>> h = f*g
862 + >>> h('a'), h('b'), h('c'), h('d'), h('e'), h('y'), h('x')
863 + (1, 3, 2, 3, 4, 6, 'x')
864 +
865 + @param other: another substitution
866 + @type other: C{Substitution}
867 + @return: the composition of the substitutions
868 + @rtype: C{Substitution}
869 + """
870 + res = self.copy()
871 + for var in other :
872 + res._dict[var] = self(other(var))
873 + return res
874 +
875 +class Symbol (object) :
876 + """A symbol that may be used as a constant
877 + """
878 + def __init__ (self, name, export=True) :
879 + """
880 + If C{export} is C{True}, the created symbol is exported under
881 + its name. If C{export} is C{False}, no export is made.
882 + Finally, if C{export} is a string, it specifies the name of
883 + the exported symbol.
884 +
885 + @param name: the name (or value of the symbol)
886 + @type name: C{str}
887 + @param export: the name under which the symbol is exported
888 + @type export: C{str} or C{bool} or C{None}
889 +
890 + >>> Symbol('foo')
891 + Symbol('foo')
892 + >>> foo
893 + Symbol('foo')
894 + >>> Symbol('egg', 'spam')
895 + Symbol('egg', 'spam')
896 + >>> spam
897 + Symbol('egg', 'spam')
898 + >>> Symbol('bar', False)
899 + Symbol('bar', False)
900 + >>> bar
901 + Traceback (most recent call last):
902 + ...
903 + NameError: ...
904 +
905 + """
906 + self.name = name
907 + if export is True :
908 + export = name
909 + self._export = export
910 + if export :
911 + inspect.stack()[1][0].f_globals[export] = self
912 + __pnmltag__ = "symbol"
913 + def __pnmldump__ (self) :
914 + """
915 + >>> Symbol('egg', 'spam').__pnmldump__()
916 + <?xml version="1.0" encoding="utf-8"?>
917 + <pnml>
918 + <symbol name="egg">
919 + <object type="str">
920 + spam
921 + </object>
922 + </symbol>
923 + </pnml>
924 + >>> Symbol('foo').__pnmldump__()
925 + <?xml version="1.0" encoding="utf-8"?>
926 + <pnml>
927 + <symbol name="foo"/>
928 + </pnml>
929 + >>> Symbol('bar', False).__pnmldump__()
930 + <?xml version="1.0" encoding="utf-8"?>
931 + <pnml>
932 + <symbol name="bar">
933 + <object type="bool">
934 + False
935 + </object>
936 + </symbol>
937 + </pnml>
938 + """
939 + if self.name == self._export :
940 + children = []
941 + else :
942 + children = [Tree.from_obj(self._export)]
943 + return Tree(self.__pnmltag__, None, *children, **dict(name=self.name))
944 + @classmethod
945 + def __pnmlload__ (cls, tree) :
946 + """
947 + >>> Symbol.__pnmlload__(Symbol('foo', 'bar').__pnmldump__())
948 + Symbol('foo', 'bar')
949 + >>> Symbol.__pnmlload__(Symbol('foo').__pnmldump__())
950 + Symbol('foo')
951 + >>> Symbol.__pnmlload__(Symbol('foo', False).__pnmldump__())
952 + Symbol('foo', False)
953 + """
954 + name = tree["name"]
955 + try :
956 + export = tree.child().to_obj()
957 + except :
958 + export = name
959 + return cls(name, export)
960 + def __eq__ (self, other) :
961 + """
962 + >>> Symbol('foo', 'bar') == Symbol('foo')
963 + True
964 + >>> Symbol('egg') == Symbol('spam')
965 + False
966 + """
967 + try :
968 + return (self.__class__.__name__ == other.__class__.__name__
969 + and self.name == other.name)
970 + except AttributeError :
971 + return False
972 + def __ne__ (self, other) :
973 + """
974 + >>> Symbol('foo', 'bar') != Symbol('foo')
975 + False
976 + >>> Symbol('egg') != Symbol('spam')
977 + True
978 + """
979 + return not (self == other)
980 + def __hash__ (self) :
981 + """
982 + >>> hash(Symbol('foo', 'bar')) == hash(Symbol('foo'))
983 + True
984 + """
985 + return hash((self.__class__.__name__, self.name))
986 + def __str__ (self) :
987 + """
988 + >>> str(Symbol('foo'))
989 + 'foo'
990 + """
991 + return self.name
992 + def __repr__ (self) :
993 + """
994 + >>> Symbol('foo')
995 + Symbol('foo')
996 + >>> Symbol('egg', 'spam')
997 + Symbol('egg', 'spam')
998 + >>> Symbol('bar', False)
999 + Symbol('bar', False)
1000 + """
1001 + if self._export == self.name :
1002 + return "%s(%r)" % (self.__class__.__name__, self.name)
1003 + else :
1004 + return "%s(%r, %r)" % (self.__class__.__name__, self.name,
1005 + self._export)
1 +"""Hashable mutable objets.
2 +
3 +This module proposes hashable version of the mutable containers
4 +C{list}, C{dict} and C{set}, called respectively C{hlist}, C{hdict}
5 +and C{hset}. After one object has been hashed, it becomes not mutable
6 +and raises a ValueError if a method which changes the object (let call
7 +a I{mutation} such a method) is invoqued. The object can be then
8 +un-hashed by calling C{unhash} on it so that it becomes mutable again.
9 +Notice that this may cause troubles if the object is stored in a set
10 +or dict which uses its hashcode to locate it. Notice also that
11 +hashable containers cannot be hashed if they contain non hashable
12 +objects.
13 +
14 +>>> l = hlist(range(5))
15 +>>> l
16 +hlist([0, 1, 2, 3, 4])
17 +>>> _ = hash(l)
18 +>>> l.append(5)
19 +Traceback (most recent call last):
20 +...
21 +ValueError: hashed 'hlist' object is not mutable
22 +>>> unhash(l)
23 +>>> l.append(5)
24 +>>> l
25 +hlist([0, 1, 2, 3, 4, 5])
26 +
27 +Testing if a C{hlist} is in a C{set}, C{dict}, C{hset} or C{hdict}
28 +causes its hashing. If this is not desirable, you should either
29 +compensate with a call to C{unhash}, or test if a copy is in the set:
30 +
31 +>>> s = set()
32 +>>> s.add(list(range(4)))
33 +Traceback (most recent call last):
34 +...
35 +TypeError: ...
36 +>>> s.add(hlist(range(4)))
37 +>>> l = hlist(range(3))
38 +>>> l in s
39 +False
40 +>>> l.append(3)
41 +Traceback (most recent call last):
42 +...
43 +ValueError: hashed 'hlist' object is not mutable
44 +>>> unhash(l)
45 +>>> l.append(3)
46 +>>> l[:] in s
47 +True
48 +>>> l.append(4)
49 +"""
50 +
51 +import inspect
52 +from operator import xor
53 +from snakes.compat import *
54 +
55 +def unhash (obj) :
56 + """Make the object hashable again.
57 +
58 + >>> l = hlist(range(3))
59 + >>> _ = hash(l)
60 + >>> l.append(3)
61 + Traceback (most recent call last):
62 + ...
63 + ValueError: hashed 'hlist' object is not mutable
64 + >>> unhash(l)
65 + >>> l.append(3)
66 +
67 + @param obj: any object
68 + @type obj: C{object}
69 + """
70 + try :
71 + del obj._hash
72 + except :
73 + pass
74 +
75 +def hashable (cls) :
76 + """Wrap methods in a class in order to make it hashable.
77 + """
78 + classname, bases, classdict = cls.__name__, cls.__bases__, cls.__dict__
79 + for name, attr in classdict.items() :
80 + try :
81 + doc = inspect.getdoc(attr)
82 + if doc is None :
83 + attr.__doc__ = getattr(bases[0], name).__doc__
84 + else :
85 + attr.__doc__ = "\n".join([inspect.getdoc(getattr(bases[0],
86 + name)),
87 + doc])
88 + except :
89 + pass
90 + def __hash__ (self) :
91 + if not hasattr(self, "_hash") :
92 + self._hash = self.__hash__.HASH(self)
93 + return self._hash
94 + __hash__.HASH = classdict["__hash__"]
95 + __hash__.__doc__ = classdict["__hash__"].__doc__
96 + cls.__hash__ = __hash__
97 + def __mutable__ (self) :
98 + "Raise C{ValueError} if the %s has been hashed."
99 + if self.hashed() :
100 + raise ValueError("hashed '%s' object is not mutable" % classname)
101 + try :
102 + __mutable__.__doc__ = __mutable__.__doc__ % classname
103 + except :
104 + pass
105 + cls.__mutable__ = __mutable__
106 + def hashed (self) :
107 + "Return 'True' if the %s has been hashed, 'False' otherwise."
108 + return hasattr(self, "_hash")
109 + try :
110 + hashed.__doc__ = hashed.__doc__ % classname
111 + except :
112 + pass
113 + cls.hashed = hashed
114 + def mutable (self) :
115 + "Return 'True' if the %s is not hashed, 'False' otherwise."
116 + return not self.hashed()
117 + try :
118 + mutable.__doc__ = mutable.__doc__ % classname
119 + except :
120 + pass
121 + cls.mutable = mutable
122 + return cls
123 +
124 +class hlist (list) :
125 + """Hashable lists.
126 +
127 + >>> l = hlist(range(5))
128 + >>> l
129 + hlist([0, 1, 2, 3, 4])
130 + >>> l.append(5)
131 + >>> l
132 + hlist([0, 1, 2, 3, 4, 5])
133 + >>> _ = hash(l)
134 + >>> l.append(6)
135 + Traceback (most recent call last):
136 + ...
137 + ValueError: hashed 'hlist' object is not mutable
138 + >>> unhash(l)
139 + >>> l.append(6)
140 + """
141 + def __add__ (self, other) :
142 + return self.__class__(list.__add__(self, other))
143 + def __delitem__ (self, item) :
144 + self.__mutable__()
145 + list.__delitem__(self, item)
146 + def __delslice__ (self, *l, **d) :
147 + self.__mutable__()
148 + list.__delslice__(self, *l, **d)
149 + def __getslice__ (self, first, last) :
150 + return self.__class__(list.__getslice__(self, first, last))
151 + def __getitem__ (self, item) :
152 + ret = list.__getitem__(self, item)
153 + if ret.__class__ is list :
154 + return self.__class__(ret)
155 + return ret
156 + def __hash__ (self) :
157 + return hash(tuple(self))
158 + def __iadd__ (self, other) :
159 + return self.__class__(list.__iadd__(self, other))
160 + def __imul__ (self, n) :
161 + return self.__class__(list.__imul__(self, n))
162 + def __mul__ (self, n) :
163 + return self.__class__(list.__mul__(self, n))
164 + def __repr__ (self) :
165 + """
166 + >>> repr(hlist(range(3)))
167 + 'hlist([0, 1, 2])'
168 + """
169 + return "%s(%s)" % (self.__class__.__name__, list.__repr__(self))
170 + def __rmul__ (self, n) :
171 + return self._class__(list.__rmul__(self, n))
172 + def __setitem__ (self, index, item) :
173 + self.__mutable__()
174 + list.__setitem__(self, index, item)
175 + def __setslice__ (self, first, last, item) :
176 + self.__mutable__()
177 + list.__setslice__(self, first, last, item)
178 + def append (self, item) :
179 + self.__mutable__()
180 + list.append(self, item)
181 + def extend (self, iterable) :
182 + self.__mutable__()
183 + list.extend(self, iterable)
184 + def insert (self, index, item) :
185 + self.__mutable__()
186 + list.insert(self, index, item)
187 + def pop (self, index=-1) :
188 + self.__mutable__()
189 + return list.pop(self, index)
190 + def remove (self, item) :
191 + self.__mutable__()
192 + list.remove(self, item)
193 + def reverse (self) :
194 + self.__mutable__()
195 + list.reverse(self)
196 + def sort (self, cmp=None, key=None, reverse=False) :
197 + self.__mutable__()
198 + list.sort(self, cmp, key, reverse)
199 +
200 +hlist = hashable(hlist)
201 +
202 +class hdict (dict) :
203 + """Hashable dictionnaries.
204 +
205 + >>> l = hlist(range(5))
206 + >>> d = hdict([(l, 0)])
207 + >>> d
208 + hdict({hlist([0, 1, 2, 3, 4]): 0})
209 + >>> l in d
210 + True
211 + >>> [0, 1, 2, 3, 4] in d
212 + Traceback (most recent call last):
213 + ...
214 + TypeError: ...
215 + >>> hlist([0, 1, 2, 3, 4]) in d
216 + True
217 + >>> d[hlist([0, 1, 2, 3, 4])]
218 + 0
219 + >>> l.append(5)
220 + Traceback (most recent call last):
221 + ...
222 + ValueError: hashed 'hlist' object is not mutable
223 + >>> _ = hash(d)
224 + >>> d.pop(l) # any mutation would produce the same error
225 + Traceback (most recent call last):
226 + ...
227 + ValueError: hashed 'hdict' object is not mutable
228 + >>> unhash(d)
229 + >>> d.pop(l)
230 + 0
231 + """
232 + def __delitem__ (self, key) :
233 + self.__mutable__()
234 + dict.__delitem__(self, key)
235 + def __hash__ (self) :
236 + """
237 + >>> _ = hash(hdict(a=1, b=2))
238 + """
239 + # 252756382 = hash("snakes.hashables.hlist")
240 + return reduce(xor, (hash(i) for i in self.items()), 252756382)
241 + def __repr__ (self) :
242 + """
243 + >>> repr(hdict(a=1))
244 + "hdict({'a': 1})"
245 + """
246 + return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))
247 + def __setitem__ (self, key, item) :
248 + self.__mutable__()
249 + dict.__setitem__(self, key, item)
250 + def clear (self) :
251 + self.__mutable__()
252 + dict.clear(self)
253 + def copy (self) :
254 + return self.__class__(dict.copy(self))
255 + @classmethod
256 + def fromkeys (_class, *args) :
257 + return _class(dict.fromkeys(*args))
258 + def pop (self, *args) :
259 + self.__mutable__()
260 + return dict.pop(self, *args)
261 + def popitem (self) :
262 + self.__mutable__()
263 + return dict.popitem(self)
264 + def setdefault (self, key, item=None) :
265 + self.__mutable__()
266 + return dict.setdefault (self, key, item)
267 + def update (self, other, **more) :
268 + self.__mutable__()
269 + return dict.update(self, other, **more)
270 +
271 +hdict = hashable(hdict)
272 +
273 +class hset (set) :
274 + """Hashable sets.
275 +
276 + >>> s = hset()
277 + >>> l = hlist(range(5))
278 + >>> s.add(l)
279 + >>> s
280 + hset([hlist([0, 1, 2, 3, 4])])
281 + >>> l in s
282 + True
283 + >>> [0, 1, 2, 3, 4] in s
284 + Traceback (most recent call last):
285 + ...
286 + TypeError: ...
287 + >>> hlist([0, 1, 2, 3, 4]) in s
288 + True
289 + >>> l.append(5)
290 + Traceback (most recent call last):
291 + ...
292 + ValueError: hashed 'hlist' object is not mutable
293 + >>> _ = hash(s)
294 + >>> s.discard(l) # any mutation would produce the same error
295 + Traceback (most recent call last):
296 + ...
297 + ValueError: hashed 'hset' object is not mutable
298 + >>> unhash(s)
299 + >>> s.discard(l)
300 + """
301 + def __and__ (self, other) :
302 + return self.__class__(set.__and__(self, other))
303 + def __hash__ (self) :
304 + """
305 + >>> _ = hash(hset([1, 2, 3]))
306 + >>> _ = hash(hset(range(5)) - set([0, 4]))
307 + """
308 + # 196496309 = hash("snakes.hashables.hset")
309 + return reduce(xor, (hash(x) for x in self), 196496309)
310 + def __iand__ (self, other) :
311 + return self.__class__(set.__iand__(self, other))
312 + def __ior__ (self, other) :
313 + return self.__class__(set.__ior__(self, other))
314 + def __isub__ (self, other) :
315 + return self.__class__(set.__isub__(self, other))
316 + def __ixor__ (self, other) :
317 + return self.__class__(set.__ixor__(self, other))
318 + def __or__ (self, other) :
319 + return self.__class__(set.__or__(self, other))
320 + def __rand__ (self, other) :
321 + return self.__class__(set.__rand__(self, other))
322 + def __repr__ (self) :
323 + """
324 + >>> repr(hset([1]))
325 + 'hset([1])'
326 + """
327 + return "%s([%s])" % (self.__class__.__name__,
328 + set.__repr__(self)[6:-2])
329 + def __ror__ (self, other) :
330 + return self.__class__(set.__ror__(self, other))
331 + def __rsub__ (self, other) :
332 + return self.__class__(set.__rsub__(self, other))
333 + def __rxor__ (self, other) :
334 + return self.__class__(set.__rxor__(self, other))
335 + def __str__ (self) :
336 + return self.__class__.__name__ + "(" + set.__str__(self).split("(", 1)[1]
337 + def __sub__ (self, other) :
338 + return self.__class__(set.__sub__(self, other))
339 + def __xor__ (self, other) :
340 + return self.__class__(set.__xor__(self, other))
341 + def add (self, item) :
342 + self.__mutable__()
343 + set.add(self, item)
344 + def clear (self) :
345 + self.__mutable__()
346 + set.clear(self)
347 + def copy (self) :
348 + return self.__class__(set.copy(self))
349 + def difference (self, other) :
350 + return self.__class__(set.difference(self, other))
351 + def difference_update (self, other) :
352 + self.__mutable__()
353 + set.difference_update(self, other)
354 + def discard (self, item) :
355 + self.__mutable__()
356 + set.discard(self, item)
357 + def intersection (self, other) :
358 + return self._class__(set.intersection(self, other))
359 + def intersection_update (self, other) :
360 + self.__mutable__()
361 + set.intersection_update(self, other)
362 + def pop (self) :
363 + self.__mutable__()
364 + return set.pop(self)
365 + def remove (self, item) :
366 + self.__mutable__()
367 + set.remove(self, item)
368 + def symmetric_difference (self, other) :
369 + self.__mutable__()
370 + set.symmetric_difference(self, other)
371 + def symmetric_difference_update (self, other) :
372 + self.__mutable__()
373 + set.symmetric_difference_update(self, other)
374 + def union (self, other) :
375 + return self.__class__(set.union(self, other))
376 + def update (self, other) :
377 + self.__mutable__()
378 + set.update(self, other)
379 +
380 +hset = hashable(hset)
1 +import sys
2 +if sys.version_info[:2] in ((2, 6), (2, 7)) :
3 + import ast
4 +elif sys.version_info[0] == 3 :
5 + import ast
6 +elif hasattr(sys, "pypy_version_info") :
7 + import astpypy as ast
8 +elif hasattr(sys, "JYTHON_JAR") :
9 + import astjy25 as ast
10 +elif sys.version_info[:2] == (2, 5) :
11 + import astpy25 as ast
12 +else :
13 + raise NotImplementedError("unsupported Python version")
14 +
15 +sys.modules["snkast"] = ast
16 +
17 +from . import unparse as _unparse
18 +from snakes.compat import *
19 +
20 +class Names (ast.NodeVisitor) :
21 + def __init__ (self) :
22 + ast.NodeVisitor.__init__(self)
23 + self.names = set()
24 + def visit_Name (self, node) :
25 + self.names.add(node.id)
26 +
27 +def getvars (expr) :
28 + """
29 + >>> list(sorted(getvars('x+y<z')))
30 + ['x', 'y', 'z']
31 + >>> list(sorted(getvars('x+y<z+f(3,t)')))
32 + ['f', 't', 'x', 'y', 'z']
33 + """
34 + names = Names()
35 + names.visit(ast.parse(expr))
36 + return names.names - set(['None', 'True', 'False'])
37 +
38 +class Unparser(_unparse.Unparser) :
39 + boolops = {"And": 'and', "Or": 'or'}
40 + def _Interactive (self, tree) :
41 + for stmt in tree.body :
42 + self.dispatch(stmt)
43 + def _Expression (self, tree) :
44 + self.dispatch(tree.body)
45 + def _ClassDef(self, tree):
46 + self.write("\n")
47 + for deco in tree.decorator_list:
48 + self.fill("@")
49 + self.dispatch(deco)
50 + self.fill("class "+tree.name)
51 + if tree.bases:
52 + self.write("(")
53 + for a in tree.bases:
54 + self.dispatch(a)
55 + self.write(", ")
56 + self.write(")")
57 + self.enter()
58 + self.dispatch(tree.body)
59 + self.leave()
60 +
61 +def unparse (st) :
62 + output = io.StringIO()
63 + Unparser(st, output)
64 + return output.getvalue().strip()
65 +
66 +class Renamer (ast.NodeTransformer) :
67 + def __init__ (self, map_names) :
68 + ast.NodeTransformer.__init__(self)
69 + self.map = [map_names]
70 + def visit_ListComp (self, node) :
71 + bind = self.map[-1].copy()
72 + for comp in node.generators :
73 + for name in getvars(comp.target) :
74 + if name in bind :
75 + del bind[name]
76 + self.map.append(bind)
77 + node.elt = self.visit(node.elt)
78 + self.map.pop(-1)
79 + return node
80 + def visit_SetComp (self, node) :
81 + return self.visit_ListComp(node)
82 + def visit_DictComp (self, node) :
83 + bind = self.map[-1].copy()
84 + for comp in node.generators :
85 + for name in getvars(comp.target) :
86 + if name in bind :
87 + del bind[name]
88 + self.map.append(bind)
89 + node.key = self.visit(node.key)
90 + node.value = self.visit(node.value)
91 + self.map.pop(-1)
92 + return node
93 + def visit_Name (self, node) :
94 + return ast.copy_location(ast.Name(id=self.map[-1].get(node.id,
95 + node.id),
96 + ctx=ast.Load()), node)
97 +
98 +def rename (expr, map={}, **ren) :
99 + """
100 + >>> rename('x+y<z', x='t')
101 + '((t + y) < z)'
102 + >>> rename('x+y<z+f(3,t)', f='g', t='z', z='t')
103 + '((x + y) < (t + g(3, z)))'
104 + >>> rename('[x+y for x in range(3)]', x='z')
105 + '[(x + y) for x in range(3)]'
106 + >>> rename('[x+y for x in range(3)]', y='z')
107 + '[(x + z) for x in range(3)]'
108 + """
109 + map_names = dict(map)
110 + map_names.update(ren)
111 + transf = Renamer(map_names)
112 + return unparse(transf.visit(ast.parse(expr)))
113 +
114 +class Binder (Renamer) :
115 + def visit_Name (self, node) :
116 + if node.id in self.map[-1] :
117 + return self.map[-1][node.id]
118 + else :
119 + return node
120 +
121 +def bind (expr, map={}, **ren) :
122 + """
123 + >>> bind('x+y<z', x=ast.Num(n=2))
124 + '((2 + y) < z)'
125 + >>> bind('x+y<z', y=ast.Num(n=2))
126 + '((x + 2) < z)'
127 + >>> bind('[x+y for x in range(3)]', x=ast.Num(n=2))
128 + '[(x + y) for x in range(3)]'
129 + >>> bind('[x+y for x in range(3)]', y=ast.Num(n=2))
130 + '[(x + 2) for x in range(3)]'
131 + """
132 + map_names = dict(map)
133 + map_names.update(ren)
134 + transf = Binder(map_names)
135 + return unparse(transf.visit(ast.parse(expr)))
136 +
137 +if __name__ == "__main__" :
138 + import doctest
139 + doctest.testmod()
1 +module ABCD version "$Revision: 1 $"
2 +{
3 + abcd = AbcdSpec(decl* context, process body, expr* asserts)
4 +
5 + decl = AbcdTypedef(identifier name, abcdtype type)
6 + | AbcdBuffer(identifier name, abcdtype type,
7 + Slice? capacity, expr content)
8 + | AbcdSymbol(identifier* symbols)
9 + | AbcdConst(identifier name, expr value)
10 + | AbcdNet(identifier name, arguments args, AbcdSpec body)
11 + | AbcdTask(identifier name, AbcdSpec body,
12 + abcdtype* input, ancdtype* output)
13 + | stmt -- this too much, but does not harm
14 +
15 + attributes (int lineno, int col_offset)
16 +
17 + process = AbcdAction(access* accesses, object guard)
18 + | AbcdFlowOp(process left, flowop op, process right)
19 + | AbcdInstance(identifier net, identifier? asname, expr* args,
20 + keyword* keywords, expr? starargs, expr? kwargs)
21 +
22 + attributes (int lineno, int col_offset)
23 +
24 + flowop = Sequence | Choice | Parallel | Loop
25 +
26 + access = SimpleAccess(identifier buffer, arc arc, expr tokens)
27 + | FlushAccess(identifier buffer, identifier target)
28 + | SwapAccess(identifier buffer, expr target, expr tokens)
29 + | Spawn(identifier net, expr pid, expr args)
30 + | Wait(identifier net, expr pid, expr args)
31 + | Suspend(identifier net, expr pid)
32 + | Resume(identifier net, expr pid)
33 +
34 + attributes (int lineno, int col_offset)
35 +
36 + arc = Produce | Test | Consume | Fill
37 +
38 + abcdtype = UnionType(abcdtype* types)
39 + | IntersectionType(abcdtype* types)
40 + | CrossType(abcdtype* types)
41 + | ListType(abcdtype items)
42 + | TupleType(abcdtype items)
43 + | SetType(abcdtype items)
44 + | DictType(abcdtype keys, abcdtype values)
45 + | EnumType(expr* items)
46 + | NamedType(identifier name)
47 +
48 + attributes (int lineno, int col_offset)
49 +
50 + --------------------------------------------------------------
51 + -- the rest is copied from "snakes/lang/python/python.asdl" --
52 + --------------------------------------------------------------
53 +
54 + stmt = FunctionDef(identifier name, arguments args,
55 + stmt* body, expr* decorator_list, expr? returns)
56 + | ClassDef(identifier name,
57 + expr* bases,
58 + keyword* keywords,
59 + expr? starargs,
60 + expr? kwargs,
61 + stmt* body,
62 + expr *decorator_list)
63 + | Return(expr? value)
64 +
65 + | Delete(expr* targets)
66 + | Assign(expr* targets, expr value)
67 + | AugAssign(expr target, operator op, expr value)
68 +
69 + | For(expr target, expr iter, stmt* body, stmt* orelse)
70 + | While(expr test, stmt* body, stmt* orelse)
71 + | If(expr test, stmt* body, stmt* orelse)
72 + | With(expr context_expr, expr? optional_vars, stmt* body)
73 +
74 + | Raise(expr? exc, expr? cause)
75 + | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
76 + | TryFinally(stmt* body, stmt* finalbody)
77 + | Assert(expr test, expr? msg)
78 +
79 + | Import(alias* names)
80 + | ImportFrom(identifier module, alias* names, int? level)
81 +
82 + | Exec(expr body, expr? globals, expr? locals)
83 +
84 + | Global(identifier* names)
85 + | Nonlocal(identifier* names)
86 + | Expr(expr value)
87 + | Pass | Break | Continue
88 +
89 + attributes (int lineno, int col_offset)
90 +
91 + expr = BoolOp(boolop op, expr* values)
92 + | BinOp(expr left, operator op, expr right)
93 + | UnaryOp(unaryop op, expr operand)
94 + | Lambda(arguments args, expr body)
95 + | IfExp(expr test, expr body, expr orelse)
96 + | Dict(expr* keys, expr* values)
97 + | Set(expr* elts)
98 + | ListComp(expr elt, comprehension* generators)
99 + | SetComp(expr elt, comprehension* generators)
100 + | DictComp(expr key, expr value, comprehension* generators)
101 + | GeneratorExp(expr elt, comprehension* generators)
102 + | Yield(expr? value)
103 + | Compare(expr left, cmpop* ops, expr* comparators)
104 + | Call(expr func, expr* args, keyword* keywords,
105 + expr? starargs, expr? kwargs)
106 + | Num(object n)
107 + | Str(string s)
108 + | Ellipsis
109 +
110 + | Attribute(expr value, identifier attr, expr_context ctx)
111 + | Subscript(expr value, slice slice, expr_context ctx)
112 + | Starred(expr value, expr_context ctx)
113 + | Name(identifier id, expr_context ctx)
114 + | List(expr* elts, expr_context ctx)
115 + | Tuple(expr* elts, expr_context ctx)
116 +
117 + attributes (int lineno, int col_offset)
118 +
119 + expr_context = Load | Store | Del | AugLoad | AugStore | Param
120 +
121 + slice = Slice(expr? lower, expr? upper, expr? step)
122 + | ExtSlice(slice* dims)
123 + | Index(expr value)
124 +
125 + boolop = And | Or
126 +
127 + operator = Add | Sub | Mult | Div | Mod | Pow | LShift
128 + | RShift | BitOr | BitXor | BitAnd | FloorDiv
129 +
130 + unaryop = Invert | Not | UAdd | USub
131 +
132 + cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
133 +
134 + comprehension = (expr target, expr iter, expr* ifs)
135 +
136 + excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
137 + attributes (int lineno, int col_offset)
138 +
139 + arguments = (arg* args, identifier? vararg, expr? varargannotation,
140 + arg* kwonlyargs, identifier? kwarg,
141 + expr? kwargannotation, expr* defaults,
142 + expr* kw_defaults)
143 + arg = (identifier arg, expr? annotation)
144 +
145 + keyword = (identifier arg, expr value)
146 +
147 + alias = (identifier name, identifier? asname)
148 +}
1 +# Grammar for ABCD
2 +
3 +# new tokens
4 +$QUESTION '?'
5 +$ELLIPSIS '...'
6 +
7 +file_input: abcd_main ENDMARKER
8 +
9 +abcd_main: (NEWLINE | abcd_global)* abcd_expr abcd_prop*
10 +abcd_global: import_stmt | abcd_symbol | abcd_typedef | abcd_const | abcd_decl
11 +abcd_spec: (NEWLINE | abcd_decl)* abcd_expr
12 +abcd_decl: abcd_net | abcd_task | abcd_buffer
13 +
14 +abcd_const: 'const' NAME '=' testlist
15 +abcd_symbol: 'symbol' abcd_namelist
16 +abcd_typedef: 'typedef' NAME ':' abcd_type
17 +abcd_net: 'net' NAME parameters ':' abcd_suite
18 +abcd_task: 'task' NAME typelist '-' '>' typelist ':' abcd_suite
19 +abcd_suite: abcd_expr | NEWLINE INDENT abcd_spec DEDENT
20 +abcd_buffer: [ decorators ] 'buffer' NAME ['[' test ']'] ':' abcd_type '=' testlist
21 +
22 +abcd_namelist: NAME (',' NAME)*
23 +typelist: '(' [abcd_type (',' abcd_type)*] ')'
24 +
25 +abcd_type: abcd_and_type ('|' abcd_and_type)*
26 +abcd_and_type: abcd_cross_type ('&' abcd_cross_type)*
27 +abcd_cross_type: abcd_base_type ('*' abcd_base_type)*
28 +abcd_base_type: (NAME ['(' abcd_type (',' abcd_type)* ')']
29 + | 'enum' '(' test (',' test)* ')' | '(' abcd_type ')')
30 +
31 +abcd_expr: abcd_choice_expr ('|' abcd_choice_expr)*
32 +abcd_choice_expr: abcd_iter_expr ('+' abcd_iter_expr)*
33 +abcd_iter_expr: abcd_seq_expr ('*' abcd_seq_expr)*
34 +abcd_seq_expr: abcd_base_expr (';' abcd_base_expr)*
35 +abcd_base_expr: (abcd_action | '(' abcd_expr ')') (NEWLINE)*
36 +abcd_action: ('[' 'True' ']' |
37 + '[' 'False' ']' |
38 + '[' abcd_access_list ['if' test] ']' |
39 + abcd_instance)
40 +abcd_access_list: abcd_access (',' abcd_access)*
41 +abcd_access: (NAME '+' '(' testlist ')' |
42 + NAME '?' '(' testlist ')' |
43 + NAME '-' '(' testlist ')' |
44 + NAME '<>' '(' testlist '=' testlist ')' |
45 + NAME '>>' '(' NAME ')' |
46 + NAME '<<' '(' testlist_comp ')' |
47 + NAME '.' NAME '(' test (',' test)* ')')
48 +abcd_instance: [NAME ':' ':'] NAME '(' [arglist] ')'
49 +
50 +tfpdef: NAME [':' ('net' | 'buffer' | 'task')]
51 +
52 +abcd_prop: 'assert' test (NEWLINE)*
53 +
54 +#
55 +# the rest is from SNAKES/Python grammar
56 +#
57 +
58 +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
59 +decorators: decorator+
60 +decorated: decorators (classdef | funcdef)
61 +funcdef: 'def' NAME parameters ['-' '>' test] ':' suite
62 +parameters: '(' [typedargslist] ')'
63 +typedargslist: ((tfpdef ['=' test] ',')*
64 + ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef]
65 + | '**' tfpdef)
66 + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
67 +varargslist: ((vfpdef ['=' test] ',')*
68 + ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef]
69 + | '**' vfpdef)
70 + | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
71 +vfpdef: NAME
72 +
73 +stmt: simple_stmt | compound_stmt
74 +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
75 +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
76 + import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
77 +expr_stmt: testlist (augassign (yield_expr|testlist) |
78 + ('=' (yield_expr|testlist))*)
79 +augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
80 + '<<=' | '>>=' | '**=' | '//=')
81 +del_stmt: 'del' exprlist
82 +pass_stmt: 'pass'
83 +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
84 +break_stmt: 'break'
85 +continue_stmt: 'continue'
86 +return_stmt: 'return' [testlist]
87 +yield_stmt: yield_expr
88 +raise_stmt: 'raise' [test ['from' test]]
89 +import_stmt: import_name | import_from
90 +import_name: 'import' dotted_as_names
91 +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
92 + 'import' ('*' | '(' import_as_names ')' | import_as_names))
93 +import_as_name: NAME ['as' NAME]
94 +dotted_as_name: dotted_name ['as' NAME]
95 +import_as_names: import_as_name (',' import_as_name)* [',']
96 +dotted_as_names: dotted_as_name (',' dotted_as_name)*
97 +dotted_name: NAME ('.' NAME)*
98 +global_stmt: 'global' NAME (',' NAME)*
99 +nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
100 +assert_stmt: 'assert' test [',' test]
101 +
102 +compound_stmt: (if_stmt | while_stmt | for_stmt | try_stmt | with_stmt
103 + | funcdef | classdef | decorated)
104 +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
105 +while_stmt: 'while' test ':' suite ['else' ':' suite]
106 +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
107 +try_stmt: ('try' ':' suite
108 + ((except_clause ':' suite)+
109 + ['else' ':' suite]
110 + ['finally' ':' suite] |
111 + 'finally' ':' suite))
112 +with_stmt: 'with' test [ with_var ] ':' suite
113 +with_var: 'as' expr
114 +except_clause: 'except' [test ['as' NAME]]
115 +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
116 +
117 +test: or_test ['if' or_test 'else' test] | lambdef
118 +test_nocond: or_test | lambdef_nocond
119 +lambdef: 'lambda' [varargslist] ':' test
120 +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
121 +or_test: and_test ('or' and_test)*
122 +and_test: not_test ('and' not_test)*
123 +not_test: 'not' not_test | comparison
124 +comparison: star_expr (comp_op star_expr)*
125 +comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'<>'|'in'|'not' 'in'|'is'|'is' 'not'
126 +star_expr: ['*'] expr
127 +expr: xor_expr ('|' xor_expr)*
128 +xor_expr: and_expr ('^' and_expr)*
129 +and_expr: shift_expr ('&' shift_expr)*
130 +shift_expr: arith_expr (('<<'|'>>') arith_expr)*
131 +arith_expr: term (('+'|'-') term)*
132 +term: factor (('*'|'/'|'%'|'//') factor)*
133 +factor: ('+'|'-'|'~') factor | power
134 +power: atom trailer* ['**' factor]
135 +atom: ('(' [yield_expr|testlist_comp] ')' |
136 + '[' [testlist_comp] ']' |
137 + '{' [dictorsetmaker] '}' |
138 + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
139 +testlist_comp: test ( comp_for | (',' test)* [','] )
140 +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
141 +subscriptlist: subscript (',' subscript)* [',']
142 +subscript: test | [test] ':' [test] [sliceop]
143 +sliceop: ':' [test]
144 +exprlist: star_expr (',' star_expr)* [',']
145 +testlist: test (',' test)* [',']
146 +dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
147 + (test (comp_for | (',' test)* [','])) )
148 +
149 +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
150 +
151 +arglist: (argument ',')* (argument [',']
152 + |'*' test (',' argument)* [',' '**' test]
153 + |'**' test)
154 +argument: test [comp_for] | test '=' test
155 +
156 +comp_iter: comp_for | comp_if
157 +comp_for: 'for' exprlist 'in' or_test [comp_iter]
158 +comp_if: 'if' test_nocond [comp_iter]
159 +
160 +yield_expr: 'yield' [testlist]
1 +# this file has been automatically generated running:
2 +# snakes/lang/asdl.py --output=snakes/lang/abcd/asdl.py snakes/lang/abcd/abcd.asdl
3 +# timestamp: 2011-09-29 11:07:13.505687
4 +
5 +from snakes.lang import ast
6 +from ast import *
7 +
8 +class _AST (ast.AST):
9 + def __init__ (self, **ARGS):
10 + ast.AST.__init__(self)
11 + for k, v in ARGS.items():
12 + setattr(self, k, v)
13 +
14 +class abcd (_AST):
15 + pass
16 +
17 +class AbcdSpec (abcd):
18 + _fields = ('context', 'body', 'asserts')
19 + _attributes = ()
20 + def __init__ (self, body, context=[], asserts=[], **ARGS):
21 + abcd.__init__(self, **ARGS)
22 + self.context = list(context)
23 + self.body = body
24 + self.asserts = list(asserts)
25 +
26 +class decl (_AST):
27 + pass
28 +
29 +class AbcdTypedef (decl):
30 + _fields = ('name', 'type')
31 + _attributes = ('lineno', 'col_offset')
32 + def __init__ (self, name, type, lineno=0, col_offset=0, **ARGS):
33 + decl.__init__(self, **ARGS)
34 + self.name = name
35 + self.type = type
36 + self.lineno = int(lineno)
37 + self.col_offset = int(col_offset)
38 +
39 +class AbcdBuffer (decl):
40 + _fields = ('name', 'type', 'capacity', 'content')
41 + _attributes = ('lineno', 'col_offset')
42 + def __init__ (self, name, type, content, capacity=None, lineno=0, col_offset=0, **ARGS):
43 + decl.__init__(self, **ARGS)
44 + self.name = name
45 + self.type = type
46 + self.capacity = capacity
47 + self.content = content
48 + self.lineno = int(lineno)
49 + self.col_offset = int(col_offset)
50 +
51 +class AbcdSymbol (decl):
52 + _fields = ('symbols',)
53 + _attributes = ('lineno', 'col_offset')
54 + def __init__ (self, symbols=[], lineno=0, col_offset=0, **ARGS):
55 + decl.__init__(self, **ARGS)
56 + self.symbols = list(symbols)
57 + self.lineno = int(lineno)
58 + self.col_offset = int(col_offset)
59 +
60 +class AbcdConst (decl):
61 + _fields = ('name', 'value')
62 + _attributes = ('lineno', 'col_offset')
63 + def __init__ (self, name, value, lineno=0, col_offset=0, **ARGS):
64 + decl.__init__(self, **ARGS)
65 + self.name = name
66 + self.value = value
67 + self.lineno = int(lineno)
68 + self.col_offset = int(col_offset)
69 +
70 +class AbcdNet (decl):
71 + _fields = ('name', 'args', 'body')
72 + _attributes = ('lineno', 'col_offset')
73 + def __init__ (self, name, args, body, lineno=0, col_offset=0, **ARGS):
74 + decl.__init__(self, **ARGS)
75 + self.name = name
76 + self.args = args
77 + self.body = body
78 + self.lineno = int(lineno)
79 + self.col_offset = int(col_offset)
80 +
81 +class AbcdTask (decl):
82 + _fields = ('name', 'body', 'input', 'output')
83 + _attributes = ('lineno', 'col_offset')
84 + def __init__ (self, name, body, input=[], output=[], lineno=0, col_offset=0, **ARGS):
85 + decl.__init__(self, **ARGS)
86 + self.name = name
87 + self.body = body
88 + self.input = list(input)
89 + self.output = list(output)
90 + self.lineno = int(lineno)
91 + self.col_offset = int(col_offset)
92 +
93 +class stmt (decl):
94 + _fields = ()
95 + _attributes = ('lineno', 'col_offset')
96 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
97 + decl.__init__(self, **ARGS)
98 + self.lineno = int(lineno)
99 + self.col_offset = int(col_offset)
100 +
101 +class slice (_AST):
102 + pass
103 +
104 +class Slice (slice):
105 + _fields = ('lower', 'upper', 'step')
106 + _attributes = ()
107 + def __init__ (self, lower=None, upper=None, step=None, **ARGS):
108 + slice.__init__(self, **ARGS)
109 + self.lower = lower
110 + self.upper = upper
111 + self.step = step
112 +
113 +class ExtSlice (slice):
114 + _fields = ('dims',)
115 + _attributes = ()
116 + def __init__ (self, dims=[], **ARGS):
117 + slice.__init__(self, **ARGS)
118 + self.dims = list(dims)
119 +
120 +class Index (slice):
121 + _fields = ('value',)
122 + _attributes = ()
123 + def __init__ (self, value, **ARGS):
124 + slice.__init__(self, **ARGS)
125 + self.value = value
126 +
127 +class arc (_AST):
128 + pass
129 +
130 +class Produce (arc):
131 + _fields = ()
132 + _attributes = ()
133 +
134 +class Test (arc):
135 + _fields = ()
136 + _attributes = ()
137 +
138 +class Consume (arc):
139 + _fields = ()
140 + _attributes = ()
141 +
142 +class Fill (arc):
143 + _fields = ()
144 + _attributes = ()
145 +
146 +class expr_context (_AST):
147 + pass
148 +
149 +class Load (expr_context):
150 + _fields = ()
151 + _attributes = ()
152 +
153 +class Store (expr_context):
154 + _fields = ()
155 + _attributes = ()
156 +
157 +class Del (expr_context):
158 + _fields = ()
159 + _attributes = ()
160 +
161 +class AugLoad (expr_context):
162 + _fields = ()
163 + _attributes = ()
164 +
165 +class AugStore (expr_context):
166 + _fields = ()
167 + _attributes = ()
168 +
169 +class Param (expr_context):
170 + _fields = ()
171 + _attributes = ()
172 +
173 +class keyword (_AST):
174 + _fields = ('arg', 'value')
175 + _attributes = ()
176 + def __init__ (self, arg, value, **ARGS):
177 + _AST.__init__(self, **ARGS)
178 + self.arg = arg
179 + self.value = value
180 +
181 +class unaryop (_AST):
182 + pass
183 +
184 +class Invert (unaryop):
185 + _fields = ()
186 + _attributes = ()
187 +
188 +class Not (unaryop):
189 + _fields = ()
190 + _attributes = ()
191 +
192 +class UAdd (unaryop):
193 + _fields = ()
194 + _attributes = ()
195 +
196 +class USub (unaryop):
197 + _fields = ()
198 + _attributes = ()
199 +
200 +class process (_AST):
201 + pass
202 +
203 +class AbcdAction (process):
204 + _fields = ('accesses', 'guard')
205 + _attributes = ('lineno', 'col_offset')
206 + def __init__ (self, guard, accesses=[], lineno=0, col_offset=0, **ARGS):
207 + process.__init__(self, **ARGS)
208 + self.accesses = list(accesses)
209 + self.guard = guard
210 + self.lineno = int(lineno)
211 + self.col_offset = int(col_offset)
212 +
213 +class AbcdFlowOp (process):
214 + _fields = ('left', 'op', 'right')
215 + _attributes = ('lineno', 'col_offset')
216 + def __init__ (self, left, op, right, lineno=0, col_offset=0, **ARGS):
217 + process.__init__(self, **ARGS)
218 + self.left = left
219 + self.op = op
220 + self.right = right
221 + self.lineno = int(lineno)
222 + self.col_offset = int(col_offset)
223 +
224 +class AbcdInstance (process):
225 + _fields = ('net', 'asname', 'args', 'keywords', 'starargs', 'kwargs')
226 + _attributes = ('lineno', 'col_offset')
227 + def __init__ (self, net, asname=None, args=[], keywords=[], starargs=None, kwargs=None, lineno=0, col_offset=0, **ARGS):
228 + process.__init__(self, **ARGS)
229 + self.net = net
230 + self.asname = asname
231 + self.args = list(args)
232 + self.keywords = list(keywords)
233 + self.starargs = starargs
234 + self.kwargs = kwargs
235 + self.lineno = int(lineno)
236 + self.col_offset = int(col_offset)
237 +
238 +class expr (_AST):
239 + pass
240 +
241 +class BoolOp (expr):
242 + _fields = ('op', 'values')
243 + _attributes = ('lineno', 'col_offset')
244 + def __init__ (self, op, values=[], lineno=0, col_offset=0, **ARGS):
245 + expr.__init__(self, **ARGS)
246 + self.op = op
247 + self.values = list(values)
248 + self.lineno = int(lineno)
249 + self.col_offset = int(col_offset)
250 +
251 +class BinOp (expr):
252 + _fields = ('left', 'op', 'right')
253 + _attributes = ('lineno', 'col_offset')
254 + def __init__ (self, left, op, right, lineno=0, col_offset=0, **ARGS):
255 + expr.__init__(self, **ARGS)
256 + self.left = left
257 + self.op = op
258 + self.right = right
259 + self.lineno = int(lineno)
260 + self.col_offset = int(col_offset)
261 +
262 +class UnaryOp (expr):
263 + _fields = ('op', 'operand')
264 + _attributes = ('lineno', 'col_offset')
265 + def __init__ (self, op, operand, lineno=0, col_offset=0, **ARGS):
266 + expr.__init__(self, **ARGS)
267 + self.op = op
268 + self.operand = operand
269 + self.lineno = int(lineno)
270 + self.col_offset = int(col_offset)
271 +
272 +class Lambda (expr):
273 + _fields = ('args', 'body')
274 + _attributes = ('lineno', 'col_offset')
275 + def __init__ (self, args, body, lineno=0, col_offset=0, **ARGS):
276 + expr.__init__(self, **ARGS)
277 + self.args = args
278 + self.body = body
279 + self.lineno = int(lineno)
280 + self.col_offset = int(col_offset)
281 +
282 +class IfExp (expr):
283 + _fields = ('test', 'body', 'orelse')
284 + _attributes = ('lineno', 'col_offset')
285 + def __init__ (self, test, body, orelse, lineno=0, col_offset=0, **ARGS):
286 + expr.__init__(self, **ARGS)
287 + self.test = test
288 + self.body = body
289 + self.orelse = orelse
290 + self.lineno = int(lineno)
291 + self.col_offset = int(col_offset)
292 +
293 +class Dict (expr):
294 + _fields = ('keys', 'values')
295 + _attributes = ('lineno', 'col_offset')
296 + def __init__ (self, keys=[], values=[], lineno=0, col_offset=0, **ARGS):
297 + expr.__init__(self, **ARGS)
298 + self.keys = list(keys)
299 + self.values = list(values)
300 + self.lineno = int(lineno)
301 + self.col_offset = int(col_offset)
302 +
303 +class Set (expr):
304 + _fields = ('elts',)
305 + _attributes = ('lineno', 'col_offset')
306 + def __init__ (self, elts=[], lineno=0, col_offset=0, **ARGS):
307 + expr.__init__(self, **ARGS)
308 + self.elts = list(elts)
309 + self.lineno = int(lineno)
310 + self.col_offset = int(col_offset)
311 +
312 +class ListComp (expr):
313 + _fields = ('elt', 'generators')
314 + _attributes = ('lineno', 'col_offset')
315 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
316 + expr.__init__(self, **ARGS)
317 + self.elt = elt
318 + self.generators = list(generators)
319 + self.lineno = int(lineno)
320 + self.col_offset = int(col_offset)
321 +
322 +class SetComp (expr):
323 + _fields = ('elt', 'generators')
324 + _attributes = ('lineno', 'col_offset')
325 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
326 + expr.__init__(self, **ARGS)
327 + self.elt = elt
328 + self.generators = list(generators)
329 + self.lineno = int(lineno)
330 + self.col_offset = int(col_offset)
331 +
332 +class DictComp (expr):
333 + _fields = ('key', 'value', 'generators')
334 + _attributes = ('lineno', 'col_offset')
335 + def __init__ (self, key, value, generators=[], lineno=0, col_offset=0, **ARGS):
336 + expr.__init__(self, **ARGS)
337 + self.key = key
338 + self.value = value
339 + self.generators = list(generators)
340 + self.lineno = int(lineno)
341 + self.col_offset = int(col_offset)
342 +
343 +class GeneratorExp (expr):
344 + _fields = ('elt', 'generators')
345 + _attributes = ('lineno', 'col_offset')
346 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
347 + expr.__init__(self, **ARGS)
348 + self.elt = elt
349 + self.generators = list(generators)
350 + self.lineno = int(lineno)
351 + self.col_offset = int(col_offset)
352 +
353 +class Yield (expr):
354 + _fields = ('value',)
355 + _attributes = ('lineno', 'col_offset')
356 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
357 + expr.__init__(self, **ARGS)
358 + self.value = value
359 + self.lineno = int(lineno)
360 + self.col_offset = int(col_offset)
361 +
362 +class Compare (expr):
363 + _fields = ('left', 'ops', 'comparators')
364 + _attributes = ('lineno', 'col_offset')
365 + def __init__ (self, left, ops=[], comparators=[], lineno=0, col_offset=0, **ARGS):
366 + expr.__init__(self, **ARGS)
367 + self.left = left
368 + self.ops = list(ops)
369 + self.comparators = list(comparators)
370 + self.lineno = int(lineno)
371 + self.col_offset = int(col_offset)
372 +
373 +class Call (expr):
374 + _fields = ('func', 'args', 'keywords', 'starargs', 'kwargs')
375 + _attributes = ('lineno', 'col_offset')
376 + def __init__ (self, func, args=[], keywords=[], starargs=None, kwargs=None, lineno=0, col_offset=0, **ARGS):
377 + expr.__init__(self, **ARGS)
378 + self.func = func
379 + self.args = list(args)
380 + self.keywords = list(keywords)
381 + self.starargs = starargs
382 + self.kwargs = kwargs
383 + self.lineno = int(lineno)
384 + self.col_offset = int(col_offset)
385 +
386 +class Num (expr):
387 + _fields = ('n',)
388 + _attributes = ('lineno', 'col_offset')
389 + def __init__ (self, n, lineno=0, col_offset=0, **ARGS):
390 + expr.__init__(self, **ARGS)
391 + self.n = n
392 + self.lineno = int(lineno)
393 + self.col_offset = int(col_offset)
394 +
395 +class Str (expr):
396 + _fields = ('s',)
397 + _attributes = ('lineno', 'col_offset')
398 + def __init__ (self, s, lineno=0, col_offset=0, **ARGS):
399 + expr.__init__(self, **ARGS)
400 + self.s = s
401 + self.lineno = int(lineno)
402 + self.col_offset = int(col_offset)
403 +
404 +class Ellipsis (expr):
405 + _fields = ()
406 + _attributes = ('lineno', 'col_offset')
407 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
408 + expr.__init__(self, **ARGS)
409 + self.lineno = int(lineno)
410 + self.col_offset = int(col_offset)
411 +
412 +class Attribute (expr):
413 + _fields = ('value', 'attr', 'ctx')
414 + _attributes = ('lineno', 'col_offset')
415 + def __init__ (self, value, attr, ctx=None, lineno=0, col_offset=0, **ARGS):
416 + expr.__init__(self, **ARGS)
417 + self.value = value
418 + self.attr = attr
419 + self.ctx = ctx
420 + self.lineno = int(lineno)
421 + self.col_offset = int(col_offset)
422 +
423 +class Subscript (expr):
424 + _fields = ('value', 'slice', 'ctx')
425 + _attributes = ('lineno', 'col_offset')
426 + def __init__ (self, value, slice, ctx=None, lineno=0, col_offset=0, **ARGS):
427 + expr.__init__(self, **ARGS)
428 + self.value = value
429 + self.slice = slice
430 + self.ctx = ctx
431 + self.lineno = int(lineno)
432 + self.col_offset = int(col_offset)
433 +
434 +class Starred (expr):
435 + _fields = ('value', 'ctx')
436 + _attributes = ('lineno', 'col_offset')
437 + def __init__ (self, value, ctx=None, lineno=0, col_offset=0, **ARGS):
438 + expr.__init__(self, **ARGS)
439 + self.value = value
440 + self.ctx = ctx
441 + self.lineno = int(lineno)
442 + self.col_offset = int(col_offset)
443 +
444 +class Name (expr):
445 + _fields = ('id', 'ctx')
446 + _attributes = ('lineno', 'col_offset')
447 + def __init__ (self, id, ctx=None, lineno=0, col_offset=0, **ARGS):
448 + expr.__init__(self, **ARGS)
449 + self.id = id
450 + self.ctx = ctx
451 + self.lineno = int(lineno)
452 + self.col_offset = int(col_offset)
453 +
454 +class List (expr):
455 + _fields = ('elts', 'ctx')
456 + _attributes = ('lineno', 'col_offset')
457 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
458 + expr.__init__(self, **ARGS)
459 + self.elts = list(elts)
460 + self.ctx = ctx
461 + self.lineno = int(lineno)
462 + self.col_offset = int(col_offset)
463 +
464 +class Tuple (expr):
465 + _fields = ('elts', 'ctx')
466 + _attributes = ('lineno', 'col_offset')
467 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
468 + expr.__init__(self, **ARGS)
469 + self.elts = list(elts)
470 + self.ctx = ctx
471 + self.lineno = int(lineno)
472 + self.col_offset = int(col_offset)
473 +
474 +class cmpop (_AST):
475 + pass
476 +
477 +class Eq (cmpop):
478 + _fields = ()
479 + _attributes = ()
480 +
481 +class NotEq (cmpop):
482 + _fields = ()
483 + _attributes = ()
484 +
485 +class Lt (cmpop):
486 + _fields = ()
487 + _attributes = ()
488 +
489 +class LtE (cmpop):
490 + _fields = ()
491 + _attributes = ()
492 +
493 +class Gt (cmpop):
494 + _fields = ()
495 + _attributes = ()
496 +
497 +class GtE (cmpop):
498 + _fields = ()
499 + _attributes = ()
500 +
501 +class Is (cmpop):
502 + _fields = ()
503 + _attributes = ()
504 +
505 +class IsNot (cmpop):
506 + _fields = ()
507 + _attributes = ()
508 +
509 +class In (cmpop):
510 + _fields = ()
511 + _attributes = ()
512 +
513 +class NotIn (cmpop):
514 + _fields = ()
515 + _attributes = ()
516 +
517 +class boolop (_AST):
518 + pass
519 +
520 +class And (boolop):
521 + _fields = ()
522 + _attributes = ()
523 +
524 +class Or (boolop):
525 + _fields = ()
526 + _attributes = ()
527 +
528 +class stmt (_AST):
529 + pass
530 +
531 +class FunctionDef (stmt):
532 + _fields = ('name', 'args', 'body', 'decorator_list', 'returns')
533 + _attributes = ('lineno', 'col_offset')
534 + def __init__ (self, name, args, body=[], decorator_list=[], returns=None, lineno=0, col_offset=0, **ARGS):
535 + stmt.__init__(self, **ARGS)
536 + self.name = name
537 + self.args = args
538 + self.body = list(body)
539 + self.decorator_list = list(decorator_list)
540 + self.returns = returns
541 + self.lineno = int(lineno)
542 + self.col_offset = int(col_offset)
543 +
544 +class ClassDef (stmt):
545 + _fields = ('name', 'bases', 'keywords', 'starargs', 'kwargs', 'body', 'decorator_list')
546 + _attributes = ('lineno', 'col_offset')
547 + def __init__ (self, name, bases=[], keywords=[], starargs=None, kwargs=None, body=[], decorator_list=[], lineno=0, col_offset=0, **ARGS):
548 + stmt.__init__(self, **ARGS)
549 + self.name = name
550 + self.bases = list(bases)
551 + self.keywords = list(keywords)
552 + self.starargs = starargs
553 + self.kwargs = kwargs
554 + self.body = list(body)
555 + self.decorator_list = list(decorator_list)
556 + self.lineno = int(lineno)
557 + self.col_offset = int(col_offset)
558 +
559 +class Return (stmt):
560 + _fields = ('value',)
561 + _attributes = ('lineno', 'col_offset')
562 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
563 + stmt.__init__(self, **ARGS)
564 + self.value = value
565 + self.lineno = int(lineno)
566 + self.col_offset = int(col_offset)
567 +
568 +class Delete (stmt):
569 + _fields = ('targets',)
570 + _attributes = ('lineno', 'col_offset')
571 + def __init__ (self, targets=[], lineno=0, col_offset=0, **ARGS):
572 + stmt.__init__(self, **ARGS)
573 + self.targets = list(targets)
574 + self.lineno = int(lineno)
575 + self.col_offset = int(col_offset)
576 +
577 +class Assign (stmt):
578 + _fields = ('targets', 'value')
579 + _attributes = ('lineno', 'col_offset')
580 + def __init__ (self, value, targets=[], lineno=0, col_offset=0, **ARGS):
581 + stmt.__init__(self, **ARGS)
582 + self.targets = list(targets)
583 + self.value = value
584 + self.lineno = int(lineno)
585 + self.col_offset = int(col_offset)
586 +
587 +class AugAssign (stmt):
588 + _fields = ('target', 'op', 'value')
589 + _attributes = ('lineno', 'col_offset')
590 + def __init__ (self, target, op, value, lineno=0, col_offset=0, **ARGS):
591 + stmt.__init__(self, **ARGS)
592 + self.target = target
593 + self.op = op
594 + self.value = value
595 + self.lineno = int(lineno)
596 + self.col_offset = int(col_offset)
597 +
598 +class For (stmt):
599 + _fields = ('target', 'iter', 'body', 'orelse')
600 + _attributes = ('lineno', 'col_offset')
601 + def __init__ (self, target, iter, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
602 + stmt.__init__(self, **ARGS)
603 + self.target = target
604 + self.iter = iter
605 + self.body = list(body)
606 + self.orelse = list(orelse)
607 + self.lineno = int(lineno)
608 + self.col_offset = int(col_offset)
609 +
610 +class While (stmt):
611 + _fields = ('test', 'body', 'orelse')
612 + _attributes = ('lineno', 'col_offset')
613 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
614 + stmt.__init__(self, **ARGS)
615 + self.test = test
616 + self.body = list(body)
617 + self.orelse = list(orelse)
618 + self.lineno = int(lineno)
619 + self.col_offset = int(col_offset)
620 +
621 +class If (stmt):
622 + _fields = ('test', 'body', 'orelse')
623 + _attributes = ('lineno', 'col_offset')
624 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
625 + stmt.__init__(self, **ARGS)
626 + self.test = test
627 + self.body = list(body)
628 + self.orelse = list(orelse)
629 + self.lineno = int(lineno)
630 + self.col_offset = int(col_offset)
631 +
632 +class With (stmt):
633 + _fields = ('context_expr', 'optional_vars', 'body')
634 + _attributes = ('lineno', 'col_offset')
635 + def __init__ (self, context_expr, optional_vars=None, body=[], lineno=0, col_offset=0, **ARGS):
636 + stmt.__init__(self, **ARGS)
637 + self.context_expr = context_expr
638 + self.optional_vars = optional_vars
639 + self.body = list(body)
640 + self.lineno = int(lineno)
641 + self.col_offset = int(col_offset)
642 +
643 +class Raise (stmt):
644 + _fields = ('exc', 'cause')
645 + _attributes = ('lineno', 'col_offset')
646 + def __init__ (self, exc=None, cause=None, lineno=0, col_offset=0, **ARGS):
647 + stmt.__init__(self, **ARGS)
648 + self.exc = exc
649 + self.cause = cause
650 + self.lineno = int(lineno)
651 + self.col_offset = int(col_offset)
652 +
653 +class TryExcept (stmt):
654 + _fields = ('body', 'handlers', 'orelse')
655 + _attributes = ('lineno', 'col_offset')
656 + def __init__ (self, body=[], handlers=[], orelse=[], lineno=0, col_offset=0, **ARGS):
657 + stmt.__init__(self, **ARGS)
658 + self.body = list(body)
659 + self.handlers = list(handlers)
660 + self.orelse = list(orelse)
661 + self.lineno = int(lineno)
662 + self.col_offset = int(col_offset)
663 +
664 +class TryFinally (stmt):
665 + _fields = ('body', 'finalbody')
666 + _attributes = ('lineno', 'col_offset')
667 + def __init__ (self, body=[], finalbody=[], lineno=0, col_offset=0, **ARGS):
668 + stmt.__init__(self, **ARGS)
669 + self.body = list(body)
670 + self.finalbody = list(finalbody)
671 + self.lineno = int(lineno)
672 + self.col_offset = int(col_offset)
673 +
674 +class Assert (stmt):
675 + _fields = ('test', 'msg')
676 + _attributes = ('lineno', 'col_offset')
677 + def __init__ (self, test, msg=None, lineno=0, col_offset=0, **ARGS):
678 + stmt.__init__(self, **ARGS)
679 + self.test = test
680 + self.msg = msg
681 + self.lineno = int(lineno)
682 + self.col_offset = int(col_offset)
683 +
684 +class Import (stmt):
685 + _fields = ('names',)
686 + _attributes = ('lineno', 'col_offset')
687 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
688 + stmt.__init__(self, **ARGS)
689 + self.names = list(names)
690 + self.lineno = int(lineno)
691 + self.col_offset = int(col_offset)
692 +
693 +class ImportFrom (stmt):
694 + _fields = ('module', 'names', 'level')
695 + _attributes = ('lineno', 'col_offset')
696 + def __init__ (self, module, names=[], level=None, lineno=0, col_offset=0, **ARGS):
697 + stmt.__init__(self, **ARGS)
698 + self.module = module
699 + self.names = list(names)
700 + self.level = level
701 + self.lineno = int(lineno)
702 + self.col_offset = int(col_offset)
703 +
704 +class Exec (stmt):
705 + _fields = ('body', 'globals', 'locals')
706 + _attributes = ('lineno', 'col_offset')
707 + def __init__ (self, body, globals=None, locals=None, lineno=0, col_offset=0, **ARGS):
708 + stmt.__init__(self, **ARGS)
709 + self.body = body
710 + self.globals = globals
711 + self.locals = locals
712 + self.lineno = int(lineno)
713 + self.col_offset = int(col_offset)
714 +
715 +class Global (stmt):
716 + _fields = ('names',)
717 + _attributes = ('lineno', 'col_offset')
718 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
719 + stmt.__init__(self, **ARGS)
720 + self.names = list(names)
721 + self.lineno = int(lineno)
722 + self.col_offset = int(col_offset)
723 +
724 +class Nonlocal (stmt):
725 + _fields = ('names',)
726 + _attributes = ('lineno', 'col_offset')
727 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
728 + stmt.__init__(self, **ARGS)
729 + self.names = list(names)
730 + self.lineno = int(lineno)
731 + self.col_offset = int(col_offset)
732 +
733 +class Expr (stmt):
734 + _fields = ('value',)
735 + _attributes = ('lineno', 'col_offset')
736 + def __init__ (self, value, lineno=0, col_offset=0, **ARGS):
737 + stmt.__init__(self, **ARGS)
738 + self.value = value
739 + self.lineno = int(lineno)
740 + self.col_offset = int(col_offset)
741 +
742 +class Pass (stmt):
743 + _fields = ()
744 + _attributes = ('lineno', 'col_offset')
745 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
746 + stmt.__init__(self, **ARGS)
747 + self.lineno = int(lineno)
748 + self.col_offset = int(col_offset)
749 +
750 +class Break (stmt):
751 + _fields = ()
752 + _attributes = ('lineno', 'col_offset')
753 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
754 + stmt.__init__(self, **ARGS)
755 + self.lineno = int(lineno)
756 + self.col_offset = int(col_offset)
757 +
758 +class Continue (stmt):
759 + _fields = ()
760 + _attributes = ('lineno', 'col_offset')
761 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
762 + stmt.__init__(self, **ARGS)
763 + self.lineno = int(lineno)
764 + self.col_offset = int(col_offset)
765 +
766 +class access (_AST):
767 + pass
768 +
769 +class SimpleAccess (access):
770 + _fields = ('buffer', 'arc', 'tokens')
771 + _attributes = ('lineno', 'col_offset')
772 + def __init__ (self, buffer, arc, tokens, lineno=0, col_offset=0, **ARGS):
773 + access.__init__(self, **ARGS)
774 + self.buffer = buffer
775 + self.arc = arc
776 + self.tokens = tokens
777 + self.lineno = int(lineno)
778 + self.col_offset = int(col_offset)
779 +
780 +class FlushAccess (access):
781 + _fields = ('buffer', 'target')
782 + _attributes = ('lineno', 'col_offset')
783 + def __init__ (self, buffer, target, lineno=0, col_offset=0, **ARGS):
784 + access.__init__(self, **ARGS)
785 + self.buffer = buffer
786 + self.target = target
787 + self.lineno = int(lineno)
788 + self.col_offset = int(col_offset)
789 +
790 +class SwapAccess (access):
791 + _fields = ('buffer', 'target', 'tokens')
792 + _attributes = ('lineno', 'col_offset')
793 + def __init__ (self, buffer, target, tokens, lineno=0, col_offset=0, **ARGS):
794 + access.__init__(self, **ARGS)
795 + self.buffer = buffer
796 + self.target = target
797 + self.tokens = tokens
798 + self.lineno = int(lineno)
799 + self.col_offset = int(col_offset)
800 +
801 +class Spawn (access):
802 + _fields = ('net', 'pid', 'args')
803 + _attributes = ('lineno', 'col_offset')
804 + def __init__ (self, net, pid, args, lineno=0, col_offset=0, **ARGS):
805 + access.__init__(self, **ARGS)
806 + self.net = net
807 + self.pid = pid
808 + self.args = args
809 + self.lineno = int(lineno)
810 + self.col_offset = int(col_offset)
811 +
812 +class Wait (access):
813 + _fields = ('net', 'pid', 'args')
814 + _attributes = ('lineno', 'col_offset')
815 + def __init__ (self, net, pid, args, lineno=0, col_offset=0, **ARGS):
816 + access.__init__(self, **ARGS)
817 + self.net = net
818 + self.pid = pid
819 + self.args = args
820 + self.lineno = int(lineno)
821 + self.col_offset = int(col_offset)
822 +
823 +class Suspend (access):
824 + _fields = ('net', 'pid')
825 + _attributes = ('lineno', 'col_offset')
826 + def __init__ (self, net, pid, lineno=0, col_offset=0, **ARGS):
827 + access.__init__(self, **ARGS)
828 + self.net = net
829 + self.pid = pid
830 + self.lineno = int(lineno)
831 + self.col_offset = int(col_offset)
832 +
833 +class Resume (access):
834 + _fields = ('net', 'pid')
835 + _attributes = ('lineno', 'col_offset')
836 + def __init__ (self, net, pid, lineno=0, col_offset=0, **ARGS):
837 + access.__init__(self, **ARGS)
838 + self.net = net
839 + self.pid = pid
840 + self.lineno = int(lineno)
841 + self.col_offset = int(col_offset)
842 +
843 +class alias (_AST):
844 + _fields = ('name', 'asname')
845 + _attributes = ()
846 + def __init__ (self, name, asname=None, **ARGS):
847 + _AST.__init__(self, **ARGS)
848 + self.name = name
849 + self.asname = asname
850 +
851 +class flowop (_AST):
852 + pass
853 +
854 +class Sequence (flowop):
855 + _fields = ()
856 + _attributes = ()
857 +
858 +class Choice (flowop):
859 + _fields = ()
860 + _attributes = ()
861 +
862 +class Parallel (flowop):
863 + _fields = ()
864 + _attributes = ()
865 +
866 +class Loop (flowop):
867 + _fields = ()
868 + _attributes = ()
869 +
870 +class comprehension (_AST):
871 + _fields = ('target', 'iter', 'ifs')
872 + _attributes = ()
873 + def __init__ (self, target, iter, ifs=[], **ARGS):
874 + _AST.__init__(self, **ARGS)
875 + self.target = target
876 + self.iter = iter
877 + self.ifs = list(ifs)
878 +
879 +class arg (_AST):
880 + _fields = ('arg', 'annotation')
881 + _attributes = ()
882 + def __init__ (self, arg, annotation=None, **ARGS):
883 + _AST.__init__(self, **ARGS)
884 + self.arg = arg
885 + self.annotation = annotation
886 +
887 +class operator (_AST):
888 + pass
889 +
890 +class Add (operator):
891 + _fields = ()
892 + _attributes = ()
893 +
894 +class Sub (operator):
895 + _fields = ()
896 + _attributes = ()
897 +
898 +class Mult (operator):
899 + _fields = ()
900 + _attributes = ()
901 +
902 +class Div (operator):
903 + _fields = ()
904 + _attributes = ()
905 +
906 +class Mod (operator):
907 + _fields = ()
908 + _attributes = ()
909 +
910 +class Pow (operator):
911 + _fields = ()
912 + _attributes = ()
913 +
914 +class LShift (operator):
915 + _fields = ()
916 + _attributes = ()
917 +
918 +class RShift (operator):
919 + _fields = ()
920 + _attributes = ()
921 +
922 +class BitOr (operator):
923 + _fields = ()
924 + _attributes = ()
925 +
926 +class BitXor (operator):
927 + _fields = ()
928 + _attributes = ()
929 +
930 +class BitAnd (operator):
931 + _fields = ()
932 + _attributes = ()
933 +
934 +class FloorDiv (operator):
935 + _fields = ()
936 + _attributes = ()
937 +
938 +class abcdtype (_AST):
939 + pass
940 +
941 +class UnionType (abcdtype):
942 + _fields = ('types',)
943 + _attributes = ('lineno', 'col_offset')
944 + def __init__ (self, types=[], lineno=0, col_offset=0, **ARGS):
945 + abcdtype.__init__(self, **ARGS)
946 + self.types = list(types)
947 + self.lineno = int(lineno)
948 + self.col_offset = int(col_offset)
949 +
950 +class IntersectionType (abcdtype):
951 + _fields = ('types',)
952 + _attributes = ('lineno', 'col_offset')
953 + def __init__ (self, types=[], lineno=0, col_offset=0, **ARGS):
954 + abcdtype.__init__(self, **ARGS)
955 + self.types = list(types)
956 + self.lineno = int(lineno)
957 + self.col_offset = int(col_offset)
958 +
959 +class CrossType (abcdtype):
960 + _fields = ('types',)
961 + _attributes = ('lineno', 'col_offset')
962 + def __init__ (self, types=[], lineno=0, col_offset=0, **ARGS):
963 + abcdtype.__init__(self, **ARGS)
964 + self.types = list(types)
965 + self.lineno = int(lineno)
966 + self.col_offset = int(col_offset)
967 +
968 +class ListType (abcdtype):
969 + _fields = ('items',)
970 + _attributes = ('lineno', 'col_offset')
971 + def __init__ (self, items, lineno=0, col_offset=0, **ARGS):
972 + abcdtype.__init__(self, **ARGS)
973 + self.items = items
974 + self.lineno = int(lineno)
975 + self.col_offset = int(col_offset)
976 +
977 +class TupleType (abcdtype):
978 + _fields = ('items',)
979 + _attributes = ('lineno', 'col_offset')
980 + def __init__ (self, items, lineno=0, col_offset=0, **ARGS):
981 + abcdtype.__init__(self, **ARGS)
982 + self.items = items
983 + self.lineno = int(lineno)
984 + self.col_offset = int(col_offset)
985 +
986 +class SetType (abcdtype):
987 + _fields = ('items',)
988 + _attributes = ('lineno', 'col_offset')
989 + def __init__ (self, items, lineno=0, col_offset=0, **ARGS):
990 + abcdtype.__init__(self, **ARGS)
991 + self.items = items
992 + self.lineno = int(lineno)
993 + self.col_offset = int(col_offset)
994 +
995 +class DictType (abcdtype):
996 + _fields = ('keys', 'values')
997 + _attributes = ('lineno', 'col_offset')
998 + def __init__ (self, keys, values, lineno=0, col_offset=0, **ARGS):
999 + abcdtype.__init__(self, **ARGS)
1000 + self.keys = keys
1001 + self.values = values
1002 + self.lineno = int(lineno)
1003 + self.col_offset = int(col_offset)
1004 +
1005 +class EnumType (abcdtype):
1006 + _fields = ('items',)
1007 + _attributes = ('lineno', 'col_offset')
1008 + def __init__ (self, items=[], lineno=0, col_offset=0, **ARGS):
1009 + abcdtype.__init__(self, **ARGS)
1010 + self.items = list(items)
1011 + self.lineno = int(lineno)
1012 + self.col_offset = int(col_offset)
1013 +
1014 +class NamedType (abcdtype):
1015 + _fields = ('name',)
1016 + _attributes = ('lineno', 'col_offset')
1017 + def __init__ (self, name, lineno=0, col_offset=0, **ARGS):
1018 + abcdtype.__init__(self, **ARGS)
1019 + self.name = name
1020 + self.lineno = int(lineno)
1021 + self.col_offset = int(col_offset)
1022 +
1023 +class excepthandler (_AST):
1024 + pass
1025 +
1026 +class ExceptHandler (excepthandler):
1027 + _fields = ('type', 'name', 'body')
1028 + _attributes = ('lineno', 'col_offset')
1029 + def __init__ (self, type=None, name=None, body=[], lineno=0, col_offset=0, **ARGS):
1030 + excepthandler.__init__(self, **ARGS)
1031 + self.type = type
1032 + self.name = name
1033 + self.body = list(body)
1034 + self.lineno = int(lineno)
1035 + self.col_offset = int(col_offset)
1036 +
1037 +class arguments (_AST):
1038 + _fields = ('args', 'vararg', 'varargannotation', 'kwonlyargs', 'kwarg', 'kwargannotation', 'defaults', 'kw_defaults')
1039 + _attributes = ()
1040 + def __init__ (self, args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[], **ARGS):
1041 + _AST.__init__(self, **ARGS)
1042 + self.args = list(args)
1043 + self.vararg = vararg
1044 + self.varargannotation = varargannotation
1045 + self.kwonlyargs = list(kwonlyargs)
1046 + self.kwarg = kwarg
1047 + self.kwargannotation = kwargannotation
1048 + self.defaults = list(defaults)
1049 + self.kw_defaults = list(kw_defaults)
1 +"""
2 +>>> testparser(Translator)
3 +"""
4 +
5 +import operator, sys
6 +import snakes
7 +from snakes.lang.python.parser import (ParseTree, ParseTestParser,
8 + Translator as PyTranslator,
9 + ParseTree as PyParseTree,
10 + testparser)
11 +from snakes.lang.pgen import ParseError
12 +from snakes.lang.abcd.pgen import parser
13 +import snakes.lang.abcd.asdl as ast
14 +
15 +_symbols = parser.tokenizer.tok_name.copy()
16 +# next statement overrides 'NT_OFFSET' entry with 'single_input'
17 +# (this is desired)
18 +_symbols.update(parser.symbolMap)
19 +
20 +def skip (token) :
21 + if token.kind == token.lexer.COMMENT :
22 + words = token.strip().split()
23 + if words[:2] == ["#", "coding="] :
24 + coding = words[2]
25 + elif words[:3] == ["#", "-*-", "coding:"] :
26 + coding = words[3]
27 + else :
28 + return
29 + snakes.defaultencoding = coding
30 +
31 +parser.tokenizer.skip_token = skip
32 +
33 +class ParseTree (PyParseTree) :
34 + _symbols = _symbols
35 +
36 +class Translator (PyTranslator) :
37 + ParseTree = ParseTree
38 + parser = parser
39 + ST = ast
40 + def do_file_input (self, st, ctx) :
41 + """file_input: abcd_main ENDMARKER
42 + -> ast.AbcdSpec
43 +
44 + <<< symbol FOO, BAR
45 + ... [egg+(spam) if spam == ham] ; [spam-(FOO, BAR)]
46 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO', 'BAR'])], body=AbcdFlowOp(left=AbcdAction(accesses=[SimpleAccess(buffer='egg', arc=Produce(), tokens=Name(id='spam', ctx=Load()))], guard=Compare(left=Name(id='spam', ctx=Load()), ops=[Eq()], comparators=[Name(id='ham', ctx=Load())])), op=Sequence(), right=AbcdAction(accesses=[SimpleAccess(buffer='spam', arc=Consume(), tokens=Tuple(elts=[Name(id='FOO', ctx=Load()), Name(id='BAR', ctx=Load())], ctx=Load()))], guard=Name(id='True', ctx=Load()))), asserts=[])"
47 + """
48 + return self.do(st[0])
49 + def do_abcd_main (self, st, ctx) :
50 + """abcd_main: (NEWLINE | abcd_global)* abcd_expr (abcd_prop)*
51 + -> ast.AbcdSpec
52 +
53 + <<< symbol FOO, BAR
54 + ... [egg+(spam) if spam == ham] ; [spam-(FOO, BAR)]
55 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO', 'BAR'])], body=AbcdFlowOp(left=AbcdAction(accesses=[SimpleAccess(buffer='egg', arc=Produce(), tokens=Name(id='spam', ctx=Load()))], guard=Compare(left=Name(id='spam', ctx=Load()), ops=[Eq()], comparators=[Name(id='ham', ctx=Load())])), op=Sequence(), right=AbcdAction(accesses=[SimpleAccess(buffer='spam', arc=Consume(), tokens=Tuple(elts=[Name(id='FOO', ctx=Load()), Name(id='BAR', ctx=Load())], ctx=Load()))], guard=Name(id='True', ctx=Load()))), asserts=[])"
56 + """
57 + expr = len(st)-1
58 + for i, child in enumerate(st) :
59 + if child.symbol == "abcd_expr" :
60 + expr = i
61 + break
62 + return self.ST.AbcdSpec(lineno=st.srow, col_offset=st.scol,
63 + context=[self.do(child) for child in st[:expr]
64 + if child.kind != self.NEWLINE],
65 + body=self.do(st[expr]),
66 + asserts=[self.do(child) for child in st[expr+1:]])
67 + def do_abcd_prop (self, st, ctx) :
68 + """abcd_prop: 'assert' test (NEWLINE)*
69 + -> ast.test
70 +
71 + <<< [True]
72 + ... assert True
73 + ... assert False
74 + "AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[Name(id='True', ctx=Load()), Name(id='False', ctx=Load())])"
75 + """
76 + return self.do(st[1])
77 + def do_abcd_global (self, st, ctx) :
78 + """abcd_global: (import_stmt | abcd_symbol | abcd_typedef
79 + | abcd_const | abcd_decl)
80 + -> ast.AST
81 +
82 + <<< import module
83 + ... from module import content
84 + ... symbol EGG, SPAM, HAM
85 + ... typedef t : enum(EGG, SPAM, HAM)
86 + ... net Foo() : [True]
87 + ... buffer bar : t = ()
88 + ... [True]
89 + "AbcdSpec(context=[Import(names=[alias(name='module', asname=None)]), ImportFrom(module='module', names=[alias(name='content', asname=None)], level=0), AbcdSymbol(symbols=['EGG', 'SPAM', 'HAM']), AbcdTypedef(name='t', type=EnumType(items=[Name(id='EGG', ctx=Load()), Name(id='SPAM', ctx=Load()), Name(id='HAM', ctx=Load())])), AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])), AbcdBuffer(name='bar', type=NamedType(name='t'), capacity=None, content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
90 + """
91 + return self.do(st[0])
92 + def do_abcd_spec (self, st, ctx) :
93 + """abcd_spec: (NEWLINE | abcd_decl)* abcd_expr
94 + -> ast.AbcdSpec
95 +
96 + <<< net Foo () :
97 + ... buffer bar : spam = ()
98 + ... net Bar () : [False]
99 + ... [True]
100 + ... Foo()
101 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[AbcdBuffer(name='bar', type=NamedType(name='spam'), capacity=None, content=Tuple(elts=[], ctx=Load())), AbcdNet(name='Bar', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=False), asserts=[]))], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdInstance(net='Foo', asname=None, args=[], keywords=[], starargs=None, kwargs=None), asserts=[])"
102 + """
103 + tree = self.do_abcd_main(st, ctx)
104 + tree.st = st
105 + return tree
106 + def do_abcd_decl (self, st, ctx) :
107 + """abcd_decl: abcd_net | abcd_task | abcd_buffer
108 + -> ast.AST
109 +
110 + <<< net Foo () :
111 + ... buffer bar : spam = ()
112 + ... net Bar () : [False]
113 + ... [True]
114 + ... Foo()
115 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[AbcdBuffer(name='bar', type=NamedType(name='spam'), capacity=None, content=Tuple(elts=[], ctx=Load())), AbcdNet(name='Bar', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=False), asserts=[]))], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdInstance(net='Foo', asname=None, args=[], keywords=[], starargs=None, kwargs=None), asserts=[])"
116 + """
117 + tree = self.do_abcd_global(st, ctx)
118 + tree.st = st
119 + return tree
120 + def do_abcd_const (self, st, ctx) :
121 + """abcd_const: 'const' NAME '=' testlist
122 + -> ast.AbcdConst
123 +
124 + <<< const FOO = 5
125 + ... [True]
126 + "AbcdSpec(context=[AbcdConst(name='FOO', value=Num(n=5))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
127 + <<< const FOO = 1, 2, 3
128 + ... [True]
129 + "AbcdSpec(context=[AbcdConst(name='FOO', value=Tuple(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
130 + """
131 + return self.ST.AbcdConst(lineno=st.srow, col_offset=st.scol,
132 + name=st[1].text, value=self.do(st[3], ctx))
133 + def do_abcd_symbol (self, st, ctx) :
134 + """abcd_symbol: 'symbol' abcd_namelist
135 + -> ast.AbcdSymbol
136 +
137 + <<< symbol FOO
138 + ... [True]
139 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO'])], body=AbcdAction(accesses=[], guard=True), asserts=[])"
140 + <<< symbol FOO, BAR
141 + ... [True]
142 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO', 'BAR'])], body=AbcdAction(accesses=[], guard=True), asserts=[])"
143 + """
144 + return self.ST.AbcdSymbol(lineno=st.srow, col_offset=st.scol,
145 + symbols=self.do(st[1]))
146 + def do_abcd_namelist (self, st, ctx) :
147 + """abcd_namelist: NAME (',' NAME)*
148 + -> str+
149 +
150 + <<< symbol FOO
151 + ... [True]
152 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO'])], body=AbcdAction(accesses=[], guard=True), asserts=[])"
153 + <<< symbol FOO, BAR
154 + ... [True]
155 + "AbcdSpec(context=[AbcdSymbol(symbols=['FOO', 'BAR'])], body=AbcdAction(accesses=[], guard=True), asserts=[])"
156 + """
157 + return [child.text for child in st[::2]]
158 + def _do_flowop (self, st, op) :
159 + nodes = [self.do(child) for child in st[::2]]
160 + while len(nodes) > 1 :
161 + left = nodes.pop(0)
162 + right = nodes.pop(0)
163 + nodes.insert(0, self.ST.AbcdFlowOp(lineno=left.lineno,
164 + col_offset=left.col_offset,
165 + left=left, op=op(), right=right))
166 + return nodes[0]
167 + def do_abcd_expr (self, st, ctx) :
168 + """abcd_expr: abcd_choice_expr ('|' abcd_choice_expr)*
169 + -> ast.process
170 +
171 + <<< [True]
172 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
173 + <<< [True] | [False]
174 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Parallel(), right=AbcdAction(accesses=[], guard=False)), asserts=[])'
175 + <<< [True] | [False] | [True]
176 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Parallel(), right=AbcdAction(accesses=[], guard=False)), op=Parallel(), right=AbcdAction(accesses=[], guard=True)), asserts=[])'
177 +
178 + """
179 + return self._do_flowop(st, self.ST.Parallel)
180 + def do_abcd_choice_expr (self, st, ctx) :
181 + """abcd_choice_expr: abcd_iter_expr ('+' abcd_iter_expr)*
182 + -> ast.process
183 +
184 + <<< [True]
185 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
186 + <<< [True] + [False]
187 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Choice(), right=AbcdAction(accesses=[], guard=False)), asserts=[])'
188 + <<< [True] + [False] + [True]
189 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Choice(), right=AbcdAction(accesses=[], guard=False)), op=Choice(), right=AbcdAction(accesses=[], guard=True)), asserts=[])'
190 + """
191 + return self._do_flowop(st, self.ST.Choice)
192 + def do_abcd_iter_expr (self, st, ctx) :
193 + """abcd_iter_expr: abcd_seq_expr ('*' abcd_seq_expr)*
194 + -> ast.process
195 +
196 + <<< [True]
197 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
198 + <<< [True] * [False]
199 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Loop(), right=AbcdAction(accesses=[], guard=False)), asserts=[])'
200 + <<< [True] * [False] * [True]
201 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Loop(), right=AbcdAction(accesses=[], guard=False)), op=Loop(), right=AbcdAction(accesses=[], guard=True)), asserts=[])'
202 + """
203 + return self._do_flowop(st, self.ST.Loop)
204 + def do_abcd_seq_expr (self, st, ctx) :
205 + """abcd_seq_expr: abcd_base_expr (';' abcd_base_expr)*
206 + -> ast.process
207 +
208 + <<< [True]
209 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
210 + <<< [True] ; [False]
211 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Sequence(), right=AbcdAction(accesses=[], guard=False)), asserts=[])'
212 + <<< [True] ; [False] ; [True]
213 + 'AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Sequence(), right=AbcdAction(accesses=[], guard=False)), op=Sequence(), right=AbcdAction(accesses=[], guard=True)), asserts=[])'
214 + """
215 + return self._do_flowop(st, self.ST.Sequence)
216 + def do_abcd_base_expr (self, st, ctx) :
217 + """abcd_base_expr: (abcd_action | '(' abcd_expr ')') (NEWLINE)*
218 + -> ast.process
219 +
220 + <<< [True]
221 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
222 + <<< ([True])
223 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
224 + """
225 + if st[0].text == "(" :
226 + return self.do(st[1])
227 + else :
228 + return self.do(st[0])
229 + def do_abcd_action (self, st, ctx) :
230 + """abcd_action: ('[' 'True' ']' | '[' 'False' ']' |
231 + '[' abcd_access_list ['if' test] ']' |
232 + abcd_instance)
233 + -> ast.AbcdAction | ast.AbcdInstance
234 +
235 + <<< [True]
236 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[])'
237 + <<< [False]
238 + 'AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=False), asserts=[])'
239 + <<< Foo(1, 2)
240 + "AbcdSpec(context=[], body=AbcdInstance(net='Foo', asname=None, args=[Num(n=1), Num(n=2)], keywords=[], starargs=None, kwargs=None), asserts=[])"
241 + """
242 + if len(st) == 1 :
243 + return self.do(st[0])
244 + elif st[1].text == "True" :
245 + return self.ST.AbcdAction(lineno=st.srow, col_offset=st.scol,
246 + accesses=[], guard=True)
247 + elif st[1].text == "False" :
248 + return self.ST.AbcdAction(lineno=st.srow, col_offset=st.scol,
249 + accesses=[], guard=False)
250 + elif len(st) == 3 :
251 + return self.ST.AbcdAction(lineno=st.srow, col_offset=st.scol,
252 + accesses=self.do(st[1]),
253 + guard=self.ST.Name(lineno=st[-1].srow,
254 + col_offset=st[-1].scol,
255 + id="True",
256 + ctx=self.ST.Load()))
257 + else :
258 + return self.ST.AbcdAction(lineno=st.srow, col_offset=st.scol,
259 + accesses=self.do(st[1]),
260 + guard=self.do(st[3]))
261 + def do_abcd_access_list (self, st, ctx) :
262 + """abcd_access_list: abcd_access (',' abcd_access)*
263 + -> ast.access+
264 +
265 + <<< [foo-(1)]
266 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='foo', arc=Consume(), tokens=Num(n=1))], guard=Name(id='True', ctx=Load())), asserts=[])"
267 + <<< [foo-(1), bar+(2)]
268 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='foo', arc=Consume(), tokens=Num(n=1)), SimpleAccess(buffer='bar', arc=Produce(), tokens=Num(n=2))], guard=Name(id='True', ctx=Load())), asserts=[])"
269 + """
270 + return [self.do(child) for child in st[::2]]
271 + _arc = {"+" : ast.Produce,
272 + "-" : ast.Consume,
273 + "?" : ast.Test,
274 + "<<" : ast.Fill}
275 + def do_abcd_access (self, st, ctx) :
276 + """abcd_access: (NAME '+' '(' testlist ')' |
277 + NAME '?' '(' testlist ')' |
278 + NAME '-' '(' testlist ')' |
279 + NAME '<>' '(' testlist '=' testlist ')' |
280 + NAME '>>' '(' NAME ')' |
281 + NAME '<<' '(' testlist_comp ')' |
282 + NAME '.' NAME '(' test (',' test)* ')')
283 + -> ast.access
284 +
285 + <<< [egg+(x), spam-(y), ham?(z), foo<<(bar)]
286 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='egg', arc=Produce(), tokens=Name(id='x', ctx=Load())), SimpleAccess(buffer='spam', arc=Consume(), tokens=Name(id='y', ctx=Load())), SimpleAccess(buffer='ham', arc=Test(), tokens=Name(id='z', ctx=Load())), SimpleAccess(buffer='foo', arc=Fill(), tokens=Name(id='bar', ctx=Load()))], guard=Name(id='True', ctx=Load())), asserts=[])"
287 + <<< [foo<<(spam(egg) for egg in ham)]
288 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='foo', arc=Fill(), tokens=ListComp(elt=Call(func=Name(id='spam', ctx=Load()), args=[Name(id='egg', ctx=Load())], keywords=[], starargs=None, kwargs=None), generators=[comprehension(target=Name(id='egg', ctx=Store()), iter=Name(id='ham', ctx=Load()), ifs=[])]))], guard=Name(id='True', ctx=Load())), asserts=[])"
289 + <<< [bar-(l), foo<<(l)]
290 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='bar', arc=Consume(), tokens=Name(id='l', ctx=Load())), SimpleAccess(buffer='foo', arc=Fill(), tokens=Name(id='l', ctx=Load()))], guard=Name(id='True', ctx=Load())), asserts=[])"
291 + <<< [bar-(l), foo<<(l,)]
292 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SimpleAccess(buffer='bar', arc=Consume(), tokens=Name(id='l', ctx=Load())), SimpleAccess(buffer='foo', arc=Fill(), tokens=Tuple(elts=[Name(id='l', ctx=Load())], ctx=Load()))], guard=Name(id='True', ctx=Load())), asserts=[])"
293 + <<< [spam>>(ham) if spam is egg]
294 + "AbcdSpec(context=[], body=AbcdAction(accesses=[FlushAccess(buffer='spam', target='ham')], guard=Compare(left=Name(id='spam', ctx=Load()), ops=[Is()], comparators=[Name(id='egg', ctx=Load())])), asserts=[])"
295 + <<< [count<>(n = n+1)]
296 + "AbcdSpec(context=[], body=AbcdAction(accesses=[SwapAccess(buffer='count', target=Name(id='n', ctx=Load()), tokens=BinOp(left=Name(id='n', ctx=Load()), op=Add(), right=Num(n=1)))], guard=Name(id='True', ctx=Load())), asserts=[])"
297 + <<< [foo.spawn(child, 1, 2, 3)]
298 + "AbcdSpec(context=[], body=AbcdAction(accesses=[Spawn(net='foo', pid=Name(id='child', ctx=Load()), args=[Num(n=1), Num(n=2), Num(n=3)])], guard=Name(id='True', ctx=Load())), asserts=[])"
299 + <<< [foo.wait(child, y, z)]
300 + "AbcdSpec(context=[], body=AbcdAction(accesses=[Wait(net='foo', pid=Name(id='child', ctx=Load()), args=[Name(id='y', ctx=Load()), Name(id='z', ctx=Load())])], guard=Name(id='True', ctx=Load())), asserts=[])"
301 + <<< [foo.suspend(pid)]
302 + "AbcdSpec(context=[], body=AbcdAction(accesses=[Suspend(net='foo', pid=Name(id='pid', ctx=Load()))], guard=Name(id='True', ctx=Load())), asserts=[])"
303 + <<< [foo.resume(pid)]
304 + "AbcdSpec(context=[], body=AbcdAction(accesses=[Resume(net='foo', pid=Name(id='pid', ctx=Load()))], guard=Name(id='True', ctx=Load())), asserts=[])"
305 + """
306 + if st[1].text in ("+", "?", "-") :
307 + return self.ST.SimpleAccess(lineno=st.srow, col_offset=st.scol,
308 + buffer=st[0].text,
309 + arc=self._arc[st[1].text](),
310 + tokens=self.do(st[3]))
311 + elif st[1].text == "<<" :
312 + loop, elts, atom = self.do(st[3], ctx)
313 + if atom is not None :
314 + args = atom
315 + elif loop is None :
316 + args = self.ST.Tuple(lineno=st.srow, col_offset=st.scol,
317 + elts=elts, ctx=ctx())
318 + else :
319 + args = self.ST.ListComp(lineno=st.srow, col_offset=st.scol,
320 + elt=loop, generators=elts)
321 + return self.ST.SimpleAccess(lineno=st.srow, col_offset=st.scol,
322 + buffer=st[0].text,
323 + arc=self._arc[st[1].text](),
324 + tokens=args)
325 + elif st[1].text == ">>" :
326 + return self.ST.FlushAccess(lineno=st.srow, col_offset=st.scol,
327 + buffer=st[0].text,
328 + target=st[3].text)
329 + elif st[1].text == "<>" :
330 + return self.ST.SwapAccess(lineno=st.srow, col_offset=st.scol,
331 + buffer=st[0].text,
332 + target=self.do(st[3]),
333 + tokens=self.do(st[5]))
334 + elif st[2].text in ("suspend", "resume") : # st[1].text == "."
335 + if len(st) > 6 :
336 + raise ParseError(st.text, reason="too many arguments for %s"
337 + % st[2].text)
338 + if st[2].text == "suspend" :
339 + tree = self.ST.Suspend
340 + else :
341 + tree = self.ST.Resume
342 + return tree(lineno=st.srow, col_offset=st.scol,
343 + net=st[0].text,
344 + pid=self.do(st[4]))
345 + elif st[2].text in ("spawn", "wait") : # st[1].text == "."
346 + if len(st) > 6 :
347 + args = [self.do(child) for child in st[6:-1:2]]
348 + else :
349 + args = []
350 + if st[2].text == "spawn" :
351 + tree = self.ST.Spawn
352 + else :
353 + tree = self.ST.Wait
354 + return tree(lineno=st.srow, col_offset=st.scol,
355 + net=st[0].text,
356 + pid=self.do(st[4]),
357 + args=args)
358 + else :
359 + raise ParseError(st[2].text, reason=("expected 'spawn', 'wait', "
360 + "'suspend' or 'resume', but found '%s'")
361 + % st[2].text)
362 + def do_abcd_instance (self, st, ctx) :
363 + """abcd_instance: [NAME ':' ':'] NAME '(' [arglist] ')'
364 + -> ast.AbcdInstance
365 +
366 + <<< sub()
367 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname=None, args=[], keywords=[], starargs=None, kwargs=None), asserts=[])"
368 + <<< sub(1, 2, 3)
369 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname=None, args=[Num(n=1), Num(n=2), Num(n=3)], keywords=[], starargs=None, kwargs=None), asserts=[])"
370 + <<< sub(1, *l)
371 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname=None, args=[Num(n=1)], keywords=[], starargs=Name(id='l', ctx=Load()), kwargs=None), asserts=[])"
372 + <<< sub(1, **d)
373 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname=None, args=[Num(n=1)], keywords=[], starargs=None, kwargs=Name(id='d', ctx=Load())), asserts=[])"
374 + <<< sub(a=1, b=2)
375 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname=None, args=[], keywords=[keyword(arg='a', value=Num(n=1)), keyword(arg='b', value=Num(n=2))], starargs=None, kwargs=None), asserts=[])"
376 + <<< foo::sub()
377 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname='foo', args=[], keywords=[], starargs=None, kwargs=None), asserts=[])"
378 + <<< foo::sub(1, 2)
379 + "AbcdSpec(context=[], body=AbcdInstance(net='sub', asname='foo', args=[Num(n=1), Num(n=2)], keywords=[], starargs=None, kwargs=None), asserts=[])"
380 + """
381 + if len(st) in (3, 6) :
382 + args, keywords, starargs, kwargs = [], [], None, None
383 + else :
384 + args, keywords, starargs, kwargs = self.do(st[-2])
385 + if st[1].text == ':' :
386 + net = st[3].text
387 + asname = st[0].text
388 + else :
389 + net = st[0].text
390 + asname = None
391 + return self.ST.AbcdInstance(lineno=st.srow, col_offset=st.scol,
392 + net=net, asname=asname, args=args,
393 + keywords=keywords, starargs=starargs,
394 + kwargs=kwargs)
395 + def do_abcd_net (self, st, ctx) :
396 + """abcd_net: 'net' NAME parameters ':' abcd_suite
397 + -> ast.AbcdNet
398 +
399 + <<< net Foo () : [True]
400 + ... [False]
401 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdAction(accesses=[], guard=False), asserts=[])"
402 + <<< net Foo (x, y) : [True]
403 + ... [False]
404 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdAction(accesses=[], guard=False), asserts=[])"
405 + """
406 + params = self.do(st[2])
407 + return self.ST.AbcdNet(lineno=st.srow, col_offset=st.scol,
408 + name=st[1].text,
409 + args=params,
410 + body=self.do(st[4]))
411 + def do_abcd_task (self, st, ctx) :
412 + """abcd_task: 'task' NAME typelist '-' '>' typelist ':' abcd_suite
413 + -> ast.AbcdTask
414 +
415 + <<< task Foo (int) -> () : [True]
416 + ... [False]
417 + "AbcdSpec(context=[AbcdTask(name='Foo', body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]), input=[NamedType(name='int')], output=[])], body=AbcdAction(accesses=[], guard=False), asserts=[])"
418 + """
419 + return self.ST.AbcdTask(lineno=st.srow, col_offset=st.scol,
420 + name=st[1].text,
421 + body=self.do(st[-1]),
422 + input=self.do(st[2]),
423 + output=self.do(st[5]))
424 + def do_typelist (self, st, ctx) :
425 + """typelist: '(' [abcd_type (',' abcd_type)*] ')'
426 + -> ast.abcdtype*
427 +
428 + <<< task Foo () -> (int, int, int|bool) : [True]
429 + ... [False]
430 + "AbcdSpec(context=[AbcdTask(name='Foo', body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]), input=[], output=[NamedType(name='int'), NamedType(name='int'), UnionType(types=[NamedType(name='int'), NamedType(name='bool')])])], body=AbcdAction(accesses=[], guard=False), asserts=[])"
431 + """
432 + return [self.do(child) for child in st[1:-1:2]]
433 + def do_abcd_suite (self, st, ctx) :
434 + """abcd_suite: abcd_expr | NEWLINE INDENT abcd_spec DEDENT
435 + -> ast.AbcdSpec
436 +
437 + <<< net Foo () :
438 + ... [True]
439 + ... [False]
440 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdAction(accesses=[], guard=False), asserts=[])"
441 + <<< net Foo () : ([True]
442 + ... + [False])
443 + ... [False]
444 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdFlowOp(left=AbcdAction(accesses=[], guard=True), op=Choice(), right=AbcdAction(accesses=[], guard=False)), asserts=[]))], body=AbcdAction(accesses=[], guard=False), asserts=[])"
445 + """
446 + if len(st) == 1 :
447 + return self.ST.AbcdSpec(lineno=st.srow, col_offset=st.scol,
448 + context=[],
449 + body=self.do(st[0]))
450 + else :
451 + return self.do(st[2])
452 + def do_abcd_buffer (self, st, ctx) :
453 + """[ decorators ] 'buffer' NAME ['[' test ']'] ':' abcd_type '=' testlist
454 + -> ast.AbcdBuffer
455 +
456 + <<< buffer foo : int = ()
457 + ... [True]
458 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=NamedType(name='int'), capacity=None, content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
459 + <<< buffer foo : int = 1, 2
460 + ... [True]
461 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=NamedType(name='int'), capacity=None, content=Tuple(elts=[Num(n=1), Num(n=2)], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
462 + <<< buffer foo : int|bool = ()
463 + ... [True]
464 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=UnionType(types=[NamedType(name='int'), NamedType(name='bool')]), capacity=None, content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
465 + <<< @capacity(max=5)
466 + ... buffer foo : int = ()
467 + ... [True]
468 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=NamedType(name='int'), capacity=[None, Num(n=5)], content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
469 + <<< @capacity(min=2)
470 + ... buffer foo : int = ()
471 + ... [True]
472 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=NamedType(name='int'), capacity=[Num(n=2), None], content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
473 + <<< @capacity(min=2, max=5)
474 + ... buffer foo : int = ()
475 + ... [True]
476 + "AbcdSpec(context=[AbcdBuffer(name='foo', type=NamedType(name='int'), capacity=[Num(n=2), Num(n=5)], content=Tuple(elts=[], ctx=Load()))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
477 + """
478 + if len(st) == 6 : # no decorator, no array
479 + return self.ST.AbcdBuffer(lineno=st.srow, col_offset=st.scol,
480 + name=st[1].text,
481 + type=self.do(st[3]),
482 + capacity=None,
483 + content=self.do(st[-1]))
484 + elif len(st) == 7 : # decorator, no array
485 + deco = self.do_buffer_decorators(st[0], ctx)
486 + return self.ST.AbcdBuffer(lineno=st.srow, col_offset=st.scol,
487 + name=st[2].text,
488 + type=self.do(st[4]),
489 + capacity=deco["capacity"],
490 + content=self.do(st[-1]))
491 + else :
492 + raise ParseError(st.text,
493 + reason="arrays not (yet) supported")
494 + def do_buffer_decorators (self, st, ctx) :
495 + deco = {}
496 + for child in st :
497 + tree = self.do(child)
498 + if isinstance(tree, self.ST.Call) and tree.func.id == "capacity" :
499 + if tree.args or tree.starargs or tree.kwargs :
500 + raise ParseError(child, reason="invalid parameters")
501 + min, max = None, None
502 + for kw in tree.keywords :
503 + if kw.arg == "min" :
504 + min = kw.value
505 + elif kw.arg == "max" :
506 + max = kw.value
507 + else :
508 + raise ParseError(child,
509 + reason="invalid parameter %r" % kw.arg)
510 + if min or max :
511 + deco["capacity"] = [min, max]
512 + else :
513 + deco["capacity"] = None
514 + continue
515 + raise ParseError(child, reason="invalid buffer decorator")
516 + return deco
517 + def do_abcd_typedef (self, st, ctx) :
518 + """abcd_typedef: 'typedef' NAME ':' abcd_type
519 + -> ast.AbcdTypedef
520 +
521 + <<< typedef foo : int
522 + ... [True]
523 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=NamedType(name='int'))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
524 + """
525 + return self.ST.AbcdTypedef(lineno=st.srow, col_offset=st.scol,
526 + name=st[1].text,
527 + type=self.do(st[3]))
528 + def do_abcd_type (self, st, ctx) :
529 + """abcd_type: abcd_and_type ('|' abcd_and_type)*
530 + -> snakes.typing.Type
531 +
532 + <<< typedef foo : int
533 + ... [True]
534 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=NamedType(name='int'))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
535 + <<< typedef foo : int | bool
536 + ... [True]
537 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=UnionType(types=[NamedType(name='int'), NamedType(name='bool')]))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
538 + """
539 + if len(st) == 1 :
540 + return self.do(st[0])
541 + else :
542 + return self.ST.UnionType(lineno=st.srow, col_offset=st.scol,
543 + types=[self.do(child) for child in st[::2]])
544 + def do_abcd_and_type (self, st, ctx) :
545 + """abcd_and_type: abcd_cross_type ('&' abcd_cross_type)*
546 + -> snakes.typing.Type
547 +
548 + <<< typedef foo : int
549 + ... [True]
550 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=NamedType(name='int'))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
551 + <<< typedef foo : int & bool
552 + ... [True]
553 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=IntersectionType(types=[NamedType(name='int'), NamedType(name='bool')]))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
554 + """
555 + if len(st) == 1 :
556 + return self.do(st[0])
557 + else :
558 + return self.ST.IntersectionType(lineno=st.srow, col_offset=st.scol,
559 + types=[self.do(child) for child in st[::2]])
560 + def do_abcd_cross_type (self, st, ctx) :
561 + """abcd_cross_type: abcd_base_type ('*' abcd_base_type)*
562 + -> snakes.typing.Type
563 +
564 + <<< typedef foo : int
565 + ... [True]
566 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=NamedType(name='int'))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
567 + <<< typedef foo : int * bool
568 + ... [True]
569 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=CrossType(types=[NamedType(name='int'), NamedType(name='bool')]))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
570 + """
571 + if len(st) == 1 :
572 + return self.do(st[0])
573 + else :
574 + return self.ST.CrossType(lineno=st.srow, col_offset=st.scol,
575 + types=[self.do(child) for child in st[::2]])
576 + def do_abcd_base_type (self, st, ctx) :
577 + """abcd_base_type: (NAME ['(' abcd_type (',' abcd_type)* ')']
578 + | 'enum' '(' test (',' test)* ')' | '(' abcd_type ')')
579 + -> snakes.typing.Type
580 +
581 + <<< typedef foo : list(int)
582 + ... [True]
583 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=ListType(items=NamedType(name='int')))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
584 + <<< typedef foo : set(int)
585 + ... [True]
586 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=SetType(items=NamedType(name='int')))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
587 + <<< typedef foo : dict(int, bool)
588 + ... [True]
589 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=DictType(keys=NamedType(name='int'), values=NamedType(name='bool')))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
590 + <<< typedef foo : enum(1, 2, 3)
591 + ... [True]
592 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=EnumType(items=[Num(n=1), Num(n=2), Num(n=3)]))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
593 + <<< typedef foo : int
594 + ... [True]
595 + "AbcdSpec(context=[AbcdTypedef(name='foo', type=NamedType(name='int'))], body=AbcdAction(accesses=[], guard=True), asserts=[])"
596 + """
597 + if len(st) == 1 :
598 + return self.ST.NamedType(lineno=st.srow, col_offset=st.scol,
599 + name=st[0].text)
600 + elif len(st) == 3 :
601 + return self.do(st[1])
602 + elif st[0].text in ("list", "set", "tuple") :
603 + if len(st) > 4 :
604 + raise ParseError(st.text,
605 + reason="too many arguments for %s type"
606 + % st[0].text)
607 + if st[0].text == "list" :
608 + tree = self.ST.ListType
609 + elif st[0].text == "tuple" :
610 + tree = self.ST.TupleType
611 + else :
612 + tree = self.ST.SetType
613 + return tree(lineno=st.srow, col_offset=st.scol,
614 + items=self.do(st[2]))
615 + elif st[0].text == "dict" :
616 + if len(st) > 6 :
617 + raise ParseError(st.text,
618 + reason="too many arguments for dict type")
619 + return self.ST.DictType(lineno=st.srow, col_offset=st.scol,
620 + keys=self.do(st[2]),
621 + values=self.do(st[4]))
622 + elif st[0].text == "enum" :
623 + return self.ST.EnumType(lineno=st.srow, col_offset=st.scol,
624 + items=[self.do(child) for child in st[2:-1:2]])
625 + else :
626 + raise ParseError(st[0].text,
627 + reason=("expected 'enum', 'list', 'set' or"
628 + " 'dict' but found '%s'") % st[0].text)
629 + def do_tfpdef (self, st, ctx) :
630 + """tfpdef: NAME [':' ('net' | 'buffer' | 'task')]
631 + -> str, ast.AST?
632 +
633 + <<< net Foo (x, y, n:net, b:buffer, t:task) : [True]
634 + ... [False]
635 + "AbcdSpec(context=[AbcdNet(name='Foo', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None), arg(arg='n', annotation=Name(id='net', ctx=Load())), arg(arg='b', annotation=Name(id='buffer', ctx=Load())), arg(arg='t', annotation=Name(id='task', ctx=Load()))], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=AbcdSpec(context=[], body=AbcdAction(accesses=[], guard=True), asserts=[]))], body=AbcdAction(accesses=[], guard=False), asserts=[])"
636 + """
637 + if len(st) == 1 :
638 + return st[0].text, None
639 + else :
640 + return st[0].text, self.ST.Name(lineno=st[2].srow,
641 + col_offset=st[2].scol,
642 + id=st[2].text,
643 + ctx=ctx())
644 +
645 +parse = Translator.parse
646 +
647 +if __name__ == "__main__" :
648 + testparser(Translator)
This diff could not be displayed because it is too large.
1 +import sys, datetime
2 +from snakes.lang.pylib import asdl
3 +from collections import defaultdict
4 +from functools import partial
5 +
6 +class memoize(object):
7 + def __init__(self, function):
8 + self.function = function
9 + self.memoized = {}
10 +
11 + def __call__(self, *args):
12 + try:
13 + return self.memoized[args]
14 + except KeyError:
15 + call = self.function(*args)
16 + self.memoized[args] = call
17 + return call
18 +
19 + def __get__(self, obj, objtype):
20 + """Support instance methods."""
21 + return partial(self.__call__, obj)
22 +
23 +def component_has_cycle(node, graph, proceeding, visited):
24 + if node in visited:
25 + return False
26 + if node in proceeding:
27 + proceeding.append(node) # populate trace
28 + return True
29 + proceeding.append(node)
30 + if node in graph:
31 + for successor in graph[node]:
32 + if component_has_cycle(successor, graph, proceeding, visited):
33 + return True
34 + proceeding.remove(node)
35 + visited.add(node)
36 + return False
37 +
38 +def has_cycle(graph):
39 + visited = set()
40 + proceeding = list()
41 + todo = set(graph.keys())
42 +
43 + while todo:
44 + node = todo.pop()
45 + if component_has_cycle(node, graph, proceeding, visited):
46 + i = proceeding.index(proceeding[-1])
47 + return proceeding[i:]
48 + todo.difference_update(visited)
49 + return []
50 +
51 +class CyclicDependencies(Exception):
52 + def __init__(self, seq):
53 + self.seq = seq
54 +
55 + def __str__(self):
56 + return "cyclic dependencies: {}".format(" -> ".join(self.seq))
57 +
58 +def remove_duplicates(l):
59 + d = {}
60 + nl = []
61 + for e in l:
62 + if not e in d:
63 + d[e] = 1
64 + nl.append(e)
65 + return nl
66 +
67 +class CodeGen (asdl.VisitorBase) :
68 +
69 + def __init__(self, node):
70 + asdl.VisitorBase.__init__(self)
71 +
72 + self.starting_node = None
73 + self.current_node = None
74 + self.hierarchy = defaultdict(list)
75 + self.hierarchy['_AST'] = []
76 + self.fields = defaultdict(list)
77 + self.attributes = defaultdict(list)
78 + self.code = defaultdict(list)
79 +
80 + self.visit(node)
81 + ret = has_cycle(self.hierarchy)
82 + if ret:
83 + raise CyclicDependencies(ret)
84 + self._gen_code(node)
85 +
86 + def visitModule(self, node):
87 + for name, child in node.types.items():
88 + if not self.starting_node:
89 + self.starting_node = str(name)
90 + self.current_node = str(name)
91 + self.hierarchy[name]
92 + self.visit(child)
93 +
94 + def visitSum(self, node):
95 + if hasattr(node, "fields"):
96 + self.fields[self.current_node] = node.fields
97 + else:
98 + self.fields[self.current_node] = []
99 + if hasattr(node, "attributes"):
100 + self.attributes[self.current_node] = node.attributes
101 + else:
102 + self.attributes[self.current_node] = []
103 +
104 + for child in node.types:
105 + self.visit(child)
106 +
107 + def visitConstructor (self, node):
108 + if str(node.name) in self.fields:
109 + #print >> sys.stderr, "constructor '{!s}' appears twice !".format(node.name)
110 + #exit(0)
111 + return
112 + self.fields[str(node.name)].extend(node.fields)
113 + self.hierarchy[str(node.name)].append(self.current_node)
114 +
115 + def visitProduct(self, node):
116 + self.fields[self.current_node].extend(node.fields)
117 +
118 + @memoize
119 + def _get_fields(self, name):
120 + if self.fields.has_key(name):
121 + fields = map(lambda f : f.name, self.fields[name])
122 + for parent in self.hierarchy[name]:
123 + fields.extend(self._get_fields(parent))
124 + return fields
125 + else:
126 + return []
127 +
128 + @memoize
129 + def _get_attributes(self, name):
130 + if name == '_AST':
131 + return []
132 + attributes = map(lambda a : a.name, self.attributes[name])
133 + for parent in self.hierarchy[name]:
134 + attributes.extend(self._get_attributes(parent))
135 + return attributes
136 +
137 + def _gen_code(self, node):
138 + is_methods = []
139 + for name in sorted(self.hierarchy):
140 + if name != '_AST':
141 + is_methods.extend(["",
142 + "def is{!s}(self):".format(name),
143 + ["return False"]
144 + ])
145 + cls = ["class _AST (ast.AST):",
146 + ["_fields = ()",
147 + "_attributes = ()",
148 + "",
149 + "def __init__ (self, **ARGS):",
150 + ["ast.AST.__init__(self)",
151 + "for k, v in ARGS.items():",
152 + ["setattr(self, k, v)"]
153 + ]
154 + ] + is_methods
155 + ]
156 + self.code['_AST'] = cls
157 +
158 + for name, parents in self.hierarchy.iteritems():
159 + if name == '_AST':
160 + continue
161 + if not parents:
162 + parents = ['_AST']
163 + fields = self.fields[name]
164 + args = []
165 + assign = []
166 + body = []
167 + _fields = remove_duplicates(self._get_fields(name))
168 + _attributes = remove_duplicates(self._get_attributes(name))
169 +
170 + body = []
171 + cls = ["class {!s} ({!s}):".format(name, ", ".join(parents)), body]
172 +
173 + non_default_args = []
174 + default_args = []
175 + for f in fields:
176 + if f.name.value == 'ctx':
177 + f.opt = True
178 +
179 + if f.opt:
180 + default_args.append("{!s}=None".format(f.name))
181 + assign.append("self.{0!s} = {0!s}".format(f.name))
182 + elif f.seq:
183 + default_args.append("{!s}=[]".format(f.name))
184 + assign.append("self.{0!s} = list({0!s})".format(f.name))
185 + else:
186 + non_default_args.append("{!s}".format(f.name))
187 + assign.append("self.{0!s} = {0!s}".format(f.name))
188 +
189 + args = non_default_args + default_args
190 +
191 + body.append("_fields = {!r}".format( tuple(map(repr, _fields))))
192 + body.append("_attributes = {!r}".format( tuple(map(repr, _attributes))))
193 + body.append("")
194 + # ctor
195 + args_str = ", ".join(args)
196 + if args_str != "":
197 + args_str += ", "
198 + body.append("def __init__ (self, {!s} **ARGS):".format(args_str))
199 + ctor_body = []
200 + body.append(ctor_body)
201 + ctor_body.extend(map(lambda base : "{!s}.__init__(self, **ARGS)".format(base), parents))
202 + ctor_body.extend(assign)
203 +
204 + body.extend(["", "def is{}(self):".format(name), ["return True"]])
205 +
206 + self.code[name] = cls
207 +
208 + @memoize
209 + def _cost(self, name):
210 + # print "call cost {}".format(name)
211 + if name == '_AST':
212 + return 0
213 + parents = self.hierarchy[name]
214 + return reduce(lambda acc, x: acc + self._cost(x), parents, 1)
215 +
216 + @property
217 + def python(self):
218 +
219 + classes = self.hierarchy.keys()
220 + classes.sort(lambda a, b: self._cost(a) - self._cost(b))
221 +
222 + code = ["from snakes.lang import ast",
223 + "from ast import *",
224 + ""]
225 +
226 + for cls in classes:
227 + code.extend(self.code[cls])
228 + code.append("")
229 +
230 + def python (code, indent) :
231 + for line in code :
232 + if isinstance(line, str) :
233 + yield (4*indent) * " " + line
234 + else :
235 + for sub in python(line, indent+1) :
236 + yield sub
237 + return "\n".join(python(code, 0))
238 +
239 +def compile_asdl(infilename, outfilename):
240 + """ Helper function to compile asdl files. """
241 +
242 + infile = open(infilename, 'r')
243 + outfile = open(outfilename, 'w')
244 +
245 + scanner = asdl.ASDLScanner()
246 + parser = asdl.ASDLParser()
247 + tokens = scanner.tokenize(infile.read())
248 + node = parser.parse(tokens)
249 +
250 + outfile.write(("# this file has been automatically generated running:\n"
251 + "# %s\n# timestamp: %s\n\n") % (" ".join(sys.argv),
252 + datetime.datetime.now()))
253 + outfile.write(CodeGen(node).python)
254 + outfile.close()
255 + infile.close()
256 +
257 +if __name__ == "__main__":
258 + # a simple CLI
259 + import getopt
260 + outfile = sys.stdout
261 + try :
262 + opts, args = getopt.getopt(sys.argv[1:], "ho:",
263 + ["help", "output="])
264 + if ("-h", "") in opts or ("--help", "") in opts :
265 + opts = [("-h", "")]
266 + args = [None]
267 + elif not args :
268 + raise getopt.GetoptError("no input file provided"
269 + " (try -h to get help)")
270 + elif len(args) > 1 :
271 + raise getopt.GetoptError("more than one input file provided")
272 + except getopt.GetoptError :
273 + sys.stderr.write("%s: %s\n" % (__file__, sys.exc_info()[1]))
274 + sys.exit(1)
275 + for (flag, arg) in opts :
276 + if flag in ("-h", "--help") :
277 + print("""usage: %s [OPTIONS] INFILE
278 + Options:
279 + -h, --help print this help and exit
280 + --output=OUTPUT set output file""" % __file__)
281 + sys.exit(0)
282 + elif flag in ("-o", "--output") :
283 + outfile = open(arg, "w")
284 + scanner = asdl.ASDLScanner()
285 + parser = asdl.ASDLParser()
286 + tokens = scanner.tokenize(open(args[0]).read())
287 + node = parser.parse(tokens)
288 + outfile.write(("# this file has been automatically generated running:\n"
289 + "# %s\n# timestamp: %s\n\n") % (" ".join(sys.argv),
290 + datetime.datetime.now()))
291 + try:
292 + outfile.write(CodeGen(node).python)
293 + except CyclicDependencies as cycle:
294 + msg = "[E] {!s}".format(cycle)
295 + outfile.write(msg)
296 + if outfile != sys.stdout:
297 + print >> sys.stderr, msg
298 + outfile.close()
1 +"""Backport of Python 2.6 'ast' module.
2 +"""
3 +
4 +from _ast import *
5 +
6 +def parse(expr, filename='<unknown>', mode='exec'):
7 + """
8 + Parse an expression into an AST node.
9 + Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
10 + """
11 + return compile(expr, filename, mode, PyCF_ONLY_AST)
12 +
13 +
14 +def literal_eval(node_or_string):
15 + """
16 + Safely evaluate an expression node or a string containing a Python
17 + expression. The string or node provided may only consist of the following
18 + Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
19 + and None.
20 + """
21 + _safe_names = {'None': None, 'True': True, 'False': False}
22 + if isinstance(node_or_string, basestring):
23 + node_or_string = parse(node_or_string, mode='eval')
24 + if isinstance(node_or_string, Expression):
25 + node_or_string = node_or_string.body
26 + def _convert(node):
27 + if isinstance(node, Str):
28 + return node.s
29 + elif isinstance(node, Num):
30 + return node.n
31 + elif isinstance(node, Tuple):
32 + return tuple(map(_convert, node.elts))
33 + elif isinstance(node, List):
34 + return list(map(_convert, node.elts))
35 + elif isinstance(node, Dict):
36 + return dict((_convert(k), _convert(v)) for k, v
37 + in zip(node.keys, node.values))
38 + elif isinstance(node, Name):
39 + if node.id in _safe_names:
40 + return _safe_names[node.id]
41 + raise ValueError('malformed string')
42 + return _convert(node_or_string)
43 +
44 +
45 +def dump(node, annotate_fields=True, include_attributes=False):
46 + """
47 + Return a formatted dump of the tree in *node*. This is mainly useful for
48 + debugging purposes. The returned string will show the names and the values
49 + for fields. This makes the code impossible to evaluate, so if evaluation is
50 + wanted *annotate_fields* must be set to False. Attributes such as line
51 + numbers and column offsets are not dumped by default. If this is wanted,
52 + *include_attributes* can be set to True.
53 + """
54 + def _format(node):
55 + if isinstance(node, AST):
56 + fields = [(a, _format(b)) for a, b in iter_fields(node)]
57 + rv = '%s(%s' % (node.__class__.__name__, ', '.join(
58 + ('%s=%s' % field for field in fields)
59 + if annotate_fields else
60 + (b for a, b in fields)
61 + ))
62 + if include_attributes and node._attributes:
63 + rv += fields and ', ' or ' '
64 + rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
65 + for a in node._attributes)
66 + return rv + ')'
67 + elif isinstance(node, list):
68 + return '[%s]' % ', '.join(_format(x) for x in node)
69 + return repr(node)
70 + if not isinstance(node, AST):
71 + raise TypeError('expected AST, got %r' % node.__class__.__name__)
72 + return _format(node)
73 +
74 +
75 +def copy_location(new_node, old_node):
76 + """
77 + Copy source location (`lineno` and `col_offset` attributes) from
78 + *old_node* to *new_node* if possible, and return *new_node*.
79 + """
80 + for attr in 'lineno', 'col_offset':
81 + if attr in old_node._attributes and attr in new_node._attributes \
82 + and hasattr(old_node, attr):
83 + setattr(new_node, attr, getattr(old_node, attr))
84 + return new_node
85 +
86 +
87 +def fix_missing_locations(node):
88 + """
89 + When you compile a node tree with compile(), the compiler expects lineno and
90 + col_offset attributes for every node that supports them. This is rather
91 + tedious to fill in for generated nodes, so this helper adds these attributes
92 + recursively where not already set, by setting them to the values of the
93 + parent node. It works recursively starting at *node*.
94 + """
95 + def _fix(node, lineno, col_offset):
96 + if 'lineno' in node._attributes:
97 + if not hasattr(node, 'lineno'):
98 + node.lineno = lineno
99 + else:
100 + lineno = node.lineno
101 + if 'col_offset' in node._attributes:
102 + if not hasattr(node, 'col_offset'):
103 + node.col_offset = col_offset
104 + else:
105 + col_offset = node.col_offset
106 + for child in iter_child_nodes(node):
107 + _fix(child, lineno, col_offset)
108 + _fix(node, 1, 0)
109 + return node
110 +
111 +
112 +def increment_lineno(node, n=1):
113 + """
114 + Increment the line number of each node in the tree starting at *node* by *n*.
115 + This is useful to "move code" to a different location in a file.
116 + """
117 + if 'lineno' in node._attributes:
118 + node.lineno = getattr(node, 'lineno', 0) + n
119 + for child in walk(node):
120 + if 'lineno' in child._attributes:
121 + child.lineno = getattr(child, 'lineno', 0) + n
122 + return node
123 +
124 +
125 +def iter_fields(node):
126 + """
127 + Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
128 + that is present on *node*.
129 + """
130 + for field in node._fields:
131 + try:
132 + yield field, getattr(node, field)
133 + except AttributeError:
134 + pass
135 +
136 +
137 +def iter_child_nodes(node):
138 + """
139 + Yield all direct child nodes of *node*, that is, all fields that are nodes
140 + and all items of fields that are lists of nodes.
141 + """
142 + for name, field in iter_fields(node):
143 + if isinstance(field, AST):
144 + yield field
145 + elif isinstance(field, list):
146 + for item in field:
147 + if isinstance(item, AST):
148 + yield item
149 +
150 +
151 +def get_docstring(node, clean=True):
152 + """
153 + Return the docstring for the given node or None if no docstring can
154 + be found. If the node provided does not have docstrings a TypeError
155 + will be raised.
156 + """
157 + if not isinstance(node, (FunctionDef, ClassDef, Module)):
158 + raise TypeError("%r can't have docstrings" % node.__class__.__name__)
159 + if node.body and isinstance(node.body[0], Expr) and \
160 + isinstance(node.body[0].value, Str):
161 + if clean:
162 + import inspect
163 + return inspect.cleandoc(node.body[0].value.s)
164 + return node.body[0].value.s
165 +
166 +
167 +def walk(node):
168 + """
169 + Recursively yield all child nodes of *node*, in no specified order. This is
170 + useful if you only want to modify nodes in place and don't care about the
171 + context.
172 + """
173 + from collections import deque
174 + todo = deque([node])
175 + while todo:
176 + node = todo.popleft()
177 + todo.extend(iter_child_nodes(node))
178 + yield node
179 +
180 +
181 +class NodeVisitor(object):
182 + """
183 + A node visitor base class that walks the abstract syntax tree and calls a
184 + visitor function for every node found. This function may return a value
185 + which is forwarded by the `visit` method.
186 +
187 + This class is meant to be subclassed, with the subclass adding visitor
188 + methods.
189 +
190 + Per default the visitor functions for the nodes are ``'visit_'`` +
191 + class name of the node. So a `TryFinally` node visit function would
192 + be `visit_TryFinally`. This behavior can be changed by overriding
193 + the `visit` method. If no visitor function exists for a node
194 + (return value `None`) the `generic_visit` visitor is used instead.
195 +
196 + Don't use the `NodeVisitor` if you want to apply changes to nodes during
197 + traversing. For this a special visitor exists (`NodeTransformer`) that
198 + allows modifications.
199 + """
200 +
201 + def visit(self, node):
202 + """Visit a node."""
203 + method = 'visit_' + node.__class__.__name__
204 + visitor = getattr(self, method, self.generic_visit)
205 + return visitor(node)
206 +
207 + def generic_visit(self, node):
208 + """Called if no explicit visitor function exists for a node."""
209 + for field, value in iter_fields(node):
210 + if isinstance(value, list):
211 + for item in value:
212 + if isinstance(item, AST):
213 + self.visit(item)
214 + elif isinstance(value, AST):
215 + self.visit(value)
216 +
217 +
218 +class NodeTransformer(NodeVisitor):
219 + """
220 + A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
221 + allows modification of nodes.
222 +
223 + The `NodeTransformer` will walk the AST and use the return value of the
224 + visitor methods to replace or remove the old node. If the return value of
225 + the visitor method is ``None``, the node will be removed from its location,
226 + otherwise it is replaced with the return value. The return value may be the
227 + original node in which case no replacement takes place.
228 +
229 + Here is an example transformer that rewrites all occurrences of name lookups
230 + (``foo``) to ``data['foo']``::
231 +
232 + class RewriteName(NodeTransformer):
233 +
234 + def visit_Name(self, node):
235 + return copy_location(Subscript(
236 + value=Name(id='data', ctx=Load()),
237 + slice=Index(value=Str(s=node.id)),
238 + ctx=node.ctx
239 + ), node)
240 +
241 + Keep in mind that if the node you're operating on has child nodes you must
242 + either transform the child nodes yourself or call the :meth:`generic_visit`
243 + method for the node first.
244 +
245 + For nodes that were part of a collection of statements (that applies to all
246 + statement nodes), the visitor may also return a list of nodes rather than
247 + just a single node.
248 +
249 + Usually you use the transformer like this::
250 +
251 + node = YourTransformer().visit(node)
252 + """
253 +
254 + def generic_visit(self, node):
255 + for field, old_value in iter_fields(node):
256 + old_value = getattr(node, field, None)
257 + if isinstance(old_value, list):
258 + new_values = []
259 + for value in old_value:
260 + if isinstance(value, AST):
261 + value = self.visit(value)
262 + if value is None:
263 + continue
264 + elif not isinstance(value, AST):
265 + new_values.extend(value)
266 + continue
267 + new_values.append(value)
268 + old_value[:] = new_values
269 + elif isinstance(old_value, AST):
270 + new_node = self.visit(old_value)
271 + if new_node is None:
272 + delattr(node, field)
273 + else:
274 + setattr(node, field, new_node)
275 + return node
1 +"""Backport of Python 2.6 'ast' module.
2 +
3 +AST classes are wrappers around classes in Python 2.5 '_ast' module.
4 +The rest is copied from Python 2.6 'ast.py'.
5 +"""
6 +
7 +import inspect
8 +import _ast
9 +from _ast import PyCF_ONLY_AST
10 +from _ast import AST
11 +
12 +for name, cls in inspect.getmembers(_ast, inspect.isclass) :
13 + if issubclass(cls, AST) :
14 + if cls is AST :
15 + continue
16 + class _Ast (cls, AST) :
17 + def __init__ (self, *larg, **karg) :
18 + if len(larg) > 0 and len(larg) != len(self._fields) :
19 + raise TypeError, ("%s constructor takes either 0 or "
20 + "%u positional arguments"
21 + % (self.__class__.__name__,
22 + len(self._fields)))
23 + for name, arg in zip(self._fields, larg) + karg.items() :
24 + if name in self._fields :
25 + setattr(self, name, arg)
26 + try :
27 + _Ast._fields = tuple(cls._fields)
28 + except :
29 + _Ast._fields = ()
30 + _Ast.__name__ = name
31 + globals()[name] = _Ast
32 +
33 +del _Ast, cls, name
34 +
35 +def literal_eval(node_or_string):
36 + """
37 + Safely evaluate an expression node or a string containing a Python
38 + expression. The string or node provided may only consist of the following
39 + Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
40 + and None.
41 + """
42 + _safe_names = {'None': None, 'True': True, 'False': False}
43 + if isinstance(node_or_string, basestring):
44 + node_or_string = parse(node_or_string, mode='eval')
45 + if isinstance(node_or_string, Expression):
46 + node_or_string = node_or_string.body
47 + def _convert(node):
48 + if isinstance(node, Str):
49 + return node.s
50 + elif isinstance(node, Num):
51 + return node.n
52 + elif isinstance(node, Tuple):
53 + return tuple(map(_convert, node.elts))
54 + elif isinstance(node, List):
55 + return list(map(_convert, node.elts))
56 + elif isinstance(node, Dict):
57 + return dict((_convert(k), _convert(v)) for k, v
58 + in zip(node.keys, node.values))
59 + elif isinstance(node, Name):
60 + if node.id in _safe_names:
61 + return _safe_names[node.id]
62 + raise ValueError('malformed string')
63 + return _convert(node_or_string)
64 +
65 +def dump(node, annotate_fields=True, include_attributes=False):
66 + """
67 + Return a formatted dump of the tree in *node*. This is mainly useful for
68 + debugging purposes. The returned string will show the names and the values
69 + for fields. This makes the code impossible to evaluate, so if evaluation is
70 + wanted *annotate_fields* must be set to False. Attributes such as line
71 + numbers and column offsets are not dumped by default. If this is wanted,
72 + *include_attributes* can be set to True.
73 + """
74 + def _format(node):
75 + if isinstance(node, AST):
76 + fields = [(a, _format(b)) for a, b in iter_fields(node)]
77 + rv = '%s(%s' % (node.__class__.__name__, ', '.join(
78 + ('%s=%s' % field for field in fields)
79 + if annotate_fields else
80 + (b for a, b in fields)
81 + ))
82 + if include_attributes and node._attributes:
83 + rv += fields and ', ' or ' '
84 + rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
85 + for a in node._attributes)
86 + return rv + ')'
87 + elif isinstance(node, list):
88 + return '[%s]' % ', '.join(_format(x) for x in node)
89 + return repr(node)
90 + if not isinstance(node, AST):
91 + raise TypeError('expected AST, got %r' % node.__class__.__name__)
92 + return _format(node)
93 +
94 +def copy_location(new_node, old_node):
95 + """
96 + Copy source location (`lineno` and `col_offset` attributes) from
97 + *old_node* to *new_node* if possible, and return *new_node*.
98 + """
99 + for attr in 'lineno', 'col_offset':
100 + if attr in old_node._attributes and attr in new_node._attributes \
101 + and hasattr(old_node, attr):
102 + setattr(new_node, attr, getattr(old_node, attr))
103 + return new_node
104 +
105 +def fix_missing_locations(node):
106 + """
107 + When you compile a node tree with compile(), the compiler expects lineno and
108 + col_offset attributes for every node that supports them. This is rather
109 + tedious to fill in for generated nodes, so this helper adds these attributes
110 + recursively where not already set, by setting them to the values of the
111 + parent node. It works recursively starting at *node*.
112 + """
113 + def _fix(node, lineno, col_offset):
114 + if 'lineno' in node._attributes:
115 + if not hasattr(node, 'lineno'):
116 + node.lineno = lineno
117 + else:
118 + lineno = node.lineno
119 + if 'col_offset' in node._attributes:
120 + if not hasattr(node, 'col_offset'):
121 + node.col_offset = col_offset
122 + else:
123 + col_offset = node.col_offset
124 + for child in iter_child_nodes(node):
125 + _fix(child, lineno, col_offset)
126 + _fix(node, 1, 0)
127 + return node
128 +
129 +def increment_lineno(node, n=1):
130 + """
131 + Increment the line number of each node in the tree starting at *node* by *n*.
132 + This is useful to "move code" to a different location in a file.
133 + """
134 + if 'lineno' in node._attributes:
135 + node.lineno = getattr(node, 'lineno', 0) + n
136 + for child in walk(node):
137 + if 'lineno' in child._attributes:
138 + child.lineno = getattr(child, 'lineno', 0) + n
139 + return node
140 +
141 +def iter_fields(node):
142 + """
143 + Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
144 + that is present on *node*.
145 + """
146 + for field in node._fields:
147 + try:
148 + yield field, getattr(node, field)
149 + except AttributeError:
150 + pass
151 +
152 +def iter_child_nodes(node):
153 + """
154 + Yield all direct child nodes of *node*, that is, all fields that are nodes
155 + and all items of fields that are lists of nodes.
156 + """
157 + for name, field in iter_fields(node):
158 + if isinstance(field, AST):
159 + yield field
160 + elif isinstance(field, list):
161 + for item in field:
162 + if isinstance(item, AST):
163 + yield item
164 +
165 +def get_docstring(node, clean=True):
166 + """
167 + Return the docstring for the given node or None if no docstring can
168 + be found. If the node provided does not have docstrings a TypeError
169 + will be raised.
170 + """
171 + if not isinstance(node, (FunctionDef, ClassDef, Module)):
172 + raise TypeError("%r can't have docstrings" % node.__class__.__name__)
173 + if node.body and isinstance(node.body[0], Expr) and \
174 + isinstance(node.body[0].value, Str):
175 + if clean:
176 + return inspect.cleandoc(node.body[0].value.s)
177 + return node.body[0].value.s
178 +
179 +def walk(node):
180 + """
181 + Recursively yield all child nodes of *node*, in no specified order. This is
182 + useful if you only want to modify nodes in place and don't care about the
183 + context.
184 + """
185 + from collections import deque
186 + todo = deque([node])
187 + while todo:
188 + node = todo.popleft()
189 + todo.extend(iter_child_nodes(node))
190 + yield node
191 +
192 +class NodeVisitor(object):
193 + """
194 + A node visitor base class that walks the abstract syntax tree and calls a
195 + visitor function for every node found. This function may return a value
196 + which is forwarded by the `visit` method.
197 +
198 + This class is meant to be subclassed, with the subclass adding visitor
199 + methods.
200 +
201 + Per default the visitor functions for the nodes are ``'visit_'`` +
202 + class name of the node. So a `TryFinally` node visit function would
203 + be `visit_TryFinally`. This behavior can be changed by overriding
204 + the `visit` method. If no visitor function exists for a node
205 + (return value `None`) the `generic_visit` visitor is used instead.
206 +
207 + Don't use the `NodeVisitor` if you want to apply changes to nodes during
208 + traversing. For this a special visitor exists (`NodeTransformer`) that
209 + allows modifications.
210 + """
211 + def visit(self, node):
212 + """Visit a node."""
213 + method = 'visit_' + node.__class__.__name__
214 + visitor = getattr(self, method, self.generic_visit)
215 + return visitor(node)
216 + def generic_visit(self, node):
217 + """Called if no explicit visitor function exists for a node."""
218 + for field, value in iter_fields(node):
219 + if isinstance(value, list):
220 + for item in value:
221 + if isinstance(item, AST):
222 + self.visit(item)
223 + elif isinstance(value, AST):
224 + self.visit(value)
225 +
226 +class NodeTransformer(NodeVisitor):
227 + """
228 + A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
229 + allows modification of nodes.
230 +
231 + The `NodeTransformer` will walk the AST and use the return value of the
232 + visitor methods to replace or remove the old node. If the return value of
233 + the visitor method is ``None``, the node will be removed from its location,
234 + otherwise it is replaced with the return value. The return value may be the
235 + original node in which case no replacement takes place.
236 +
237 + Here is an example transformer that rewrites all occurrences of name lookups
238 + (``foo``) to ``data['foo']``::
239 +
240 + class RewriteName(NodeTransformer):
241 +
242 + def visit_Name(self, node):
243 + return copy_location(Subscript(
244 + value=Name(id='data', ctx=Load()),
245 + slice=Index(value=Str(s=node.id)),
246 + ctx=node.ctx
247 + ), node)
248 +
249 + Keep in mind that if the node you're operating on has child nodes you must
250 + either transform the child nodes yourself or call the :meth:`generic_visit`
251 + method for the node first.
252 +
253 + For nodes that were part of a collection of statements (that applies to all
254 + statement nodes), the visitor may also return a list of nodes rather than
255 + just a single node.
256 +
257 + Usually you use the transformer like this::
258 +
259 + node = YourTransformer().visit(node)
260 + """
261 + def generic_visit(self, node):
262 + for field, old_value in iter_fields(node):
263 + old_value = getattr(node, field, None)
264 + if isinstance(old_value, list):
265 + new_values = []
266 + for value in old_value:
267 + if isinstance(value, AST):
268 + value = self.visit(value)
269 + if value is None:
270 + continue
271 + elif not isinstance(value, AST):
272 + new_values.extend(value)
273 + continue
274 + new_values.append(value)
275 + old_value[:] = new_values
276 + elif isinstance(old_value, AST):
277 + new_node = self.visit(old_value)
278 + if new_node is None:
279 + delattr(node, field)
280 + else:
281 + setattr(node, field, new_node)
282 + return node
283 +
284 +def _ast2ast (node) :
285 + new = globals()[node.__class__.__name__]()
286 + if not hasattr(node, "_fields") or node._fields is None :
287 + node._fields = ()
288 + for name, field in iter_fields(node) :
289 + new_field = field
290 + if field is None :
291 + new_field = None
292 + elif isinstance(field, AST) :
293 + new_field = _ast2ast(field)
294 + elif isinstance(field, list) :
295 + new_field = []
296 + for value in field :
297 + if isinstance(value, AST) :
298 + new_field.append(_ast2ast(value))
299 + else :
300 + new_field.append(value)
301 + setattr(new, name, new_field)
302 + copy_location(new, node)
303 + return new
304 +
305 +def parse(expr, filename='<unknown>', mode='exec'):
306 + """
307 + Parse an expression into an AST node.
308 + Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
309 + """
310 + return _ast2ast(compile(expr, filename, mode, PyCF_ONLY_AST))
1 +"""Backport of Python 2.6 'ast' module.
2 +"""
3 +
4 +import _ast
5 +from _ast import *
6 +
7 +try :
8 + PyCF_ONLY_AST
9 +except NameError :
10 + PyCF_ONLY_AST = PyCF_AST_ONLY
11 +
12 +def parse(expr, filename='<unknown>', mode='exec'):
13 + """
14 + Parse an expression into an AST node.
15 + Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
16 + """
17 + return compile(expr, filename, mode, PyCF_ONLY_AST)
18 +
19 +def literal_eval(node_or_string):
20 + """
21 + Safely evaluate an expression node or a string containing a Python
22 + expression. The string or node provided may only consist of the following
23 + Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
24 + and None.
25 + """
26 + _safe_names = {'None': None, 'True': True, 'False': False}
27 + if isinstance(node_or_string, basestring):
28 + node_or_string = parse(node_or_string, mode='eval')
29 + if isinstance(node_or_string, Expression):
30 + node_or_string = node_or_string.body
31 + def _convert(node):
32 + if isinstance(node, Str):
33 + return node.s
34 + elif isinstance(node, Num):
35 + return node.n
36 + elif isinstance(node, Tuple):
37 + return tuple(map(_convert, node.elts))
38 + elif isinstance(node, List):
39 + return list(map(_convert, node.elts))
40 + elif isinstance(node, Dict):
41 + return dict((_convert(k), _convert(v)) for k, v
42 + in zip(node.keys, node.values))
43 + elif isinstance(node, Name):
44 + if node.id in _safe_names:
45 + return _safe_names[node.id]
46 + raise ValueError('malformed string')
47 + return _convert(node_or_string)
48 +
49 +
50 +def dump(node, annotate_fields=True, include_attributes=False):
51 + """
52 + Return a formatted dump of the tree in *node*. This is mainly useful for
53 + debugging purposes. The returned string will show the names and the values
54 + for fields. This makes the code impossible to evaluate, so if evaluation is
55 + wanted *annotate_fields* must be set to False. Attributes such as line
56 + numbers and column offsets are not dumped by default. If this is wanted,
57 + *include_attributes* can be set to True.
58 + """
59 + def _format(node):
60 + if isinstance(node, AST):
61 + fields = [(a, _format(b)) for a, b in iter_fields(node)]
62 + rv = '%s(%s' % (node.__class__.__name__, ', '.join(
63 + ('%s=%s' % field for field in fields)
64 + if annotate_fields else
65 + (b for a, b in fields)
66 + ))
67 + if include_attributes and node._attributes:
68 + rv += fields and ', ' or ' '
69 + rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
70 + for a in node._attributes)
71 + return rv + ')'
72 + elif isinstance(node, list):
73 + return '[%s]' % ', '.join(_format(x) for x in node)
74 + return repr(node)
75 + if not isinstance(node, AST):
76 + raise TypeError('expected AST, got %r' % node.__class__.__name__)
77 + return _format(node)
78 +
79 +
80 +def copy_location(new_node, old_node):
81 + """
82 + Copy source location (`lineno` and `col_offset` attributes) from
83 + *old_node* to *new_node* if possible, and return *new_node*.
84 + """
85 + for attr in 'lineno', 'col_offset':
86 + if attr in old_node._attributes and attr in new_node._attributes \
87 + and hasattr(old_node, attr):
88 + setattr(new_node, attr, getattr(old_node, attr))
89 + return new_node
90 +
91 +
92 +def fix_missing_locations(node):
93 + """
94 + When you compile a node tree with compile(), the compiler expects lineno and
95 + col_offset attributes for every node that supports them. This is rather
96 + tedious to fill in for generated nodes, so this helper adds these attributes
97 + recursively where not already set, by setting them to the values of the
98 + parent node. It works recursively starting at *node*.
99 + """
100 + def _fix(node, lineno, col_offset):
101 + if 'lineno' in node._attributes:
102 + if not hasattr(node, 'lineno'):
103 + node.lineno = lineno
104 + else:
105 + lineno = node.lineno
106 + if 'col_offset' in node._attributes:
107 + if not hasattr(node, 'col_offset'):
108 + node.col_offset = col_offset
109 + else:
110 + col_offset = node.col_offset
111 + for child in iter_child_nodes(node):
112 + _fix(child, lineno, col_offset)
113 + _fix(node, 1, 0)
114 + return node
115 +
116 +
117 +def increment_lineno(node, n=1):
118 + """
119 + Increment the line number of each node in the tree starting at *node* by *n*.
120 + This is useful to "move code" to a different location in a file.
121 + """
122 + if 'lineno' in node._attributes:
123 + node.lineno = getattr(node, 'lineno', 0) + n
124 + for child in walk(node):
125 + if 'lineno' in child._attributes:
126 + child.lineno = getattr(child, 'lineno', 0) + n
127 + return node
128 +
129 +
130 +def iter_fields(node):
131 + """
132 + Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
133 + that is present on *node*.
134 + """
135 + for field in node._fields:
136 + try:
137 + yield field, getattr(node, field)
138 + except AttributeError:
139 + pass
140 +
141 +
142 +def iter_child_nodes(node):
143 + """
144 + Yield all direct child nodes of *node*, that is, all fields that are nodes
145 + and all items of fields that are lists of nodes.
146 + """
147 + for name, field in iter_fields(node):
148 + if isinstance(field, AST):
149 + yield field
150 + elif isinstance(field, list):
151 + for item in field:
152 + if isinstance(item, AST):
153 + yield item
154 +
155 +
156 +def get_docstring(node, clean=True):
157 + """
158 + Return the docstring for the given node or None if no docstring can
159 + be found. If the node provided does not have docstrings a TypeError
160 + will be raised.
161 + """
162 + if not isinstance(node, (FunctionDef, ClassDef, Module)):
163 + raise TypeError("%r can't have docstrings" % node.__class__.__name__)
164 + if node.body and isinstance(node.body[0], Expr) and \
165 + isinstance(node.body[0].value, Str):
166 + if clean:
167 + import inspect
168 + return inspect.cleandoc(node.body[0].value.s)
169 + return node.body[0].value.s
170 +
171 +
172 +def walk(node):
173 + """
174 + Recursively yield all child nodes of *node*, in no specified order. This is
175 + useful if you only want to modify nodes in place and don't care about the
176 + context.
177 + """
178 + from collections import deque
179 + todo = deque([node])
180 + while todo:
181 + node = todo.popleft()
182 + todo.extend(iter_child_nodes(node))
183 + yield node
184 +
185 +
186 +class NodeVisitor(object):
187 + """
188 + A node visitor base class that walks the abstract syntax tree and calls a
189 + visitor function for every node found. This function may return a value
190 + which is forwarded by the `visit` method.
191 +
192 + This class is meant to be subclassed, with the subclass adding visitor
193 + methods.
194 +
195 + Per default the visitor functions for the nodes are ``'visit_'`` +
196 + class name of the node. So a `TryFinally` node visit function would
197 + be `visit_TryFinally`. This behavior can be changed by overriding
198 + the `visit` method. If no visitor function exists for a node
199 + (return value `None`) the `generic_visit` visitor is used instead.
200 +
201 + Don't use the `NodeVisitor` if you want to apply changes to nodes during
202 + traversing. For this a special visitor exists (`NodeTransformer`) that
203 + allows modifications.
204 + """
205 +
206 + def visit(self, node):
207 + """Visit a node."""
208 + method = 'visit_' + node.__class__.__name__
209 + visitor = getattr(self, method, self.generic_visit)
210 + return visitor(node)
211 +
212 + def generic_visit(self, node):
213 + """Called if no explicit visitor function exists for a node."""
214 + for field, value in iter_fields(node):
215 + if isinstance(value, list):
216 + for item in value:
217 + if isinstance(item, AST):
218 + self.visit(item)
219 + elif isinstance(value, AST):
220 + self.visit(value)
221 +
222 +
223 +class NodeTransformer(NodeVisitor):
224 + """
225 + A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
226 + allows modification of nodes.
227 +
228 + The `NodeTransformer` will walk the AST and use the return value of the
229 + visitor methods to replace or remove the old node. If the return value of
230 + the visitor method is ``None``, the node will be removed from its location,
231 + otherwise it is replaced with the return value. The return value may be the
232 + original node in which case no replacement takes place.
233 +
234 + Here is an example transformer that rewrites all occurrences of name lookups
235 + (``foo``) to ``data['foo']``::
236 +
237 + class RewriteName(NodeTransformer):
238 +
239 + def visit_Name(self, node):
240 + return copy_location(Subscript(
241 + value=Name(id='data', ctx=Load()),
242 + slice=Index(value=Str(s=node.id)),
243 + ctx=node.ctx
244 + ), node)
245 +
246 + Keep in mind that if the node you're operating on has child nodes you must
247 + either transform the child nodes yourself or call the :meth:`generic_visit`
248 + method for the node first.
249 +
250 + For nodes that were part of a collection of statements (that applies to all
251 + statement nodes), the visitor may also return a list of nodes rather than
252 + just a single node.
253 +
254 + Usually you use the transformer like this::
255 +
256 + node = YourTransformer().visit(node)
257 + """
258 +
259 + def generic_visit(self, node):
260 + for field, old_value in iter_fields(node):
261 + old_value = getattr(node, field, None)
262 + if isinstance(old_value, list):
263 + new_values = []
264 + for value in old_value:
265 + if isinstance(value, AST):
266 + value = self.visit(value)
267 + if value is None:
268 + continue
269 + elif not isinstance(value, AST):
270 + new_values.extend(value)
271 + continue
272 + new_values.append(value)
273 + old_value[:] = new_values
274 + elif isinstance(old_value, AST):
275 + new_node = self.visit(old_value)
276 + if new_node is None:
277 + delattr(node, field)
278 + else:
279 + setattr(node, field, new_node)
280 + return node
1 +# this file has been automatically generated running:
2 +# snakes/lang/asdl.py --output=snakes/lang/ctlstar/asdl.py snakes/lang/ctlstar/ctlstar.asdl
3 +# timestamp: 2011-11-16 13:30:34.219385
4 +
5 +from snakes.lang import ast
6 +from ast import *
7 +
8 +class _AST (ast.AST):
9 + def __init__ (self, **ARGS):
10 + ast.AST.__init__(self)
11 + for k, v in ARGS.items():
12 + setattr(self, k, v)
13 +
14 +class expr_context (_AST):
15 + pass
16 +
17 +class Load (expr_context):
18 + _fields = ()
19 + _attributes = ()
20 +
21 +class Store (expr_context):
22 + _fields = ()
23 + _attributes = ()
24 +
25 +class Del (expr_context):
26 + _fields = ()
27 + _attributes = ()
28 +
29 +class AugLoad (expr_context):
30 + _fields = ()
31 + _attributes = ()
32 +
33 +class AugStore (expr_context):
34 + _fields = ()
35 + _attributes = ()
36 +
37 +class Param (expr_context):
38 + _fields = ()
39 + _attributes = ()
40 +
41 +class comprehension (_AST):
42 + _fields = ('target', 'iter', 'ifs')
43 + _attributes = ()
44 + def __init__ (self, target, iter, ifs=[], **ARGS):
45 + _AST.__init__(self, **ARGS)
46 + self.target = target
47 + self.iter = iter
48 + self.ifs = list(ifs)
49 +
50 +class arg (_AST):
51 + _fields = ('arg', 'annotation')
52 + _attributes = ()
53 + def __init__ (self, arg, annotation=None, **ARGS):
54 + _AST.__init__(self, **ARGS)
55 + self.arg = arg
56 + self.annotation = annotation
57 +
58 +class operator (_AST):
59 + pass
60 +
61 +class Add (operator):
62 + _fields = ()
63 + _attributes = ()
64 +
65 +class Sub (operator):
66 + _fields = ()
67 + _attributes = ()
68 +
69 +class Mult (operator):
70 + _fields = ()
71 + _attributes = ()
72 +
73 +class Div (operator):
74 + _fields = ()
75 + _attributes = ()
76 +
77 +class Mod (operator):
78 + _fields = ()
79 + _attributes = ()
80 +
81 +class Pow (operator):
82 + _fields = ()
83 + _attributes = ()
84 +
85 +class LShift (operator):
86 + _fields = ()
87 + _attributes = ()
88 +
89 +class RShift (operator):
90 + _fields = ()
91 + _attributes = ()
92 +
93 +class BitOr (operator):
94 + _fields = ()
95 + _attributes = ()
96 +
97 +class BitXor (operator):
98 + _fields = ()
99 + _attributes = ()
100 +
101 +class BitAnd (operator):
102 + _fields = ()
103 + _attributes = ()
104 +
105 +class FloorDiv (operator):
106 + _fields = ()
107 + _attributes = ()
108 +
109 +class slice (_AST):
110 + pass
111 +
112 +class Slice (slice):
113 + _fields = ('lower', 'upper', 'step')
114 + _attributes = ()
115 + def __init__ (self, lower=None, upper=None, step=None, **ARGS):
116 + slice.__init__(self, **ARGS)
117 + self.lower = lower
118 + self.upper = upper
119 + self.step = step
120 +
121 +class ExtSlice (slice):
122 + _fields = ('dims',)
123 + _attributes = ()
124 + def __init__ (self, dims=[], **ARGS):
125 + slice.__init__(self, **ARGS)
126 + self.dims = list(dims)
127 +
128 +class Index (slice):
129 + _fields = ('value',)
130 + _attributes = ()
131 + def __init__ (self, value, **ARGS):
132 + slice.__init__(self, **ARGS)
133 + self.value = value
134 +
135 +class excepthandler (_AST):
136 + pass
137 +
138 +class ExceptHandler (excepthandler):
139 + _fields = ('type', 'name', 'body')
140 + _attributes = ('lineno', 'col_offset')
141 + def __init__ (self, type=None, name=None, body=[], lineno=0, col_offset=0, **ARGS):
142 + excepthandler.__init__(self, **ARGS)
143 + self.type = type
144 + self.name = name
145 + self.body = list(body)
146 + self.lineno = int(lineno)
147 + self.col_offset = int(col_offset)
148 +
149 +class arguments (_AST):
150 + _fields = ('args', 'vararg', 'varargannotation', 'kwonlyargs', 'kwarg', 'kwargannotation', 'defaults', 'kw_defaults')
151 + _attributes = ()
152 + def __init__ (self, args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[], **ARGS):
153 + _AST.__init__(self, **ARGS)
154 + self.args = list(args)
155 + self.vararg = vararg
156 + self.varargannotation = varargannotation
157 + self.kwonlyargs = list(kwonlyargs)
158 + self.kwarg = kwarg
159 + self.kwargannotation = kwargannotation
160 + self.defaults = list(defaults)
161 + self.kw_defaults = list(kw_defaults)
162 +
163 +class ctlbinary (_AST):
164 + pass
165 +
166 +class boolop (ctlbinary):
167 + _fields = ()
168 + _attributes = ()
169 +
170 +class Imply (ctlbinary):
171 + _fields = ()
172 + _attributes = ()
173 +
174 +class Iff (ctlbinary):
175 + _fields = ()
176 + _attributes = ()
177 +
178 +class Until (ctlbinary):
179 + _fields = ()
180 + _attributes = ()
181 +
182 +class WeakUntil (ctlbinary):
183 + _fields = ()
184 + _attributes = ()
185 +
186 +class Release (ctlbinary):
187 + _fields = ()
188 + _attributes = ()
189 +
190 +class ctlunary (_AST):
191 + pass
192 +
193 +class notop (ctlunary):
194 + _fields = ()
195 + _attributes = ()
196 +
197 +class All (ctlunary):
198 + _fields = ()
199 + _attributes = ()
200 +
201 +class Exists (ctlunary):
202 + _fields = ()
203 + _attributes = ()
204 +
205 +class Next (ctlunary):
206 + _fields = ()
207 + _attributes = ()
208 +
209 +class Future (ctlunary):
210 + _fields = ()
211 + _attributes = ()
212 +
213 +class Globally (ctlunary):
214 + _fields = ()
215 + _attributes = ()
216 +
217 +class form (_AST):
218 + pass
219 +
220 +class atom (form):
221 + _fields = ()
222 + _attributes = ('lineno', 'col_offset')
223 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
224 + form.__init__(self, **ARGS)
225 + self.lineno = int(lineno)
226 + self.col_offset = int(col_offset)
227 +
228 +class CtlUnary (form):
229 + _fields = ('op', 'child')
230 + _attributes = ('lineno', 'col_offset')
231 + def __init__ (self, op, child, lineno=0, col_offset=0, **ARGS):
232 + form.__init__(self, **ARGS)
233 + self.op = op
234 + self.child = child
235 + self.lineno = int(lineno)
236 + self.col_offset = int(col_offset)
237 +
238 +class CtlBinary (form):
239 + _fields = ('op', 'left', 'right')
240 + _attributes = ('lineno', 'col_offset')
241 + def __init__ (self, op, left, right, lineno=0, col_offset=0, **ARGS):
242 + form.__init__(self, **ARGS)
243 + self.op = op
244 + self.left = left
245 + self.right = right
246 + self.lineno = int(lineno)
247 + self.col_offset = int(col_offset)
248 +
249 +class unaryop (_AST):
250 + pass
251 +
252 +class Invert (unaryop):
253 + _fields = ()
254 + _attributes = ()
255 +
256 +class notop (unaryop):
257 + _fields = ()
258 + _attributes = ()
259 +
260 +class UAdd (unaryop):
261 + _fields = ()
262 + _attributes = ()
263 +
264 +class USub (unaryop):
265 + _fields = ()
266 + _attributes = ()
267 +
268 +class boolop (_AST):
269 + pass
270 +
271 +class And (boolop):
272 + _fields = ()
273 + _attributes = ()
274 +
275 +class Or (boolop):
276 + _fields = ()
277 + _attributes = ()
278 +
279 +class stmt (_AST):
280 + pass
281 +
282 +class FunctionDef (stmt):
283 + _fields = ('name', 'args', 'body', 'decorator_list', 'returns')
284 + _attributes = ('lineno', 'col_offset')
285 + def __init__ (self, name, args, body=[], decorator_list=[], returns=None, lineno=0, col_offset=0, **ARGS):
286 + stmt.__init__(self, **ARGS)
287 + self.name = name
288 + self.args = args
289 + self.body = list(body)
290 + self.decorator_list = list(decorator_list)
291 + self.returns = returns
292 + self.lineno = int(lineno)
293 + self.col_offset = int(col_offset)
294 +
295 +class ClassDef (stmt):
296 + _fields = ('name', 'bases', 'keywords', 'starargs', 'kwargs', 'body', 'decorator_list')
297 + _attributes = ('lineno', 'col_offset')
298 + def __init__ (self, name, bases=[], keywords=[], starargs=None, kwargs=None, body=[], decorator_list=[], lineno=0, col_offset=0, **ARGS):
299 + stmt.__init__(self, **ARGS)
300 + self.name = name
301 + self.bases = list(bases)
302 + self.keywords = list(keywords)
303 + self.starargs = starargs
304 + self.kwargs = kwargs
305 + self.body = list(body)
306 + self.decorator_list = list(decorator_list)
307 + self.lineno = int(lineno)
308 + self.col_offset = int(col_offset)
309 +
310 +class Return (stmt):
311 + _fields = ('value',)
312 + _attributes = ('lineno', 'col_offset')
313 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
314 + stmt.__init__(self, **ARGS)
315 + self.value = value
316 + self.lineno = int(lineno)
317 + self.col_offset = int(col_offset)
318 +
319 +class Delete (stmt):
320 + _fields = ('targets',)
321 + _attributes = ('lineno', 'col_offset')
322 + def __init__ (self, targets=[], lineno=0, col_offset=0, **ARGS):
323 + stmt.__init__(self, **ARGS)
324 + self.targets = list(targets)
325 + self.lineno = int(lineno)
326 + self.col_offset = int(col_offset)
327 +
328 +class Assign (stmt):
329 + _fields = ('targets', 'value')
330 + _attributes = ('lineno', 'col_offset')
331 + def __init__ (self, value, targets=[], lineno=0, col_offset=0, **ARGS):
332 + stmt.__init__(self, **ARGS)
333 + self.targets = list(targets)
334 + self.value = value
335 + self.lineno = int(lineno)
336 + self.col_offset = int(col_offset)
337 +
338 +class AugAssign (stmt):
339 + _fields = ('target', 'op', 'value')
340 + _attributes = ('lineno', 'col_offset')
341 + def __init__ (self, target, op, value, lineno=0, col_offset=0, **ARGS):
342 + stmt.__init__(self, **ARGS)
343 + self.target = target
344 + self.op = op
345 + self.value = value
346 + self.lineno = int(lineno)
347 + self.col_offset = int(col_offset)
348 +
349 +class For (stmt):
350 + _fields = ('target', 'iter', 'body', 'orelse')
351 + _attributes = ('lineno', 'col_offset')
352 + def __init__ (self, target, iter, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
353 + stmt.__init__(self, **ARGS)
354 + self.target = target
355 + self.iter = iter
356 + self.body = list(body)
357 + self.orelse = list(orelse)
358 + self.lineno = int(lineno)
359 + self.col_offset = int(col_offset)
360 +
361 +class While (stmt):
362 + _fields = ('test', 'body', 'orelse')
363 + _attributes = ('lineno', 'col_offset')
364 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
365 + stmt.__init__(self, **ARGS)
366 + self.test = test
367 + self.body = list(body)
368 + self.orelse = list(orelse)
369 + self.lineno = int(lineno)
370 + self.col_offset = int(col_offset)
371 +
372 +class If (stmt):
373 + _fields = ('test', 'body', 'orelse')
374 + _attributes = ('lineno', 'col_offset')
375 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
376 + stmt.__init__(self, **ARGS)
377 + self.test = test
378 + self.body = list(body)
379 + self.orelse = list(orelse)
380 + self.lineno = int(lineno)
381 + self.col_offset = int(col_offset)
382 +
383 +class With (stmt):
384 + _fields = ('context_expr', 'optional_vars', 'body')
385 + _attributes = ('lineno', 'col_offset')
386 + def __init__ (self, context_expr, optional_vars=None, body=[], lineno=0, col_offset=0, **ARGS):
387 + stmt.__init__(self, **ARGS)
388 + self.context_expr = context_expr
389 + self.optional_vars = optional_vars
390 + self.body = list(body)
391 + self.lineno = int(lineno)
392 + self.col_offset = int(col_offset)
393 +
394 +class Raise (stmt):
395 + _fields = ('exc', 'cause')
396 + _attributes = ('lineno', 'col_offset')
397 + def __init__ (self, exc=None, cause=None, lineno=0, col_offset=0, **ARGS):
398 + stmt.__init__(self, **ARGS)
399 + self.exc = exc
400 + self.cause = cause
401 + self.lineno = int(lineno)
402 + self.col_offset = int(col_offset)
403 +
404 +class TryExcept (stmt):
405 + _fields = ('body', 'handlers', 'orelse')
406 + _attributes = ('lineno', 'col_offset')
407 + def __init__ (self, body=[], handlers=[], orelse=[], lineno=0, col_offset=0, **ARGS):
408 + stmt.__init__(self, **ARGS)
409 + self.body = list(body)
410 + self.handlers = list(handlers)
411 + self.orelse = list(orelse)
412 + self.lineno = int(lineno)
413 + self.col_offset = int(col_offset)
414 +
415 +class TryFinally (stmt):
416 + _fields = ('body', 'finalbody')
417 + _attributes = ('lineno', 'col_offset')
418 + def __init__ (self, body=[], finalbody=[], lineno=0, col_offset=0, **ARGS):
419 + stmt.__init__(self, **ARGS)
420 + self.body = list(body)
421 + self.finalbody = list(finalbody)
422 + self.lineno = int(lineno)
423 + self.col_offset = int(col_offset)
424 +
425 +class Assert (stmt):
426 + _fields = ('test', 'msg')
427 + _attributes = ('lineno', 'col_offset')
428 + def __init__ (self, test, msg=None, lineno=0, col_offset=0, **ARGS):
429 + stmt.__init__(self, **ARGS)
430 + self.test = test
431 + self.msg = msg
432 + self.lineno = int(lineno)
433 + self.col_offset = int(col_offset)
434 +
435 +class Import (stmt):
436 + _fields = ('names',)
437 + _attributes = ('lineno', 'col_offset')
438 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
439 + stmt.__init__(self, **ARGS)
440 + self.names = list(names)
441 + self.lineno = int(lineno)
442 + self.col_offset = int(col_offset)
443 +
444 +class ImportFrom (stmt):
445 + _fields = ('module', 'names', 'level')
446 + _attributes = ('lineno', 'col_offset')
447 + def __init__ (self, module, names=[], level=None, lineno=0, col_offset=0, **ARGS):
448 + stmt.__init__(self, **ARGS)
449 + self.module = module
450 + self.names = list(names)
451 + self.level = level
452 + self.lineno = int(lineno)
453 + self.col_offset = int(col_offset)
454 +
455 +class Exec (stmt):
456 + _fields = ('body', 'globals', 'locals')
457 + _attributes = ('lineno', 'col_offset')
458 + def __init__ (self, body, globals=None, locals=None, lineno=0, col_offset=0, **ARGS):
459 + stmt.__init__(self, **ARGS)
460 + self.body = body
461 + self.globals = globals
462 + self.locals = locals
463 + self.lineno = int(lineno)
464 + self.col_offset = int(col_offset)
465 +
466 +class Global (stmt):
467 + _fields = ('names',)
468 + _attributes = ('lineno', 'col_offset')
469 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
470 + stmt.__init__(self, **ARGS)
471 + self.names = list(names)
472 + self.lineno = int(lineno)
473 + self.col_offset = int(col_offset)
474 +
475 +class Nonlocal (stmt):
476 + _fields = ('names',)
477 + _attributes = ('lineno', 'col_offset')
478 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
479 + stmt.__init__(self, **ARGS)
480 + self.names = list(names)
481 + self.lineno = int(lineno)
482 + self.col_offset = int(col_offset)
483 +
484 +class Expr (stmt):
485 + _fields = ('value',)
486 + _attributes = ('lineno', 'col_offset')
487 + def __init__ (self, value, lineno=0, col_offset=0, **ARGS):
488 + stmt.__init__(self, **ARGS)
489 + self.value = value
490 + self.lineno = int(lineno)
491 + self.col_offset = int(col_offset)
492 +
493 +class Pass (stmt):
494 + _fields = ()
495 + _attributes = ('lineno', 'col_offset')
496 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
497 + stmt.__init__(self, **ARGS)
498 + self.lineno = int(lineno)
499 + self.col_offset = int(col_offset)
500 +
501 +class Break (stmt):
502 + _fields = ()
503 + _attributes = ('lineno', 'col_offset')
504 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
505 + stmt.__init__(self, **ARGS)
506 + self.lineno = int(lineno)
507 + self.col_offset = int(col_offset)
508 +
509 +class Continue (stmt):
510 + _fields = ()
511 + _attributes = ('lineno', 'col_offset')
512 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
513 + stmt.__init__(self, **ARGS)
514 + self.lineno = int(lineno)
515 + self.col_offset = int(col_offset)
516 +
517 +class notop (_AST):
518 + pass
519 +
520 +class Not (notop):
521 + _fields = ()
522 + _attributes = ()
523 +
524 +class ctlstar (_AST):
525 + pass
526 +
527 +class Spec (ctlstar):
528 + _fields = ('atoms', 'properties', 'main')
529 + _attributes = ('lineno', 'col_offset')
530 + def __init__ (self, atoms=[], properties=[], main=None, lineno=0, col_offset=0, **ARGS):
531 + ctlstar.__init__(self, **ARGS)
532 + self.atoms = list(atoms)
533 + self.properties = list(properties)
534 + self.main = main
535 + self.lineno = int(lineno)
536 + self.col_offset = int(col_offset)
537 +
538 +class atom (_AST):
539 + pass
540 +
541 +class InPlace (atom):
542 + _fields = ('data', 'place')
543 + _attributes = ('lineno', 'col_offset')
544 + def __init__ (self, place, data=[], lineno=0, col_offset=0, **ARGS):
545 + atom.__init__(self, **ARGS)
546 + self.data = list(data)
547 + self.place = place
548 + self.lineno = int(lineno)
549 + self.col_offset = int(col_offset)
550 +
551 +class NotInPlace (atom):
552 + _fields = ('data', 'place')
553 + _attributes = ('lineno', 'col_offset')
554 + def __init__ (self, place, data=[], lineno=0, col_offset=0, **ARGS):
555 + atom.__init__(self, **ARGS)
556 + self.data = list(data)
557 + self.place = place
558 + self.lineno = int(lineno)
559 + self.col_offset = int(col_offset)
560 +
561 +class EmptyPlace (atom):
562 + _fields = ('place',)
563 + _attributes = ('lineno', 'col_offset')
564 + def __init__ (self, place, lineno=0, col_offset=0, **ARGS):
565 + atom.__init__(self, **ARGS)
566 + self.place = place
567 + self.lineno = int(lineno)
568 + self.col_offset = int(col_offset)
569 +
570 +class MarkedPlace (atom):
571 + _fields = ('place',)
572 + _attributes = ('lineno', 'col_offset')
573 + def __init__ (self, place, lineno=0, col_offset=0, **ARGS):
574 + atom.__init__(self, **ARGS)
575 + self.place = place
576 + self.lineno = int(lineno)
577 + self.col_offset = int(col_offset)
578 +
579 +class Deadlock (atom):
580 + _fields = ()
581 + _attributes = ('lineno', 'col_offset')
582 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
583 + atom.__init__(self, **ARGS)
584 + self.lineno = int(lineno)
585 + self.col_offset = int(col_offset)
586 +
587 +class Boolean (atom):
588 + _fields = ('val',)
589 + _attributes = ('lineno', 'col_offset')
590 + def __init__ (self, val, lineno=0, col_offset=0, **ARGS):
591 + atom.__init__(self, **ARGS)
592 + self.val = val
593 + self.lineno = int(lineno)
594 + self.col_offset = int(col_offset)
595 +
596 +class Instance (atom):
597 + _fields = ('name', 'args')
598 + _attributes = ('lineno', 'col_offset')
599 + def __init__ (self, name, args=[], lineno=0, col_offset=0, **ARGS):
600 + atom.__init__(self, **ARGS)
601 + self.name = name
602 + self.args = list(args)
603 + self.lineno = int(lineno)
604 + self.col_offset = int(col_offset)
605 +
606 +class Quantifier (atom):
607 + _fields = ('op', 'vars', 'place', 'child', 'distinct')
608 + _attributes = ('lineno', 'col_offset')
609 + def __init__ (self, op, place, child, distinct, vars=[], lineno=0, col_offset=0, **ARGS):
610 + atom.__init__(self, **ARGS)
611 + self.op = op
612 + self.vars = list(vars)
613 + self.place = place
614 + self.child = child
615 + self.distinct = distinct
616 + self.lineno = int(lineno)
617 + self.col_offset = int(col_offset)
618 +
619 +class cmpop (_AST):
620 + pass
621 +
622 +class Eq (cmpop):
623 + _fields = ()
624 + _attributes = ()
625 +
626 +class NotEq (cmpop):
627 + _fields = ()
628 + _attributes = ()
629 +
630 +class Lt (cmpop):
631 + _fields = ()
632 + _attributes = ()
633 +
634 +class LtE (cmpop):
635 + _fields = ()
636 + _attributes = ()
637 +
638 +class Gt (cmpop):
639 + _fields = ()
640 + _attributes = ()
641 +
642 +class GtE (cmpop):
643 + _fields = ()
644 + _attributes = ()
645 +
646 +class Is (cmpop):
647 + _fields = ()
648 + _attributes = ()
649 +
650 +class IsNot (cmpop):
651 + _fields = ()
652 + _attributes = ()
653 +
654 +class In (cmpop):
655 + _fields = ()
656 + _attributes = ()
657 +
658 +class NotIn (cmpop):
659 + _fields = ()
660 + _attributes = ()
661 +
662 +class keyword (_AST):
663 + _fields = ('arg', 'value')
664 + _attributes = ()
665 + def __init__ (self, arg, value, **ARGS):
666 + _AST.__init__(self, **ARGS)
667 + self.arg = arg
668 + self.value = value
669 +
670 +class ctlarg (_AST):
671 + pass
672 +
673 +class Place (ctlarg):
674 + _fields = ('name', 'place')
675 + _attributes = ('lineno', 'col_offset')
676 + def __init__ (self, name, place, lineno=0, col_offset=0, **ARGS):
677 + ctlarg.__init__(self, **ARGS)
678 + self.name = name
679 + self.place = place
680 + self.lineno = int(lineno)
681 + self.col_offset = int(col_offset)
682 +
683 +class Token (ctlarg):
684 + _fields = ('name', 'place')
685 + _attributes = ('lineno', 'col_offset')
686 + def __init__ (self, name, place, lineno=0, col_offset=0, **ARGS):
687 + ctlarg.__init__(self, **ARGS)
688 + self.name = name
689 + self.place = place
690 + self.lineno = int(lineno)
691 + self.col_offset = int(col_offset)
692 +
693 +class Argument (ctlarg):
694 + _fields = ('name', 'value', 'type')
695 + _attributes = ('lineno', 'col_offset')
696 + def __init__ (self, name, value, type, lineno=0, col_offset=0, **ARGS):
697 + ctlarg.__init__(self, **ARGS)
698 + self.name = name
699 + self.value = value
700 + self.type = type
701 + self.lineno = int(lineno)
702 + self.col_offset = int(col_offset)
703 +
704 +class expr (_AST):
705 + pass
706 +
707 +class BoolOp (expr):
708 + _fields = ('op', 'values')
709 + _attributes = ('lineno', 'col_offset')
710 + def __init__ (self, op, values=[], lineno=0, col_offset=0, **ARGS):
711 + expr.__init__(self, **ARGS)
712 + self.op = op
713 + self.values = list(values)
714 + self.lineno = int(lineno)
715 + self.col_offset = int(col_offset)
716 +
717 +class BinOp (expr):
718 + _fields = ('left', 'op', 'right')
719 + _attributes = ('lineno', 'col_offset')
720 + def __init__ (self, left, op, right, lineno=0, col_offset=0, **ARGS):
721 + expr.__init__(self, **ARGS)
722 + self.left = left
723 + self.op = op
724 + self.right = right
725 + self.lineno = int(lineno)
726 + self.col_offset = int(col_offset)
727 +
728 +class UnaryOp (expr):
729 + _fields = ('op', 'operand')
730 + _attributes = ('lineno', 'col_offset')
731 + def __init__ (self, op, operand, lineno=0, col_offset=0, **ARGS):
732 + expr.__init__(self, **ARGS)
733 + self.op = op
734 + self.operand = operand
735 + self.lineno = int(lineno)
736 + self.col_offset = int(col_offset)
737 +
738 +class Lambda (expr):
739 + _fields = ('args', 'body')
740 + _attributes = ('lineno', 'col_offset')
741 + def __init__ (self, args, body, lineno=0, col_offset=0, **ARGS):
742 + expr.__init__(self, **ARGS)
743 + self.args = args
744 + self.body = body
745 + self.lineno = int(lineno)
746 + self.col_offset = int(col_offset)
747 +
748 +class IfExp (expr):
749 + _fields = ('test', 'body', 'orelse')
750 + _attributes = ('lineno', 'col_offset')
751 + def __init__ (self, test, body, orelse, lineno=0, col_offset=0, **ARGS):
752 + expr.__init__(self, **ARGS)
753 + self.test = test
754 + self.body = body
755 + self.orelse = orelse
756 + self.lineno = int(lineno)
757 + self.col_offset = int(col_offset)
758 +
759 +class Dict (expr):
760 + _fields = ('keys', 'values')
761 + _attributes = ('lineno', 'col_offset')
762 + def __init__ (self, keys=[], values=[], lineno=0, col_offset=0, **ARGS):
763 + expr.__init__(self, **ARGS)
764 + self.keys = list(keys)
765 + self.values = list(values)
766 + self.lineno = int(lineno)
767 + self.col_offset = int(col_offset)
768 +
769 +class Set (expr):
770 + _fields = ('elts',)
771 + _attributes = ('lineno', 'col_offset')
772 + def __init__ (self, elts=[], lineno=0, col_offset=0, **ARGS):
773 + expr.__init__(self, **ARGS)
774 + self.elts = list(elts)
775 + self.lineno = int(lineno)
776 + self.col_offset = int(col_offset)
777 +
778 +class ListComp (expr):
779 + _fields = ('elt', 'generators')
780 + _attributes = ('lineno', 'col_offset')
781 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
782 + expr.__init__(self, **ARGS)
783 + self.elt = elt
784 + self.generators = list(generators)
785 + self.lineno = int(lineno)
786 + self.col_offset = int(col_offset)
787 +
788 +class SetComp (expr):
789 + _fields = ('elt', 'generators')
790 + _attributes = ('lineno', 'col_offset')
791 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
792 + expr.__init__(self, **ARGS)
793 + self.elt = elt
794 + self.generators = list(generators)
795 + self.lineno = int(lineno)
796 + self.col_offset = int(col_offset)
797 +
798 +class DictComp (expr):
799 + _fields = ('key', 'value', 'generators')
800 + _attributes = ('lineno', 'col_offset')
801 + def __init__ (self, key, value, generators=[], lineno=0, col_offset=0, **ARGS):
802 + expr.__init__(self, **ARGS)
803 + self.key = key
804 + self.value = value
805 + self.generators = list(generators)
806 + self.lineno = int(lineno)
807 + self.col_offset = int(col_offset)
808 +
809 +class GeneratorExp (expr):
810 + _fields = ('elt', 'generators')
811 + _attributes = ('lineno', 'col_offset')
812 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
813 + expr.__init__(self, **ARGS)
814 + self.elt = elt
815 + self.generators = list(generators)
816 + self.lineno = int(lineno)
817 + self.col_offset = int(col_offset)
818 +
819 +class Yield (expr):
820 + _fields = ('value',)
821 + _attributes = ('lineno', 'col_offset')
822 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
823 + expr.__init__(self, **ARGS)
824 + self.value = value
825 + self.lineno = int(lineno)
826 + self.col_offset = int(col_offset)
827 +
828 +class Compare (expr):
829 + _fields = ('left', 'ops', 'comparators')
830 + _attributes = ('lineno', 'col_offset')
831 + def __init__ (self, left, ops=[], comparators=[], lineno=0, col_offset=0, **ARGS):
832 + expr.__init__(self, **ARGS)
833 + self.left = left
834 + self.ops = list(ops)
835 + self.comparators = list(comparators)
836 + self.lineno = int(lineno)
837 + self.col_offset = int(col_offset)
838 +
839 +class Call (expr):
840 + _fields = ('func', 'args', 'keywords', 'starargs', 'kwargs')
841 + _attributes = ('lineno', 'col_offset')
842 + def __init__ (self, func, args=[], keywords=[], starargs=None, kwargs=None, lineno=0, col_offset=0, **ARGS):
843 + expr.__init__(self, **ARGS)
844 + self.func = func
845 + self.args = list(args)
846 + self.keywords = list(keywords)
847 + self.starargs = starargs
848 + self.kwargs = kwargs
849 + self.lineno = int(lineno)
850 + self.col_offset = int(col_offset)
851 +
852 +class Num (expr):
853 + _fields = ('n',)
854 + _attributes = ('lineno', 'col_offset')
855 + def __init__ (self, n, lineno=0, col_offset=0, **ARGS):
856 + expr.__init__(self, **ARGS)
857 + self.n = n
858 + self.lineno = int(lineno)
859 + self.col_offset = int(col_offset)
860 +
861 +class Str (expr):
862 + _fields = ('s',)
863 + _attributes = ('lineno', 'col_offset')
864 + def __init__ (self, s, lineno=0, col_offset=0, **ARGS):
865 + expr.__init__(self, **ARGS)
866 + self.s = s
867 + self.lineno = int(lineno)
868 + self.col_offset = int(col_offset)
869 +
870 +class Ellipsis (expr):
871 + _fields = ()
872 + _attributes = ('lineno', 'col_offset')
873 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
874 + expr.__init__(self, **ARGS)
875 + self.lineno = int(lineno)
876 + self.col_offset = int(col_offset)
877 +
878 +class Attribute (expr):
879 + _fields = ('value', 'attr', 'ctx')
880 + _attributes = ('lineno', 'col_offset')
881 + def __init__ (self, value, attr, ctx=None, lineno=0, col_offset=0, **ARGS):
882 + expr.__init__(self, **ARGS)
883 + self.value = value
884 + self.attr = attr
885 + self.ctx = ctx
886 + self.lineno = int(lineno)
887 + self.col_offset = int(col_offset)
888 +
889 +class Subscript (expr):
890 + _fields = ('value', 'slice', 'ctx')
891 + _attributes = ('lineno', 'col_offset')
892 + def __init__ (self, value, slice, ctx=None, lineno=0, col_offset=0, **ARGS):
893 + expr.__init__(self, **ARGS)
894 + self.value = value
895 + self.slice = slice
896 + self.ctx = ctx
897 + self.lineno = int(lineno)
898 + self.col_offset = int(col_offset)
899 +
900 +class Starred (expr):
901 + _fields = ('value', 'ctx')
902 + _attributes = ('lineno', 'col_offset')
903 + def __init__ (self, value, ctx=None, lineno=0, col_offset=0, **ARGS):
904 + expr.__init__(self, **ARGS)
905 + self.value = value
906 + self.ctx = ctx
907 + self.lineno = int(lineno)
908 + self.col_offset = int(col_offset)
909 +
910 +class Name (expr):
911 + _fields = ('id', 'ctx')
912 + _attributes = ('lineno', 'col_offset')
913 + def __init__ (self, id, ctx=None, lineno=0, col_offset=0, **ARGS):
914 + expr.__init__(self, **ARGS)
915 + self.id = id
916 + self.ctx = ctx
917 + self.lineno = int(lineno)
918 + self.col_offset = int(col_offset)
919 +
920 +class List (expr):
921 + _fields = ('elts', 'ctx')
922 + _attributes = ('lineno', 'col_offset')
923 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
924 + expr.__init__(self, **ARGS)
925 + self.elts = list(elts)
926 + self.ctx = ctx
927 + self.lineno = int(lineno)
928 + self.col_offset = int(col_offset)
929 +
930 +class Tuple (expr):
931 + _fields = ('elts', 'ctx')
932 + _attributes = ('lineno', 'col_offset')
933 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
934 + expr.__init__(self, **ARGS)
935 + self.elts = list(elts)
936 + self.ctx = ctx
937 + self.lineno = int(lineno)
938 + self.col_offset = int(col_offset)
939 +
940 +class ctldecl (_AST):
941 + pass
942 +
943 +class Atom (ctldecl):
944 + _fields = ('name', 'args', 'params', 'body')
945 + _attributes = ('lineno', 'col_offset')
946 + def __init__ (self, name, args=[], params=[], body=[], lineno=0, col_offset=0, **ARGS):
947 + ctldecl.__init__(self, **ARGS)
948 + self.name = name
949 + self.args = list(args)
950 + self.params = list(params)
951 + self.body = list(body)
952 + self.lineno = int(lineno)
953 + self.col_offset = int(col_offset)
954 +
955 +class Property (ctldecl):
956 + _fields = ('name', 'args', 'params', 'body')
957 + _attributes = ('lineno', 'col_offset')
958 + def __init__ (self, name, body, args=[], params=[], lineno=0, col_offset=0, **ARGS):
959 + ctldecl.__init__(self, **ARGS)
960 + self.name = name
961 + self.args = list(args)
962 + self.params = list(params)
963 + self.body = body
964 + self.lineno = int(lineno)
965 + self.col_offset = int(col_offset)
966 +
967 +class alias (_AST):
968 + _fields = ('name', 'asname')
969 + _attributes = ()
970 + def __init__ (self, name, asname=None, **ARGS):
971 + _AST.__init__(self, **ARGS)
972 + self.name = name
973 + self.asname = asname
974 +
975 +class ctlparam (_AST):
976 + pass
977 +
978 +class Parameter (ctlparam):
979 + _fields = ('name', 'type')
980 + _attributes = ('lineno', 'col_offset')
981 + def __init__ (self, name, type, lineno=0, col_offset=0, **ARGS):
982 + ctlparam.__init__(self, **ARGS)
983 + self.name = name
984 + self.type = type
985 + self.lineno = int(lineno)
986 + self.col_offset = int(col_offset)
1 +module CTLstar version "$Revision: 1 $"
2 +{
3 + ctlstar = Spec(ctldecl* atoms, ctldecl* properties, form? main)
4 + attributes (int lineno, int col_offset)
5 +
6 + ctldecl = Atom(identifier name, ctlarg* args, ctlparams* params,
7 + stmt* body)
8 + | Property(identifier name, ctlargs* args,
9 + ctlparams* params, form body)
10 + attributes (int lineno, int col_offset)
11 +
12 + ctlarg = Place(identifier name, string place)
13 + | Token(identifier name, string place)
14 + | Argument(identifier name, expr value, identifier type)
15 + attributes (int lineno, int col_offset)
16 +
17 + ctlparam = Parameter(identifier name, identifier type)
18 + attributes (int lineno, int col_offset)
19 +
20 + form = atom
21 + | CtlUnary(ctlunary op, form child)
22 + | CtlBinary(ctlbinary op, form left, form right)
23 + attributes (int lineno, int col_offset)
24 +
25 + ctlunary = notop | All | Exists | Next | Future | Globally
26 +
27 + notop = Not
28 +
29 + ctlbinary = boolop | Imply | Iff | Until | WeakUntil | Release
30 +
31 + atom = InPlace(expr* data, ctlarg place)
32 + | NotInPlace(expr* data, ctlarg place)
33 + | EmptyPlace(ctlarg place)
34 + | MarkedPlace(ctlarg place)
35 + | Deadlock
36 + | Boolean(bool val)
37 + | Instance(identifier name, arg* args)
38 + | Quantifier(ctlunary op,
39 + identifier* vars,
40 + ctlarg place,
41 + form child,
42 + bool distinct)
43 + attributes (int lineno, int col_offset)
44 +
45 + --------------------------------------------------------------
46 + -- the rest is copied from "snakes/lang/python/python.asdl" --
47 + --------------------------------------------------------------
48 +
49 + stmt = FunctionDef(identifier name, arguments args,
50 + stmt* body, expr* decorator_list, expr? returns)
51 + | ClassDef(identifier name,
52 + expr* bases,
53 + keyword* keywords,
54 + expr? starargs,
55 + expr? kwargs,
56 + stmt* body,
57 + expr *decorator_list)
58 + | Return(expr? value)
59 +
60 + | Delete(expr* targets)
61 + | Assign(expr* targets, expr value)
62 + | AugAssign(expr target, operator op, expr value)
63 +
64 + | For(expr target, expr iter, stmt* body, stmt* orelse)
65 + | While(expr test, stmt* body, stmt* orelse)
66 + | If(expr test, stmt* body, stmt* orelse)
67 + | With(expr context_expr, expr? optional_vars, stmt* body)
68 +
69 + | Raise(expr? exc, expr? cause)
70 + | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
71 + | TryFinally(stmt* body, stmt* finalbody)
72 + | Assert(expr test, expr? msg)
73 +
74 + | Import(alias* names)
75 + | ImportFrom(identifier module, alias* names, int? level)
76 +
77 + | Exec(expr body, expr? globals, expr? locals)
78 +
79 + | Global(identifier* names)
80 + | Nonlocal(identifier* names)
81 + | Expr(expr value)
82 + | Pass | Break | Continue
83 +
84 + attributes (int lineno, int col_offset)
85 +
86 + expr = BoolOp(boolop op, expr* values)
87 + | BinOp(expr left, operator op, expr right)
88 + | UnaryOp(unaryop op, expr operand)
89 + | Lambda(arguments args, expr body)
90 + | IfExp(expr test, expr body, expr orelse)
91 + | Dict(expr* keys, expr* values)
92 + | Set(expr* elts)
93 + | ListComp(expr elt, comprehension* generators)
94 + | SetComp(expr elt, comprehension* generators)
95 + | DictComp(expr key, expr value, comprehension* generators)
96 + | GeneratorExp(expr elt, comprehension* generators)
97 + | Yield(expr? value)
98 + | Compare(expr left, cmpop* ops, expr* comparators)
99 + | Call(expr func, expr* args, keyword* keywords,
100 + expr? starargs, expr? kwargs)
101 + | Num(object n)
102 + | Str(string s)
103 + | Ellipsis
104 +
105 + | Attribute(expr value, identifier attr, expr_context ctx)
106 + | Subscript(expr value, slice slice, expr_context ctx)
107 + | Starred(expr value, expr_context ctx)
108 + | Name(identifier id, expr_context ctx)
109 + | List(expr* elts, expr_context ctx)
110 + | Tuple(expr* elts, expr_context ctx)
111 +
112 + attributes (int lineno, int col_offset)
113 +
114 + expr_context = Load | Store | Del | AugLoad | AugStore | Param
115 +
116 + slice = Slice(expr? lower, expr? upper, expr? step)
117 + | ExtSlice(slice* dims)
118 + | Index(expr value)
119 +
120 + boolop = And | Or
121 +
122 + operator = Add | Sub | Mult | Div | Mod | Pow | LShift
123 + | RShift | BitOr | BitXor | BitAnd | FloorDiv
124 +
125 + unaryop = Invert | notop | UAdd | USub
126 +
127 + cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
128 +
129 + comprehension = (expr target, expr iter, expr* ifs)
130 +
131 + excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
132 + attributes (int lineno, int col_offset)
133 +
134 + arguments = (arg* args, identifier? vararg, expr? varargannotation,
135 + arg* kwonlyargs, identifier? kwarg,
136 + expr? kwargannotation, expr* defaults,
137 + expr* kw_defaults)
138 + arg = (identifier arg, expr? annotation)
139 +
140 + keyword = (identifier arg, expr value)
141 +
142 + alias = (identifier name, identifier? asname)
143 +}
1 +# Grammar for (permissive) CTL*
2 +
3 +# new tokens
4 +$ELLIPSIS '...'
5 +
6 +file_input: (NEWLINE | ctl_atomdef | ctl_propdef)* [ ctl_formula ] NEWLINE* ENDMARKER
7 +
8 +ctl_atomdef: 'atom' NAME '(' [ctl_parameters] ')' ':' suite
9 +ctl_propdef: 'prop' NAME '(' [ctl_parameters] ')' ':' ctl_suite
10 +ctl_suite: ( ctl_formula NEWLINE
11 + | NEWLINE INDENT ctl_formula NEWLINE+ DEDENT )
12 +ctl_parameters: (ctl_param ',')* ctl_param
13 +ctl_param: NAME ( '=' '@' STRING+ | ':' NAME )
14 +
15 +ctl_formula: ctl_or_formula [ ctl_connector ctl_or_formula ]
16 +ctl_connector: ( '=' '>' | '<=' '>' )
17 +ctl_or_formula: ctl_and_formula ('or' ctl_and_formula)*
18 +ctl_and_formula: ctl_not_formula ('and' ctl_not_formula)*
19 +ctl_not_formula: ('not' ctl_not_formula | ctl_binary_formula)
20 +ctl_binary_formula: ctl_unary_formula [ ctl_binary_op ctl_unary_formula ]
21 +ctl_unary_formula: [ ctl_unary_op ] (ctl_atom_formula | '(' ctl_formula ')')
22 +ctl_unary_op: ('A' | 'G' | 'F' | 'E' | 'X')
23 +ctl_binary_op: ('R' | 'U' | 'W')
24 +ctl_atom_formula: ( 'empty' '(' ctl_place ')'
25 + | 'marked' '(' ctl_place ')'
26 + | 'has' ['not'] '(' ctl_place ',' test (',' test)* ')'
27 + | 'deadlock' | 'True' | 'False'
28 + | NAME '(' ctl_arguments ')'
29 + | 'forall' [ 'distinct' ] NAME (',' NAME)*
30 + 'in' ctl_place '(' ctl_atom_formula ')'
31 + | 'exists' [ 'distinct' ] NAME (',' NAME)*
32 + 'in' ctl_place '(' ctl_atom_formula ')' )
33 +ctl_arguments: (NAME '=' ctl_place_or_test ',')* NAME '=' ctl_place_or_test
34 +ctl_place: '@' STRING+ | NAME
35 +ctl_place_or_test: test | '@' STRING+
36 +
37 +#
38 +# the rest is from SNAKES/Python grammar
39 +#
40 +
41 +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
42 +decorators: decorator+
43 +decorated: decorators (classdef | funcdef)
44 +funcdef: 'def' NAME parameters ['-' '>' test] ':' suite
45 +parameters: '(' [typedargslist] ')'
46 +typedargslist: ((tfpdef ['=' test] ',')*
47 + ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef]
48 + | '**' tfpdef)
49 + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
50 +tfpdef: NAME [':' test]
51 +varargslist: ((vfpdef ['=' test] ',')*
52 + ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef]
53 + | '**' vfpdef)
54 + | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
55 +vfpdef: NAME
56 +
57 +stmt: simple_stmt | compound_stmt
58 +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
59 +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
60 + import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
61 +expr_stmt: testlist (augassign (yield_expr|testlist) |
62 + ('=' (yield_expr|testlist))*)
63 +augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
64 + '<<=' | '>>=' | '**=' | '//=')
65 +del_stmt: 'del' exprlist
66 +pass_stmt: 'pass'
67 +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
68 +break_stmt: 'break'
69 +continue_stmt: 'continue'
70 +return_stmt: 'return' [testlist]
71 +yield_stmt: yield_expr
72 +raise_stmt: 'raise' [test ['from' test]]
73 +import_stmt: import_name | import_from
74 +import_name: 'import' dotted_as_names
75 +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
76 + 'import' ('*' | '(' import_as_names ')' | import_as_names))
77 +import_as_name: NAME ['as' NAME]
78 +dotted_as_name: dotted_name ['as' NAME]
79 +import_as_names: import_as_name (',' import_as_name)* [',']
80 +dotted_as_names: dotted_as_name (',' dotted_as_name)*
81 +dotted_name: NAME ('.' NAME)*
82 +global_stmt: 'global' NAME (',' NAME)*
83 +nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
84 +assert_stmt: 'assert' test [',' test]
85 +
86 +compound_stmt: (if_stmt | while_stmt | for_stmt | try_stmt | with_stmt
87 + | funcdef | classdef | decorated)
88 +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
89 +while_stmt: 'while' test ':' suite ['else' ':' suite]
90 +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
91 +try_stmt: ('try' ':' suite
92 + ((except_clause ':' suite)+
93 + ['else' ':' suite]
94 + ['finally' ':' suite] |
95 + 'finally' ':' suite))
96 +with_stmt: 'with' test [ with_var ] ':' suite
97 +with_var: 'as' expr
98 +except_clause: 'except' [test ['as' NAME]]
99 +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
100 +
101 +test: or_test ['if' or_test 'else' test] | lambdef
102 +test_nocond: or_test | lambdef_nocond
103 +lambdef: 'lambda' [varargslist] ':' test
104 +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
105 +or_test: and_test ('or' and_test)*
106 +and_test: not_test ('and' not_test)*
107 +not_test: 'not' not_test | comparison
108 +comparison: star_expr (comp_op star_expr)*
109 +comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'<>'|'in'|'not' 'in'|'is'|'is' 'not'
110 +star_expr: ['*'] expr
111 +expr: xor_expr ('|' xor_expr)*
112 +xor_expr: and_expr ('^' and_expr)*
113 +and_expr: shift_expr ('&' shift_expr)*
114 +shift_expr: arith_expr (('<<'|'>>') arith_expr)*
115 +arith_expr: term (('+'|'-') term)*
116 +term: factor (('*'|'/'|'%'|'//') factor)*
117 +factor: ('+'|'-'|'~') factor | power
118 +power: atom trailer* ['**' factor]
119 +atom: ('(' [yield_expr|testlist_comp] ')' |
120 + '[' [testlist_comp] ']' |
121 + '{' [dictorsetmaker] '}' |
122 + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
123 +testlist_comp: test ( comp_for | (',' test)* [','] )
124 +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
125 +subscriptlist: subscript (',' subscript)* [',']
126 +subscript: test | [test] ':' [test] [sliceop]
127 +sliceop: ':' [test]
128 +exprlist: star_expr (',' star_expr)* [',']
129 +testlist: test (',' test)* [',']
130 +dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
131 + (test (comp_for | (',' test)* [','])) )
132 +
133 +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
134 +
135 +arglist: (argument ',')* (argument [',']
136 + |'*' test (',' argument)* [',' '**' test]
137 + |'**' test)
138 +argument: test [comp_for] | test '=' test
139 +
140 +comp_iter: comp_for | comp_if
141 +comp_for: 'for' exprlist 'in' or_test [comp_iter]
142 +comp_if: 'if' test_nocond [comp_iter]
143 +
144 +yield_expr: 'yield' [testlist]
1 +"""
2 +>>> testparser(Translator)
3 +"""
4 +
5 +import operator, sys
6 +import snakes
7 +from snakes.lang.python.parser import (ParseTree, ParseTestParser,
8 + Translator as PyTranslator,
9 + ParseTree as PyParseTree,
10 + testparser)
11 +from snakes.lang.pgen import ParseError
12 +from snakes.lang.ctlstar.pgen import parser
13 +import snakes.lang.ctlstar.asdl as ast
14 +
15 +_symbols = parser.tokenizer.tok_name.copy()
16 +# next statement overrides 'NT_OFFSET' entry with 'single_input'
17 +# (this is desired)
18 +_symbols.update(parser.symbolMap)
19 +
20 +def skip (token) :
21 + if token.kind == token.lexer.COMMENT :
22 + words = token.strip().split()
23 + if words[:2] == ["#", "coding="] :
24 + snakes.defaultencoding = words[2]
25 + elif words[:3] == ["#", "-*-", "coding:"] :
26 + snakes.defaultencoding = words[3]
27 +
28 +parser.tokenizer.skip_token = skip
29 +
30 +class ParseTree (PyParseTree) :
31 + _symbols = _symbols
32 +
33 +class Translator (PyTranslator) :
34 + ParseTree = ParseTree
35 + parser = parser
36 + ST = ast
37 + def do_file_input (self, st, ctx) :
38 + """file_input: (NEWLINE | ctl_atomdef | ctl_propdef)* [ ctl_formula ] NEWLINE* ENDMARKER
39 + -> ast.Spec
40 +
41 + <<< atom foo () : return True
42 + ... prop bar () : True
43 + ... has(@'my place', x)
44 + "Spec(atoms=[Atom(name='foo', args=[], params=[], body=[Return(value=Name(id='True', ctx=Load()))])], properties=[Property(name='bar', args=[], params=[], body=Boolean(val=True))], main=InPlace(data=[Name(id='x', ctx=Load())], place=Place(name=None, place='my place')))"
45 + """
46 + atoms, props, main = [], [], None
47 + for i, child in enumerate(st) :
48 + if child.symbol == "ctl_atomdef" :
49 + atoms.append(self.do(child, ctx))
50 + elif child.symbol == "ctl_propdef" :
51 + props.append(self.do(child, ctx))
52 + elif child.symbol == "ctl_formula" :
53 + main = self.do(child, ctx)
54 + elif child.symbol in ("NEWLINE", "ENDMARKER") :
55 + pass
56 + else :
57 + raise ParseError(child.text, reason="unexpected token")
58 + return self.ST.Spec(lineno=st.srow, col_offset=st.scol,
59 + atoms=atoms, properties=props, main=main)
60 + def do_ctl_atomdef (self, st, ctx) :
61 + """ctl_atomdef: 'atom' NAME '(' [ctl_parameters] ')' ':' suite
62 + -> ast.Atom
63 +
64 + <<< atom foo () : return True
65 + "Spec(atoms=[Atom(name='foo', args=[], params=[], body=[Return(value=Name(id='True', ctx=Load()))])], properties=[], main=None)"
66 + <<< atom bar () :
67 + ... return True
68 + "Spec(atoms=[Atom(name='bar', args=[], params=[], body=[Return(value=Name(id='True', ctx=Load()))])], properties=[], main=None)"
69 + <<< atom egg (p = @'my place', x : int, q : place) :
70 + ... return x in p and x in q
71 + "Spec(atoms=[Atom(name='egg', args=[Place(name='p', place='my place')], params=[Parameter(name='x', type='int'), Parameter(name='q', type='place')], body=[Return(value=BoolOp(op=And(), values=[Compare(left=Name(id='x', ctx=Load()), ops=[In()], comparators=[Name(id='p', ctx=Load())]), Compare(left=Name(id='x', ctx=Load()), ops=[In()], comparators=[Name(id='q', ctx=Load())])]))])], properties=[], main=None)"
72 + """
73 + if len(st) == 7 :
74 + args, params = self.do(st[3], ctx)
75 + return self.ST.Atom(lineno=st.srow, col_offset=st.scol,
76 + name=st[1].text,
77 + args=args,
78 + params=params,
79 + body=self.do(st[-1], ctx))
80 + else :
81 + return self.ST.Atom(lineno=st.srow, col_offset=st.scol,
82 + name=st[1].text,
83 + args=[],
84 + params=[],
85 + body=self.do(st[-1], ctx))
86 + def do_ctl_propdef (self, st, ctx) :
87 + """ctl_propdef: 'prop' NAME '(' [ctl_parameters] ')' ':' ctl_suite
88 + -> ast.Property
89 +
90 + <<< prop foo () : True
91 + "Spec(atoms=[], properties=[Property(name='foo', args=[], params=[], body=Boolean(val=True))], main=None)"
92 + <<< prop bar (p = @'my place', x : int, q : place) : True
93 + "Spec(atoms=[], properties=[Property(name='bar', args=[Place(name='p', place='my place')], params=[Parameter(name='x', type='int'), Parameter(name='q', type='place')], body=Boolean(val=True))], main=None)"
94 + <<< prop egg (p = @'my place', x : int, q : place) : True
95 + "Spec(atoms=[], properties=[Property(name='egg', args=[Place(name='p', place='my place')], params=[Parameter(name='x', type='int'), Parameter(name='q', type='place')], body=Boolean(val=True))], main=None)"
96 + """
97 + if len(st) == 7 :
98 + args, params = self.do(st[3], ctx)
99 + return self.ST.Property(lineno=st.srow, col_offset=st.scol,
100 + name=st[1].text,
101 + args=args,
102 + params=params,
103 + body=self.do(st[-1], ctx))
104 + else :
105 + return self.ST.Property(lineno=st.srow, col_offset=st.scol,
106 + name=st[1].text,
107 + args=[],
108 + params=[],
109 + body=self.do(st[-1], ctx))
110 + def do_ctl_suite (self, st, ctx) :
111 + """ctl_suite: ( ctl_formula NEWLINE | NEWLINE INDENT ctl_formula DEDENT )
112 + -> ast.form
113 +
114 + <<< prop foo () : True
115 + "Spec(atoms=[], properties=[Property(name='foo', args=[], params=[], body=Boolean(val=True))], main=None)"
116 + <<< prop bar () :
117 + ... True
118 + "Spec(atoms=[], properties=[Property(name='bar', args=[], params=[], body=Boolean(val=True))], main=None)"
119 + """
120 + if len(st) == 2 :
121 + return self.do(st[0], ctx)
122 + else :
123 + return self.do(st[2], ctx)
124 + def do_ctl_parameters (self, st, ctx) :
125 + """ctl_parameters: (ctl_param ',')* ctl_param
126 + -> [ast.ctlarg], [ast.ctlparam]
127 +
128 + <<< prop foo (p = @'my place', q : place, x : int, r : place) : True
129 + "Spec(atoms=[], properties=[Property(name='foo', args=[Place(name='p', place='my place')], params=[Parameter(name='q', type='place'), Parameter(name='x', type='int'), Parameter(name='r', type='place')], body=Boolean(val=True))], main=None)"
130 + <<< prop bar (x : int) : True
131 + "Spec(atoms=[], properties=[Property(name='bar', args=[], params=[Parameter(name='x', type='int')], body=Boolean(val=True))], main=None)"
132 + <<< prop egg (p = @'my place') : True
133 + "Spec(atoms=[], properties=[Property(name='egg', args=[Place(name='p', place='my place')], params=[], body=Boolean(val=True))], main=None)"
134 + <<< prop spam (p = @'my place', q : int, p : place) : True
135 + Traceback (most recent call last):
136 + ...
137 + ParseError: ... duplicate parameter 'p'
138 + """
139 + args, params = [], []
140 + seen = set()
141 + for child in st[::2] :
142 + node = self.do(child, ctx)
143 + if node.name in seen :
144 + raise ParseError(child, reason="duplicate parameter %r"
145 + % node.name)
146 + seen.add(node.name)
147 + if isinstance(node, self.ST.Place) :
148 + args.append(node)
149 + else :
150 + params.append(node)
151 + return args, params
152 + def do_ctl_param (self, st, ctx) :
153 + """ctl_param: NAME ( '=' '@' STRING+ | ':' NAME )
154 + -> ast.ctlarg|ast.ctlparam
155 +
156 + <<< prop foo (p = @'my place', q : place, x : int, r : place) : True
157 + "Spec(atoms=[], properties=[Property(name='foo', args=[Place(name='p', place='my place')], params=[Parameter(name='q', type='place'), Parameter(name='x', type='int'), Parameter(name='r', type='place')], body=Boolean(val=True))], main=None)"
158 + <<< prop bar (x : int) : True
159 + "Spec(atoms=[], properties=[Property(name='bar', args=[], params=[Parameter(name='x', type='int')], body=Boolean(val=True))], main=None)"
160 + <<< prop egg (p = @'my place') : True
161 + "Spec(atoms=[], properties=[Property(name='egg', args=[Place(name='p', place='my place')], params=[], body=Boolean(val=True))], main=None)"
162 + """
163 + if st[1].text == "=" :
164 + return self.ST.Place(lineno=st.srow, col_offset=st.scol,
165 + name=st[0].text,
166 + place="".join(self.ST.literal_eval(c.text)
167 + for c in st[3:]))
168 + else :
169 + return self.ST.Parameter(lineno=st.srow, col_offset=st.scol,
170 + name=st[0].text,
171 + type=st[2].text)
172 + def do_ctl_arguments (self, st, ctx) :
173 + """ctl_arguments: (NAME '=' ctl_place_or_test ',')* NAME '=' ctl_place_or_test
174 + -> [(str, ast.expr)]
175 +
176 + <<< foo(x=3, p='my place')
177 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='x', annotation=Num(n=3)), arg(arg='p', annotation=Str(s='my place'))]))"
178 + <<< foo(x=3)
179 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='x', annotation=Num(n=3))]))"
180 + <<< foo(p='my place')
181 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='p', annotation=Str(s='my place'))]))"
182 + <<< foo(x=3, p=@'my place')
183 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='x', annotation=Num(n=3)), arg(arg='p', annotation=Place(name=None, place='my place'))]))"
184 + <<< foo(x=3)
185 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='x', annotation=Num(n=3))]))"
186 + <<< foo(p=@'my place')
187 + "Spec(atoms=[], properties=[], main=Instance(name='foo', args=[arg(arg='p', annotation=Place(name=None, place='my place'))]))"
188 + """
189 + return [self.ST.arg(name.text, self.do(value, ctx))
190 + for name, value in zip(st[::4], st[2::4])]
191 + def do_ctl_place_or_test (self, st, ctx) :
192 + """ctl_place_or_test: test | '@' STRING+
193 + -> ast.expr | ast.Place
194 +
195 + <<< Foo(s='string', p=@'place', q=place_also)
196 + "Spec(atoms=[], properties=[], main=Instance(name='Foo', args=[arg(arg='s', annotation=Str(s='string')), arg(arg='p', annotation=Place(name=None, place='place')), arg(arg='q', annotation=Name(id='place_also', ctx=Load()))]))"
197 + """
198 + if st[0].symbol == "test" :
199 + return self.do(st[0], ctx)
200 + else :
201 + return self.do_ctl_place(st, ctx)
202 + def do_ctl_formula (self, st, ctx) :
203 + """ctl_formula: ctl_or_formula [ ctl_connector ctl_or_formula ]
204 + -> ast.form
205 +
206 + <<< True
207 + 'Spec(atoms=[], properties=[], main=Boolean(val=True))'
208 + <<< False => True
209 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Imply(), left=Boolean(val=False), right=Boolean(val=True)))'
210 + <<< False <=> False
211 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Iff(), left=Boolean(val=False), right=Boolean(val=False)))'
212 + """
213 + if len(st) == 1 :
214 + return self.do(st[0], ctx)
215 + else :
216 + return self.ST.CtlBinary(lineno=st.srow, col_offset=st.scol,
217 + op=self.do_ctl_connector(st[1], ctx),
218 + left=self.do(st[0], ctx),
219 + right=self.do(st[2], ctx))
220 + def do_ctl_connector (self, st, ctx) :
221 + """ctl_connector: ( '=' '>' | '<=' '>' )
222 + -> ast.ctlbinary
223 +
224 + <<< False => True
225 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Imply(), left=Boolean(val=False), right=Boolean(val=True)))'
226 + <<< False <=> False
227 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Iff(), left=Boolean(val=False), right=Boolean(val=False)))'
228 + """
229 + op = "".join(child.text for child in st)
230 + return self._ctl_binary_op[op](lineno=st.srow,
231 + col_offset=st.scol)
232 +
233 + def do_ctl_or_formula (self, st, ctx) :
234 + """ctl_or_formula: ctl_and_formula ('or' ctl_and_formula)*
235 + -> ast.form
236 +
237 + <<< True or False
238 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Or(), left=Boolean(val=True), right=Boolean(val=False)))'
239 + <<< True or False or True
240 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Or(), left=CtlBinary(op=Or(), left=Boolean(val=True), right=Boolean(val=False)), right=Boolean(val=True)))'
241 + <<< True or False or False and True and False
242 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Or(), left=CtlBinary(op=Or(), left=Boolean(val=True), right=Boolean(val=False)), right=CtlBinary(op=And(), left=CtlBinary(op=And(), left=Boolean(val=False), right=Boolean(val=True)), right=Boolean(val=False))))'
243 + """
244 + if len(st) == 1 :
245 + return self.do(st[0], ctx)
246 + else :
247 + values = [self.do(child, ctx) for child in st[::2]]
248 + ops = [self._ctl_binary_op[child.text](lineno=child.srow,
249 + col_offset=child.scol)
250 + for child in st[1::2]]
251 + while len(values) > 1 :
252 + left = values.pop(0)
253 + right = values.pop(0)
254 + operator = ops.pop(0)
255 + values.insert(0, self.ST.CtlBinary(lineno=st.srow,
256 + col_offset=st.scol,
257 + left=left,
258 + op=operator,
259 + right=right))
260 + return values[0]
261 + def do_ctl_and_formula (self, st, ctx) :
262 + """ctl_and_formula: ctl_not_formula ('and' ctl_not_formula)*
263 + -> ast.form
264 +
265 + <<< True and False
266 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=And(), left=Boolean(val=True), right=Boolean(val=False)))'
267 + """
268 + return self.do_ctl_or_formula(st, ctx)
269 + def do_ctl_not_formula (self, st, ctx) :
270 + """ctl_not_formula: ('not' ctl_not_formula | ctl_binary_formula)
271 + -> ast.form
272 +
273 + <<< True
274 + 'Spec(atoms=[], properties=[], main=Boolean(val=True))'
275 + <<< not True
276 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Not(), child=Boolean(val=True)))'
277 + <<< not not True
278 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Not(), child=CtlUnary(op=Not(), child=Boolean(val=True))))'
279 + """
280 + if len(st) == 1 :
281 + return self.do(st[0], ctx)
282 + else :
283 + return self.ST.CtlUnary(lineno=st.srow, col_offset=st.scol,
284 + op=self.ST.Not(lineno=st[0].srow,
285 + col_offset=st[0].scol),
286 + child=self.do(st[1], ctx))
287 + def do_ctl_binary_formula (self, st, ctx) :
288 + """ctl_binary_formula: ctl_unary_formula [ ctl_binary_op ctl_unary_formula ]
289 + -> ast.form
290 +
291 + <<< True U False
292 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Until(), left=Boolean(val=True), right=Boolean(val=False)))'
293 + <<< True W False
294 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=WeakUntil(), left=Boolean(val=True), right=Boolean(val=False)))'
295 + <<< True R False
296 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Release(), left=Boolean(val=True), right=Boolean(val=False)))'
297 + """
298 + if len(st) == 1 :
299 + return self.do(st[0], ctx)
300 + else :
301 + return self.ST.CtlBinary(lineno=st.srow, col_offset=st.scol,
302 + op=self.do(st[1], ctx),
303 + left=self.do(st[0], ctx),
304 + right=self.do(st[2], ctx))
305 + def do_ctl_unary_formula (self, st, ctx) :
306 + """ctl_unary_formula: [ ctl_unary_op ] (ctl_atom_formula | '(' ctl_formula ')')
307 + -> ast.form
308 +
309 + <<< True
310 + 'Spec(atoms=[], properties=[], main=Boolean(val=True))'
311 + <<< X True
312 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Next(), child=Boolean(val=True)))'
313 + <<< A True
314 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=All(), child=Boolean(val=True)))'
315 + <<< G True
316 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Globally(), child=Boolean(val=True)))'
317 + <<< F True
318 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Future(), child=Boolean(val=True)))'
319 + <<< E True
320 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Exists(), child=Boolean(val=True)))'
321 + <<< (True or False)
322 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Or(), left=Boolean(val=True), right=Boolean(val=False)))'
323 + <<< X (True or False)
324 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Next(), child=CtlBinary(op=Or(), left=Boolean(val=True), right=Boolean(val=False))))'
325 + """
326 + if len(st) == 1 :
327 + return self.do(st[0], ctx)
328 + elif len(st) == 2 :
329 + return self.ST.CtlUnary(lineno=st.srow, col_offset=st.scol,
330 + op=self.do(st[0], ctx),
331 + child=self.do(st[1], ctx))
332 + elif len(st) == 3 :
333 + return self.do(st[1], ctx)
334 + else :
335 + return self.ST.CtlUnary(lineno=st.srow, col_offset=st.scol,
336 + op=self.do(st[0], ctx),
337 + child=self.do(st[2], ctx))
338 + _ctl_unary_op = {"A" : ast.All,
339 + "E" : ast.Exists,
340 + "X" : ast.Next,
341 + "F" : ast.Future,
342 + "G" : ast.Globally}
343 + def do_ctl_unary_op (self, st, ctx) :
344 + """ctl_unary_op: ('A' | 'G' | 'F' | 'E' | 'X')
345 + -> ast.ctlunary
346 +
347 + <<< X True
348 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Next(), child=Boolean(val=True)))'
349 + <<< A True
350 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=All(), child=Boolean(val=True)))'
351 + <<< G True
352 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Globally(), child=Boolean(val=True)))'
353 + <<< F True
354 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Future(), child=Boolean(val=True)))'
355 + <<< E True
356 + 'Spec(atoms=[], properties=[], main=CtlUnary(op=Exists(), child=Boolean(val=True)))'
357 + """
358 + return self._ctl_unary_op[st[0].text](lineno=st.srow,
359 + col_offset=st.scol)
360 + _ctl_binary_op = {"=>" : ast.Imply,
361 + "<=>" : ast.Iff,
362 + "and" : ast.And,
363 + "or" : ast.Or,
364 + "U" : ast.Until,
365 + "W" : ast.WeakUntil,
366 + "R" : ast.Release}
367 + def do_ctl_binary_op (self, st, ctx) :
368 + """ctl_binary_op: ('R' | 'U' | 'W')
369 + -> ast.ctlbinary
370 +
371 + <<< True R False
372 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Release(), left=Boolean(val=True), right=Boolean(val=False)))'
373 + <<< True U False
374 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=Until(), left=Boolean(val=True), right=Boolean(val=False)))'
375 + <<< True W False
376 + 'Spec(atoms=[], properties=[], main=CtlBinary(op=WeakUntil(), left=Boolean(val=True), right=Boolean(val=False)))'
377 + """
378 + return self._ctl_binary_op[st[0].text](lineno=st[0].srow,
379 + col_offset=st[0].scol)
380 + def do_ctl_atom_formula (self, st, ctx) :
381 + """ctl_atom_formula: ( 'empty' '(' ctl_place ')'
382 + | 'marked' '(' ctl_place ')'
383 + | 'has' ['not'] '(' ctl_place ',' test (',' test)* ')'
384 + | 'deadlock' | 'True' | 'False'
385 + | NAME '(' ctl_arguments ')'
386 + | 'forall' [ 'distinct' ] NAME (',' NAME)*
387 + 'in' ctl_place '(' ctl_atom_formula ')'
388 + | 'exists' [ 'distinct' ] NAME (',' NAME)*
389 + 'in' ctl_place '(' ctl_atom_formula ')' )
390 + -> ast.atom
391 +
392 + <<< empty(p)
393 + "Spec(atoms=[], properties=[], main=EmptyPlace(place=Parameter(name='p', type='place')))"
394 + <<< empty(@'my' 'place')
395 + "Spec(atoms=[], properties=[], main=EmptyPlace(place=Place(name=None, place='myplace')))"
396 + <<< marked(p)
397 + "Spec(atoms=[], properties=[], main=MarkedPlace(place=Parameter(name='p', type='place')))"
398 + <<< marked(@'my place')
399 + "Spec(atoms=[], properties=[], main=MarkedPlace(place=Place(name=None, place='my place')))"
400 + <<< has(p, x)
401 + "Spec(atoms=[], properties=[], main=InPlace(data=[Name(id='x', ctx=Load())], place=Parameter(name='p', type='place')))"
402 + <<< has not(p, x, y)
403 + "Spec(atoms=[], properties=[], main=NotInPlace(data=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())], place=Parameter(name='p', type='place')))"
404 + <<< deadlock
405 + 'Spec(atoms=[], properties=[], main=Deadlock())'
406 + <<< True
407 + 'Spec(atoms=[], properties=[], main=Boolean(val=True))'
408 + <<< False
409 + 'Spec(atoms=[], properties=[], main=Boolean(val=False))'
410 + <<< myprop(x=1, p='my place')
411 + "Spec(atoms=[], properties=[], main=Instance(name='myprop', args=[arg(arg='x', annotation=Num(n=1)), arg(arg='p', annotation=Str(s='my place'))]))"
412 + <<< forall x in p (has(q, y))
413 + "Spec(atoms=[], properties=[], main=Quantifier(op=All(), vars=['x'], place=Parameter(name='p', type='place'), child=InPlace(data=[Name(id='y', ctx=Load())], place=Parameter(name='q', type='place')), distinct=False))"
414 + <<< forall x, y in p (has(q, x+y, x-y))
415 + "Spec(atoms=[], properties=[], main=Quantifier(op=All(), vars=['x', 'y'], place=Parameter(name='p', type='place'), child=InPlace(data=[BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), BinOp(left=Name(id='x', ctx=Load()), op=Sub(), right=Name(id='y', ctx=Load()))], place=Parameter(name='q', type='place')), distinct=False))"
416 + <<< forall distinct x, y in p (has(q, x+y, x-y))
417 + "Spec(atoms=[], properties=[], main=Quantifier(op=All(), vars=['x', 'y'], place=Parameter(name='p', type='place'), child=InPlace(data=[BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), BinOp(left=Name(id='x', ctx=Load()), op=Sub(), right=Name(id='y', ctx=Load()))], place=Parameter(name='q', type='place')), distinct=True))"
418 + <<< exists x in p (has(q, y))
419 + "Spec(atoms=[], properties=[], main=Quantifier(op=Exists(), vars=['x'], place=Parameter(name='p', type='place'), child=InPlace(data=[Name(id='y', ctx=Load())], place=Parameter(name='q', type='place')), distinct=False))"
420 + <<< exists x, y in p (has(q, x+y, x-y))
421 + "Spec(atoms=[], properties=[], main=Quantifier(op=Exists(), vars=['x', 'y'], place=Parameter(name='p', type='place'), child=InPlace(data=[BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), BinOp(left=Name(id='x', ctx=Load()), op=Sub(), right=Name(id='y', ctx=Load()))], place=Parameter(name='q', type='place')), distinct=False))"
422 + <<< exists distinct x, y in p (has(q, x+y, x-y))
423 + "Spec(atoms=[], properties=[], main=Quantifier(op=Exists(), vars=['x', 'y'], place=Parameter(name='p', type='place'), child=InPlace(data=[BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), BinOp(left=Name(id='x', ctx=Load()), op=Sub(), right=Name(id='y', ctx=Load()))], place=Parameter(name='q', type='place')), distinct=True))"
424 + """
425 + if st[0].text in ("True", "False") :
426 + return self.ST.Boolean(lineno=st.srow, col_offset=st.scol,
427 + val=(st[0].text == "True"))
428 + elif st[0].text == "deadlock" :
429 + return self.ST.Deadlock(lineno=st.srow, col_offset=st.scol)
430 + elif st[0].text in ("empty", "marked") :
431 + node = (self.ST.EmptyPlace if st[0].text == "empty"
432 + else self.ST.MarkedPlace)
433 + return node(lineno=st.srow, col_offset=st.scol,
434 + place=self.do(st[2], ctx))
435 + elif st[0].text == "has" :
436 + if st[1].text == "not" :
437 + node = self.ST.NotInPlace
438 + start = 3
439 + else :
440 + node = self.ST.InPlace
441 + start = 2
442 + place = self.do(st[start], ctx)
443 + if (isinstance(place, self.ST.Parameter)
444 + and place.type != "place") :
445 + raise ParseError(st[-4], reason="'place' parameter expected")
446 + return node(lineno=st.srow, col_offset=st.scol,
447 + data=[self.do(c, ctx) for c in st[start+2::2]],
448 + place=place)
449 + elif st[0].text in ("forall", "exists") :
450 + op = (self.ST.All if st[0].text == "forall"
451 + else self.ST.Exists)
452 + distinct = st[1].text == "distinct"
453 + start = 2 if distinct else 1
454 + return self.ST.Quantifier(lineno=st.srow, col_offset=st.scol,
455 + op=op(),
456 + vars=[c.text for c in st[start:-5:2]],
457 + place=self.do(st[-4], ctx),
458 + child=self.do(st[-2], ctx),
459 + distinct=distinct)
460 + else :
461 + return self.ST.Instance(lineno=st.srow, col_offset=st.scol,
462 + name=st[0].text,
463 + args=self.do(st[2], ctx))
464 + def do_ctl_place (self, st, ctx) :
465 + """ctl_place: '@' STRING+ | NAME
466 + -> ast.ctlarg
467 +
468 + <<< has(@'my place', x)
469 + "Spec(atoms=[], properties=[], main=InPlace(data=[Name(id='x', ctx=Load())], place=Place(name=None, place='my place')))"
470 + <<< has(@'another' 'place', y)
471 + "Spec(atoms=[], properties=[], main=InPlace(data=[Name(id='y', ctx=Load())], place=Place(name=None, place='anotherplace')))"
472 + <<< has(@'my place', x, y)
473 + "Spec(atoms=[], properties=[], main=InPlace(data=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())], place=Place(name=None, place='my place')))"
474 + <<< has not(@'my place', x)
475 + "Spec(atoms=[], properties=[], main=NotInPlace(data=[Name(id='x', ctx=Load())], place=Place(name=None, place='my place')))"
476 + <<< has not(@'my place', x, y)
477 + "Spec(atoms=[], properties=[], main=NotInPlace(data=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())], place=Place(name=None, place='my place')))"
478 + """
479 + if st[0].symbol == "NAME" :
480 + return self.ST.Parameter(lineno=st.srow, col_offset=st.scol,
481 + name=st[0].text,
482 + type="place")
483 + else :
484 + return self.ST.Place(lineno=st.srow, col_offset=st.scol,
485 + name=None,
486 + place="".join(self.ST.literal_eval(c.text)
487 + for c in st[1:]))
488 +
489 +parse = Translator.parse
490 +
491 +if __name__ == "__main__" :
492 + testparser(Translator)
This diff could not be displayed because it is too large.
1 +"""A Python implementation of CPython's parser
2 +
3 +This module is largely based on Jonathan Riehl's PyPgen included in
4 +the Basil framework (http://code.google.com/p/basil).
5 +"""
6 +
7 +from snakes import SnakesError
8 +from snakes.lang import ast
9 +import tokenize, string, pprint, warnings, inspect, os.path
10 +from snakes.compat import *
11 +
12 +def warn (message) :
13 + """Issue a warning message.
14 + """
15 + warnings.warn(message, stacklevel=2)
16 +
17 +class Token (str) :
18 + """A token from the lexer.
19 +
20 + Behaves as a string that is either the token value (if not empty),
21 + or the token name. Additional attributes allow to extract various
22 + information:
23 +
24 + - self.kind: token number (like tokenize.ENDMARKER)
25 + also available as int(self)
26 + - self.text: token text
27 + - self.srow: start row
28 + - self.scol: start column
29 + - self.erow: end row
30 + - self.ecol: end column
31 + - self.line: full line of text from which the token comes
32 + - self.name: token name (ie, tokenize.tok_name[self.kind])
33 + - self.lexer: the Tokenizer instance than produced this token
34 + - self.filename: input file from which the token comes (or
35 + '<string>' if None available)
36 + """
37 + def __new__ (cls, token, lexer) :
38 + """Create a new instance.
39 +
40 + __new__ is used instead of __init__ because str is a
41 + non-mutable object and this __init__ could not assign a str
42 + content. For more information see:
43 +
44 + http://docs.python.org/reference/datamodel.html#object.__new__
45 + """
46 + kind = token[0]
47 + text = token[1]
48 + name = lexer.tok_name[kind]
49 + self = str.__new__(cls, text or name)
50 + self.kind = kind
51 + self.text = text
52 + self.srow, self.scol = token[2]
53 + self.erow, self.ecol = token[3]
54 + self.line = token[4]
55 + self.name = name
56 + self.lexer = lexer
57 + try :
58 + self.filename = lexer.infile.name
59 + except :
60 + self.filename = "<string>"
61 + return self
62 + def __int__ (self) :
63 + """Coercion to int (return self.kind).
64 + """
65 + return self.kind
66 +
67 +class Location (str) :
68 + """A position in a parsed file
69 +
70 + Used to aggregate the positions of all the tokens is a parse
71 + (sub)tree. The following attributes are available:
72 +
73 + - self.srow: start row
74 + - self.scol: start column
75 + - self.erow: end row
76 + - self.ecol: end column
77 + - self.filename: input file from which the token comes (or
78 + '<string>' if None available)
79 + """
80 + def __new__ (cls, first, last) :
81 + """Create a new instance
82 +
83 + Expected arguments:
84 + - first: the first Token or Location instance in the region
85 + - last: the last one
86 + """
87 + self = str.__new__(cls, "%s[%s:%s-%s:%s]"
88 + % (first.filename, first.srow, first.scol,
89 + last.erow, last.ecol))
90 + self.srow, self.scol = first.srow, first.scol
91 + self.erow, self.ecol = last.erow, last.ecol
92 + self.filename = first.filename
93 + self.lexer = first.lexer
94 + return self
95 +
96 +class ParseError (SnakesError) :
97 + """Exception raised when parsing fails.
98 +
99 + It's better not to use SyntaxError because this soes not allows to
100 + distinguish when the text being parser has a syntax error from
101 + when because the parser itself has a syntax error.
102 + """
103 + def __init__ (self, token, expected=None, reason=None) :
104 + """Initialize a new instance.
105 +
106 + Expected arguments are:
107 + - token: the erroneous token (a Token instance)
108 + - expected: either a Token instance or a token kind (like
109 + tokenize.NAME) to indicated what was expected instead
110 + """
111 + self.token = token
112 + if expected is not None :
113 + expected = int(expected)
114 + self.expected = expected
115 + if token is None :
116 + pos = ""
117 + else :
118 + pos = "%s[%s:%s]: " % (token.filename, token.srow, token.scol)
119 + if reason is not None :
120 + msg = reason
121 + elif self.expected is not None :
122 + msg = ("expected %s but found %r" %
123 + (token.lexer.tok_name[expected], token))
124 + else :
125 + msg = "unexpected token %r" % token
126 + SnakesError.__init__(self, pos + msg)
127 +
128 +class Tokenizer (object) :
129 + """A simple lexical analyser based on Python's tokenize module.
130 +
131 + The differences with tokenize module are:
132 + - new simple tokens may be added (just strings, no regexps)
133 + - Python's token may be not included
134 + - tokens may be automatically skipped (removed from the output)
135 + - tokenize.OP kind is refined (eg, ':' gets kind tokenize.COLON)
136 +
137 + This class replaces two PyPgen elements:
138 + - module basil.lang.python.TokenUtils
139 + - module basil.lang.python.StdTokenizer
140 + """
141 + _pyopmap = {
142 + '(' : tokenize.LPAR,
143 + ')' : tokenize.RPAR,
144 + '[' : tokenize.LSQB,
145 + ']' : tokenize.RSQB,
146 + ':' : tokenize.COLON,
147 + ',' : tokenize.COMMA,
148 + ';' : tokenize.SEMI,
149 + '+' : tokenize.PLUS,
150 + '+=' : tokenize.PLUSEQUAL,
151 + '-' : tokenize.MINUS,
152 + '-=' : tokenize.MINEQUAL,
153 + '*' : tokenize.STAR,
154 + '**' : tokenize.DOUBLESTAR,
155 + '**=' : tokenize.DOUBLESTAREQUAL,
156 + '*=' : tokenize.STAREQUAL,
157 + '/' : tokenize.SLASH,
158 + '//' : tokenize.DOUBLESLASH,
159 + '//=' : tokenize.DOUBLESLASHEQUAL,
160 + '/=' : tokenize.SLASHEQUAL,
161 + '|' : tokenize.VBAR,
162 + '|=' : tokenize.VBAREQUAL,
163 + '&' : tokenize.AMPER,
164 + '&=' : tokenize.AMPEREQUAL,
165 + '<' : tokenize.LESS,
166 + '<=' : tokenize.LESSEQUAL,
167 + '<<' : tokenize.LEFTSHIFT,
168 + '<<=' : tokenize.LEFTSHIFTEQUAL,
169 + '>' : tokenize.GREATER,
170 + '>=' : tokenize.GREATEREQUAL,
171 + '>>' : tokenize.RIGHTSHIFT,
172 + '>>=' : tokenize.RIGHTSHIFTEQUAL,
173 + '=' : tokenize.EQUAL,
174 + '==' : tokenize.EQEQUAL,
175 + '.' : tokenize.DOT,
176 + '%' : tokenize.PERCENT,
177 + '%=' : tokenize.PERCENTEQUAL,
178 + '{' : tokenize.LBRACE,
179 + '}' : tokenize.RBRACE,
180 + '^' : tokenize.CIRCUMFLEX,
181 + '^=' : tokenize.CIRCUMFLEXEQUAL,
182 + '~' : tokenize.TILDE,
183 + '!=' : tokenize.NOTEQUAL,
184 + '<>' : tokenize.NOTEQUAL,
185 + '@' : tokenize.AT
186 + }
187 + def __init__ (self, python=True, opmap={}, skip=None, **extra) :
188 + """Initialize a new instance.
189 +
190 + Expected arguments are:
191 + - python: a bool to indicate whether to include or not
192 + Python's tokens (default to True)
193 + - opmap: a dict to map litteral tokens (given as '...' in the
194 + grammar) to token kinds (default to {}). This parameter is
195 + useful only to redefine Python's mapping
196 + - skip: a collection of tokens that the tokenizer will
197 + automatically skip (default to [COMMENT, NL])
198 + - additional keywords arguments allow to define new tokens,
199 + for instance, providing
200 + DOLLAR='$'
201 + defines a new token called 'DOLLAR' (its kind will be
202 + automatically computed)
203 +
204 + An instance of Tokenizer has the following attributes:
205 + - self.opmap: a dict mapping operators token literals to the
206 + corresponding kind, for instance, ':' is mapped to
207 + tokenize.COLON (this can be overridden using argument
208 + opmap)
209 + - self.tok_name: a replacement of tokenize.tok_name that also
210 + include the user-defined tokens
211 + - for each token called FOO (including user-defined ones), an
212 + attribute self.FOO hols the corresponding kind
213 + """
214 + self._python = python
215 + self._opmap = opmap.copy()
216 + if python :
217 + self.opmap = self._pyopmap.copy()
218 + self.opmap.update(opmap)
219 + else :
220 + self.opmap = opmap.copy()
221 + self.tok_name = {}
222 + self._extra = {}
223 + if python :
224 + for kind, name in tokenize.tok_name.items() :
225 + self.tok_name[kind] = name
226 + setattr(self, name, kind)
227 + if not hasattr(self, "NT_OFFSET") :
228 + self.NT_OFFSET = 256
229 + last = max(n for n in self.tok_name if n != self.NT_OFFSET)
230 + for shift, (name, txt) in enumerate(sorted(extra.items())) :
231 + #WARNING: sorted above is required to guaranty that extra
232 + # tokens will always get the same number (dict order is
233 + # not guaranteed)
234 + kind = last + shift
235 + if kind >= self.NT_OFFSET :
236 + raise TypeError("too many new tokens")
237 + self.tok_name[kind] = name
238 + setattr(self, name, kind)
239 + self._extra[txt] = kind
240 + self.opmap.update(self._extra)
241 + if skip is None :
242 + skip = [self.COMMENT, self.NL]
243 + self._skip = set(skip)
244 + def __repr__ (self) :
245 + """Encodes an instance as Python source code.
246 +
247 + Non-default arguments provided to the constructor are included
248 + so that exactly the same Tokenizer instance can be recovered
249 + from the returned source code.
250 +
251 + >>> print repr(Tokenizer())
252 + Tokenizer()
253 + >>> print repr(Tokenizer(DOLLAR='$'))
254 + Tokenizer(DOLLAR='$')
255 + >>> print repr(Tokenizer(skip=[], DOLLAR='$'))
256 + Tokenizer(skip=[], DOLLAR='$')
257 + """
258 + args = []
259 + if not self._python :
260 + args.append("python=%s" % self._python)
261 + if self._opmap :
262 + args.append("opmap=%r" % self._opmap)
263 + if self._skip != set([self.COMMENT, self.NL]) :
264 + args.append("skip=%r" % list(self._skip))
265 + args.extend("%s=%r" % (self.tok_name[kind], txt) for txt, kind
266 + in self._extra.items())
267 + return "%s(%s)" % (self.__class__.__name__, ", ".join(args))
268 + def tokenize (self, stream) :
269 + """Break an input stream into tokens.
270 +
271 + Expected argument is:
272 + - stream: a file-like object (with a method readline)
273 +
274 + Return a generator of Token instances, ParseError is raised
275 + whenever an erroneous token is encountered.
276 +
277 + This is basically the same as tokenize.generate_tokens but:
278 + - the appropriate tokens are skipped
279 + - OP kind is converted according to self.opmap
280 + - user-defined tokens are handled
281 +
282 + During the iteration, two more attributes can be used:
283 + - self.last: last recognized token (ie, last yielded)
284 + - self.infile: the input stream passed to method tokenize
285 + """
286 + self.infile = stream
287 + self.last = None
288 + self.lines = []
289 + def readline () :
290 + self.lines.append(stream.readline())
291 + return self.lines[-1]
292 + err = self.ERRORTOKEN
293 + for token in tokenize.generate_tokens(readline) :
294 + if token[0] == err :
295 + try :
296 + token = (self._extra[token[1]],) + token[1:]
297 + except :
298 + raise ParseError(Token(token, self))
299 + elif token[0] in self._skip :
300 + try:
301 + self.skip_token(Token(token, self))
302 + except :
303 + pass
304 + continue
305 + elif token[0] == self.OP :
306 + token = (self.opmap[token[1]],) + token[1:]
307 + self.last = Token(token, self)
308 + yield self.last
309 + def skip_token (self, token) :
310 + pass
311 +
312 +try :
313 + Tokenizer._pyopmap['`'] = tokenize.BACKQUOTE
314 +except AttributeError :
315 + pass
316 +
317 +class PgenParser (object) :
318 + """A parser for pgen files.
319 +
320 + The following grammar is used:
321 +
322 + mstart : ( rule | NEWLINE | newtok )* ENDMARKER
323 + newtok : '$' NAME STRING NEWLINE
324 + rule : NAME COLON rhs NEWLINE
325 + rhs := alt ( VBAR alt )*
326 + alt : item+
327 + item : LSQB rhs RSQB | atom ( STAR | PLUS )?
328 + atom : LPAR rhs RPAR | NAME | STRING
329 +
330 + With respect to PyPgen, an additional rule 'newtok' has been added
331 + to allow for user-defined tokens.
332 +
333 + This class is adapted from module basil.parsing.PgenParser, it has
334 + attributes MSTART, ..., provinding to the symbol numbers for the
335 + corresponding rules.
336 + """
337 + MSTART = 256
338 + RULE = 257
339 + RHS = 258
340 + ALT = 259
341 + ITEM = 260
342 + ATOM = 261
343 + NEWTOK = 262
344 + def __init__ (self) :
345 + self.lexer = Tokenizer(NEWTOK='$')
346 + def expect (self, expected, found) :
347 + if expected != found.kind :
348 + raise ParseError(found, expected=expected)
349 + @classmethod
350 + def parse (cls, filename) :
351 + """Parse a pgen file.
352 +
353 + Expected argument is:
354 + - filename: path of pgen file to be parsed
355 +
356 + Return a 2-tuple (G, T) where:
357 + - G is the grammar's syntax tree
358 + - T is a Tokenizer instance to be used with the parser
359 + generated from this grammar (ie, including all the required
360 + user-defined tokens)
361 +
362 + This is basically the only method that is needed:
363 +
364 + >>> mygrammar = PgenParser.parse('myfile.pgen')
365 + """
366 + return cls().parse_file(filename)
367 + def parse_file (self, filename) :
368 + """Parse a pgen file.
369 +
370 + Expected argument is:
371 + - filename: path of pgen file to be parsed
372 +
373 + Return a 2-tuple (g, t):
374 + - g: is the grammar's syntax tree
375 + - t: is a Tokenizer instance to be used with the parser
376 + generated from this grammar (ie, including all the required
377 + user-defined tokens)
378 +
379 + Recognize mstart : ( rule | NEWLINE )* ENDMARKER
380 +
381 + Like in PyPgen, the parser is recursive descendent, each rule
382 + 'X' being recognized by a dedicated method 'handleX' (except
383 + for 'mstart'). Each such method expects a current token (or
384 + None if it has to be fetched from the tokenizer) and returns a
385 + 2-tuple (R, T) where:
386 + - R is the resulting syntax tree
387 + - T is the new current token (or None)
388 + """
389 + self.infile = open(filename)
390 + self.tokens = self.lexer.tokenize(self.infile)
391 + extra = {}
392 + children = []
393 + current = next(self.tokens)
394 + while current.kind != self.lexer.ENDMARKER :
395 + if current.kind == self.lexer.NEWLINE :
396 + children.append((current, []))
397 + current = None
398 + elif current.kind == self.lexer.NEWTOK :
399 + name, text = self.handleNewtok(current)
400 + current = None
401 + extra[name] = text
402 + else :
403 + ruleResult, current = self.handleRule(current)
404 + children.append(ruleResult)
405 + if current is None :
406 + current = next(self.tokens)
407 + children.append((current, []))
408 + return (self.MSTART, children), Tokenizer(**extra)
409 + def handleNewtok (self, current=None) :
410 + """Recognize newtok : '$' NAME STRING NEWLINE
411 +
412 + Unlike the other 'handleX' methods, this one does not return a
413 + syntax tree because it implements a parsing directive.
414 + Instead, it returns a 2-tuple (N, S) where:
415 + - N is the user-defined token name
416 + - S is the token string value
417 + """
418 + if current is None :
419 + current = next(self.tokens)
420 + self.expect(self.lexer.NEWTOK, current)
421 + name = next(self.tokens)
422 + self.expect(self.lexer.NAME, name)
423 + text = next(self.tokens)
424 + self.expect(self.lexer.STRING, text)
425 + nl = next(self.tokens)
426 + self.expect(self.lexer.NEWLINE, nl)
427 + return name, compile(text, "<string>", "eval",
428 + ast.PyCF_ONLY_AST).body.s
429 + def handleRule (self, current=None) :
430 + """Recognize rule : NAME COLON rhs NEWLINE
431 + """
432 + children = []
433 + if current is None :
434 + current = next(self.tokens)
435 + self.expect(self.lexer.NAME, current)
436 + children.append((current, []))
437 + current = next(self.tokens)
438 + self.expect(self.lexer.COLON, current)
439 + children.append((current, []))
440 + rhsResult, current = self.handleRhs()
441 + children.append(rhsResult)
442 + if current is None :
443 + current = next(self.tokens)
444 + self.expect(self.lexer.NEWLINE, current)
445 + children.append((current, []))
446 + result = (self.RULE, children)
447 + return result, None
448 + def handleRhs (self, current=None) :
449 + """Recognize rhs : alt ( VBAR alt )*
450 + """
451 + children = []
452 + altResult, current = self.handleAlt(current)
453 + children.append(altResult)
454 + if current is None :
455 + current = next(self.tokens)
456 + while current.kind == self.lexer.VBAR :
457 + children.append((current, []))
458 + altResult, current = self.handleAlt()
459 + children.append(altResult)
460 + if current is None :
461 + current = next(self.tokens)
462 + result = (self.RHS, children)
463 + return result, current
464 + def handleAlt (self, current=None) :
465 + """ Recognize alt : item+
466 + """
467 + children = []
468 + itemResult, current = self.handleItem(current)
469 + children.append(itemResult)
470 + if current is None :
471 + current = next(self.tokens)
472 + while current.kind in (self.lexer.LSQB, self.lexer.LPAR,
473 + self.lexer.NAME, self.lexer.STRING) :
474 + itemResult, current = self.handleItem(current)
475 + children.append(itemResult)
476 + if current is None :
477 + current = next(self.tokens)
478 + return (self.ALT, children), current
479 + def handleItem (self, current=None) :
480 + """Recognize item : LSQB rhs RSQB | atom ( STAR | PLUS )?
481 + """
482 + children = []
483 + if current is None :
484 + current = next(self.tokens)
485 + if current.kind == self.lexer.LSQB :
486 + children.append((current, []))
487 + rhsResult, current = self.handleRhs()
488 + children.append(rhsResult)
489 + if current is None :
490 + current = next(self.tokens)
491 + self.expect(self.lexer.RSQB, current)
492 + children.append((current, []))
493 + current = None
494 + else :
495 + atomResult, current = self.handleAtom(current)
496 + children.append(atomResult)
497 + if current is None :
498 + current = next(self.tokens)
499 + if current.kind in (self.lexer.STAR, self.lexer.PLUS) :
500 + children.append((current, []))
501 + current = None
502 + return (self.ITEM, children), current
503 + def handleAtom (self, current=None) :
504 + """Recognize atom : LPAR rhs RPAR | NAME | STRING
505 + """
506 + children = []
507 + if current is None :
508 + current = next(self.tokens)
509 + tokType = current.kind
510 + if tokType == self.lexer.LPAR :
511 + children.append((current, []))
512 + rhsResult, current = self.handleRhs()
513 + children.append(rhsResult)
514 + if current is None :
515 + current = next(self.tokens)
516 + self.expect(self.lexer.RPAR, current)
517 + children.append((current, []))
518 + elif tokType == self.lexer.STRING :
519 + children.append((current, []))
520 + else :
521 + self.expect(self.lexer.NAME, current)
522 + children.append((current, []))
523 + return (self.ATOM, children), None
524 +
525 +class Parser (object) :
526 + """A LL1 parser for a generated grammar.
527 +
528 + This class aggregates two elements from PyPgen:
529 + - module basil.lang.python.DFAParser
530 + - class basil.parsing.PyPgen.PyPgenParser
531 +
532 + The main differences are:
533 + - simplified interface
534 + - adapt to handle Token instances instead of 3-tuples (kind,
535 + text, lineno)
536 + - remove functions arguments that can now be retreived as
537 + instance attributes
538 + - use Python warnings instead of print statements
539 + - many minor code edits (for my own understanding)
540 +
541 + Docstrings are provided only for methods that did not have an
542 + equivalent in PyPgen or that have been changed in a significant
543 + way.
544 + """
545 + def __init__ (self, grammar, tokenizer) :
546 + """Initialize a new instance.
547 +
548 + Expected arguments are:
549 + - grammar: the gramar object as returned by PyPgen.grammar()
550 + - tokenizer: a Tokenizer instance suitable for this grammar
551 + (eg, that passed to PyPgen's constructor)
552 + """
553 + self.grammar = grammar
554 + self.start = grammar[2]
555 + self.stringMap = {}
556 + for dfa in self.grammar[0] :
557 + dfaType, dfaName = dfa[:2]
558 + self.stringMap[dfaName] = dfaType
559 + self.symbolMap = {}
560 + for dfa in self.grammar[0] :
561 + dfaType, dfaName = dfa[:2]
562 + self.symbolMap[dfaType] = dfaName
563 + self.tokenizer = tokenizer
564 + self.addAccelerators()
565 + def parseTokens (self, tokens, start=None) :
566 + """Parse a series of tokens.
567 +
568 + Expected arguments:
569 + - tokens: a generator of Token instances
570 + - start: the start symbol to be recognized (or None to use
571 + the default one)
572 +
573 + The token generator should provide only tokens that are
574 + compatible with the tokenizer passed to the Parser's
575 + constructor. Otherwise, ParseError will be raised as unknown
576 + tokens will be issued.
577 + """
578 + self.tokens = tokens
579 + return self._parse(start)
580 + def parseFile (self, filename, start=None) :
581 + """Parse a text file provided by its path.
582 +
583 + Expected arguments:
584 + - filename: a file name
585 + - start: the start symbol to be recognized (or None to use
586 + the default one)
587 +
588 + The start symbol may be provided by its number (int) or its
589 + name (str) as specified in the grammar.
590 + """
591 + self.tokens = self.tokenizer.tokenize(open(filename))
592 + return self._parse(start)
593 + def parseStream (self, stream, start=None) :
594 + """Parse text from an opened file.
595 +
596 + Expected arguments:
597 + - stream: a file-like object (with a method readline)
598 + - start: the start symbol to be recognized (or None to use
599 + the default one)
600 +
601 + The start symbol may be provided by its number (int) or its
602 + name (str) as specified in the grammar.
603 + """
604 + self.tokens = self.tokenizer.tokenize(stream)
605 + return self._parse(start)
606 + def parseString (self, text, start=None, filename="<string>") :
607 + """Parse text given as a string.
608 +
609 + Expected arguments:
610 + - text: a string-like object
611 + - start: the start symbol to be recognized (or None to use
612 + the default one)
613 +
614 + The start symbol may be provided by its number (int) or its
615 + name (str) as specified in the grammar.
616 + """
617 + data = io.StringIO(text)
618 + data.name = filename
619 + self.tokens = self.tokenizer.tokenize(data)
620 + return self._parse(start)
621 + def _parse (self, start=None) :
622 + """Main parsing method.
623 +
624 + Expected argument:
625 + - start: the start symbol to be recognized (or None to use
626 + the default one)
627 +
628 + The start symbol may be provided by its number (int) or its
629 + name (str) as specified in the grammar.
630 + """
631 + if start is None :
632 + start = self.start
633 + elif start in self.stringMap :
634 + start = self.stringMap[start]
635 + elif start not in self.symbolMap :
636 + raise ValueError("unknown start symbol %r" % start)
637 + tokens = self.tokens
638 + # initialize the parsing stack
639 + rootNode = ((start, None, 0), [])
640 + dfa = self.findDFA(start)
641 + self.stack = [(dfa[3][dfa[2]], dfa, rootNode)]
642 + # parse all of it
643 + result = self._LL1_OK
644 + while result == self._LL1_OK :
645 + result, expected = self.addToken(next(tokens))
646 + if result == self._LL1_DONE :
647 + return self._fix_locations(rootNode)
648 + elif result == self._LL1_SYNTAX :
649 + raise ParseError(self.tokenizer.last, expected=expected)
650 + def _tostrings (self, st) :
651 + """Substitute symbol numbers by strings in a syntax tree.
652 +
653 + Expected argument:
654 + - st: a syntax tree as returned by the parser
655 + """
656 + (kind, token, lineno), children = st
657 + if kind >= self.tokenizer.NT_OFFSET :
658 + name = self.symbolMap
659 + else :
660 + name = self.tokenizer.tok_name
661 + return ((name[kind], token, lineno),
662 + [self._tostrings(c) for c in children])
663 + def _fix_locations (self, st) :
664 + """Replaces None in non-terminal nodes by a Location instance.
665 +
666 + Expected argument:
667 + - st: a syntax tree as returned by the parser
668 + """
669 + (kind, token, lineno), children = st
670 + children = [self._fix_locations(c) for c in children]
671 + if kind >= self.tokenizer.NT_OFFSET :
672 + token = Location(children[0][0][1], children[-1][0][1])
673 + return ((kind, token, lineno), children)
674 + def pprint (self, st) :
675 + """Return a human-readable representation of a syntax tree.
676 +
677 + Expected argument:
678 + - st: a syntax tree as returned by the parser
679 +
680 + All symbol numbers are substituted by the corresponding names
681 + and the text is indented appropriately.
682 + """
683 + return pprint.pformat(self._tostrings(st))
684 + # the rest of the class has not changed too much
685 + def addAccelerators (self) :
686 + if self.grammar[-1] : # already has accelerators
687 + return
688 + dfas, labels, start, accel = self.grammar
689 + def handleState (state) :
690 + arcs, accel, accept = state
691 + accept = 0
692 + labelCount = len(labels)
693 + accelArray = [-1] * labelCount
694 + for arc in arcs :
695 + labelIndex, arrow = arc
696 + kind = labels[labelIndex][0]
697 + if arrow >= 128 :
698 + warn("too many states (%d >= 128)!" % arrow)
699 + continue
700 + if kind >= self.tokenizer.NT_OFFSET :
701 + targetFirstSet = self.findDFA(kind)[4]
702 + if kind - self.tokenizer.NT_OFFSET >= 128 :
703 + warn("nonterminal too high (%d >= %d)!" %
704 + (kind, 128 + self.tokenizer.NT_OFFSET))
705 + continue
706 + for ibit in range(labelCount) :
707 + if self.testbit(targetFirstSet, ibit) :
708 + accelVal = (arrow | 128 |
709 + ((kind - self.tokenizer.NT_OFFSET) << 8))
710 + oldVal = accelArray[ibit]
711 + if oldVal != -1 :
712 + # XXX Make this error reporting more better.
713 + oldType = oldVal >> 8
714 + # FIXME: bug in the original source
715 + #warn("ambiguity at bit %d (for %d: was to %x,"
716 + # " now to %x)."
717 + # % (ibit, states.index(state),
718 + # oldVal, accelVal))
719 + warn("ambiguity at bit %d" % ibit)
720 + accelArray[ibit] = (arrow | 128 |
721 + ((kind - self.tokenizer.NT_OFFSET) << 8))
722 + elif labelIndex == 0 :
723 + accept = 1
724 + elif labelIndex >= 0 and labelIndex < labelCount :
725 + accelArray[labelIndex] = arrow
726 + # Now compute the upper and lower bounds.
727 + accelUpper = labelCount
728 + while accelUpper > 0 and accelArray[accelUpper-1] == -1 :
729 + accelUpper -= 1
730 + accelLower = 0
731 + while accelLower < accelUpper and accelArray[accelLower] == -1 :
732 + accelLower += 1
733 + accelArray = accelArray[accelLower:accelUpper]
734 + return (arcs, (accelUpper, accelLower, accelArray), accept)
735 + def handleDFA (dfa) :
736 + kind, name, initial, states, first = dfa
737 + return (kind, name, initial, list(map(handleState, states)))
738 + self.grammar = (list(map(handleDFA, dfas)), labels, start, 1)
739 + _LL1_OK = 0 # replaced E_ prefix with _LL1_ to prevent potential
740 + _LL1_DONE = 1 # conflicts with grammar symbols
741 + _LL1_SYNTAX = 2
742 + def testbit (self, bitstr, ibit) :
743 + return (ord(bitstr[ibit >> 3]) & (1 << (ibit & 0x7))) != 0
744 + def classify (self, token) :
745 + labels = self.grammar[1]
746 + if token.kind == self.tokenizer.NAME :
747 + for i, label in enumerate(labels) :
748 + if (token.kind, token) == label :
749 + return i
750 + for i, label in enumerate(labels) :
751 + if (token.kind == label[0]) and (label[1] is None) :
752 + return i
753 + return -1
754 + def findDFA (self, start) :
755 + return self.grammar[0][start - self.tokenizer.NT_OFFSET]
756 + def addToken (self, token) :
757 + stack = self.stack
758 + ilabel = self.classify(token)
759 + while True :
760 + state, dfa, parent = stack[-1]
761 + # Perform accelerator
762 + arcs, (accelUpper, accelLower, accelTable), accept = state
763 + if accelLower <= ilabel < accelUpper :
764 + accelResult = accelTable[ilabel - accelLower]
765 + if accelResult != -1 :
766 + # Handle accelerator result
767 + if accelResult & 128 :
768 + # Push non-terminal
769 + nt = (accelResult >> 8) + self.tokenizer.NT_OFFSET
770 + arrow = accelResult & 127
771 + nextDFA = self.findDFA(nt)
772 + # INLINE PUSH
773 + newAstNode = ((nt, None, token.srow), [])
774 + parent[1].append(newAstNode)
775 + stack[-1] = (dfa[3][arrow], dfa, parent)
776 + stack.append((nextDFA[3][nextDFA[2]], nextDFA,
777 + newAstNode))
778 + continue
779 + # INLINE SHIFT
780 + parent[1].append(((token.kind, token, token.srow), []))
781 + nextState = dfa[3][accelResult]
782 + stack[-1] = (nextState, dfa, parent)
783 + state = nextState
784 + while state[2] and len(state[0]) == 1 :
785 + # INLINE POP
786 + stack.pop(-1)
787 + if not stack :
788 + return self._LL1_DONE, None
789 + else :
790 + state, dfa, parent = stack[-1]
791 + return self._LL1_OK, None
792 + if accept :
793 + stack.pop(-1)
794 + if not stack :
795 + return self._LL1_SYNTAX, self.tokenizer.ENDMARKER
796 + continue
797 + if ((accelUpper < accelLower) and
798 + (self.grammar[1][accelLower][1] is not None)) :
799 + expected = self.grammar[1][accelLower][1]
800 + else :
801 + expected = None
802 + return self._LL1_SYNTAX, expected
803 +
804 +class PyPgen (object) :
805 + """A grammar generator.
806 +
807 + This class aggregates two elements from PyPgen:
808 + - class basil.parsing.PyPgen.PyPgen
809 + - function basil.parsing.PyPgen.buildParser
810 + - parts of function basil.parsing.PyPgen.parserMain
811 +
812 + The main differences are:
813 + - simplified interface
814 + - adapt to handle Token instances instead of 3-tuples (kind,
815 + text, lineno)
816 + - remove functions arguments that can now be retreived as
817 + instance attributes
818 + - use Python warnings instead of print statements
819 + - many minor code edits (for my own understanding)
820 +
821 + Docstrings are provided only for methods that did not have an
822 + equivalent in PyPgen or that have been changed in a significant
823 + way.
824 + """
825 + def __init__ (self, gst, tokenizer) :
826 + """Initialize a new instance.
827 +
828 + Expected arguments are:
829 + - gst: the grammar's syntax tree as returned by
830 + PgenParser.parse()
831 + - tokenizer: a Tokenizer instance suitable for this grammar,
832 + also returned by PgenParser.parse()
833 + """
834 + self.tokenizer = tokenizer
835 + self.EMPTY = self.tokenizer.ENDMARKER
836 + self.gst = gst
837 + self.nfaGrammar = self.dfaGrammar = None
838 + self.nfa = None
839 + self.crntKind = self.tokenizer.NT_OFFSET
840 + self.operatorMap = tokenizer.opmap
841 + def grammar (self) :
842 + """Generate and return the grammar object.
843 + """
844 + nfaGrammar = self.handleStart(self.gst)
845 + grammar = self.generateDfaGrammar(nfaGrammar)
846 + self.translateLabels(grammar)
847 + self.generateFirstSets(grammar)
848 + grammar[0] = list(map(tuple, grammar[0]))
849 + # Trick to add accelerators at generation time: it's easier to
850 + # do it this way than to extract the required elements from
851 + # class Parser.
852 + return Parser(tuple(grammar), self.tokenizer).grammar
853 + def python (self, pgen="pgen", inline=False) :
854 + """Build and return Python code for parsing module.
855 +
856 + Expected arguments are:
857 + - pgen: the name of module pgen in the generated source
858 + (default to 'pgen')
859 + - inline: a bool value to indicate whether to import or
860 + inline pgen module in the generated code
861 +
862 + If inline=True, the generated code is much bigger but does not
863 + depend on any non-standard module.
864 + """
865 + pysrc = ("%(pgen)s"
866 + "tokenizer = %(prefix)s%(tokenizer)r\n"
867 + "grammar = %(grammar)s\n"
868 + "parser = %(prefix)sParser(grammar, tokenizer)\n\n"
869 + "if __name__ == '__main__' :\n"
870 + " # just for test purpose\n"
871 + " import sys, pprint\n"
872 + " st = parser.parseStream(sys.stdin)\n"
873 + " print(parser.pprint(st))\n")
874 + format = {"grammar" : pprint.pformat(self.grammar()),
875 + "prefix" : pgen + ".",
876 + "tokenizer" : self.tokenizer,
877 + "pgen" : "import tokenize, %s\n\n" % pgen,
878 + "inline" : "",
879 + }
880 + if inline :
881 + format["prefix"] = ""
882 + source = inspect.getsource(inspect.getmodule(self))
883 + source = source.rsplit("if __name__ == '__main__' :", 1)[0]
884 + format["pgen"] = ("### module '%s.py' inlined\n"
885 + "%s\n### end of '%s.py'\n\n"
886 + % (pgen, source.rstrip(), pgen))
887 + return pysrc % format
888 + @classmethod
889 + def translate (cls, src, tgt=None, pgen="pgen", inline=False) :
890 + """Translate a pgen file to a Python file that implements the
891 + corresponding parser.
892 +
893 + Expected arguments are:
894 + - src: path of the pgen file
895 + - tgt: path of target Python file, if None, its name is
896 + derived from src (replacing its extension by .py)
897 + - pgen, inline: like in PyPgen.python()
898 +
899 + Warning: the output file is silently overwritten if it already
900 + exist.
901 + """
902 + if tgt is None :
903 + tgt = os.path.splitext(src)[0] + ".py"
904 + gst, tokenizer = PgenParser.parse(src)
905 + self = PyPgen(gst, tokenizer)
906 + outfile = open(tgt, "w")
907 + outfile.write(("# this file has been automatically generated running:\n"
908 + "# %s\n\n") % " ".join(sys.argv))
909 + outfile.write(self.python(pgen, inline))
910 + outfile.close()
911 + # the rest of the class has not changed too much
912 + def addLabel (self, labelList, tokKind, tokName) :
913 + labelTup = (tokKind, tokName)
914 + if labelTup in labelList :
915 + return labelList.index(labelTup)
916 + labelIndex = len(labelList)
917 + labelList.append(labelTup)
918 + return labelIndex
919 + def handleStart (self, gst) :
920 + self.nfaGrammar = [[],[(self.tokenizer.ENDMARKER, "EMPTY")]]
921 + self.crntKind = self.tokenizer.NT_OFFSET
922 + kind, children = gst
923 + for child in children :
924 + if int(child[0]) == PgenParser.RULE :
925 + self.handleRule(child)
926 + return self.nfaGrammar
927 + def handleRule (self, gst) :
928 + # NFA := [ type : Int, name : String, [ STATE ], start : Int,
929 + # finish : Int ]
930 + # STATE := [ ARC ]
931 + # ARC := ( labelIndex : Int, stateIndex : Int )
932 + ####
933 + # build the NFA shell
934 + self.nfa = [self.crntKind, None, [], -1, -1]
935 + self.crntKind += 1
936 + # work on the AST node
937 + kind, children = gst
938 + name, colon, rhs, newline = children
939 + self.nfa[1] = name[0]
940 + if (self.tokenizer.NAME, name[0]) not in self.nfaGrammar[1] :
941 + self.nfaGrammar[1].append((self.tokenizer.NAME, name[0]))
942 + start, finish = self.handleRhs(rhs)
943 + self.nfa[3] = start
944 + self.nfa[4] = finish
945 + # append the NFA to the grammar
946 + self.nfaGrammar[0].append(self.nfa)
947 + def handleRhs (self, gst) :
948 + kind, children = gst
949 + start, finish = self.handleAlt(children[0])
950 + if len(children) > 1 :
951 + cStart = start
952 + cFinish = finish
953 + start = len(self.nfa[2])
954 + self.nfa[2].append([(self.EMPTY, cStart)])
955 + finish = len(self.nfa[2])
956 + self.nfa[2].append([])
957 + self.nfa[2][cFinish].append((self.EMPTY, finish))
958 + for child in children[2:] :
959 + if int(child[0]) == PgenParser.ALT :
960 + cStart, cFinish = self.handleAlt(child)
961 + self.nfa[2][start].append((self.EMPTY, cStart))
962 + self.nfa[2][cFinish].append((self.EMPTY, finish))
963 + return start, finish
964 + def handleAlt (self, gst) :
965 + kind, children = gst
966 + start, finish = self.handleItem(children[0])
967 + if len(children) > 1 :
968 + for child in children[1:] :
969 + cStart, cFinish = self.handleItem(child)
970 + self.nfa[2][finish].append((self.EMPTY, cStart))
971 + finish = cFinish
972 + return start, finish
973 + def handleItem (self, gst) :
974 + nodeKind, children = gst
975 + if int(children[0][0]) == PgenParser.ATOM :
976 + start, finish = self.handleAtom(children[0])
977 + if len(children) > 1 :
978 + # Short out the child NFA
979 + self.nfa[2][finish].append((self.EMPTY, start))
980 + if children[1][0].kind == self.tokenizer.STAR :
981 + finish = start
982 + else :
983 + start = len(self.nfa[2])
984 + finish = start + 1
985 + self.nfa[2].append([(self.EMPTY, finish)])
986 + self.nfa[2].append([])
987 + cStart, cFinish = self.handleRhs(children[1])
988 + self.nfa[2][start].append((self.EMPTY, cStart))
989 + self.nfa[2][cFinish].append((self.EMPTY, finish))
990 + return start, finish
991 + def handleAtom (self, gst) :
992 + nodeKind, children = gst
993 + tok = children[0][0]
994 + if tok.kind == self.tokenizer.LPAR :
995 + start, finish = self.handleRhs(children[1])
996 + elif tok.kind in (self.tokenizer.STRING, self.tokenizer.NAME) :
997 + start = len(self.nfa[2])
998 + finish = start + 1
999 + labelIndex = self.addLabel(self.nfaGrammar[1], tok.kind, tok)
1000 + self.nfa[2].append([(labelIndex, finish)])
1001 + self.nfa[2].append([])
1002 + return start, finish
1003 + def generateDfaGrammar (self, nfaGrammar, start=None) :
1004 + # See notes in pgen.lang.python.DFAParser for output schema.
1005 + dfas = []
1006 + for nfa in nfaGrammar[0] :
1007 + dfas.append(self.nfaToDfa(nfa))
1008 + kind = dfas[0][0]
1009 + if start is not None :
1010 + found = False
1011 + for dfa in dfas :
1012 + if dfa[1] == start :
1013 + kind = dfa[0]
1014 + found = True
1015 + break
1016 + if not found :
1017 + warn("couldn't find nonterminal %r, "
1018 + "using %r instead." % (start, dfas[0][1]))
1019 + return [dfas, nfaGrammar[1][:], kind, 0]
1020 + def addClosure (self, stateList, nfa, istate) :
1021 + stateList[istate] = True
1022 + arcs = nfa[2][istate]
1023 + for label, arrow in arcs :
1024 + if label == self.EMPTY :
1025 + self.addClosure(stateList, nfa, arrow)
1026 + def nfaToDfa (self, nfa) :
1027 + tempStates = []
1028 + crntTempState = [[False] * len(nfa[2]), [], False]
1029 + self.addClosure(crntTempState[0], nfa, nfa[3])
1030 + crntTempState[2] = crntTempState[0][nfa[4]]
1031 + if crntTempState[2] :
1032 + warn("nonterminal %r may produce empty." % nfa[1])
1033 + tempStates.append(crntTempState)
1034 + index = 0
1035 + while index < len(tempStates) :
1036 + crntTempState = tempStates[index]
1037 + for componentState in range(len(nfa[2])) :
1038 + if not crntTempState[0][componentState] :
1039 + continue
1040 + nfaArcs = nfa[2][componentState]
1041 + for label, nfaArrow in nfaArcs :
1042 + if label == self.EMPTY :
1043 + continue
1044 + foundTempArc = False
1045 + for tempArc in crntTempState[1] :
1046 + if tempArc[0] == label :
1047 + foundTempArc = True
1048 + break
1049 + if not foundTempArc :
1050 + tempArc = [label, -1, [False] * len(nfa[2])]
1051 + crntTempState[1].append(tempArc)
1052 + self.addClosure(tempArc[2], nfa, nfaArrow)
1053 + for arcIndex in range(len(crntTempState[1])) :
1054 + label, arrow, targetStateList = crntTempState[1][arcIndex]
1055 + targetFound = False
1056 + arrow = 0
1057 + for destTempState in tempStates :
1058 + if targetStateList == destTempState[0] :
1059 + targetFound = True
1060 + break
1061 + arrow += 1
1062 + if not targetFound :
1063 + assert arrow == len(tempStates)
1064 + tempState = [targetStateList[:], [],
1065 + targetStateList[nfa[4]]]
1066 + tempStates.append(tempState)
1067 + # Write arrow value back to the arc
1068 + crntTempState[1][arcIndex][1] = arrow
1069 + index += 1
1070 + tempStates = self.simplifyTempDfa(nfa, tempStates)
1071 + return self.tempDfaToDfa(nfa, tempStates)
1072 + def sameState (self, s1, s2) :
1073 + if len(s1[1]) != len(s2[1]) or s1[2] != s2[2] :
1074 + return False
1075 + for arcIndex in range(len(s1[1])) :
1076 + arc1 = s1[1][arcIndex]
1077 + arc2 = s2[1][arcIndex]
1078 + if arc1[:-1] != arc2[:-1] :
1079 + return False
1080 + return True
1081 + def simplifyTempDfa (self, nfa, tempStates) :
1082 + changes = True
1083 + deletedStates = []
1084 + while changes :
1085 + changes = False
1086 + for i in range(1, len(tempStates)) :
1087 + if i in deletedStates :
1088 + continue
1089 + for j in range(i) :
1090 + if j in deletedStates :
1091 + continue
1092 + if self.sameState(tempStates[i], tempStates[j]) :
1093 + deletedStates.append(i)
1094 + for k in range(len(tempStates)) :
1095 + if k in deletedStates :
1096 + continue
1097 + for arc in tempStates[k][1] :
1098 + if arc[1] == i :
1099 + arc[1] = j
1100 + changes = True
1101 + break
1102 + for stateIndex in deletedStates :
1103 + tempStates[stateIndex] = None
1104 + return tempStates
1105 + def tempDfaToDfa (self, nfa, tempStates) :
1106 + dfaStates = []
1107 + dfa = [nfa[0], nfa[1], 0, dfaStates, None]
1108 + stateMap = {}
1109 + tempIndex = 0
1110 + for tempState in tempStates :
1111 + if tempState is not None :
1112 + stateMap[tempIndex] = len(dfaStates)
1113 + dfaStates.append(([], (0,0,()), 0))
1114 + tempIndex += 1
1115 + for tempIndex in stateMap.keys() :
1116 + stateList, tempArcs, accepting = tempStates[tempIndex]
1117 + dfaStateIndex = stateMap[tempIndex]
1118 + dfaState = dfaStates[dfaStateIndex]
1119 + for tempArc in tempArcs :
1120 + dfaState[0].append((tempArc[0], stateMap[tempArc[1]]))
1121 + if accepting :
1122 + dfaState[0].append((self.EMPTY, dfaStateIndex))
1123 + return dfa
1124 + def translateLabels (self, grammar) :
1125 + tokenNames = list(self.tokenizer.tok_name.values())
1126 + # Recipe 252143 (remixed for laziness)
1127 + tokenValues = dict(([v, k] for k, v in
1128 + self.tokenizer.tok_name.items()))
1129 + labelList = grammar[1]
1130 + for labelIndex, (kind, name) in enumerate(labelList) :
1131 + if kind == self.tokenizer.NAME :
1132 + isNonTerminal = False
1133 + for dfa in grammar[0] :
1134 + if dfa[1] == name :
1135 + labelList[labelIndex] = (dfa[0], None)
1136 + isNonTerminal = True
1137 + break
1138 + if not isNonTerminal :
1139 + if name in tokenNames :
1140 + labelList[labelIndex] = (tokenValues[name], None)
1141 + else :
1142 + warn("can't translate NAME label '%s'" % name)
1143 + elif kind == self.tokenizer.STRING :
1144 + assert name[0] == name[-1]
1145 + sname = name[1:-1]
1146 + if (sname[0] in string.letters) or (sname[0] == "_") :
1147 + labelList[labelIndex] = (self.tokenizer.NAME, sname)
1148 + elif sname in self.operatorMap :
1149 + labelList[labelIndex] = (self.operatorMap[sname],
1150 + None)
1151 + else :
1152 + warn("can't translate STRING label %s" % name)
1153 + return grammar
1154 + def calcFirstSet (self, grammar, dfa) :
1155 + if dfa[4] == -1 :
1156 + warn("left-recursion for %r" % dfa[1])
1157 + return
1158 + if dfa[4] != None :
1159 + warn("re-calculating FIRST set for %r" % dfa[1])
1160 + dfa[4] = -1
1161 + symbols = []
1162 + result = 0
1163 + state = dfa[3][dfa[2]]
1164 + for arc in state[0] :
1165 + sym = arc[0]
1166 + if sym not in symbols :
1167 + symbols.append(sym)
1168 + kind = grammar[1][sym][0]
1169 + if kind >= self.tokenizer.NT_OFFSET :
1170 + # Nonterminal
1171 + ddfa = grammar[0][kind - self.tokenizer.NT_OFFSET]
1172 + if ddfa[4] == -1 :
1173 + warn("left recursion below %r" % dfa[1])
1174 + else :
1175 + if ddfa[4] == None :
1176 + self.calcFirstSet(grammar, ddfa)
1177 + result |= ddfa[4]
1178 + else :
1179 + result |= 1 << sym
1180 + dfa[4] = result
1181 + def generateFirstSets (self, grammar) :
1182 + dfas = grammar[0]
1183 + index = 0
1184 + while index < len(dfas) :
1185 + dfa = dfas[index]
1186 + if None == dfa[4] :
1187 + self.calcFirstSet(grammar, dfa)
1188 + index += 1
1189 + for dfa in dfas :
1190 + set = dfa[4]
1191 + result = []
1192 + while set > 0 :
1193 + crntBits = set & 0xff
1194 + result.append(chr(crntBits))
1195 + set >>= 8
1196 + properSize = (len(grammar[1]) / 8) + 1
1197 + if len(result) < properSize :
1198 + result.append('\x00' * (properSize - len(result)))
1199 + dfa[4] = "".join(result)
1200 + return grammar
1201 +
1202 +if __name__ == '__main__' :
1203 + # a simple CLI
1204 + import sys, getopt
1205 + tgt, pgen, inline = None, "snakes.lang.pgen", False
1206 + try :
1207 + opts, args = getopt.getopt(sys.argv[1:], "h",
1208 + ["help", "inline", "output=",
1209 + "pgen=", "start="])
1210 + if ("-h", "") in opts or ("--help", "") in opts :
1211 + opts = [("-h", "")]
1212 + args = [None]
1213 + elif not args :
1214 + raise getopt.GetoptError("no input file provided"
1215 + " (try -h to get help)")
1216 + elif len(args) > 1 :
1217 + raise getopt.GetoptError("more than one input file provided")
1218 + except getopt.GetoptError :
1219 + sys.stderr.write("%s: %s\n" % (__file__, sys.exc_info()[1]))
1220 + sys.exit(1)
1221 + for (flag, arg) in opts :
1222 + if flag in ("-h", "--help") :
1223 + print("""usage: %s [OPTIONS] INFILE
1224 + Options:
1225 + -h, --help print this help and exit
1226 + --inline inline 'pgen.py' in the generated file
1227 + --output=OUTPUT set output file
1228 + --pgen=PGEN name of 'pgen' module in output file""" % __file__)
1229 + sys.exit(0)
1230 + elif flag == "--inline" :
1231 + inline = True
1232 + elif flag == "--output" :
1233 + tgt = arg
1234 + elif flag == "--pgen" :
1235 + pgen = arg
1236 + PyPgen.translate(args[0], tgt=tgt, pgen=pgen, inline=inline)
1 +"""An implementation of the Zephyr Abstract Syntax Definition Language.
2 +
3 +See http://asdl.sourceforge.net/ and
4 +http://www.cs.princeton.edu/~danwang/Papers/dsl97/dsl97-abstract.html.
5 +
6 +Only supports top level module decl, not view. I'm guessing that view
7 +is intended to support the browser and I'm not interested in the
8 +browser.
9 +
10 +Changes for Python: Add support for module versions
11 +"""
12 +
13 +#__metaclass__ = type
14 +
15 +import os, sys
16 +import traceback
17 +
18 +from . import spark
19 +
20 +class Token:
21 + # spark seems to dispatch in the parser based on a token's
22 + # type attribute
23 + def __init__(self, type, lineno):
24 + self.type = type
25 + self.lineno = lineno
26 +
27 + def __str__(self):
28 + return self.type
29 +
30 + def __repr__(self):
31 + return str(self)
32 +
33 +class Id(Token):
34 + def __init__(self, value, lineno):
35 + self.type = 'Id'
36 + self.value = value
37 + self.lineno = lineno
38 +
39 + def __str__(self):
40 + return self.value
41 +
42 +class String(Token):
43 + def __init__(self, value, lineno):
44 + self.type = 'String'
45 + self.value = value
46 + self.lineno = lineno
47 +
48 +class ASDLSyntaxError:
49 +
50 + def __init__(self, lineno, token=None, msg=None):
51 + self.lineno = lineno
52 + self.token = token
53 + self.msg = msg
54 +
55 + def __str__(self):
56 + if self.msg is None:
57 + return "Error at '%s', line %d" % (self.token, self.lineno)
58 + else:
59 + return "%s, line %d" % (self.msg, self.lineno)
60 +
61 +class ASDLScanner(spark.GenericScanner, object):
62 +
63 + def tokenize(self, input):
64 + self.rv = []
65 + self.lineno = 1
66 + super(ASDLScanner, self).tokenize(input)
67 + return self.rv
68 +
69 + def t_id(self, s):
70 + r"[\w\.]+"
71 + # XXX doesn't distinguish upper vs. lower, which is
72 + # significant for ASDL.
73 + self.rv.append(Id(s, self.lineno))
74 +
75 + def t_string(self, s):
76 + r'"[^"]*"'
77 + self.rv.append(String(s, self.lineno))
78 +
79 + def t_xxx(self, s): # not sure what this production means
80 + r"<="
81 + self.rv.append(Token(s, self.lineno))
82 +
83 + def t_punctuation(self, s):
84 + r"[\{\}\*\=\|\(\)\,\?\:]"
85 + self.rv.append(Token(s, self.lineno))
86 +
87 + def t_comment(self, s):
88 + r"\-\-[^\n]*"
89 + pass
90 +
91 + def t_newline(self, s):
92 + r"\n"
93 + self.lineno += 1
94 +
95 + def t_whitespace(self, s):
96 + r"[ \t]+"
97 + pass
98 +
99 + def t_default(self, s):
100 + r" . +"
101 + raise ValueError("unmatched input: %r" % s)
102 +
103 +class ASDLParser(spark.GenericParser, object):
104 + def __init__(self):
105 + super(ASDLParser, self).__init__("module")
106 +
107 + def typestring(self, tok):
108 + return tok.type
109 +
110 + def error(self, tok):
111 + raise ASDLSyntaxError(tok.lineno, tok)
112 +
113 + def p_module_0(self, arg):
114 + " module ::= Id Id version { } "
115 + (module, name, version, _0, _1) = arg
116 + if module.value != "module":
117 + raise ASDLSyntaxError(module.lineno,
118 + msg="expected 'module', found %s" % module)
119 + return Module(name, None, version)
120 +
121 + def p_module(self, arg):
122 + " module ::= Id Id version { definitions } "
123 + (module, name, version, _0, definitions, _1) = arg
124 + if module.value != "module":
125 + raise ASDLSyntaxError(module.lineno,
126 + msg="expected 'module', found %s" % module)
127 + return Module(name, definitions, version)
128 +
129 + def p_version(self, arg):
130 + "version ::= Id String"
131 + (version, V) = arg
132 + if version.value != "version":
133 + raise ASDLSyntaxError(version.lineno,
134 + msg="expected 'version', found %" % version)
135 + return V
136 +
137 + def p_definition_0(self, arg):
138 + " definitions ::= definition "
139 + (definition,) = arg
140 + return definition
141 +
142 + def p_definition_1(self, arg):
143 + " definitions ::= definition definitions "
144 + (definitions, definition) = arg
145 + return definitions + definition
146 +
147 + def p_definition(self, arg):
148 + " definition ::= Id = type "
149 + (id, _, type) = arg
150 + return [Type(id, type)]
151 +
152 + def p_type_0(self, arg):
153 + " type ::= product "
154 + (product,) = arg
155 + return product
156 +
157 + def p_type_1(self, arg):
158 + " type ::= sum "
159 + (sum,) = arg
160 + return Sum(sum)
161 +
162 + def p_type_2(self, arg):
163 + " type ::= sum Id ( fields ) "
164 + (sum, id, _0, attributes, _1) = arg
165 + if id.value != "attributes":
166 + raise ASDLSyntaxError(id.lineno,
167 + msg="expected attributes, found %s" % id)
168 + if attributes:
169 + attributes.reverse()
170 + return Sum(sum, attributes)
171 +
172 + def p_product(self, arg):
173 + " product ::= ( fields ) "
174 + (_0, fields, _1) = arg
175 + fields.reverse()
176 + return Product(fields)
177 +
178 + def p_sum_0(self, arg):
179 + " sum ::= constructor """
180 + (constructor,) = arg
181 + return [constructor]
182 +
183 + def p_sum_1(self, arg):
184 + " sum ::= constructor | sum "
185 + (constructor, _, sum) = arg
186 + return [constructor] + sum
187 +
188 + def p_sum_2(self, arg):
189 + " sum ::= constructor | sum "
190 + (constructor, _, sum) = arg
191 + return [constructor] + sum
192 +
193 + def p_constructor_0(self, arg):
194 + " constructor ::= Id "
195 + (id,) = arg
196 + return Constructor(id)
197 +
198 + def p_constructor_1(self, arg):
199 + " constructor ::= Id ( fields ) "
200 + (id, _0, fields, _1) = arg
201 + fields.reverse()
202 + return Constructor(id, fields)
203 +
204 + def p_fields_0(self, arg):
205 + " fields ::= field "
206 + (field,) = arg
207 + return [field]
208 +
209 + def p_fields_1(self, arg):
210 + " fields ::= field , fields "
211 + (field, _, fields) = arg
212 + return fields + [field]
213 +
214 + def p_field_0(self, arg):
215 + " field ::= Id "
216 + (type,) = arg
217 + return Field(type)
218 +
219 + def p_field_1(self, arg):
220 + " field ::= Id Id "
221 + (type, name) = arg
222 + return Field(type, name)
223 +
224 + def p_field_2(self, arg):
225 + " field ::= Id * Id "
226 + (type, _, name) = arg
227 + return Field(type, name, seq=1)
228 +
229 + def p_field_3(self, arg):
230 + " field ::= Id ? Id "
231 + (type, _, name) = arg
232 + return Field(type, name, opt=1)
233 +
234 + def p_field_4(self, arg):
235 + " field ::= Id * "
236 + (type, _) = arg
237 + return Field(type, seq=1)
238 +
239 + def p_field_5(self, arg):
240 + " field ::= Id ? "
241 + (type, _) = arg
242 + return Field(type, opt=1)
243 +
244 +builtin_types = ("identifier", "string", "int", "bool", "object")
245 +
246 +# below is a collection of classes to capture the AST of an AST :-)
247 +# not sure if any of the methods are useful yet, but I'm adding them
248 +# piecemeal as they seem helpful
249 +
250 +class AST:
251 + pass # a marker class
252 +
253 +class Module(AST):
254 + def __init__(self, name, dfns, version):
255 + self.name = name
256 + self.dfns = dfns
257 + self.version = version
258 + self.types = {} # maps type name to value (from dfns)
259 + for type in dfns:
260 + self.types[type.name.value] = type.value
261 +
262 + def __repr__(self):
263 + return "Module(%s, %s)" % (self.name, self.dfns)
264 +
265 +class Type(AST):
266 + def __init__(self, name, value):
267 + self.name = name
268 + self.value = value
269 +
270 + def __repr__(self):
271 + return "Type(%s, %s)" % (self.name, self.value)
272 +
273 +class Constructor(AST):
274 + def __init__(self, name, fields=None):
275 + self.name = name
276 + self.fields = fields or []
277 +
278 + def __repr__(self):
279 + return "Constructor(%s, %s)" % (self.name, self.fields)
280 +
281 +class Field(AST):
282 + def __init__(self, type, name=None, seq=0, opt=0):
283 + self.type = type
284 + self.name = name
285 + self.seq = seq
286 + self.opt = opt
287 +
288 + def __repr__(self):
289 + if self.seq:
290 + extra = ", seq=1"
291 + elif self.opt:
292 + extra = ", opt=1"
293 + else:
294 + extra = ""
295 + if self.name is None:
296 + return "Field(%s%s)" % (self.type, extra)
297 + else:
298 + return "Field(%s, %s%s)" % (self.type, self.name, extra)
299 +
300 +class Sum(AST):
301 + def __init__(self, types, attributes=None):
302 + self.types = types
303 + self.attributes = attributes or []
304 +
305 + def __repr__(self):
306 + if self.attributes is None:
307 + return "Sum(%s)" % self.types
308 + else:
309 + return "Sum(%s, %s)" % (self.types, self.attributes)
310 +
311 +class Product(AST):
312 + def __init__(self, fields):
313 + self.fields = fields
314 +
315 + def __repr__(self):
316 + return "Product(%s)" % self.fields
317 +
318 +class VisitorBase(object):
319 +
320 + def __init__(self, skip=0):
321 + self.cache = {}
322 + self.skip = skip
323 +
324 + def visit(self, object, *args):
325 + meth = self._dispatch(object)
326 + if meth is None:
327 + return
328 + try:
329 + meth(object, *args)
330 + except Exception:
331 + err = sys.exc_info()[1]
332 + print("Error visiting %r" % object)
333 + print(err)
334 + traceback.print_exc()
335 + # XXX hack
336 + if hasattr(self, 'file'):
337 + self.file.flush()
338 + os._exit(1)
339 +
340 + def _dispatch(self, object):
341 + assert isinstance(object, AST), repr(object)
342 + klass = object.__class__
343 + meth = self.cache.get(klass)
344 + if meth is None:
345 + methname = "visit" + klass.__name__
346 + if self.skip:
347 + meth = getattr(self, methname, None)
348 + else:
349 + meth = getattr(self, methname)
350 + self.cache[klass] = meth
351 + return meth
352 +
353 +class Check(VisitorBase):
354 +
355 + def __init__(self):
356 + super(Check, self).__init__(skip=1)
357 + self.cons = {}
358 + self.errors = 0
359 + self.types = {}
360 +
361 + def visitModule(self, mod):
362 + for dfn in mod.dfns:
363 + self.visit(dfn)
364 +
365 + def visitType(self, type):
366 + self.visit(type.value, str(type.name))
367 +
368 + def visitSum(self, sum, name):
369 + for t in sum.types:
370 + self.visit(t, name)
371 +
372 + def visitConstructor(self, cons, name):
373 + key = str(cons.name)
374 + conflict = self.cons.get(key)
375 + if conflict is None:
376 + self.cons[key] = name
377 + else:
378 + print("Redefinition of constructor %s" % key)
379 + print("Defined in %s and %s" % (conflict, name))
380 + self.errors += 1
381 + for f in cons.fields:
382 + self.visit(f, key)
383 +
384 + def visitField(self, field, name):
385 + key = str(field.type)
386 + l = self.types.setdefault(key, [])
387 + l.append(name)
388 +
389 + def visitProduct(self, prod, name):
390 + for f in prod.fields:
391 + self.visit(f, name)
392 +
393 +def check(mod):
394 + v = Check()
395 + v.visit(mod)
396 +
397 + for t in v.types:
398 + if t not in mod.types and not t in builtin_types:
399 + v.errors += 1
400 + uses = ", ".join(v.types[t])
401 + print("Undefined type %s, used in %s" % (t, uses))
402 +
403 + return not v.errors
404 +
405 +def parse(file):
406 + scanner = ASDLScanner()
407 + parser = ASDLParser()
408 +
409 + buf = open(file).read()
410 + tokens = scanner.tokenize(buf)
411 + try:
412 + return parser.parse(tokens)
413 + except ASDLSyntaxError:
414 + err = sys.exc_info()[1]
415 + print(err)
416 + lines = buf.split("\n")
417 + print(lines[err.lineno - 1]) # lines starts at 0, files at 1
418 +
419 +if __name__ == "__main__":
420 + import glob
421 + import sys
422 +
423 + if len(sys.argv) > 1:
424 + files = sys.argv[1:]
425 + else:
426 + testdir = "tests"
427 + files = glob.glob(testdir + "/*.asdl")
428 +
429 + for file in files:
430 + print(file)
431 + mod = parse(file)
432 + print("module %s" % mod.name)
433 + print("%s definitions" % len(mod.dfns))
434 + if not check(mod):
435 + print("Check failed")
436 + else:
437 + for dfn in mod.dfns:
438 + print(dfn.type)
1 +# Copyright (c) 1998-2002 John Aycock
2 +#
3 +# Permission is hereby granted, free of charge, to any person obtaining
4 +# a copy of this software and associated documentation files (the
5 +# "Software"), to deal in the Software without restriction, including
6 +# without limitation the rights to use, copy, modify, merge, publish,
7 +# distribute, sublicense, and/or sell copies of the Software, and to
8 +# permit persons to whom the Software is furnished to do so, subject to
9 +# the following conditions:
10 +#
11 +# The above copyright notice and this permission notice shall be
12 +# included in all copies or substantial portions of the Software.
13 +#
14 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 +
22 +__version__ = 'SPARK-0.7 (pre-alpha-5)'
23 +
24 +import re
25 +import string
26 +
27 +def _namelist(instance):
28 + namelist, namedict, classlist = [], {}, [instance.__class__]
29 + for c in classlist:
30 + for b in c.__bases__:
31 + classlist.append(b)
32 + for name in c.__dict__:
33 + if name not in namedict:
34 + namelist.append(name)
35 + namedict[name] = 1
36 + return namelist
37 +
38 +class GenericScanner:
39 + def __init__(self, flags=0):
40 + pattern = self.reflect()
41 + self.re = re.compile(pattern, re.VERBOSE|flags)
42 +
43 + self.index2func = {}
44 + for name, number in self.re.groupindex.items():
45 + self.index2func[number-1] = getattr(self, 't_' + name)
46 +
47 + def makeRE(self, name):
48 + doc = getattr(self, name).__doc__
49 + rv = '(?P<%s>%s)' % (name[2:], doc)
50 + return rv
51 +
52 + def reflect(self):
53 + rv = []
54 + for name in _namelist(self):
55 + if name[:2] == 't_' and name != 't_default':
56 + rv.append(self.makeRE(name))
57 +
58 + rv.append(self.makeRE('t_default'))
59 + return string.join(rv, '|')
60 +
61 + def error(self, s, pos):
62 + print("Lexical error at position %s" % pos)
63 + raise SystemExit
64 +
65 + def tokenize(self, s):
66 + pos = 0
67 + n = len(s)
68 + while pos < n:
69 + m = self.re.match(s, pos)
70 + if m is None:
71 + self.error(s, pos)
72 +
73 + groups = m.groups()
74 + for i in range(len(groups)):
75 + if groups[i] and i in self.index2func:
76 + self.index2func[i](groups[i])
77 + pos = m.end()
78 +
79 + def t_default(self, s):
80 + r'( . | \n )+'
81 + print("Specification error: unmatched input")
82 + raise SystemExit
83 +
84 +#
85 +# Extracted from GenericParser and made global so that [un]picking works.
86 +#
87 +class _State:
88 + def __init__(self, stateno, items):
89 + self.T, self.complete, self.items = [], [], items
90 + self.stateno = stateno
91 +
92 +class GenericParser:
93 + #
94 + # An Earley parser, as per J. Earley, "An Efficient Context-Free
95 + # Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley,
96 + # "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis,
97 + # Carnegie-Mellon University, August 1968. New formulation of
98 + # the parser according to J. Aycock, "Practical Earley Parsing
99 + # and the SPARK Toolkit", Ph.D. thesis, University of Victoria,
100 + # 2001, and J. Aycock and R. N. Horspool, "Practical Earley
101 + # Parsing", unpublished paper, 2001.
102 + #
103 +
104 + def __init__(self, start):
105 + self.rules = {}
106 + self.rule2func = {}
107 + self.rule2name = {}
108 + self.collectRules()
109 + self.augment(start)
110 + self.ruleschanged = 1
111 +
112 + _NULLABLE = '\e_'
113 + _START = 'START'
114 + _BOF = '|-'
115 +
116 + #
117 + # When pickling, take the time to generate the full state machine;
118 + # some information is then extraneous, too. Unfortunately we
119 + # can't save the rule2func map.
120 + #
121 + def __getstate__(self):
122 + if self.ruleschanged:
123 + #
124 + # XXX - duplicated from parse()
125 + #
126 + self.computeNull()
127 + self.newrules = {}
128 + self.new2old = {}
129 + self.makeNewRules()
130 + self.ruleschanged = 0
131 + self.edges, self.cores = {}, {}
132 + self.states = { 0: self.makeState0() }
133 + self.makeState(0, self._BOF)
134 + #
135 + # XXX - should find a better way to do this..
136 + #
137 + changes = 1
138 + while changes:
139 + changes = 0
140 + for k, v in self.edges.items():
141 + if v is None:
142 + state, sym = k
143 + if state in self.states:
144 + self.goto(state, sym)
145 + changes = 1
146 + rv = self.__dict__.copy()
147 + for s in self.states.values():
148 + del s.items
149 + del rv['rule2func']
150 + del rv['nullable']
151 + del rv['cores']
152 + return rv
153 +
154 + def __setstate__(self, D):
155 + self.rules = {}
156 + self.rule2func = {}
157 + self.rule2name = {}
158 + self.collectRules()
159 + start = D['rules'][self._START][0][1][1] # Blech.
160 + self.augment(start)
161 + D['rule2func'] = self.rule2func
162 + D['makeSet'] = self.makeSet_fast
163 + self.__dict__ = D
164 +
165 + #
166 + # A hook for GenericASTBuilder and GenericASTMatcher. Mess
167 + # thee not with this; nor shall thee toucheth the _preprocess
168 + # argument to addRule.
169 + #
170 + def preprocess(self, rule, func): return rule, func
171 +
172 + def addRule(self, doc, func, _preprocess=1):
173 + fn = func
174 + rules = string.split(doc)
175 +
176 + index = []
177 + for i in range(len(rules)):
178 + if rules[i] == '::=':
179 + index.append(i-1)
180 + index.append(len(rules))
181 +
182 + for i in range(len(index)-1):
183 + lhs = rules[index[i]]
184 + rhs = rules[index[i]+2:index[i+1]]
185 + rule = (lhs, tuple(rhs))
186 +
187 + if _preprocess:
188 + rule, fn = self.preprocess(rule, func)
189 +
190 + if lhs in self.rules:
191 + self.rules[lhs].append(rule)
192 + else:
193 + self.rules[lhs] = [ rule ]
194 + self.rule2func[rule] = fn
195 + self.rule2name[rule] = func.__name__[2:]
196 + self.ruleschanged = 1
197 +
198 + def collectRules(self):
199 + for name in _namelist(self):
200 + if name[:2] == 'p_':
201 + func = getattr(self, name)
202 + doc = func.__doc__
203 + self.addRule(doc, func)
204 +
205 + def augment(self, start):
206 + rule = '%s ::= %s %s' % (self._START, self._BOF, start)
207 + self.addRule(rule, lambda args: args[1], 0)
208 +
209 + def computeNull(self):
210 + self.nullable = {}
211 + tbd = []
212 +
213 + for rulelist in self.rules.values():
214 + lhs = rulelist[0][0]
215 + self.nullable[lhs] = 0
216 + for rule in rulelist:
217 + rhs = rule[1]
218 + if len(rhs) == 0:
219 + self.nullable[lhs] = 1
220 + continue
221 + #
222 + # We only need to consider rules which
223 + # consist entirely of nonterminal symbols.
224 + # This should be a savings on typical
225 + # grammars.
226 + #
227 + for sym in rhs:
228 + if sym not in self.rules:
229 + break
230 + else:
231 + tbd.append(rule)
232 + changes = 1
233 + while changes:
234 + changes = 0
235 + for lhs, rhs in tbd:
236 + if self.nullable[lhs]:
237 + continue
238 + for sym in rhs:
239 + if not self.nullable[sym]:
240 + break
241 + else:
242 + self.nullable[lhs] = 1
243 + changes = 1
244 +
245 + def makeState0(self):
246 + s0 = _State(0, [])
247 + for rule in self.newrules[self._START]:
248 + s0.items.append((rule, 0))
249 + return s0
250 +
251 + def finalState(self, tokens):
252 + #
253 + # Yuck.
254 + #
255 + if len(self.newrules[self._START]) == 2 and len(tokens) == 0:
256 + return 1
257 + start = self.rules[self._START][0][1][1]
258 + return self.goto(1, start)
259 +
260 + def makeNewRules(self):
261 + worklist = []
262 + for rulelist in self.rules.values():
263 + for rule in rulelist:
264 + worklist.append((rule, 0, 1, rule))
265 +
266 + for rule, i, candidate, oldrule in worklist:
267 + lhs, rhs = rule
268 + n = len(rhs)
269 + while i < n:
270 + sym = rhs[i]
271 + if sym not in self.rules or \
272 + not self.nullable[sym]:
273 + candidate = 0
274 + i = i + 1
275 + continue
276 +
277 + newrhs = list(rhs)
278 + newrhs[i] = self._NULLABLE+sym
279 + newrule = (lhs, tuple(newrhs))
280 + worklist.append((newrule, i+1,
281 + candidate, oldrule))
282 + candidate = 0
283 + i = i + 1
284 + else:
285 + if candidate:
286 + lhs = self._NULLABLE+lhs
287 + rule = (lhs, rhs)
288 + if lhs in self.newrules:
289 + self.newrules[lhs].append(rule)
290 + else:
291 + self.newrules[lhs] = [ rule ]
292 + self.new2old[rule] = oldrule
293 +
294 + def typestring(self, token):
295 + return None
296 +
297 + def error(self, token):
298 + print("Syntax error at or near `%s' token" % token)
299 + raise SystemExit
300 +
301 + def parse(self, tokens):
302 + sets = [ [(1,0), (2,0)] ]
303 + self.links = {}
304 +
305 + if self.ruleschanged:
306 + self.computeNull()
307 + self.newrules = {}
308 + self.new2old = {}
309 + self.makeNewRules()
310 + self.ruleschanged = 0
311 + self.edges, self.cores = {}, {}
312 + self.states = { 0: self.makeState0() }
313 + self.makeState(0, self._BOF)
314 +
315 + for i in range(len(tokens)):
316 + sets.append([])
317 +
318 + if sets[i] == []:
319 + break
320 + self.makeSet(tokens[i], sets, i)
321 + else:
322 + sets.append([])
323 + self.makeSet(None, sets, len(tokens))
324 +
325 + #_dump(tokens, sets, self.states)
326 +
327 + finalitem = (self.finalState(tokens), 0)
328 + if finalitem not in sets[-2]:
329 + if len(tokens) > 0:
330 + self.error(tokens[i-1])
331 + else:
332 + self.error(None)
333 +
334 + return self.buildTree(self._START, finalitem,
335 + tokens, len(sets)-2)
336 +
337 + def isnullable(self, sym):
338 + #
339 + # For symbols in G_e only. If we weren't supporting 1.5,
340 + # could just use sym.startswith().
341 + #
342 + return self._NULLABLE == sym[0:len(self._NULLABLE)]
343 +
344 + def skip(self, lrhs, pos=0):
345 + (lhs, rhs) = lrhs
346 + n = len(rhs)
347 + while pos < n:
348 + if not self.isnullable(rhs[pos]):
349 + break
350 + pos = pos + 1
351 + return pos
352 +
353 + def makeState(self, state, sym):
354 + assert sym is not None
355 + #
356 + # Compute \epsilon-kernel state's core and see if
357 + # it exists already.
358 + #
359 + kitems = []
360 + for rule, pos in self.states[state].items:
361 + lhs, rhs = rule
362 + if rhs[pos:pos+1] == (sym,):
363 + kitems.append((rule, self.skip(rule, pos+1)))
364 + core = kitems
365 +
366 + core.sort()
367 + tcore = tuple(core)
368 + if tcore in self.cores:
369 + return self.cores[tcore]
370 + #
371 + # Nope, doesn't exist. Compute it and the associated
372 + # \epsilon-nonkernel state together; we'll need it right away.
373 + #
374 + k = self.cores[tcore] = len(self.states)
375 + K, NK = _State(k, kitems), _State(k+1, [])
376 + self.states[k] = K
377 + predicted = {}
378 +
379 + edges = self.edges
380 + rules = self.newrules
381 + for X in K, NK:
382 + worklist = X.items
383 + for item in worklist:
384 + rule, pos = item
385 + lhs, rhs = rule
386 + if pos == len(rhs):
387 + X.complete.append(rule)
388 + continue
389 +
390 + nextSym = rhs[pos]
391 + key = (X.stateno, nextSym)
392 + if nextSym not in rules:
393 + if key not in edges:
394 + edges[key] = None
395 + X.T.append(nextSym)
396 + else:
397 + edges[key] = None
398 + if nextSym not in predicted:
399 + predicted[nextSym] = 1
400 + for prule in rules[nextSym]:
401 + ppos = self.skip(prule)
402 + new = (prule, ppos)
403 + NK.items.append(new)
404 + #
405 + # Problem: we know K needs generating, but we
406 + # don't yet know about NK. Can't commit anything
407 + # regarding NK to self.edges until we're sure. Should
408 + # we delay committing on both K and NK to avoid this
409 + # hacky code? This creates other problems..
410 + #
411 + if X is K:
412 + edges = {}
413 +
414 + if NK.items == []:
415 + return k
416 +
417 + #
418 + # Check for \epsilon-nonkernel's core. Unfortunately we
419 + # need to know the entire set of predicted nonterminals
420 + # to do this without accidentally duplicating states.
421 + #
422 + core = list(predicted.keys())
423 + core.sort()
424 + tcore = tuple(core)
425 + if tcore in self.cores:
426 + self.edges[(k, None)] = self.cores[tcore]
427 + return k
428 +
429 + nk = self.cores[tcore] = self.edges[(k, None)] = NK.stateno
430 + self.edges.update(edges)
431 + self.states[nk] = NK
432 + return k
433 +
434 + def goto(self, state, sym):
435 + key = (state, sym)
436 + if key not in self.edges:
437 + #
438 + # No transitions from state on sym.
439 + #
440 + return None
441 +
442 + rv = self.edges[key]
443 + if rv is None:
444 + #
445 + # Target state isn't generated yet. Remedy this.
446 + #
447 + rv = self.makeState(state, sym)
448 + self.edges[key] = rv
449 + return rv
450 +
451 + def gotoT(self, state, t):
452 + return [self.goto(state, t)]
453 +
454 + def gotoST(self, state, st):
455 + rv = []
456 + for t in self.states[state].T:
457 + if st == t:
458 + rv.append(self.goto(state, t))
459 + return rv
460 +
461 + def add(self, set, item, i=None, predecessor=None, causal=None):
462 + if predecessor is None:
463 + if item not in set:
464 + set.append(item)
465 + else:
466 + key = (item, i)
467 + if item not in set:
468 + self.links[key] = []
469 + set.append(item)
470 + self.links[key].append((predecessor, causal))
471 +
472 + def makeSet(self, token, sets, i):
473 + cur, next = sets[i], sets[i+1]
474 +
475 + ttype = token is not None and self.typestring(token) or None
476 + if ttype is not None:
477 + fn, arg = self.gotoT, ttype
478 + else:
479 + fn, arg = self.gotoST, token
480 +
481 + for item in cur:
482 + ptr = (item, i)
483 + state, parent = item
484 + add = fn(state, arg)
485 + for k in add:
486 + if k is not None:
487 + self.add(next, (k, parent), i+1, ptr)
488 + nk = self.goto(k, None)
489 + if nk is not None:
490 + self.add(next, (nk, i+1))
491 +
492 + if parent == i:
493 + continue
494 +
495 + for rule in self.states[state].complete:
496 + lhs, rhs = rule
497 + for pitem in sets[parent]:
498 + pstate, pparent = pitem
499 + k = self.goto(pstate, lhs)
500 + if k is not None:
501 + why = (item, i, rule)
502 + pptr = (pitem, parent)
503 + self.add(cur, (k, pparent),
504 + i, pptr, why)
505 + nk = self.goto(k, None)
506 + if nk is not None:
507 + self.add(cur, (nk, i))
508 +
509 + def makeSet_fast(self, token, sets, i):
510 + #
511 + # Call *only* when the entire state machine has been built!
512 + # It relies on self.edges being filled in completely, and
513 + # then duplicates and inlines code to boost speed at the
514 + # cost of extreme ugliness.
515 + #
516 + cur, next = sets[i], sets[i+1]
517 + ttype = token is not None and self.typestring(token) or None
518 +
519 + for item in cur:
520 + ptr = (item, i)
521 + state, parent = item
522 + if ttype is not None:
523 + k = self.edges.get((state, ttype), None)
524 + if k is not None:
525 + #self.add(next, (k, parent), i+1, ptr)
526 + #INLINED --v
527 + new = (k, parent)
528 + key = (new, i+1)
529 + if new not in next:
530 + self.links[key] = []
531 + next.append(new)
532 + self.links[key].append((ptr, None))
533 + #INLINED --^
534 + #nk = self.goto(k, None)
535 + nk = self.edges.get((k, None), None)
536 + if nk is not None:
537 + #self.add(next, (nk, i+1))
538 + #INLINED --v
539 + new = (nk, i+1)
540 + if new not in next:
541 + next.append(new)
542 + #INLINED --^
543 + else:
544 + add = self.gotoST(state, token)
545 + for k in add:
546 + if k is not None:
547 + self.add(next, (k, parent), i+1, ptr)
548 + #nk = self.goto(k, None)
549 + nk = self.edges.get((k, None), None)
550 + if nk is not None:
551 + self.add(next, (nk, i+1))
552 +
553 + if parent == i:
554 + continue
555 +
556 + for rule in self.states[state].complete:
557 + lhs, rhs = rule
558 + for pitem in sets[parent]:
559 + pstate, pparent = pitem
560 + #k = self.goto(pstate, lhs)
561 + k = self.edges.get((pstate, lhs), None)
562 + if k is not None:
563 + why = (item, i, rule)
564 + pptr = (pitem, parent)
565 + #self.add(cur, (k, pparent),
566 + # i, pptr, why)
567 + #INLINED --v
568 + new = (k, pparent)
569 + key = (new, i)
570 + if new not in cur:
571 + self.links[key] = []
572 + cur.append(new)
573 + self.links[key].append((pptr, why))
574 + #INLINED --^
575 + #nk = self.goto(k, None)
576 + nk = self.edges.get((k, None), None)
577 + if nk is not None:
578 + #self.add(cur, (nk, i))
579 + #INLINED --v
580 + new = (nk, i)
581 + if new not in cur:
582 + cur.append(new)
583 + #INLINED --^
584 +
585 + def predecessor(self, key, causal):
586 + for p, c in self.links[key]:
587 + if c == causal:
588 + return p
589 + assert 0
590 +
591 + def causal(self, key):
592 + links = self.links[key]
593 + if len(links) == 1:
594 + return links[0][1]
595 + choices = []
596 + rule2cause = {}
597 + for p, c in links:
598 + rule = c[2]
599 + choices.append(rule)
600 + rule2cause[rule] = c
601 + return rule2cause[self.ambiguity(choices)]
602 +
603 + def deriveEpsilon(self, nt):
604 + if len(self.newrules[nt]) > 1:
605 + rule = self.ambiguity(self.newrules[nt])
606 + else:
607 + rule = self.newrules[nt][0]
608 + #print rule
609 +
610 + rhs = rule[1]
611 + attr = [None] * len(rhs)
612 +
613 + for i in range(len(rhs)-1, -1, -1):
614 + attr[i] = self.deriveEpsilon(rhs[i])
615 + return self.rule2func[self.new2old[rule]](attr)
616 +
617 + def buildTree(self, nt, item, tokens, k):
618 + state, parent = item
619 +
620 + choices = []
621 + for rule in self.states[state].complete:
622 + if rule[0] == nt:
623 + choices.append(rule)
624 + rule = choices[0]
625 + if len(choices) > 1:
626 + rule = self.ambiguity(choices)
627 + #print rule
628 +
629 + rhs = rule[1]
630 + attr = [None] * len(rhs)
631 +
632 + for i in range(len(rhs)-1, -1, -1):
633 + sym = rhs[i]
634 + if sym not in self.newrules:
635 + if sym != self._BOF:
636 + attr[i] = tokens[k-1]
637 + key = (item, k)
638 + item, k = self.predecessor(key, None)
639 + #elif self.isnullable(sym):
640 + elif self._NULLABLE == sym[0:len(self._NULLABLE)]:
641 + attr[i] = self.deriveEpsilon(sym)
642 + else:
643 + key = (item, k)
644 + why = self.causal(key)
645 + attr[i] = self.buildTree(sym, why[0],
646 + tokens, why[1])
647 + item, k = self.predecessor(key, why)
648 + return self.rule2func[self.new2old[rule]](attr)
649 +
650 + def ambiguity(self, rules):
651 + #
652 + # XXX - problem here and in collectRules() if the same rule
653 + # appears in >1 method. Also undefined results if rules
654 + # causing the ambiguity appear in the same method.
655 + #
656 + sortlist = []
657 + name2index = {}
658 + for i in range(len(rules)):
659 + lhs, rhs = rule = rules[i]
660 + name = self.rule2name[self.new2old[rule]]
661 + sortlist.append((len(rhs), name))
662 + name2index[name] = i
663 + sortlist.sort()
664 + list = [a_b[1] for a_b in sortlist]
665 + return rules[name2index[self.resolve(list)]]
666 +
667 + def resolve(self, list):
668 + #
669 + # Resolve ambiguity in favor of the shortest RHS.
670 + # Since we walk the tree from the top down, this
671 + # should effectively resolve in favor of a "shift".
672 + #
673 + return list[0]
674 +
675 +#
676 +# GenericASTBuilder automagically constructs a concrete/abstract syntax tree
677 +# for a given input. The extra argument is a class (not an instance!)
678 +# which supports the "__setslice__" and "__len__" methods.
679 +#
680 +# XXX - silently overrides any user code in methods.
681 +#
682 +
683 +class GenericASTBuilder(GenericParser):
684 + def __init__(self, AST, start):
685 + GenericParser.__init__(self, start)
686 + self.AST = AST
687 +
688 + def preprocess(self, rule, func):
689 + rebind = lambda lhs, self=self: \
690 + lambda args, lhs=lhs, self=self: \
691 + self.buildASTNode(args, lhs)
692 + lhs, rhs = rule
693 + return rule, rebind(lhs)
694 +
695 + def buildASTNode(self, args, lhs):
696 + children = []
697 + for arg in args:
698 + if isinstance(arg, self.AST):
699 + children.append(arg)
700 + else:
701 + children.append(self.terminal(arg))
702 + return self.nonterminal(lhs, children)
703 +
704 + def terminal(self, token): return token
705 +
706 + def nonterminal(self, type, args):
707 + rv = self.AST(type)
708 + rv[:len(args)] = args
709 + return rv
710 +
711 +#
712 +# GenericASTTraversal is a Visitor pattern according to Design Patterns. For
713 +# each node it attempts to invoke the method n_<node type>, falling
714 +# back onto the default() method if the n_* can't be found. The preorder
715 +# traversal also looks for an exit hook named n_<node type>_exit (no default
716 +# routine is called if it's not found). To prematurely halt traversal
717 +# of a subtree, call the prune() method -- this only makes sense for a
718 +# preorder traversal. Node type is determined via the typestring() method.
719 +#
720 +
721 +class GenericASTTraversalPruningException:
722 + pass
723 +
724 +class GenericASTTraversal:
725 + def __init__(self, ast):
726 + self.ast = ast
727 +
728 + def typestring(self, node):
729 + return node.type
730 +
731 + def prune(self):
732 + raise GenericASTTraversalPruningException
733 +
734 + def preorder(self, node=None):
735 + if node is None:
736 + node = self.ast
737 +
738 + try:
739 + name = 'n_' + self.typestring(node)
740 + if hasattr(self, name):
741 + func = getattr(self, name)
742 + func(node)
743 + else:
744 + self.default(node)
745 + except GenericASTTraversalPruningException:
746 + return
747 +
748 + for kid in node:
749 + self.preorder(kid)
750 +
751 + name = name + '_exit'
752 + if hasattr(self, name):
753 + func = getattr(self, name)
754 + func(node)
755 +
756 + def postorder(self, node=None):
757 + if node is None:
758 + node = self.ast
759 +
760 + for kid in node:
761 + self.postorder(kid)
762 +
763 + name = 'n_' + self.typestring(node)
764 + if hasattr(self, name):
765 + func = getattr(self, name)
766 + func(node)
767 + else:
768 + self.default(node)
769 +
770 +
771 + def default(self, node):
772 + pass
773 +
774 +#
775 +# GenericASTMatcher. AST nodes must have "__getitem__" and "__cmp__"
776 +# implemented.
777 +#
778 +# XXX - makes assumptions about how GenericParser walks the parse tree.
779 +#
780 +
781 +class GenericASTMatcher(GenericParser):
782 + def __init__(self, start, ast):
783 + GenericParser.__init__(self, start)
784 + self.ast = ast
785 +
786 + def preprocess(self, rule, func):
787 + rebind = lambda func, self=self: \
788 + lambda args, func=func, self=self: \
789 + self.foundMatch(args, func)
790 + lhs, rhs = rule
791 + rhslist = list(rhs)
792 + rhslist.reverse()
793 +
794 + return (lhs, tuple(rhslist)), rebind(func)
795 +
796 + def foundMatch(self, args, func):
797 + func(args[-1])
798 + return args[-1]
799 +
800 + def match_r(self, node):
801 + self.input.insert(0, node)
802 + children = 0
803 +
804 + for child in node:
805 + if children == 0:
806 + self.input.insert(0, '(')
807 + children = children + 1
808 + self.match_r(child)
809 +
810 + if children > 0:
811 + self.input.insert(0, ')')
812 +
813 + def match(self, ast=None):
814 + if ast is None:
815 + ast = self.ast
816 + self.input = []
817 +
818 + self.match_r(ast)
819 + self.parse(self.input)
820 +
821 + def resolve(self, list):
822 + #
823 + # Resolve ambiguity in favor of the longest RHS.
824 + #
825 + return list[-1]
826 +
827 +def _dump(tokens, sets, states):
828 + for i in range(len(sets)):
829 + print('set %s' % i)
830 + for item in sets[i]:
831 + print('\t%s' % item)
832 + for (lhs, rhs), pos in states[item[0]].items:
833 + print('\t\t %s ::= %s . %s'
834 + % (lhs, string.join(rhs[:pos]),string.join(rhs[pos:])))
835 + if i < len(tokens):
836 + print('\ntoken %s\n' % str(tokens[i]))
1 +"Usage: unparse.py <path to source file>"
2 +import sys
3 +#import _ast
4 +from snakes.lang import ast as _ast
5 +try :
6 + import io
7 +except ImportError :
8 + import cStringIO as io
9 +import os
10 +
11 +def interleave(inter, f, seq):
12 + """Call f on each item in seq, calling inter() in between.
13 + """
14 + seq = iter(seq)
15 + try:
16 + f(next(seq))
17 + except StopIteration:
18 + pass
19 + else:
20 + for x in seq:
21 + inter()
22 + f(x)
23 +
24 +class Unparser:
25 + """Methods in this class recursively traverse an AST and
26 + output source code for the abstract syntax; original formatting
27 + is disregarged. """
28 +
29 + def __init__(self, tree, file = sys.stdout):
30 + """Unparser(tree, file=sys.stdout) -> None.
31 + Print the source for tree to file."""
32 + self.f = file
33 + self._indent = 0
34 + self.dispatch(tree)
35 + self.f.write("\n")
36 + self.f.flush()
37 +
38 + def fill(self, text = ""):
39 + "Indent a piece of text, according to the current indentation level"
40 + self.f.write("\n"+" "*self._indent + text)
41 +
42 + def write(self, text):
43 + "Append a piece of text to the current line."
44 + self.f.write(text)
45 +
46 + def enter(self):
47 + "Print ':', and increase the indentation."
48 + self.write(":")
49 + self._indent += 1
50 +
51 + def leave(self):
52 + "Decrease the indentation level."
53 + self._indent -= 1
54 +
55 + def dispatch(self, tree):
56 + "Dispatcher function, dispatching tree type T to method _T."
57 + if isinstance(tree, list):
58 + for t in tree:
59 + self.dispatch(t)
60 + return
61 + meth = getattr(self, "_"+tree.__class__.__name__)
62 + meth(tree)
63 +
64 +
65 + ############### Unparsing methods ######################
66 + # There should be one method per concrete grammar type #
67 + # Constructors should be grouped by sum type. Ideally, #
68 + # this would follow the order in the grammar, but #
69 + # currently doesn't. #
70 + ########################################################
71 +
72 + def _Module(self, tree):
73 + for stmt in tree.body:
74 + self.dispatch(stmt)
75 +
76 + # stmt
77 + def _Expr(self, tree):
78 + self.fill()
79 + self.dispatch(tree.value)
80 +
81 + def _Import(self, t):
82 + self.fill("import ")
83 + interleave(lambda: self.write(", "), self.dispatch, t.names)
84 +
85 + def _ImportFrom(self, t):
86 + self.fill("from ")
87 + self.write(t.module)
88 + self.write(" import ")
89 + interleave(lambda: self.write(", "), self.dispatch, t.names)
90 + # XXX(jpe) what is level for?
91 +
92 + def _Assign(self, t):
93 + self.fill()
94 + for target in t.targets:
95 + self.dispatch(target)
96 + self.write(" = ")
97 + self.dispatch(t.value)
98 +
99 + def _AugAssign(self, t):
100 + self.fill()
101 + self.dispatch(t.target)
102 + self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
103 + self.dispatch(t.value)
104 +
105 + def _Return(self, t):
106 + self.fill("return")
107 + if t.value:
108 + self.write(" ")
109 + self.dispatch(t.value)
110 +
111 + def _Pass(self, t):
112 + self.fill("pass")
113 +
114 + def _Break(self, t):
115 + self.fill("break")
116 +
117 + def _Continue(self, t):
118 + self.fill("continue")
119 +
120 + def _Delete(self, t):
121 + self.fill("del ")
122 + self.dispatch(t.targets)
123 +
124 + def _Assert(self, t):
125 + self.fill("assert ")
126 + self.dispatch(t.test)
127 + if t.msg:
128 + self.write(", ")
129 + self.dispatch(t.msg)
130 +
131 + def _Exec(self, t):
132 + self.fill("exec ")
133 + self.dispatch(t.body)
134 + if t.globals:
135 + self.write(" in ")
136 + self.dispatch(t.globals)
137 + if t.locals:
138 + self.write(", ")
139 + self.dispatch(t.locals)
140 +
141 + def _Print(self, t):
142 + self.fill("print ")
143 + do_comma = False
144 + if t.dest:
145 + self.write(">>")
146 + self.dispatch(t.dest)
147 + do_comma = True
148 + for e in t.values:
149 + if do_comma:self.write(", ")
150 + else:do_comma=True
151 + self.dispatch(e)
152 + if not t.nl:
153 + self.write(",")
154 +
155 + def _Global(self, t):
156 + self.fill("global ")
157 + interleave(lambda: self.write(", "), self.write, t.names)
158 +
159 + def _Yield(self, t):
160 + self.write("(")
161 + self.write("yield")
162 + if t.value:
163 + self.write(" ")
164 + self.dispatch(t.value)
165 + self.write(")")
166 +
167 + def _Raise(self, t):
168 + self.fill('raise ')
169 + if t.type:
170 + self.dispatch(t.type)
171 + if t.inst:
172 + self.write(", ")
173 + self.dispatch(t.inst)
174 + if t.tback:
175 + self.write(", ")
176 + self.dispatch(t.tback)
177 +
178 + def _TryExcept(self, t):
179 + self.fill("try")
180 + self.enter()
181 + self.dispatch(t.body)
182 + self.leave()
183 +
184 + for ex in t.handlers:
185 + self.dispatch(ex)
186 + if t.orelse:
187 + self.fill("else")
188 + self.enter()
189 + self.dispatch(t.orelse)
190 + self.leave()
191 +
192 + def _TryFinally(self, t):
193 + self.fill("try")
194 + self.enter()
195 + self.dispatch(t.body)
196 + self.leave()
197 +
198 + self.fill("finally")
199 + self.enter()
200 + self.dispatch(t.finalbody)
201 + self.leave()
202 +
203 + def _ExceptHandler(self, t):
204 + self.fill("except")
205 + if t.type:
206 + self.write(" ")
207 + self.dispatch(t.type)
208 + if t.name:
209 + self.write(", ")
210 + self.dispatch(t.name)
211 + self.enter()
212 + self.dispatch(t.body)
213 + self.leave()
214 +
215 + def _ClassDef(self, t):
216 + self.write("\n")
217 + self.fill("class "+t.name)
218 + if t.bases:
219 + self.write("(")
220 + for a in t.bases:
221 + self.dispatch(a)
222 + self.write(", ")
223 + self.write(")")
224 + self.enter()
225 + self.dispatch(t.body)
226 + self.leave()
227 +
228 + def _FunctionDef(self, t):
229 + self.write("\n")
230 + for deco in t.decorator_list:
231 + self.fill("@")
232 + self.dispatch(deco)
233 + self.fill("def "+t.name + "(")
234 + self.dispatch(t.args)
235 + self.write(")")
236 + self.enter()
237 + self.dispatch(t.body)
238 + self.leave()
239 +
240 + def _For(self, t):
241 + self.fill("for ")
242 + self.dispatch(t.target)
243 + self.write(" in ")
244 + self.dispatch(t.iter)
245 + self.enter()
246 + self.dispatch(t.body)
247 + self.leave()
248 + if t.orelse:
249 + self.fill("else")
250 + self.enter()
251 + self.dispatch(t.orelse)
252 + self.leave
253 +
254 + def _If(self, t):
255 + self.fill("if ")
256 + self.dispatch(t.test)
257 + self.enter()
258 + # XXX elif?
259 + self.dispatch(t.body)
260 + self.leave()
261 + if t.orelse:
262 + self.fill("else")
263 + self.enter()
264 + self.dispatch(t.orelse)
265 + self.leave()
266 +
267 + def _While(self, t):
268 + self.fill("while ")
269 + self.dispatch(t.test)
270 + self.enter()
271 + self.dispatch(t.body)
272 + self.leave()
273 + if t.orelse:
274 + self.fill("else")
275 + self.enter()
276 + self.dispatch(t.orelse)
277 + self.leave
278 +
279 + def _With(self, t):
280 + self.fill("with ")
281 + self.dispatch(t.context_expr)
282 + if t.optional_vars:
283 + self.write(" as ")
284 + self.dispatch(t.optional_vars)
285 + self.enter()
286 + self.dispatch(t.body)
287 + self.leave()
288 +
289 + # expr
290 + def _Str(self, tree):
291 + self.write(repr(tree.s))
292 +
293 + def _Name(self, t):
294 + self.write(t.id)
295 +
296 + def _Repr(self, t):
297 + self.write("`")
298 + self.dispatch(t.value)
299 + self.write("`")
300 +
301 + def _Num(self, t):
302 + self.write(repr(t.n))
303 +
304 + def _List(self, t):
305 + self.write("[")
306 + interleave(lambda: self.write(", "), self.dispatch, t.elts)
307 + self.write("]")
308 +
309 + def _ListComp(self, t):
310 + self.write("[")
311 + self.dispatch(t.elt)
312 + for gen in t.generators:
313 + self.dispatch(gen)
314 + self.write("]")
315 +
316 + def _GeneratorExp(self, t):
317 + self.write("(")
318 + self.dispatch(t.elt)
319 + for gen in t.generators:
320 + self.dispatch(gen)
321 + self.write(")")
322 +
323 + def _comprehension(self, t):
324 + self.write(" for ")
325 + self.dispatch(t.target)
326 + self.write(" in ")
327 + self.dispatch(t.iter)
328 + for if_clause in t.ifs:
329 + self.write(" if ")
330 + self.dispatch(if_clause)
331 +
332 + def _IfExp(self, t):
333 + self.write("(")
334 + self.dispatch(t.body)
335 + self.write(" if ")
336 + self.dispatch(t.test)
337 + self.write(" else ")
338 + self.dispatch(t.orelse)
339 + self.write(")")
340 +
341 + def _Dict(self, t):
342 + self.write("{")
343 + def writem(arg):
344 + (k, v) = arg
345 + self.dispatch(k)
346 + self.write(": ")
347 + self.dispatch(v)
348 + interleave(lambda: self.write(", "), writem, zip(t.keys, t.values))
349 + self.write("}")
350 +
351 + def _Tuple(self, t):
352 + self.write("(")
353 + if len(t.elts) == 1:
354 + (elt,) = t.elts
355 + self.dispatch(elt)
356 + self.write(",")
357 + else:
358 + interleave(lambda: self.write(", "), self.dispatch, t.elts)
359 + self.write(")")
360 +
361 + unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
362 + def _UnaryOp(self, t):
363 + self.write(self.unop[t.op.__class__.__name__])
364 + self.write("(")
365 + self.dispatch(t.operand)
366 + self.write(")")
367 +
368 + binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%",
369 + "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
370 + "FloorDiv":"//", "Pow": "**"}
371 + def _BinOp(self, t):
372 + self.write("(")
373 + self.dispatch(t.left)
374 + self.write(" " + self.binop[t.op.__class__.__name__] + " ")
375 + self.dispatch(t.right)
376 + self.write(")")
377 +
378 + cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
379 + "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
380 + def _Compare(self, t):
381 + self.write("(")
382 + self.dispatch(t.left)
383 + for o, e in zip(t.ops, t.comparators):
384 + self.write(" " + self.cmpops[o.__class__.__name__] + " ")
385 + self.dispatch(e)
386 + self.write(")")
387 +
388 + boolops = {_ast.And: 'and', _ast.Or: 'or'}
389 + def _BoolOp(self, t):
390 + self.write("(")
391 + s = " %s " % self.boolops[t.op.__class__]
392 + interleave(lambda: self.write(s), self.dispatch, t.values)
393 + self.write(")")
394 +
395 + def _Attribute(self,t):
396 + self.dispatch(t.value)
397 + self.write(".")
398 + self.write(t.attr)
399 +
400 + def _Call(self, t):
401 + self.dispatch(t.func)
402 + self.write("(")
403 + comma = False
404 + for e in t.args:
405 + if comma: self.write(", ")
406 + else: comma = True
407 + self.dispatch(e)
408 + for e in t.keywords:
409 + if comma: self.write(", ")
410 + else: comma = True
411 + self.dispatch(e)
412 + if t.starargs:
413 + if comma: self.write(", ")
414 + else: comma = True
415 + self.write("*")
416 + self.dispatch(t.starargs)
417 + if t.kwargs:
418 + if comma: self.write(", ")
419 + else: comma = True
420 + self.write("**")
421 + self.dispatch(t.kwargs)
422 + self.write(")")
423 +
424 + def _Subscript(self, t):
425 + self.dispatch(t.value)
426 + self.write("[")
427 + self.dispatch(t.slice)
428 + self.write("]")
429 +
430 + # slice
431 + def _Ellipsis(self, t):
432 + self.write("...")
433 +
434 + def _Index(self, t):
435 + self.dispatch(t.value)
436 +
437 + def _Slice(self, t):
438 + if t.lower:
439 + self.dispatch(t.lower)
440 + self.write(":")
441 + if t.upper:
442 + self.dispatch(t.upper)
443 + if t.step:
444 + self.write(":")
445 + self.dispatch(t.step)
446 +
447 + def _ExtSlice(self, t):
448 + interleave(lambda: self.write(', '), self.dispatch, t.dims)
449 +
450 + # others
451 + def _arguments(self, t):
452 + first = True
453 + nonDef = len(t.args)-len(t.defaults)
454 + for a in t.args[0:nonDef]:
455 + if first:first = False
456 + else: self.write(", ")
457 + self.dispatch(a)
458 + for a,d in zip(t.args[nonDef:], t.defaults):
459 + if first:first = False
460 + else: self.write(", ")
461 + self.dispatch(a),
462 + self.write("=")
463 + self.dispatch(d)
464 + if t.vararg:
465 + if first:first = False
466 + else: self.write(", ")
467 + self.write("*"+t.vararg)
468 + if t.kwarg:
469 + if first:first = False
470 + else: self.write(", ")
471 + self.write("**"+t.kwarg)
472 +
473 + def _keyword(self, t):
474 + self.write(t.arg)
475 + self.write("=")
476 + self.dispatch(t.value)
477 +
478 + def _Lambda(self, t):
479 + self.write("lambda ")
480 + self.dispatch(t.args)
481 + self.write(": ")
482 + self.dispatch(t.body)
483 +
484 + def _alias(self, t):
485 + self.write(t.name)
486 + if t.asname:
487 + self.write(" as "+t.asname)
488 +
489 +def roundtrip(filename, output=sys.stdout):
490 + source = open(filename).read()
491 + tree = compile(source, filename, "exec", _ast.PyCF_ONLY_AST)
492 + Unparser(tree, output)
493 +
494 +
495 +
496 +def testdir(a):
497 + try:
498 + names = [n for n in os.listdir(a) if n.endswith('.py')]
499 + except OSError:
500 + sys.stderr.write("Directory not readable: %s\n" % a)
501 + else:
502 + for n in names:
503 + fullname = os.path.join(a, n)
504 + if os.path.isfile(fullname):
505 + output = io.StringIO()
506 + print('Testing %s' % fullname)
507 + try:
508 + roundtrip(fullname, output)
509 + except Exception :
510 + e = sys.exc_info()[1]
511 + print(' Failed to compile, exception is %s' % repr(e))
512 + elif os.path.isdir(fullname):
513 + testdir(fullname)
514 +
515 +def main(args):
516 + if args[0] == '--testdir':
517 + for a in args[1:]:
518 + testdir(a)
519 + else:
520 + for a in args:
521 + roundtrip(a)
522 +
523 +if __name__=='__main__':
524 + main(sys.argv[1:])
1 +# this file has been automatically generated running:
2 +# snakes/lang/asdl.py --output=snakes/lang/python/asdl.py snakes/lang/python/python.asdl
3 +# timestamp: 2010-04-22 11:40:44.120690
4 +
5 +from snakes.lang import ast
6 +from snkast import *
7 +
8 +class _AST (ast.AST):
9 + def __init__ (self, **ARGS):
10 + ast.AST.__init__(self)
11 + for k, v in ARGS.items():
12 + setattr(self, k, v)
13 +
14 +class arguments (_AST):
15 + _fields = ('args', 'vararg', 'varargannotation', 'kwonlyargs', 'kwarg', 'kwargannotation', 'defaults', 'kw_defaults')
16 + _attributes = ()
17 + def __init__ (self, args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[], **ARGS):
18 + _AST.__init__(self, **ARGS)
19 + self.args = list(args)
20 + self.vararg = vararg
21 + self.varargannotation = varargannotation
22 + self.kwonlyargs = list(kwonlyargs)
23 + self.kwarg = kwarg
24 + self.kwargannotation = kwargannotation
25 + self.defaults = list(defaults)
26 + self.kw_defaults = list(kw_defaults)
27 +
28 +class slice (_AST):
29 + pass
30 +
31 +class Slice (slice):
32 + _fields = ('lower', 'upper', 'step')
33 + _attributes = ()
34 + def __init__ (self, lower=None, upper=None, step=None, **ARGS):
35 + slice.__init__(self, **ARGS)
36 + self.lower = lower
37 + self.upper = upper
38 + self.step = step
39 +
40 +class ExtSlice (slice):
41 + _fields = ('dims',)
42 + _attributes = ()
43 + def __init__ (self, dims=[], **ARGS):
44 + slice.__init__(self, **ARGS)
45 + self.dims = list(dims)
46 +
47 +class Index (slice):
48 + _fields = ('value',)
49 + _attributes = ()
50 + def __init__ (self, value, **ARGS):
51 + slice.__init__(self, **ARGS)
52 + self.value = value
53 +
54 +class cmpop (_AST):
55 + pass
56 +
57 +class Eq (cmpop):
58 + _fields = ()
59 + _attributes = ()
60 +
61 +class NotEq (cmpop):
62 + _fields = ()
63 + _attributes = ()
64 +
65 +class Lt (cmpop):
66 + _fields = ()
67 + _attributes = ()
68 +
69 +class LtE (cmpop):
70 + _fields = ()
71 + _attributes = ()
72 +
73 +class Gt (cmpop):
74 + _fields = ()
75 + _attributes = ()
76 +
77 +class GtE (cmpop):
78 + _fields = ()
79 + _attributes = ()
80 +
81 +class Is (cmpop):
82 + _fields = ()
83 + _attributes = ()
84 +
85 +class IsNot (cmpop):
86 + _fields = ()
87 + _attributes = ()
88 +
89 +class In (cmpop):
90 + _fields = ()
91 + _attributes = ()
92 +
93 +class NotIn (cmpop):
94 + _fields = ()
95 + _attributes = ()
96 +
97 +class expr_context (_AST):
98 + pass
99 +
100 +class Load (expr_context):
101 + _fields = ()
102 + _attributes = ()
103 +
104 +class Store (expr_context):
105 + _fields = ()
106 + _attributes = ()
107 +
108 +class Del (expr_context):
109 + _fields = ()
110 + _attributes = ()
111 +
112 +class AugLoad (expr_context):
113 + _fields = ()
114 + _attributes = ()
115 +
116 +class AugStore (expr_context):
117 + _fields = ()
118 + _attributes = ()
119 +
120 +class Param (expr_context):
121 + _fields = ()
122 + _attributes = ()
123 +
124 +class keyword (_AST):
125 + _fields = ('arg', 'value')
126 + _attributes = ()
127 + def __init__ (self, arg, value, **ARGS):
128 + _AST.__init__(self, **ARGS)
129 + self.arg = arg
130 + self.value = value
131 +
132 +class unaryop (_AST):
133 + pass
134 +
135 +class Invert (unaryop):
136 + _fields = ()
137 + _attributes = ()
138 +
139 +class Not (unaryop):
140 + _fields = ()
141 + _attributes = ()
142 +
143 +class UAdd (unaryop):
144 + _fields = ()
145 + _attributes = ()
146 +
147 +class USub (unaryop):
148 + _fields = ()
149 + _attributes = ()
150 +
151 +class expr (_AST):
152 + pass
153 +
154 +class BoolOp (expr):
155 + _fields = ('op', 'values')
156 + _attributes = ('lineno', 'col_offset')
157 + def __init__ (self, op, values=[], lineno=0, col_offset=0, **ARGS):
158 + expr.__init__(self, **ARGS)
159 + self.op = op
160 + self.values = list(values)
161 + self.lineno = int(lineno)
162 + self.col_offset = int(col_offset)
163 +
164 +class BinOp (expr):
165 + _fields = ('left', 'op', 'right')
166 + _attributes = ('lineno', 'col_offset')
167 + def __init__ (self, left, op, right, lineno=0, col_offset=0, **ARGS):
168 + expr.__init__(self, **ARGS)
169 + self.left = left
170 + self.op = op
171 + self.right = right
172 + self.lineno = int(lineno)
173 + self.col_offset = int(col_offset)
174 +
175 +class UnaryOp (expr):
176 + _fields = ('op', 'operand')
177 + _attributes = ('lineno', 'col_offset')
178 + def __init__ (self, op, operand, lineno=0, col_offset=0, **ARGS):
179 + expr.__init__(self, **ARGS)
180 + self.op = op
181 + self.operand = operand
182 + self.lineno = int(lineno)
183 + self.col_offset = int(col_offset)
184 +
185 +class Lambda (expr):
186 + _fields = ('args', 'body')
187 + _attributes = ('lineno', 'col_offset')
188 + def __init__ (self, args, body, lineno=0, col_offset=0, **ARGS):
189 + expr.__init__(self, **ARGS)
190 + self.args = args
191 + self.body = body
192 + self.lineno = int(lineno)
193 + self.col_offset = int(col_offset)
194 +
195 +class IfExp (expr):
196 + _fields = ('test', 'body', 'orelse')
197 + _attributes = ('lineno', 'col_offset')
198 + def __init__ (self, test, body, orelse, lineno=0, col_offset=0, **ARGS):
199 + expr.__init__(self, **ARGS)
200 + self.test = test
201 + self.body = body
202 + self.orelse = orelse
203 + self.lineno = int(lineno)
204 + self.col_offset = int(col_offset)
205 +
206 +class Dict (expr):
207 + _fields = ('keys', 'values')
208 + _attributes = ('lineno', 'col_offset')
209 + def __init__ (self, keys=[], values=[], lineno=0, col_offset=0, **ARGS):
210 + expr.__init__(self, **ARGS)
211 + self.keys = list(keys)
212 + self.values = list(values)
213 + self.lineno = int(lineno)
214 + self.col_offset = int(col_offset)
215 +
216 +class Set (expr):
217 + _fields = ('elts',)
218 + _attributes = ('lineno', 'col_offset')
219 + def __init__ (self, elts=[], lineno=0, col_offset=0, **ARGS):
220 + expr.__init__(self, **ARGS)
221 + self.elts = list(elts)
222 + self.lineno = int(lineno)
223 + self.col_offset = int(col_offset)
224 +
225 +class ListComp (expr):
226 + _fields = ('elt', 'generators')
227 + _attributes = ('lineno', 'col_offset')
228 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
229 + expr.__init__(self, **ARGS)
230 + self.elt = elt
231 + self.generators = list(generators)
232 + self.lineno = int(lineno)
233 + self.col_offset = int(col_offset)
234 +
235 +class SetComp (expr):
236 + _fields = ('elt', 'generators')
237 + _attributes = ('lineno', 'col_offset')
238 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
239 + expr.__init__(self, **ARGS)
240 + self.elt = elt
241 + self.generators = list(generators)
242 + self.lineno = int(lineno)
243 + self.col_offset = int(col_offset)
244 +
245 +class DictComp (expr):
246 + _fields = ('key', 'value', 'generators')
247 + _attributes = ('lineno', 'col_offset')
248 + def __init__ (self, key, value, generators=[], lineno=0, col_offset=0, **ARGS):
249 + expr.__init__(self, **ARGS)
250 + self.key = key
251 + self.value = value
252 + self.generators = list(generators)
253 + self.lineno = int(lineno)
254 + self.col_offset = int(col_offset)
255 +
256 +class GeneratorExp (expr):
257 + _fields = ('elt', 'generators')
258 + _attributes = ('lineno', 'col_offset')
259 + def __init__ (self, elt, generators=[], lineno=0, col_offset=0, **ARGS):
260 + expr.__init__(self, **ARGS)
261 + self.elt = elt
262 + self.generators = list(generators)
263 + self.lineno = int(lineno)
264 + self.col_offset = int(col_offset)
265 +
266 +class Yield (expr):
267 + _fields = ('value',)
268 + _attributes = ('lineno', 'col_offset')
269 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
270 + expr.__init__(self, **ARGS)
271 + self.value = value
272 + self.lineno = int(lineno)
273 + self.col_offset = int(col_offset)
274 +
275 +class Compare (expr):
276 + _fields = ('left', 'ops', 'comparators')
277 + _attributes = ('lineno', 'col_offset')
278 + def __init__ (self, left, ops=[], comparators=[], lineno=0, col_offset=0, **ARGS):
279 + expr.__init__(self, **ARGS)
280 + self.left = left
281 + self.ops = list(ops)
282 + self.comparators = list(comparators)
283 + self.lineno = int(lineno)
284 + self.col_offset = int(col_offset)
285 +
286 +class Call (expr):
287 + _fields = ('func', 'args', 'keywords', 'starargs', 'kwargs')
288 + _attributes = ('lineno', 'col_offset')
289 + def __init__ (self, func, args=[], keywords=[], starargs=None, kwargs=None, lineno=0, col_offset=0, **ARGS):
290 + expr.__init__(self, **ARGS)
291 + self.func = func
292 + self.args = list(args)
293 + self.keywords = list(keywords)
294 + self.starargs = starargs
295 + self.kwargs = kwargs
296 + self.lineno = int(lineno)
297 + self.col_offset = int(col_offset)
298 +
299 +class Num (expr):
300 + _fields = ('n',)
301 + _attributes = ('lineno', 'col_offset')
302 + def __init__ (self, n, lineno=0, col_offset=0, **ARGS):
303 + expr.__init__(self, **ARGS)
304 + self.n = n
305 + self.lineno = int(lineno)
306 + self.col_offset = int(col_offset)
307 +
308 +class Str (expr):
309 + _fields = ('s',)
310 + _attributes = ('lineno', 'col_offset')
311 + def __init__ (self, s, lineno=0, col_offset=0, **ARGS):
312 + expr.__init__(self, **ARGS)
313 + self.s = s
314 + self.lineno = int(lineno)
315 + self.col_offset = int(col_offset)
316 +
317 +class Ellipsis (expr):
318 + _fields = ()
319 + _attributes = ('lineno', 'col_offset')
320 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
321 + expr.__init__(self, **ARGS)
322 + self.lineno = int(lineno)
323 + self.col_offset = int(col_offset)
324 +
325 +class Attribute (expr):
326 + _fields = ('value', 'attr', 'ctx')
327 + _attributes = ('lineno', 'col_offset')
328 + def __init__ (self, value, attr, ctx=None, lineno=0, col_offset=0, **ARGS):
329 + expr.__init__(self, **ARGS)
330 + self.value = value
331 + self.attr = attr
332 + self.ctx = ctx
333 + self.lineno = int(lineno)
334 + self.col_offset = int(col_offset)
335 +
336 +class Subscript (expr):
337 + _fields = ('value', 'slice', 'ctx')
338 + _attributes = ('lineno', 'col_offset')
339 + def __init__ (self, value, slice, ctx=None, lineno=0, col_offset=0, **ARGS):
340 + expr.__init__(self, **ARGS)
341 + self.value = value
342 + self.slice = slice
343 + self.ctx = ctx
344 + self.lineno = int(lineno)
345 + self.col_offset = int(col_offset)
346 +
347 +class Starred (expr):
348 + _fields = ('value', 'ctx')
349 + _attributes = ('lineno', 'col_offset')
350 + def __init__ (self, value, ctx=None, lineno=0, col_offset=0, **ARGS):
351 + expr.__init__(self, **ARGS)
352 + self.value = value
353 + self.ctx = ctx
354 + self.lineno = int(lineno)
355 + self.col_offset = int(col_offset)
356 +
357 +class Name (expr):
358 + _fields = ('id', 'ctx')
359 + _attributes = ('lineno', 'col_offset')
360 + def __init__ (self, id, ctx=None, lineno=0, col_offset=0, **ARGS):
361 + expr.__init__(self, **ARGS)
362 + self.id = id
363 + self.ctx = ctx
364 + self.lineno = int(lineno)
365 + self.col_offset = int(col_offset)
366 +
367 +class List (expr):
368 + _fields = ('elts', 'ctx')
369 + _attributes = ('lineno', 'col_offset')
370 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
371 + expr.__init__(self, **ARGS)
372 + self.elts = list(elts)
373 + self.ctx = ctx
374 + self.lineno = int(lineno)
375 + self.col_offset = int(col_offset)
376 +
377 +class Tuple (expr):
378 + _fields = ('elts', 'ctx')
379 + _attributes = ('lineno', 'col_offset')
380 + def __init__ (self, elts=[], ctx=None, lineno=0, col_offset=0, **ARGS):
381 + expr.__init__(self, **ARGS)
382 + self.elts = list(elts)
383 + self.ctx = ctx
384 + self.lineno = int(lineno)
385 + self.col_offset = int(col_offset)
386 +
387 +class boolop (_AST):
388 + pass
389 +
390 +class And (boolop):
391 + _fields = ()
392 + _attributes = ()
393 +
394 +class Or (boolop):
395 + _fields = ()
396 + _attributes = ()
397 +
398 +class stmt (_AST):
399 + pass
400 +
401 +class FunctionDef (stmt):
402 + _fields = ('name', 'args', 'body', 'decorator_list', 'returns')
403 + _attributes = ('lineno', 'col_offset')
404 + def __init__ (self, name, args, body=[], decorator_list=[], returns=None, lineno=0, col_offset=0, **ARGS):
405 + stmt.__init__(self, **ARGS)
406 + self.name = name
407 + self.args = args
408 + self.body = list(body)
409 + self.decorator_list = list(decorator_list)
410 + self.returns = returns
411 + self.lineno = int(lineno)
412 + self.col_offset = int(col_offset)
413 +
414 +class ClassDef (stmt):
415 + _fields = ('name', 'bases', 'keywords', 'starargs', 'kwargs', 'body', 'decorator_list')
416 + _attributes = ('lineno', 'col_offset')
417 + def __init__ (self, name, bases=[], keywords=[], starargs=None, kwargs=None, body=[], decorator_list=[], lineno=0, col_offset=0, **ARGS):
418 + stmt.__init__(self, **ARGS)
419 + self.name = name
420 + self.bases = list(bases)
421 + self.keywords = list(keywords)
422 + self.starargs = starargs
423 + self.kwargs = kwargs
424 + self.body = list(body)
425 + self.decorator_list = list(decorator_list)
426 + self.lineno = int(lineno)
427 + self.col_offset = int(col_offset)
428 +
429 +class Return (stmt):
430 + _fields = ('value',)
431 + _attributes = ('lineno', 'col_offset')
432 + def __init__ (self, value=None, lineno=0, col_offset=0, **ARGS):
433 + stmt.__init__(self, **ARGS)
434 + self.value = value
435 + self.lineno = int(lineno)
436 + self.col_offset = int(col_offset)
437 +
438 +class Delete (stmt):
439 + _fields = ('targets',)
440 + _attributes = ('lineno', 'col_offset')
441 + def __init__ (self, targets=[], lineno=0, col_offset=0, **ARGS):
442 + stmt.__init__(self, **ARGS)
443 + self.targets = list(targets)
444 + self.lineno = int(lineno)
445 + self.col_offset = int(col_offset)
446 +
447 +class Assign (stmt):
448 + _fields = ('targets', 'value')
449 + _attributes = ('lineno', 'col_offset')
450 + def __init__ (self, value, targets=[], lineno=0, col_offset=0, **ARGS):
451 + stmt.__init__(self, **ARGS)
452 + self.targets = list(targets)
453 + self.value = value
454 + self.lineno = int(lineno)
455 + self.col_offset = int(col_offset)
456 +
457 +class AugAssign (stmt):
458 + _fields = ('target', 'op', 'value')
459 + _attributes = ('lineno', 'col_offset')
460 + def __init__ (self, target, op, value, lineno=0, col_offset=0, **ARGS):
461 + stmt.__init__(self, **ARGS)
462 + self.target = target
463 + self.op = op
464 + self.value = value
465 + self.lineno = int(lineno)
466 + self.col_offset = int(col_offset)
467 +
468 +class For (stmt):
469 + _fields = ('target', 'iter', 'body', 'orelse')
470 + _attributes = ('lineno', 'col_offset')
471 + def __init__ (self, target, iter, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
472 + stmt.__init__(self, **ARGS)
473 + self.target = target
474 + self.iter = iter
475 + self.body = list(body)
476 + self.orelse = list(orelse)
477 + self.lineno = int(lineno)
478 + self.col_offset = int(col_offset)
479 +
480 +class While (stmt):
481 + _fields = ('test', 'body', 'orelse')
482 + _attributes = ('lineno', 'col_offset')
483 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
484 + stmt.__init__(self, **ARGS)
485 + self.test = test
486 + self.body = list(body)
487 + self.orelse = list(orelse)
488 + self.lineno = int(lineno)
489 + self.col_offset = int(col_offset)
490 +
491 +class If (stmt):
492 + _fields = ('test', 'body', 'orelse')
493 + _attributes = ('lineno', 'col_offset')
494 + def __init__ (self, test, body=[], orelse=[], lineno=0, col_offset=0, **ARGS):
495 + stmt.__init__(self, **ARGS)
496 + self.test = test
497 + self.body = list(body)
498 + self.orelse = list(orelse)
499 + self.lineno = int(lineno)
500 + self.col_offset = int(col_offset)
501 +
502 +class With (stmt):
503 + _fields = ('context_expr', 'optional_vars', 'body')
504 + _attributes = ('lineno', 'col_offset')
505 + def __init__ (self, context_expr, optional_vars=None, body=[], lineno=0, col_offset=0, **ARGS):
506 + stmt.__init__(self, **ARGS)
507 + self.context_expr = context_expr
508 + self.optional_vars = optional_vars
509 + self.body = list(body)
510 + self.lineno = int(lineno)
511 + self.col_offset = int(col_offset)
512 +
513 +class Raise (stmt):
514 + _fields = ('exc', 'cause')
515 + _attributes = ('lineno', 'col_offset')
516 + def __init__ (self, exc=None, cause=None, lineno=0, col_offset=0, **ARGS):
517 + stmt.__init__(self, **ARGS)
518 + self.exc = exc
519 + self.cause = cause
520 + self.lineno = int(lineno)
521 + self.col_offset = int(col_offset)
522 +
523 +class TryExcept (stmt):
524 + _fields = ('body', 'handlers', 'orelse')
525 + _attributes = ('lineno', 'col_offset')
526 + def __init__ (self, body=[], handlers=[], orelse=[], lineno=0, col_offset=0, **ARGS):
527 + stmt.__init__(self, **ARGS)
528 + self.body = list(body)
529 + self.handlers = list(handlers)
530 + self.orelse = list(orelse)
531 + self.lineno = int(lineno)
532 + self.col_offset = int(col_offset)
533 +
534 +class TryFinally (stmt):
535 + _fields = ('body', 'finalbody')
536 + _attributes = ('lineno', 'col_offset')
537 + def __init__ (self, body=[], finalbody=[], lineno=0, col_offset=0, **ARGS):
538 + stmt.__init__(self, **ARGS)
539 + self.body = list(body)
540 + self.finalbody = list(finalbody)
541 + self.lineno = int(lineno)
542 + self.col_offset = int(col_offset)
543 +
544 +class Assert (stmt):
545 + _fields = ('test', 'msg')
546 + _attributes = ('lineno', 'col_offset')
547 + def __init__ (self, test, msg=None, lineno=0, col_offset=0, **ARGS):
548 + stmt.__init__(self, **ARGS)
549 + self.test = test
550 + self.msg = msg
551 + self.lineno = int(lineno)
552 + self.col_offset = int(col_offset)
553 +
554 +class Import (stmt):
555 + _fields = ('names',)
556 + _attributes = ('lineno', 'col_offset')
557 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
558 + stmt.__init__(self, **ARGS)
559 + self.names = list(names)
560 + self.lineno = int(lineno)
561 + self.col_offset = int(col_offset)
562 +
563 +class ImportFrom (stmt):
564 + _fields = ('module', 'names', 'level')
565 + _attributes = ('lineno', 'col_offset')
566 + def __init__ (self, module, names=[], level=None, lineno=0, col_offset=0, **ARGS):
567 + stmt.__init__(self, **ARGS)
568 + self.module = module
569 + self.names = list(names)
570 + self.level = level
571 + self.lineno = int(lineno)
572 + self.col_offset = int(col_offset)
573 +
574 +class Exec (stmt):
575 + _fields = ('body', 'globals', 'locals')
576 + _attributes = ('lineno', 'col_offset')
577 + def __init__ (self, body, globals=None, locals=None, lineno=0, col_offset=0, **ARGS):
578 + stmt.__init__(self, **ARGS)
579 + self.body = body
580 + self.globals = globals
581 + self.locals = locals
582 + self.lineno = int(lineno)
583 + self.col_offset = int(col_offset)
584 +
585 +class Global (stmt):
586 + _fields = ('names',)
587 + _attributes = ('lineno', 'col_offset')
588 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
589 + stmt.__init__(self, **ARGS)
590 + self.names = list(names)
591 + self.lineno = int(lineno)
592 + self.col_offset = int(col_offset)
593 +
594 +class Nonlocal (stmt):
595 + _fields = ('names',)
596 + _attributes = ('lineno', 'col_offset')
597 + def __init__ (self, names=[], lineno=0, col_offset=0, **ARGS):
598 + stmt.__init__(self, **ARGS)
599 + self.names = list(names)
600 + self.lineno = int(lineno)
601 + self.col_offset = int(col_offset)
602 +
603 +class Expr (stmt):
604 + _fields = ('value',)
605 + _attributes = ('lineno', 'col_offset')
606 + def __init__ (self, value, lineno=0, col_offset=0, **ARGS):
607 + stmt.__init__(self, **ARGS)
608 + self.value = value
609 + self.lineno = int(lineno)
610 + self.col_offset = int(col_offset)
611 +
612 +class Pass (stmt):
613 + _fields = ()
614 + _attributes = ('lineno', 'col_offset')
615 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
616 + stmt.__init__(self, **ARGS)
617 + self.lineno = int(lineno)
618 + self.col_offset = int(col_offset)
619 +
620 +class Break (stmt):
621 + _fields = ()
622 + _attributes = ('lineno', 'col_offset')
623 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
624 + stmt.__init__(self, **ARGS)
625 + self.lineno = int(lineno)
626 + self.col_offset = int(col_offset)
627 +
628 +class Continue (stmt):
629 + _fields = ()
630 + _attributes = ('lineno', 'col_offset')
631 + def __init__ (self, lineno=0, col_offset=0, **ARGS):
632 + stmt.__init__(self, **ARGS)
633 + self.lineno = int(lineno)
634 + self.col_offset = int(col_offset)
635 +
636 +class excepthandler (_AST):
637 + pass
638 +
639 +class ExceptHandler (excepthandler):
640 + _fields = ('type', 'name', 'body')
641 + _attributes = ('lineno', 'col_offset')
642 + def __init__ (self, type=None, name=None, body=[], lineno=0, col_offset=0, **ARGS):
643 + excepthandler.__init__(self, **ARGS)
644 + self.type = type
645 + self.name = name
646 + self.body = list(body)
647 + self.lineno = int(lineno)
648 + self.col_offset = int(col_offset)
649 +
650 +class alias (_AST):
651 + _fields = ('name', 'asname')
652 + _attributes = ()
653 + def __init__ (self, name, asname=None, **ARGS):
654 + _AST.__init__(self, **ARGS)
655 + self.name = name
656 + self.asname = asname
657 +
658 +class comprehension (_AST):
659 + _fields = ('target', 'iter', 'ifs')
660 + _attributes = ()
661 + def __init__ (self, target, iter, ifs=[], **ARGS):
662 + _AST.__init__(self, **ARGS)
663 + self.target = target
664 + self.iter = iter
665 + self.ifs = list(ifs)
666 +
667 +class arg (_AST):
668 + _fields = ('arg', 'annotation')
669 + _attributes = ()
670 + def __init__ (self, arg, annotation=None, **ARGS):
671 + _AST.__init__(self, **ARGS)
672 + self.arg = arg
673 + self.annotation = annotation
674 +
675 +class operator (_AST):
676 + pass
677 +
678 +class Add (operator):
679 + _fields = ()
680 + _attributes = ()
681 +
682 +class Sub (operator):
683 + _fields = ()
684 + _attributes = ()
685 +
686 +class Mult (operator):
687 + _fields = ()
688 + _attributes = ()
689 +
690 +class Div (operator):
691 + _fields = ()
692 + _attributes = ()
693 +
694 +class Mod (operator):
695 + _fields = ()
696 + _attributes = ()
697 +
698 +class Pow (operator):
699 + _fields = ()
700 + _attributes = ()
701 +
702 +class LShift (operator):
703 + _fields = ()
704 + _attributes = ()
705 +
706 +class RShift (operator):
707 + _fields = ()
708 + _attributes = ()
709 +
710 +class BitOr (operator):
711 + _fields = ()
712 + _attributes = ()
713 +
714 +class BitXor (operator):
715 + _fields = ()
716 + _attributes = ()
717 +
718 +class BitAnd (operator):
719 + _fields = ()
720 + _attributes = ()
721 +
722 +class FloorDiv (operator):
723 + _fields = ()
724 + _attributes = ()
725 +
726 +class mod (_AST):
727 + pass
728 +
729 +class Module (mod):
730 + _fields = ('body',)
731 + _attributes = ()
732 + def __init__ (self, body=[], **ARGS):
733 + mod.__init__(self, **ARGS)
734 + self.body = list(body)
735 +
736 +class Interactive (mod):
737 + _fields = ('body',)
738 + _attributes = ()
739 + def __init__ (self, body=[], **ARGS):
740 + mod.__init__(self, **ARGS)
741 + self.body = list(body)
742 +
743 +class Expression (mod):
744 + _fields = ('body',)
745 + _attributes = ()
746 + def __init__ (self, body, **ARGS):
747 + mod.__init__(self, **ARGS)
748 + self.body = body
749 +
750 +class Suite (mod):
751 + _fields = ('body',)
752 + _attributes = ()
753 + def __init__ (self, body=[], **ARGS):
754 + mod.__init__(self, **ARGS)
755 + self.body = list(body)
1 +"""
2 +>>> testparser(Translator)
3 +"""
4 +
5 +import doctest, traceback, sys, re, operator, inspect
6 +from snakes.lang.pgen import ParseError
7 +from snakes.lang.python.pgen import parser
8 +import snakes.lang.python.asdl as ast
9 +from snakes import *
10 +
11 +_symbols = parser.tokenizer.tok_name.copy()
12 +# next statement overrides 'NT_OFFSET' entry with 'single_input'
13 +# (this is desired)
14 +_symbols.update(parser.symbolMap)
15 +
16 +class ParseTree (list) :
17 + _symbols = _symbols
18 + def __init__ (self, st) :
19 + self.symbol = self._symbols[st[0][0]]
20 + self.kind = st[0][0]
21 + self.srow = st[0][1].srow
22 + self.erow = st[0][1].erow
23 + self.scol = st[0][1].scol
24 + self.ecol = st[0][1].ecol
25 + self.text = st[0][1]
26 + self.filename = self.text.filename
27 + list.__init__(self, (self.__class__(child) for child in st[1]))
28 + def __repr__ (self) :
29 + return repr(self.text)
30 + def involve (self, rule) :
31 + if self.kind == rule :
32 + return True
33 + for child in self :
34 + if child.involve(rule) :
35 + return True
36 + return False
37 + def source (self) :
38 + lines = self.text.lexer.lines[self.srow-1:self.erow]
39 + if self.srow == self.erow :
40 + lines[0] = lines[0][self.scol:self.ecol]
41 + else :
42 + lines[0] = lines[0][self.scol:]
43 + lines[-1] = lines[-1][:self.ecol]
44 + return str("\n".join(l.rstrip("\n") for l in lines))
45 +
46 +class Translator (object) :
47 + ParseTree = ParseTree
48 + parser = parser
49 + ST = ast
50 + def __init__ (self, st) :
51 + for value, name in self.parser.tokenizer.tok_name.items() :
52 + setattr(self, name, value)
53 + def isdict (obj) : return isinstance(obj, dict)
54 + for name, d in inspect.getmembers(self.__class__, isdict) :
55 + d = d.copy()
56 + for key, val in d.items() :
57 + try :
58 + d[key] = getattr(self.ST, val.__name__)
59 + except AttributeError :
60 + pass
61 + setattr(self, name, d)
62 + self.ast = self.do(self.ParseTree(st))
63 + def do (self, st, ctx=None) :
64 + if ctx is None :
65 + ctx = self.ST.Load
66 + name = st.symbol
67 + meth = getattr(self, "do_" + name)
68 + tree = meth(st, ctx)
69 + try :
70 + tree.st = st
71 + except AttributeError :
72 + pass
73 + return tree
74 + # unary operations
75 + _unary = {"not" : ast.Not,
76 + "+" : ast.UAdd,
77 + "-" : ast.USub,
78 + "~" : ast.Invert}
79 + def _do_unary (self, st, ctx) :
80 + """unary: not_test | factor
81 + """
82 + if len(st) == 1 :
83 + return self.do(st[0], ctx)
84 + else :
85 + return self.ST.UnaryOp(lineno=st.srow, col_offset=st.scol,
86 + op=self._unary[st[0].text](lineno=st[0].srow,
87 + col_offset=st[0].scol),
88 + operand=self.do(st[1], ctx))
89 + # binary operations
90 + _binary = {"+" : ast.Add,
91 + "-" : ast.Sub,
92 + "*" : ast.Mult,
93 + "/" : ast.Div,
94 + "%" : ast.Mod,
95 + "&" : ast.BitAnd,
96 + "|" : ast.BitOr,
97 + "^" : ast.BitXor,
98 + "<<" : ast.LShift,
99 + ">>" : ast.RShift,
100 + "**" : ast.Pow,
101 + "//" : ast.FloorDiv}
102 + def _do_binary (self, st, ctx) :
103 + """binary: expr | xor_expr | and_expr | shift_expr | arith_expr | term
104 + """
105 + if len(st) == 1 :
106 + return self.do(st[0], ctx)
107 + else :
108 + values = [self.do(child, ctx) for child in st[::2]]
109 + ops = [(self._binary[child.text], child) for child in st[1::2]]
110 + while len(values) > 1 :
111 + left = values.pop(0)
112 + right = values.pop(0)
113 + operator, node = ops.pop(0)
114 + values.insert(0, self.ST.BinOp(lineno=st.srow,
115 + col_offset=st.scol,
116 + left=left,
117 + op=operator(lineno=node.srow,
118 + col_offset=node.scol),
119 + right=right))
120 + return values[0]
121 + # boolean operation
122 + _boolean = {"and" : ast.And,
123 + "or" : ast.Or}
124 + def _do_boolean (self, st, ctx) :
125 + if len(st) == 1 :
126 + return self.do(st[0], ctx)
127 + else :
128 + return self.ST.BoolOp(lineno=st.srow, col_offset=st.scol,
129 + op=self._boolean[st[1].text](lineno=st[1].srow,
130 + col_offset=st[1].scol),
131 + values=[self.do(child, ctx)
132 + for child in st[::2]])
133 + # start of rule handlers
134 + def do_file_input (self, st, ctx) :
135 + """file_input: (NEWLINE | stmt)* ENDMARKER
136 + -> ast.Module
137 +
138 + <<< pass
139 + 'Module(body=[Pass()])'
140 + """
141 + body = reduce(operator.add,
142 + (self.do(child, ctx) for child in st
143 + if child.kind not in (self.NEWLINE, self.ENDMARKER)),
144 + [])
145 + return self.ST.Module(lineno=st.srow,
146 + col_offset=st.scol,
147 + body=body)
148 + def do_decorator (self, st, ctx) :
149 + """decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
150 + -> ast.AST
151 +
152 + <<< @foo
153 + ... def f() : pass
154 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Name(id='foo', ctx=Load())], returns=None)])"
155 + <<< @foo.bar
156 + ... def f() : pass
157 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Attribute(value=Name(id='foo', ctx=Load()), attr='bar', ctx=Load())], returns=None)])"
158 + <<< @foo()
159 + ... def f() : pass
160 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Call(func=Name(id='foo', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None)], returns=None)])"
161 + <<< @foo(x, y)
162 + ... def f() : pass
163 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Call(func=Name(id='foo', ctx=Load()), args=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())], keywords=[], starargs=None, kwargs=None)], returns=None)])"
164 + <<< @foo.bar(x, y)
165 + ... def f() : pass
166 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Call(func=Attribute(value=Name(id='foo', ctx=Load()), attr='bar', ctx=Load()), args=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())], keywords=[], starargs=None, kwargs=None)], returns=None)])"
167 + """
168 + names = self.do(st[1], ctx).split(".")
169 + obj = self.ST.Name(lineno=st[1].srow, col_offset=st[1].scol,
170 + id=names.pop(0), ctx=ctx())
171 + while names :
172 + obj = self.ST.Attribute(lineno=st[1].srow,
173 + col_offset=st[1].scol,
174 + value=obj, attr=names.pop(0), ctx=ctx())
175 + if len(st) == 3 :
176 + return obj
177 + elif len(st) == 5 :
178 + return self.ST.Call(lineno=st[1].srow, col_offset=st[1].scol,
179 + func=obj, args=[], keywords=[],
180 + starargs=None, kwargs=None)
181 + else :
182 + args, keywords, starargs, kwargs = self.do(st[3], ctx)
183 + return self.ST.Call(lineno=st[1].srow, col_offset=st[1].scol,
184 + func=obj, args=args, keywords=keywords,
185 + starargs=starargs, kwargs=kwargs)
186 + def do_decorators (self, st, ctx) :
187 + """decorators: decorator+
188 + -> ast.AST+
189 +
190 + <<< @foo
191 + ... @bar
192 + ... @spam.egg()
193 + ... def f () :
194 + ... pass
195 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Name(id='foo', ctx=Load()), Name(id='bar', ctx=Load()), Call(func=Attribute(value=Name(id='spam', ctx=Load()), attr='egg', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None)], returns=None)])"
196 + """
197 + return [self.do(child, ctx) for child in st]
198 + def do_decorated (self, st, ctx) :
199 + """decorated: decorators (classdef | funcdef)
200 + -> ast.AST
201 +
202 + <<< @foo
203 + ... def f () :
204 + ... pass
205 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Name(id='foo', ctx=Load())], returns=None)])"
206 + <<< @foo
207 + ... class c () :
208 + ... pass
209 + "Module(body=[ClassDef(name='c', bases=[], keywords=[], starargs=None, kwargs=None, body=[Pass()], decorator_list=[Name(id='foo', ctx=Load())])])"
210 + """
211 + child = self.do(st[1], ctx)
212 + child.decorator_list.extend(self.do(st[0], ctx))
213 + return child
214 + def do_funcdef (self, st, ctx) :
215 + """funcdef: 'def' NAME parameters ['->' test] ':' suite
216 + -> ast.FunctionDef
217 +
218 + <<< def f(x, y) : x+y
219 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Expr(value=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())))], decorator_list=[], returns=None)])"
220 + <<< def f(x, y) -> int : x+y
221 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Expr(value=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())))], decorator_list=[], returns=Name(id='int', ctx=Load()))])"
222 + """
223 + if len(st) == 5 :
224 + return self.ST.FunctionDef(lineno=st.srow, col_offset=st.scol,
225 + name=st[1].text,
226 + args=self.do(st[2], ctx),
227 + returns=None,
228 + body=self.do(st[-1], ctx),
229 + decorator_list=[])
230 + else :
231 + return self.ST.FunctionDef(lineno=st.srow, col_offset=st.scol,
232 + name=st[1].text,
233 + args=self.do(st[2], ctx),
234 + returns=self.do(st[5], ctx),
235 + body=self.do(st[-1], ctx),
236 + decorator_list=[])
237 + def do_parameters (self, st, ctx) :
238 + """parameters: '(' [typedargslist] ')'
239 + -> ast.arguments
240 +
241 + <<< def f () : pass
242 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
243 + <<< def f(x, y) : pass
244 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
245 + """
246 + if len(st) == 2 :
247 + return self.ST.arguments(lineno=st.srow, col_offset=st.scol,
248 + args=[], vararg=None,
249 + varargannotation=None,
250 + kwonlyargs=[], kwarg=None,
251 + kwargannotation=None,
252 + defaults=[], kw_defaults=[])
253 + else :
254 + return self.do(st[1], ctx)
255 + def do_typedargslist (self, st, ctx) :
256 + """typedargslist: ((tfpdef ['=' test] ',')*
257 + ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef]
258 + | '**' tfpdef)
259 + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
260 + -> ast.arguments
261 +
262 + <<< def f(x) : pass
263 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
264 + <<< def f(x, *y, z) : pass
265 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None)], vararg='y', varargannotation=None, kwonlyargs=[arg(arg='z', annotation=None)], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[None]), body=[Pass()], decorator_list=[], returns=None)])"
266 + <<< def f(*, x=1) : pass
267 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[arg(arg='x', annotation=None)], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[Num(n=1)]), body=[Pass()], decorator_list=[], returns=None)])"
268 + <<< def f(x, y, *z, a=1, b=2, **d) : pass
269 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg='z', varargannotation=None, kwonlyargs=[arg(arg='a', annotation=None), arg(arg='b', annotation=None)], kwarg='d', kwargannotation=None, defaults=[], kw_defaults=[Num(n=1), Num(n=2)]), body=[Pass()], decorator_list=[], returns=None)])"
270 + <<< def f(x:int) : pass
271 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=Name(id='int', ctx=Load()))], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
272 + <<< def f(x:int, *y:float, z:bool) : pass
273 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=Name(id='int', ctx=Load()))], vararg='y', varargannotation=Name(id='float', ctx=Load()), kwonlyargs=[arg(arg='z', annotation=Name(id='bool', ctx=Load()))], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[None]), body=[Pass()], decorator_list=[], returns=None)])"
274 + <<< def f(*, x:int=1) : pass
275 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[arg(arg='x', annotation=Name(id='int', ctx=Load()))], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[Num(n=1)]), body=[Pass()], decorator_list=[], returns=None)])"
276 + <<< def f(x:int, y, *z:float, a=1, b:bool=False, **d:object) : pass
277 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=Name(id='int', ctx=Load())), arg(arg='y', annotation=None)], vararg='z', varargannotation=Name(id='float', ctx=Load()), kwonlyargs=[arg(arg='a', annotation=None), arg(arg='b', annotation=Name(id='bool', ctx=Load()))], kwarg='d', kwargannotation=Name(id='object', ctx=Load()), defaults=[], kw_defaults=[Num(n=1), Name(id='False', ctx=Load())]), body=[Pass()], decorator_list=[], returns=None)])"
278 + """
279 + args = []
280 + vararg = None
281 + varargannotation = None
282 + star = False
283 + kwonlyargs = []
284 + kwarg = None
285 + kwargannotation = None
286 + defaults = []
287 + kw_defaults = []
288 + nodes = list(st)
289 + while nodes :
290 + first = nodes.pop(0)
291 + if first.text == "," :
292 + pass
293 + elif first.text == "*" :
294 + star = True
295 + if nodes and nodes[0].text != "," :
296 + vararg, varargannotation = self.do(nodes.pop(0), ctx)
297 + elif first.text == "**" :
298 + kwarg, kwargannotation = self.do(nodes.pop(0), ctx)
299 + else :
300 + n, a = self.do(first, ctx)
301 + arg = self.ST.arg(lineno=first.srow, col_offset=first.scol,
302 + arg=n, annotation=a)
303 + if nodes and nodes[0].text == "=" :
304 + del nodes[0]
305 + d = self.do(nodes.pop(0), ctx)
306 + else :
307 + d = None
308 + if star :
309 + kwonlyargs.append(arg)
310 + kw_defaults.append(d)
311 + else :
312 + args.append(arg)
313 + if d is not None :
314 + defaults.append(d)
315 + return self.ST.arguments(lineno=st.srow, col_offset=st.scol,
316 + args=args,
317 + vararg=vararg,
318 + varargannotation=varargannotation,
319 + kwonlyargs=kwonlyargs,
320 + kwarg=kwarg,
321 + kwargannotation=kwargannotation,
322 + defaults=defaults,
323 + kw_defaults=kw_defaults)
324 + def do_tfpdef (self, st, ctx) :
325 + """tfpdef: NAME [':' test]
326 + -> str, ast.AST?
327 +
328 + <<< def f(x:int) : pass
329 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=Name(id='int', ctx=Load()))], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
330 + <<< def f(x:int, y:float) : pass
331 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=Name(id='int', ctx=Load())), arg(arg='y', annotation=Name(id='float', ctx=Load()))], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
332 + <<< def f(x, y:int) : pass
333 + "Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=Name(id='int', ctx=Load()))], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
334 + """
335 + if len(st) == 1 :
336 + return st[0].text, None
337 + else :
338 + return st[0].text, self.do(st[2], ctx)
339 + def do_varargslist (self, st, ctx) :
340 + """varargslist: ((vfpdef ['=' test] ',')*
341 + ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef]
342 + | '**' vfpdef)
343 + | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
344 + -> ast.arguments
345 +
346 + <<< lambda x, y : x+y
347 + "Module(body=[Expr(value=Lambda(args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load()))))])"
348 + <<< lambda x, y, z=1 : x+y+z
349 + "Module(body=[Expr(value=Lambda(args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None), arg(arg='z', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[Num(n=1)], kw_defaults=[]), body=BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), op=Add(), right=Name(id='z', ctx=Load()))))])"
350 + """
351 + tree = self.do_typedargslist(st, ctx)
352 + tree.st = st
353 + return tree
354 + def do_vfpdef (self, st, ctx) :
355 + """vfpdef: NAME
356 + -> str, None
357 +
358 + Return value (str, None) is choosen for compatibility with
359 + do_tfpdef, so that do_typedargslist can be used for
360 + do_varargslist.
361 +
362 + <<< lambda x, y : x+y
363 + "Module(body=[Expr(value=Lambda(args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load()))))])"
364 + """
365 + return st[0].text, None
366 + def do_stmt (self, st, ctx) :
367 + """stmt: simple_stmt | compound_stmt
368 + -> ast.AST+
369 +
370 + <<< pass
371 + 'Module(body=[Pass()])'
372 + <<< pass; pass
373 + 'Module(body=[Pass(), Pass()])'
374 + <<< with x : y
375 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Expr(value=Name(id='y', ctx=Load()))])])"
376 + """
377 + child = self.do(st[0], ctx)
378 + if isinstance(child, self.ST.AST) :
379 + return [child]
380 + else :
381 + return child
382 + def do_simple_stmt (self, st, ctx) :
383 + """simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
384 + -> ast.AST+
385 +
386 + <<< del x; pass; import spam as egg
387 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del())]), Pass(), Import(names=[alias(name='spam', asname='egg')])])"
388 + """
389 + return [self.do(child, ctx) for child in st[::2]
390 + if child.kind != self.NEWLINE]
391 + def do_small_stmt (self, st, ctx) :
392 + """small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
393 + import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
394 + -> ast.AST
395 +
396 + <<< x
397 + "Module(body=[Expr(value=Name(id='x', ctx=Load()))])"
398 + <<< del x
399 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del())])])"
400 + <<< pass
401 + 'Module(body=[Pass()])'
402 + <<< import egg as spam
403 + "Module(body=[Import(names=[alias(name='egg', asname='spam')])])"
404 + <<< global x
405 + "Module(body=[Global(names=['x'])])"
406 + <<< nonlocal x
407 + "Module(body=[Nonlocal(names=['x'])])"
408 + <<< assert x
409 + "Module(body=[Assert(test=Name(id='x', ctx=Load()), msg=None)])"
410 + """
411 + return self.do(st[0], ctx)
412 + def do_expr_stmt (self, st, ctx) :
413 + """expr_stmt: testlist (augassign (yield_expr|testlist) |
414 + ('=' (yield_expr|testlist))*)
415 + -> ast.Expr
416 +
417 + <<< x = y = 1
418 + "Module(body=[Assign(targets=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], value=Num(n=1))])"
419 + <<< x, y = z = 1, 2
420 + "Module(body=[Assign(targets=[Tuple(elts=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], ctx=Store()), Name(id='z', ctx=Store())], value=Tuple(elts=[Num(n=1), Num(n=2)], ctx=Load()))])"
421 + <<< x += 1
422 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Add(), value=Num(n=1))])"
423 + <<< x += yield 5
424 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Add(), value=Yield(value=Num(n=5)))])"
425 + <<< x = y = yield 1, 2
426 + "Module(body=[Assign(targets=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], value=Yield(value=Tuple(elts=[Num(n=1), Num(n=2)], ctx=Load())))])"
427 + """
428 + if len(st) == 1 :
429 + return self.ST.Expr(lineno=st.srow, col_offset=st.scol,
430 + value=self.do(st[0], ctx))
431 + elif st[1].symbol == "augassign" :
432 + target = self.do(st[0], self.ST.Store)
433 + if isinstance(target, self.ST.Tuple) :
434 + raise ParseError(st[0].text, reason="illegal expression for"
435 + " augmented assignment")
436 + return self.ST.AugAssign(lineno=st.srow, col_offset=st.scol,
437 + target=target,
438 + op=self.do(st[1], ctx),
439 + value=self.do(st[2], ctx))
440 + else :
441 + return self.ST.Assign(lineno=st.srow, col_offset=st.scol,
442 + targets=[self.do(child, ast.Store)
443 + for child in st[:-1:2]],
444 + value=self.do(st[-1], ctx))
445 + def do_augassign (self, st, ctx) :
446 + """augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
447 + '<<=' | '>>=' | '**=' | '//=')
448 + -> ast.AST
449 +
450 + <<< x += 1
451 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Add(), value=Num(n=1))])"
452 + <<< x -= 1
453 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Sub(), value=Num(n=1))])"
454 + <<< x *= 1
455 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Mult(), value=Num(n=1))])"
456 + <<< x /= 1
457 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Div(), value=Num(n=1))])"
458 + <<< x %= 1
459 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Mod(), value=Num(n=1))])"
460 + <<< x &= 1
461 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=BitAnd(), value=Num(n=1))])"
462 + <<< x |= 1
463 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=BitOr(), value=Num(n=1))])"
464 + <<< x ^= 1
465 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=BitXor(), value=Num(n=1))])"
466 + <<< x <<= 1
467 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=LShift(), value=Num(n=1))])"
468 + <<< x >>= 1
469 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=RShift(), value=Num(n=1))])"
470 + <<< x **= 1
471 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=Pow(), value=Num(n=1))])"
472 + <<< x //= 1
473 + "Module(body=[AugAssign(target=Name(id='x', ctx=Store()), op=FloorDiv(), value=Num(n=1))])"
474 + """
475 + return self._binary[st[0].text[:-1]](lineno=st.srow, col_offset=st.scol)
476 + def do_del_stmt (self, st, ctx) :
477 + """del_stmt: 'del' exprlist
478 + -> ast.Delete
479 +
480 + <<< del x
481 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del())])])"
482 + <<< del x, y
483 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del()), Name(id='y', ctx=Del())])])"
484 + """
485 + targets = self.do(st[1], ctx=self.ST.Del)
486 + if isinstance(targets, self.ST.Tuple) :
487 + targets = targets.elts
488 + else :
489 + targets = [targets]
490 + return self.ST.Delete(lineno=st.srow, col_offset=st.scol,
491 + targets=targets)
492 + def do_pass_stmt (self, st, ctx) :
493 + """pass_stmt: 'pass'
494 + -> ast.Pass
495 +
496 + <<< pass
497 + 'Module(body=[Pass()])'
498 + """
499 + return self.ST.Pass(lineno=st.srow, col_offset=st.scol)
500 + def do_flow_stmt (self, st, ctx) :
501 + """flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt
502 + | yield_stmt
503 + -> ast.AST
504 +
505 + <<< break
506 + 'Module(body=[Break()])'
507 + <<< continue
508 + 'Module(body=[Continue()])'
509 + <<< return
510 + 'Module(body=[Return(value=None)])'
511 + <<< raise
512 + 'Module(body=[Raise(exc=None, cause=None)])'
513 + <<< yield
514 + 'Module(body=[Expr(value=Yield(value=None))])'
515 + """
516 + return self.do(st[0], ctx)
517 + def do_break_stmt (self, st, ctx) :
518 + """break_stmt: 'break'
519 + -> ast.Break()
520 +
521 + <<< break
522 + 'Module(body=[Break()])'
523 + """
524 + return self.ST.Break(lineno=st.srow, col_offset=st.scol)
525 + def do_continue_stmt (self, st, ctx) :
526 + """continue_stmt: 'continue'
527 + -> ast.Continue
528 +
529 + <<< continue
530 + 'Module(body=[Continue()])'
531 + """
532 + return self.ST.Continue(lineno=st.srow, col_offset=st.scol)
533 + def do_return_stmt (self, st, ctx) :
534 + """return_stmt: 'return' [testlist]
535 + -> ast.Return
536 +
537 + <<< return
538 + 'Module(body=[Return(value=None)])'
539 + <<< return 1
540 + 'Module(body=[Return(value=Num(n=1))])'
541 + <<< return 1, 2
542 + 'Module(body=[Return(value=Tuple(elts=[Num(n=1), Num(n=2)], ctx=Load()))])'
543 + """
544 + if len(st) == 1 :
545 + return self.ST.Return(lineno=st.srow, col_offset=st.scol,
546 + value=None)
547 +
548 + else :
549 + return self.ST.Return(lineno=st.srow, col_offset=st.scol,
550 + value=self.do(st[1], ctx))
551 + def do_yield_stmt (self, st, ctx) :
552 + """yield_stmt: yield_expr
553 + -> ast.Expr
554 +
555 + <<< yield
556 + 'Module(body=[Expr(value=Yield(value=None))])'
557 + <<< yield 42
558 + 'Module(body=[Expr(value=Yield(value=Num(n=42)))])'
559 + """
560 + return self.ST.Expr(lineno=st.srow, col_offset=st.scol,
561 + value=self.do(st[0], ctx))
562 + def do_raise_stmt (self, st, ctx) :
563 + """raise_stmt: 'raise' [test ['from' test]]
564 + -> ast.Raise
565 +
566 + <<< raise
567 + 'Module(body=[Raise(exc=None, cause=None)])'
568 + <<< raise Exception
569 + "Module(body=[Raise(exc=Name(id='Exception', ctx=Load()), cause=None)])"
570 + <<< raise Exception from Exception
571 + "Module(body=[Raise(exc=Name(id='Exception', ctx=Load()), cause=Name(id='Exception', ctx=Load()))])"
572 + """
573 + count = len(st)
574 + if count == 1 :
575 + return self.ST.Raise(lineno=st.srow, col_offset=st.scol,
576 + exc=None, cause=None)
577 + elif count == 2 :
578 + return self.ST.Raise(lineno=st.srow, col_offset=st.scol,
579 + exc=self.do(st[1], ctx),
580 + cause=None)
581 + else :
582 + return self.ST.Raise(lineno=st.srow, col_offset=st.scol,
583 + exc=self.do(st[1], ctx),
584 + cause=self.do(st[3], ctx))
585 + def do_import_stmt (self, st, ctx) :
586 + """import_stmt: import_name | import_from
587 + -> ast.AST
588 +
589 + <<< import foo
590 + "Module(body=[Import(names=[alias(name='foo', asname=None)])])"
591 + <<< import foo.bar
592 + "Module(body=[Import(names=[alias(name='foo.bar', asname=None)])])"
593 + <<< import foo as bar
594 + "Module(body=[Import(names=[alias(name='foo', asname='bar')])])"
595 + <<< import foo.bar as egg
596 + "Module(body=[Import(names=[alias(name='foo.bar', asname='egg')])])"
597 + <<< import foo as bar, egg as spam
598 + "Module(body=[Import(names=[alias(name='foo', asname='bar'), alias(name='egg', asname='spam')])])"
599 + <<< import foo, bar, egg as spam
600 + "Module(body=[Import(names=[alias(name='foo', asname=None), alias(name='bar', asname=None), alias(name='egg', asname='spam')])])"
601 + """
602 + return self.do(st[0], ctx)
603 + def do_import_name (self, st, ctx) :
604 + """import_name: 'import' dotted_as_names
605 + -> ast.Import
606 +
607 + <<< import foo
608 + "Module(body=[Import(names=[alias(name='foo', asname=None)])])"
609 + <<< import foo.bar
610 + "Module(body=[Import(names=[alias(name='foo.bar', asname=None)])])"
611 + <<< import foo as bar
612 + "Module(body=[Import(names=[alias(name='foo', asname='bar')])])"
613 + <<< import foo.bar as egg
614 + "Module(body=[Import(names=[alias(name='foo.bar', asname='egg')])])"
615 + <<< import foo as bar, egg as spam
616 + "Module(body=[Import(names=[alias(name='foo', asname='bar'), alias(name='egg', asname='spam')])])"
617 + <<< import foo, bar, egg as spam
618 + "Module(body=[Import(names=[alias(name='foo', asname=None), alias(name='bar', asname=None), alias(name='egg', asname='spam')])])"
619 + """
620 + return self.ST.Import(lineno=st.srow, col_offset=st.scol,
621 + names=self.do(st[1], ctx))
622 + def do_import_from (self, st, ctx) :
623 + """import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
624 + 'import' ('*' | '(' import_as_names ')' | import_as_names))
625 + -> ast.ImportFrom
626 +
627 + <<< from foo import egg as spam
628 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam')], level=0)])"
629 + <<< from .foo import egg as spam
630 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam')], level=1)])"
631 + <<< from ...foo import egg as spam
632 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam')], level=3)])"
633 + <<< from ...foo.bar import egg as spam
634 + "Module(body=[ImportFrom(module='foo.bar', names=[alias(name='egg', asname='spam')], level=3)])"
635 + <<< from foo import egg as spam, bar as baz
636 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam'), alias(name='bar', asname='baz')], level=0)])"
637 + <<< from .foo import egg as spam, bar as baz
638 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam'), alias(name='bar', asname='baz')], level=1)])"
639 + <<< from ...foo import egg as spam, bar as baz
640 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam'), alias(name='bar', asname='baz')], level=3)])"
641 + <<< from ...foo.bar import egg as spam, baz
642 + "Module(body=[ImportFrom(module='foo.bar', names=[alias(name='egg', asname='spam'), alias(name='baz', asname=None)], level=3)])"
643 + <<< from foo import *
644 + "Module(body=[ImportFrom(module='foo', names=[alias(name='*', asname=None)], level=0)])"
645 + <<< from .foo import *
646 + "Module(body=[ImportFrom(module='foo', names=[alias(name='*', asname=None)], level=1)])"
647 + <<< from ...foo import *
648 + "Module(body=[ImportFrom(module='foo', names=[alias(name='*', asname=None)], level=3)])"
649 + <<< from ...foo.bar import *
650 + "Module(body=[ImportFrom(module='foo.bar', names=[alias(name='*', asname=None)], level=3)])"
651 + """
652 + level = 0
653 + next = 1
654 + for i, child in enumerate(st[1:]) :
655 + text = child.text
656 + if text not in (".", "...") :
657 + next = i+1
658 + break
659 + level += len(text)
660 + if text == "import" :
661 + module = ""
662 + next += 1
663 + else :
664 + module = self.do(st[next], ctx)
665 + next += 2
666 + text = st[next].text
667 + if text == "*" :
668 + names = [self.ST.alias(lineno=st[next].srow,
669 + col_offset=st[next].scol,
670 + name="*", asname=None)]
671 + elif text == "(" :
672 + names = self.do(st[next+1], ctx)
673 + else :
674 + names = self.do(st[next], ctx)
675 + return self.ST.ImportFrom(lineno=st.srow, col_offset=st.scol,
676 + module=module, names=names, level=level)
677 + def do_import_as_name (self, st, ctx) :
678 + """import_as_name: NAME ['as' NAME]
679 + -> ast.alias
680 +
681 + <<< from foo import egg as spam
682 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam')], level=0)])"
683 + """
684 + if len(st) == 1 :
685 + return self.ST.alias(lineno=st.srow, col_offset=st.scol,
686 + name=st[0].text,
687 + asname=None)
688 + else :
689 + return self.ST.alias(lineno=st.srow, col_offset=st.scol,
690 + name=st[0].text,
691 + asname=st[2].text)
692 + def do_dotted_as_name (self, st, ctx) :
693 + """dotted_as_name: dotted_name ['as' NAME]
694 + -> ast.alias
695 +
696 + <<< import foo.bar as egg
697 + "Module(body=[Import(names=[alias(name='foo.bar', asname='egg')])])"
698 + """
699 + if len(st) == 1 :
700 + return self.ST.alias(lineno=st.srow, col_offset=st.scol,
701 + name=self.do(st[0], ctx),
702 + asname=None)
703 + else :
704 + return self.ST.alias(lineno=st.srow, col_offset=st.scol,
705 + name=self.do(st[0], ctx),
706 + asname=st[2].text)
707 + def do_import_as_names (self, st, ctx) :
708 + """import_as_names: import_as_name (',' import_as_name)* [',']
709 + -> ast.alias+
710 +
711 + <<< from foo import egg as spam
712 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam')], level=0)])"
713 + <<< from foo import egg as spam, bar as baz
714 + "Module(body=[ImportFrom(module='foo', names=[alias(name='egg', asname='spam'), alias(name='bar', asname='baz')], level=0)])"
715 + """
716 + return [self.do(child, ctx) for child in st[::2]]
717 + def do_dotted_as_names (self, st, ctx) :
718 + """dotted_as_names: dotted_as_name (',' dotted_as_name)*
719 + -> ast.alias+
720 +
721 + <<< import foo.bar, egg.spam as baz
722 + "Module(body=[Import(names=[alias(name='foo.bar', asname=None), alias(name='egg.spam', asname='baz')])])"
723 + """
724 + return [self.do(child, ctx) for child in st[::2]]
725 + def do_dotted_name (self, st, ctx) :
726 + """dotted_name: NAME ('.' NAME)*
727 + -> str
728 +
729 + <<< import foo.bar
730 + "Module(body=[Import(names=[alias(name='foo.bar', asname=None)])])"
731 + """
732 + return ".".join(child.text for child in st[::2])
733 + def do_global_stmt (self, st, ctx) :
734 + """global_stmt: 'global' NAME (',' NAME)*
735 + -> ast.Global
736 +
737 + <<< global x
738 + "Module(body=[Global(names=['x'])])"
739 + <<< global x, y
740 + "Module(body=[Global(names=['x', 'y'])])"
741 + """
742 + return self.ST.Global(lineno=st.srow, col_offset=st.scol,
743 + names=[child.text for child in st[1::2]])
744 + def do_nonlocal_stmt (self, st, ctx) :
745 + """nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
746 + -> ast.Nonlocal
747 +
748 + <<< nonlocal x
749 + "Module(body=[Nonlocal(names=['x'])])"
750 + <<< nonlocal x, y
751 + "Module(body=[Nonlocal(names=['x', 'y'])])"
752 + """
753 + return self.ST.Nonlocal(lineno=st.srow, col_offset=st.scol,
754 + names=[child.text for child in st[1::2]])
755 + def do_assert_stmt (self, st, ctx) :
756 + """assert_stmt: 'assert' test [',' test]
757 + -> ast.Assert
758 +
759 + <<< assert x
760 + "Module(body=[Assert(test=Name(id='x', ctx=Load()), msg=None)])"
761 + <<< assert x, y
762 + "Module(body=[Assert(test=Name(id='x', ctx=Load()), msg=Name(id='y', ctx=Load()))])"
763 + """
764 + if len(st) == 2 :
765 + return self.ST.Assert(lineno=st.srow, col_offset=st.scol,
766 + test=self.do(st[1], ctx),
767 + msg=None)
768 + else :
769 + return self.ST.Assert(lineno=st.srow, col_offset=st.scol,
770 + test=self.do(st[1], ctx),
771 + msg=self.do(st[3], ctx))
772 + def do_compound_stmt (self, st, ctx) :
773 + """compound_stmt: (if_stmt | while_stmt | for_stmt | try_stmt
774 + | with_stmt | funcdef | classdef | decorated)
775 + -> ast.AST
776 +
777 + <<< with x : pass
778 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass()])])"
779 + <<< if x : pass
780 + "Module(body=[If(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[])])"
781 + <<< while x : pass
782 + "Module(body=[While(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[])])"
783 + <<< for x in l : pass
784 + "Module(body=[For(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), body=[Pass()], orelse=[])])"
785 + <<< try : pass
786 + ... except : pass
787 + 'Module(body=[TryExcept(body=[Pass()], handlers=[ExceptHandler(type=None, name=None, body=[Pass()])], orelse=[])])'
788 + <<< def f () : pass
789 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[], returns=None)])"
790 + <<< class c () : pass
791 + "Module(body=[ClassDef(name='c', bases=[], keywords=[], starargs=None, kwargs=None, body=[Pass()], decorator_list=[])])"
792 + <<< @foo
793 + ... def f () : pass
794 + "Module(body=[FunctionDef(name='f', args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=[Pass()], decorator_list=[Name(id='foo', ctx=Load())], returns=None)])"
795 + """
796 + return self.do(st[0], ctx)
797 + def do_if_stmt (self, st, ctx) :
798 + """if_stmt: 'if' test ':' suite ('elif' test ':' suite)*
799 + ['else' ':' suite]
800 + -> ast.If
801 +
802 + <<< if x == 1 : pass
803 + "Module(body=[If(test=Compare(left=Name(id='x', ctx=Load()), ops=[Eq()], comparators=[Num(n=1)]), body=[Pass()], orelse=[])])"
804 + <<< if x == 1 : pass
805 + ... else : pass
806 + "Module(body=[If(test=Compare(left=Name(id='x', ctx=Load()), ops=[Eq()], comparators=[Num(n=1)]), body=[Pass()], orelse=[Pass()])])"
807 + <<< if x == 1 : pass
808 + ... elif y == 2 : pass
809 + ... elif z == 3 : pass
810 + ... else : pass
811 + "Module(body=[If(test=Compare(left=Name(id='x', ctx=Load()), ops=[Eq()], comparators=[Num(n=1)]), body=[Pass()], orelse=[If(test=Compare(left=Name(id='y', ctx=Load()), ops=[Eq()], comparators=[Num(n=2)]), body=[Pass()], orelse=[If(test=Compare(left=Name(id='z', ctx=Load()), ops=[Eq()], comparators=[Num(n=3)]), body=[Pass()], orelse=[Pass()])])])])"
812 +
813 + """
814 + nodes = list(st)
815 + first = None
816 + last = None
817 + while nodes :
818 + if len(nodes) == 3 :
819 + last.orelse.extend(self.do(nodes[2], ctx))
820 + del nodes[:3]
821 + else :
822 + next = self.ST.If(lineno=nodes[0].srow,
823 + col_offset=nodes[0].scol,
824 + test=self.do(nodes[1], ctx),
825 + body=self.do(nodes[3], ctx),
826 + orelse=[])
827 + if first is None :
828 + first = next
829 + if last is not None :
830 + last.orelse.append(next)
831 + last = next
832 + del nodes[:4]
833 + return first
834 + def do_while_stmt (self, st, ctx) :
835 + """while_stmt: 'while' test ':' suite ['else' ':' suite]
836 + -> ast.While
837 +
838 + <<< while x : pass
839 + "Module(body=[While(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[])])"
840 + <<< while x :
841 + ... pass
842 + ... pass
843 + "Module(body=[While(test=Name(id='x', ctx=Load()), body=[Pass(), Pass()], orelse=[])])"
844 + <<< while x :
845 + ... pass
846 + ... else :
847 + ... pass
848 + "Module(body=[While(test=Name(id='x', ctx=Load()), body=[Pass()], orelse=[Pass()])])"
849 + """
850 + if len(st) == 4 :
851 + return self.ST.While(lineno=st.srow, col_offset=st.scol,
852 + test=self.do(st[1], ctx),
853 + body=self.do(st[3], ctx),
854 + orelse=[])
855 + else :
856 + return self.ST.While(lineno=st.srow, col_offset=st.scol,
857 + test=self.do(st[1], ctx),
858 + body=self.do(st[3], ctx),
859 + orelse=self.do(st[6], ctx))
860 + def do_for_stmt (self, st, ctx) :
861 + """for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
862 + -> ast.For
863 +
864 + <<< for x in l : pass
865 + "Module(body=[For(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), body=[Pass()], orelse=[])])"
866 + <<< for x in l :
867 + ... pass
868 + ... pass
869 + "Module(body=[For(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), body=[Pass(), Pass()], orelse=[])])"
870 + <<< for x in l :
871 + ... pass
872 + ... else :
873 + ... pass
874 + "Module(body=[For(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), body=[Pass()], orelse=[Pass()])])"
875 + <<< for x, y in l : pass
876 + "Module(body=[For(target=Tuple(elts=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], ctx=Store()), iter=Name(id='l', ctx=Load()), body=[Pass()], orelse=[])])"
877 + <<< for x in a, b : pass
878 + "Module(body=[For(target=Name(id='x', ctx=Store()), iter=Tuple(elts=[Name(id='a', ctx=Load()), Name(id='b', ctx=Load())], ctx=Load()), body=[Pass()], orelse=[])])"
879 + """
880 + if len(st) == 6 :
881 + return self.ST.For(lineno=st.srow, col_offset=st.scol,
882 + target=self.do(st[1], ast.Store),
883 + iter=self.do(st[3], ctx),
884 + body=self.do(st[5], ctx),
885 + orelse=[])
886 + else :
887 + return self.ST.For(lineno=st.srow, col_offset=st.scol,
888 + target=self.do(st[1], ast.Store),
889 + iter=self.do(st[3], ctx),
890 + body=self.do(st[5], ctx),
891 + orelse=self.do(st[8], ctx))
892 + def do_try_stmt (self, st, ctx) :
893 + """try_stmt: ('try' ':' suite
894 + ((except_clause ':' suite)+
895 + ['else' ':' suite]
896 + ['finally' ':' suite] |
897 + 'finally' ':' suite))
898 + -> ast.TryExcept | ast.TryFinally
899 +
900 + <<< try : pass
901 + ... except : pass
902 + 'Module(body=[TryExcept(body=[Pass()], handlers=[ExceptHandler(type=None, name=None, body=[Pass()])], orelse=[])])'
903 + <<< try : pass
904 + ... except : pass
905 + ... else : pass
906 + ... finally : pass
907 + 'Module(body=[TryFinally(body=[TryExcept(body=[Pass()], handlers=[ExceptHandler(type=None, name=None, body=[Pass()])], orelse=[Pass()])], finalbody=[Pass()])])'
908 + <<< try : pass
909 + ... except TypeError : pass
910 + ... except ValueError : pass
911 + ... except : pass
912 + "Module(body=[TryExcept(body=[Pass()], handlers=[ExceptHandler(type=Name(id='TypeError', ctx=Load()), name=None, body=[Pass()]), ExceptHandler(type=Name(id='ValueError', ctx=Load()), name=None, body=[Pass()]), ExceptHandler(type=None, name=None, body=[Pass()])], orelse=[])])"
913 + """
914 + handlers = []
915 + finalbody = None
916 + orelse = []
917 + nodes = st[3:]
918 + while nodes :
919 + if nodes[0].text == "else" :
920 + orelse.extend(self.do(nodes[2], ctx))
921 + elif nodes[0].text == "finally" :
922 + finalbody = self.do(nodes[2], ctx)
923 + else :
924 + t, n = self.do(nodes[0], ctx)
925 + handlers.append(self.ST.ExceptHandler(lineno=nodes[0].srow,
926 + col_offset=nodes[0].scol,
927 + type=t, name=n,
928 + body=self.do(nodes[2], ctx)))
929 + del nodes[:3]
930 + stmt = self.ST.TryExcept(lineno=st.srow, col_offset=st.scol,
931 + body=self.do(st[2], ctx),
932 + handlers=handlers,
933 + orelse=orelse)
934 + if finalbody is None :
935 + return stmt
936 + else :
937 + return self.ST.TryFinally(lineno=st.srow, col_offset=st.scol,
938 + body=[stmt], finalbody=finalbody)
939 + def do_with_stmt (self, st, ctx) :
940 + """with_stmt: 'with' test [ with_var ] ':' suite
941 + -> ast.With
942 +
943 + <<< with x : pass
944 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass()])])"
945 + <<< with x as y : pass
946 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=Name(id='y', ctx=Store()), body=[Pass()])])"
947 + <<< with x :
948 + ... pass
949 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass()])])"
950 + <<< with x as y :
951 + ... pass
952 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=Name(id='y', ctx=Store()), body=[Pass()])])"
953 + """
954 + if len(st) == 5 :
955 + return self.ST.With(lineno=st.srow, col_offset=st.scol,
956 + context_expr=self.do(st[1], ctx),
957 + optional_vars=self.do(st[2], self.ST.Store),
958 + body=self.do(st[4], ctx))
959 + else :
960 + return self.ST.With(lineno=st.srow, col_offset=st.scol,
961 + context_expr=self.do(st[1], ctx),
962 + optional_vars=None,
963 + body=self.do(st[3], ctx))
964 + def do_with_var (self, st, ctx) :
965 + """with_var: 'as' expr
966 + -> ast.Name
967 +
968 + <<< with x as y : pass
969 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=Name(id='y', ctx=Store()), body=[Pass()])])"
970 + """
971 + return self.do(st[1], ctx)
972 + def do_except_clause (self, st, ctx) :
973 + """except_clause: 'except' [test ['as' NAME]]
974 + -> ast.AST?, ast.Name?
975 +
976 + <<< try : pass
977 + ... except NameError : pass
978 + ... except TypeError as err : pass
979 + ... except : pass
980 + "Module(body=[TryExcept(body=[Pass()], handlers=[ExceptHandler(type=Name(id='NameError', ctx=Load()), name=None, body=[Pass()]), ExceptHandler(type=Name(id='TypeError', ctx=Load()), name=Name(id='err', ctx=Store()), body=[Pass()]), ExceptHandler(type=None, name=None, body=[Pass()])], orelse=[])])"
981 + """
982 + if len(st) == 1 :
983 + return None, None
984 + elif len(st) == 2 :
985 + return self.do(st[1], ctx), None
986 + else :
987 + return self.do(st[1], ctx), self.ST.Name(lineno=st[3].srow,
988 + col_offset=st[3].scol,
989 + id=st[3].text,
990 + ctx=self.ST.Store())
991 + def do_suite (self, st, ctx) :
992 + """suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
993 + -> ast.AST+
994 +
995 + <<< with x : pass
996 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass()])])"
997 + <<< with x :
998 + ... pass
999 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass()])])"
1000 + <<< with x :
1001 + ... pass
1002 + ... pass
1003 + "Module(body=[With(context_expr=Name(id='x', ctx=Load()), optional_vars=None, body=[Pass(), Pass()])])"
1004 + """
1005 + if len(st) == 1 :
1006 + return self.do(st[0], ctx)
1007 + else :
1008 + return reduce(operator.add,
1009 + (self.do(child, ctx) for child in st[2:-1]),
1010 + [])
1011 + def do_test (self, st, ctx) :
1012 + """test: or_test ['if' or_test 'else' test] | lambdef
1013 + -> ast.AST
1014 +
1015 + <<< 3
1016 + 'Module(body=[Expr(value=Num(n=3))])'
1017 + <<< 3 if x else 4
1018 + "Module(body=[Expr(value=IfExp(test=Name(id='x', ctx=Load()), body=Num(n=3), orelse=Num(n=4)))])"
1019 + <<< lambda x : x+1
1020 + "Module(body=[Expr(value=Lambda(args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Num(n=1))))])"
1021 + """
1022 + if len(st) == 1 :
1023 + return self.do(st[0], ctx)
1024 + else :
1025 + return self.ST.IfExp(lineno=st.srow, col_offset=st.scol,
1026 + test=self.do(st[2], ctx),
1027 + body=self.do(st[0], ctx),
1028 + orelse=self.do(st[4], ctx))
1029 + def do_test_nocond (self, st, ctx) :
1030 + """test_nocond: or_test | lambdef_nocond
1031 + -> ast.AST
1032 +
1033 + <<< [x for x in (lambda: True, lambda: False) if x()]
1034 + "Module(body=[Expr(value=ListComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Tuple(elts=[Lambda(args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=Name(id='True', ctx=Load())), Lambda(args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=Name(id='False', ctx=Load()))], ctx=Load()), ifs=[Call(func=Name(id='x', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None)])]))])"
1035 + """
1036 + return self.do(st[0], ctx)
1037 + def do_lambdef (self, st, ctx) :
1038 + """lambdef: 'lambda' [varargslist] ':' test
1039 + -> ast.Lambda
1040 +
1041 + <<< lambda : True
1042 + "Module(body=[Expr(value=Lambda(args=arguments(args=[], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=Name(id='True', ctx=Load())))])"
1043 + <<< lambda x : x+1
1044 + "Module(body=[Expr(value=Lambda(args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Num(n=1))))])"
1045 + """
1046 + if len(st) == 3 :
1047 + return self.ST.Lambda(lineno=st.srow, col_offset=st.scol,
1048 + args=self.ST.arguments(lineno=st.srow,
1049 + col_offset=st.scol,
1050 + args=[], vararg=None,
1051 + varargannotation=None,
1052 + kwonlyargs=[], kwarg=None,
1053 + kwargannotation=None,
1054 + defaults=[], kw_defaults=[]),
1055 + body=self.do(st[-1], ctx))
1056 + else :
1057 + return self.ST.Lambda(lineno=st.srow, col_offset=st.scol,
1058 + args=self.do(st[1], ctx),
1059 + body=self.do(st[-1], ctx))
1060 + def do_lambdef_nocond (self, st, ctx) :
1061 + """lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
1062 + -> ast.Lambda
1063 +
1064 + <<< [x for x in l if lambda y : x]
1065 + "Module(body=[Expr(value=ListComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[Lambda(args=arguments(args=[arg(arg='y', annotation=None)], vararg=None, varargannotation=None, kwonlyargs=[], kwarg=None, kwargannotation=None, defaults=[], kw_defaults=[]), body=Name(id='x', ctx=Load()))])]))])"
1066 + """
1067 + tree = self.do_lambdef(st, ctx)
1068 + tree.st = st
1069 + return tree
1070 + def do_or_test (self, st, ctx) :
1071 + """or_test: and_test ('or' and_test)*
1072 + -> ast.AST
1073 +
1074 + <<< x or y
1075 + "Module(body=[Expr(value=BoolOp(op=Or(), values=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())]))])"
1076 + <<< x or y or z
1077 + "Module(body=[Expr(value=BoolOp(op=Or(), values=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load()), Name(id='z', ctx=Load())]))])"
1078 + """
1079 + return self._do_boolean(st, ctx)
1080 + def do_and_test (self, st, ctx) :
1081 + """and_test: not_test ('and' not_test)*
1082 + -> ast.AST
1083 +
1084 + <<< x and y
1085 + "Module(body=[Expr(value=BoolOp(op=And(), values=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load())]))])"
1086 + <<< x and y and z
1087 + "Module(body=[Expr(value=BoolOp(op=And(), values=[Name(id='x', ctx=Load()), Name(id='y', ctx=Load()), Name(id='z', ctx=Load())]))])"
1088 + """
1089 + return self._do_boolean(st, ctx)
1090 + def do_not_test (self, st, ctx) :
1091 + """not_test: 'not' not_test | comparison
1092 + -> ast.AST
1093 +
1094 + <<< not x
1095 + "Module(body=[Expr(value=UnaryOp(op=Not(), operand=Name(id='x', ctx=Load())))])"
1096 + <<< not not x
1097 + "Module(body=[Expr(value=UnaryOp(op=Not(), operand=UnaryOp(op=Not(), operand=Name(id='x', ctx=Load()))))])"
1098 + """
1099 + return self._do_unary(st, ctx)
1100 + def do_comparison (self, st, ctx) :
1101 + """comparison: star_expr (comp_op star_expr)*
1102 + -> ast.AST
1103 +
1104 + <<< *x < *y <= *z
1105 + "Module(body=[Expr(value=Compare(left=Starred(value=Name(id='x', ctx=Load()), ctx=Load()), ops=[Lt(), LtE()], comparators=[Starred(value=Name(id='y', ctx=Load()), ctx=Load()), Starred(value=Name(id='z', ctx=Load()), ctx=Load())]))])"
1106 + <<< x < y <= z
1107 + "Module(body=[Expr(value=Compare(left=Name(id='x', ctx=Load()), ops=[Lt(), LtE()], comparators=[Name(id='y', ctx=Load()), Name(id='z', ctx=Load())]))])"
1108 + """
1109 + if len(st) == 1 :
1110 + return self.do(st[0], ctx)
1111 + else :
1112 + return self.ST.Compare(lineno=st.srow, col_offset=st.scol,
1113 + left=self.do(st[0], ctx),
1114 + ops = [self.do(child, ctx)
1115 + for child in st[1::2]],
1116 + comparators = [self.do(child, ctx)
1117 + for child in st[2::2]])
1118 + _comparison = {"<" : ast.Lt,
1119 + ">" : ast.Gt,
1120 + "==" : ast.Eq,
1121 + ">=" : ast.GtE,
1122 + "<=" : ast.LtE,
1123 + "!=" : ast.NotEq,
1124 + "<>" : ast.NotEq,
1125 + "in" : ast.In,
1126 + "not in" : ast.NotIn,
1127 + "is" : ast.Is,
1128 + "is not" : ast.IsNot}
1129 + def do_comp_op (self, st, ctx) :
1130 + """comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'<>'|'in'|'not' 'in'|'is'
1131 + |'is' 'not'
1132 + -> ast.cmpop
1133 +
1134 + <<< 1 < 2
1135 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Lt()], comparators=[Num(n=2)]))])'
1136 + <<< 1 > 2
1137 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Gt()], comparators=[Num(n=2)]))])'
1138 + <<< 1 == 2
1139 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Eq()], comparators=[Num(n=2)]))])'
1140 + <<< 1 >= 2
1141 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[GtE()], comparators=[Num(n=2)]))])'
1142 + <<< 1 <= 2
1143 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[LtE()], comparators=[Num(n=2)]))])'
1144 + <<< 1 != 2
1145 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[NotEq()], comparators=[Num(n=2)]))])'
1146 + <<< 1 <> 2
1147 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[NotEq()], comparators=[Num(n=2)]))])'
1148 + <<< 1 in 2
1149 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[In()], comparators=[Num(n=2)]))])'
1150 + <<< 1 not in 2
1151 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[NotIn()], comparators=[Num(n=2)]))])'
1152 + <<< 1 is 2
1153 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Is()], comparators=[Num(n=2)]))])'
1154 + <<< 1 is not 2
1155 + 'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[IsNot()], comparators=[Num(n=2)]))])'
1156 + """
1157 + text = " ".join(child.text for child in st)
1158 + return self._comparison[text](lineno=st.srow, col_offset=st.scol)
1159 + def do_star_expr (self, st, ctx) :
1160 + """star_expr: ['*'] expr
1161 + -> ast.AST
1162 +
1163 + <<< x
1164 + "Module(body=[Expr(value=Name(id='x', ctx=Load()))])"
1165 + <<< *x
1166 + "Module(body=[Expr(value=Starred(value=Name(id='x', ctx=Load()), ctx=Load()))])"
1167 + """
1168 + if len(st) == 1 :
1169 + return self.do(st[0], ctx)
1170 + else :
1171 + return self.ST.Starred(lineno=st.srow, col_offset=st.scol,
1172 + value=self.do(st[1], ctx),
1173 + ctx=ctx())
1174 + def do_expr (self, st, ctx) :
1175 + """expr: xor_expr ('|' xor_expr)*
1176 + -> ast.AST
1177 +
1178 + <<< 1
1179 + 'Module(body=[Expr(value=Num(n=1))])'
1180 + <<< 1 | 2
1181 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=BitOr(), right=Num(n=2)))])'
1182 + <<< 1 | 2 | 3
1183 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=BitOr(), right=Num(n=2)), op=BitOr(), right=Num(n=3)))])'
1184 + """
1185 + return self._do_binary(st, ctx)
1186 + def do_xor_expr (self, st, ctx) :
1187 + """xor_expr: and_expr ('^' and_expr)*
1188 + -> ast.AST
1189 +
1190 + <<< 1
1191 + 'Module(body=[Expr(value=Num(n=1))])'
1192 + <<< 1 ^ 2
1193 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=BitXor(), right=Num(n=2)))])'
1194 + <<< 1 ^ 2 ^ 3
1195 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=BitXor(), right=Num(n=2)), op=BitXor(), right=Num(n=3)))])'
1196 + """
1197 + return self._do_binary(st, ctx)
1198 + def do_and_expr (self, st, ctx) :
1199 + """and_expr: shift_expr ('&' shift_expr)*
1200 + -> ast.AST
1201 +
1202 + <<< 1
1203 + 'Module(body=[Expr(value=Num(n=1))])'
1204 + <<< 1 & 2
1205 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=BitAnd(), right=Num(n=2)))])'
1206 + <<< 1 & 2 & 3
1207 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=BitAnd(), right=Num(n=2)), op=BitAnd(), right=Num(n=3)))])'
1208 + """
1209 + return self._do_binary(st, ctx)
1210 + def do_shift_expr (self, st, ctx) :
1211 + """shift_expr: arith_expr (('<<'|'>>') arith_expr)*
1212 + -> ast.AST
1213 +
1214 + <<< 1
1215 + 'Module(body=[Expr(value=Num(n=1))])'
1216 + <<< 1 << 2
1217 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=LShift(), right=Num(n=2)))])'
1218 + <<< 1 << 2 >> 3
1219 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=LShift(), right=Num(n=2)), op=RShift(), right=Num(n=3)))])'
1220 + """
1221 + return self._do_binary(st, ctx)
1222 + def do_arith_expr (self, st, ctx) :
1223 + """arith_expr: term (('+'|'-') term)*
1224 + -> ast.AST
1225 +
1226 + <<< 1
1227 + 'Module(body=[Expr(value=Num(n=1))])'
1228 + <<< 1 + 2
1229 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=Add(), right=Num(n=2)))])'
1230 + <<< 1 + 2 - 3
1231 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=Add(), right=Num(n=2)), op=Sub(), right=Num(n=3)))])'
1232 + """
1233 + return self._do_binary(st, ctx)
1234 + def do_term (self, st, ctx) :
1235 + """term: factor (('*'|'/'|'%'|'//') factor)*
1236 + -> ast.AST
1237 +
1238 + <<< 1
1239 + 'Module(body=[Expr(value=Num(n=1))])'
1240 + <<< 1 * 2
1241 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=Mult(), right=Num(n=2)))])'
1242 + <<< 1 * 2 / 3
1243 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=Num(n=1), op=Mult(), right=Num(n=2)), op=Div(), right=Num(n=3)))])'
1244 + <<< 1 * 2 / 3 % 4
1245 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=BinOp(left=Num(n=1), op=Mult(), right=Num(n=2)), op=Div(), right=Num(n=3)), op=Mod(), right=Num(n=4)))])'
1246 + <<< 1 * 2 / 3 % 4 // 5
1247 + 'Module(body=[Expr(value=BinOp(left=BinOp(left=BinOp(left=BinOp(left=Num(n=1), op=Mult(), right=Num(n=2)), op=Div(), right=Num(n=3)), op=Mod(), right=Num(n=4)), op=FloorDiv(), right=Num(n=5)))])'
1248 + """
1249 + return self._do_binary(st, ctx)
1250 + def do_factor (self, st, ctx) :
1251 + """factor: ('+'|'-'|'~') factor | power
1252 + -> ast.AST
1253 +
1254 + <<< 1
1255 + 'Module(body=[Expr(value=Num(n=1))])'
1256 + <<< +1
1257 + 'Module(body=[Expr(value=UnaryOp(op=UAdd(), operand=Num(n=1)))])'
1258 + <<< -1
1259 + 'Module(body=[Expr(value=Num(n=-1))])'
1260 + <<< ~1
1261 + 'Module(body=[Expr(value=UnaryOp(op=Invert(), operand=Num(n=1)))])'
1262 + <<< +-1
1263 + 'Module(body=[Expr(value=UnaryOp(op=UAdd(), operand=Num(n=-1)))])'
1264 + <<< -+1
1265 + 'Module(body=[Expr(value=UnaryOp(op=USub(), operand=UnaryOp(op=UAdd(), operand=Num(n=1))))])'
1266 + <<< +-~1
1267 + 'Module(body=[Expr(value=UnaryOp(op=UAdd(), operand=UnaryOp(op=USub(), operand=UnaryOp(op=Invert(), operand=Num(n=1)))))])'
1268 + """
1269 + if len(st) == 1 :
1270 + return self.do(st[0], ctx)
1271 + else :
1272 + tree = self._do_unary(st, ctx)
1273 + if (isinstance(tree.op, self.ST.USub)
1274 + and isinstance(tree.operand, self.ST.Num)
1275 + and tree.operand.n > 0) :
1276 + tree = self.ST.Num(lineno=st.srow, col_offset=st.scol,
1277 + n = -tree.operand.n)
1278 + return tree
1279 + def do_power (self, st, ctx) :
1280 + """power: atom trailer* ['**' factor]
1281 + -> ast.AST
1282 +
1283 + <<< 1 ** 2
1284 + 'Module(body=[Expr(value=BinOp(left=Num(n=1), op=Pow(), right=Num(n=2)))])'
1285 + <<< a.b ** 2
1286 + "Module(body=[Expr(value=BinOp(left=Attribute(value=Name(id='a', ctx=Load()), attr='b', ctx=Load()), op=Pow(), right=Num(n=2)))])"
1287 + """
1288 + if len(st) == 1 :
1289 + return self.do(st[0], ctx)
1290 + else :
1291 + left = self.do(st[0], ctx)
1292 + power = None
1293 + for child in st[1:] :
1294 + if child.text == "**" :
1295 + power = self.do(st[-1], ctx)
1296 + break
1297 + trailer = self.do(child, ctx)
1298 + left = trailer(left, st.srow, st.scol)
1299 + if power :
1300 + return self.ST.BinOp(lineno=st.srow, col_offset=st.scol,
1301 + left=left,
1302 + op=self.ST.Pow(lineno=st[-2].srow,
1303 + col_offset=st[-2].scol),
1304 + right=power)
1305 + else :
1306 + return left
1307 + def do_atom (self, st, ctx) :
1308 + """atom: ('(' [yield_expr|testlist_comp] ')' |
1309 + '[' [testlist_comp] ']' |
1310 + '{' [dictorsetmaker] '}' |
1311 + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
1312 + -> ast.AST
1313 +
1314 + <<< foo
1315 + "Module(body=[Expr(value=Name(id='foo', ctx=Load()))])"
1316 + <<< True
1317 + "Module(body=[Expr(value=Name(id='True', ctx=Load()))])"
1318 + <<< 42
1319 + 'Module(body=[Expr(value=Num(n=42))])'
1320 + <<< 1.5
1321 + 'Module(body=[Expr(value=Num(n=1.5))])'
1322 + <<< 'hello'
1323 + "Module(body=[Expr(value=Str(s='hello'))])"
1324 + <<< 'hello' 'world'
1325 + "Module(body=[Expr(value=Str(s='helloworld'))])"
1326 + <<< '''hello
1327 + ... world'''
1328 + "Module(body=[Expr(value=Str(s='hello\\\\nworld'))])"
1329 + <<< [1, 2, 3]
1330 + 'Module(body=[Expr(value=List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load()))])'
1331 + <<< [x for x in l]
1332 + "Module(body=[Expr(value=ListComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1333 + <<< (1, 2, 3)
1334 + 'Module(body=[Expr(value=Tuple(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load()))])'
1335 + <<< (1,)
1336 + 'Module(body=[Expr(value=Tuple(elts=[Num(n=1)], ctx=Load()))])'
1337 + <<< (1)
1338 + 'Module(body=[Expr(value=Num(n=1))])'
1339 + <<< (x for x in l)
1340 + "Module(body=[Expr(value=GeneratorExp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1341 + <<< {1, 2, 3}
1342 + 'Module(body=[Expr(value=Set(elts=[Num(n=1), Num(n=2), Num(n=3)]))])'
1343 + <<< {x for x in l}
1344 + "Module(body=[Expr(value=SetComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1345 + <<< {1:2, 3:4}
1346 + 'Module(body=[Expr(value=Dict(keys=[Num(n=1), Num(n=3)], values=[Num(n=2), Num(n=4)]))])'
1347 + <<< {x:y for x, y in l}
1348 + "Module(body=[Expr(value=DictComp(key=Name(id='x', ctx=Load()), value=Name(id='y', ctx=Load()), generators=[comprehension(target=Tuple(elts=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1349 + """
1350 + kind, text = st[0].kind, st[0].text
1351 + if kind == self.NUMBER :
1352 + return self.ST.Num(lineno=st.srow, col_offset=st.scol,
1353 + n=self.ST.literal_eval(text))
1354 + elif kind == self.NAME :
1355 + return self.ST.Name(lineno=st.srow, col_offset=st.scol,
1356 + id=text, ctx=ctx())
1357 + elif kind == self.STRING :
1358 + return self.ST.Str(lineno=st.srow, col_offset=st.scol,
1359 + s="".join(self.ST.literal_eval(child.text)
1360 + for child in st))
1361 + elif text == "..." :
1362 + return self.ST.Ellipsis(lineno=st.srow, col_offset=st.scol)
1363 + elif text == "[" :
1364 + if len(st) == 2 :
1365 + return self.ST.List(lineno=st.srow, col_offset=st.scol,
1366 + elts=[], ctx=ctx())
1367 + else :
1368 + loop, elts, atom = self.do(st[1], ctx)
1369 + if atom is not None :
1370 + elts=[atom]
1371 + if loop is None :
1372 + return self.ST.List(lineno=st.srow, col_offset=st.scol,
1373 + elts=elts, ctx=ctx())
1374 + else :
1375 + return self.ST.ListComp(lineno=st.srow, col_offset=st.scol,
1376 + elt=loop, generators=elts)
1377 + elif text == "(" :
1378 + if len(st) == 2 :
1379 + return self.ST.Tuple(lineno=st.srow, col_offset=st.scol,
1380 + elts=[], ctx=ctx())
1381 + elif st[1].symbol == "yield_expr" :
1382 + return self.do(st[1], ctx)
1383 + else :
1384 + loop, elts, atom = self.do(st[1], ctx)
1385 + if atom is not None :
1386 + return atom
1387 + elif loop is None :
1388 + return self.ST.Tuple(lineno=st.srow, col_offset=st.scol,
1389 + elts=elts, ctx=ctx())
1390 + else :
1391 + return self.ST.GeneratorExp(lineno=st.srow, col_offset=st.scol,
1392 + elt=loop, generators=elts)
1393 + else : # text == "{"
1394 + if len(st) == 2 :
1395 + return self.ST.Dict(lineno=st.srow, col_offset=st.scol,
1396 + keys=[], values=[])
1397 + else :
1398 + return self.do(st[1], ctx)
1399 + def do_testlist_comp (self, st, ctx) :
1400 + """testlist_comp: test ( comp_for | (',' test)* [','] )
1401 + -> ast.AST?, ast.AST+
1402 +
1403 + <<< [1, 2, 3]
1404 + 'Module(body=[Expr(value=List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load()))])'
1405 + <<< [x for x in l]
1406 + "Module(body=[Expr(value=ListComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1407 + """
1408 + if len(st) == 1 :
1409 + return None, None, self.do(st[0])
1410 + elif st[1].text == "," :
1411 + return None, [self.do(child, ctx) for child in st[::2]], None
1412 + else :
1413 + return self.do(st[0], ctx), self.do(st[1], ctx)[0], None
1414 + def do_trailer (self, st, ctx) :
1415 + """trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
1416 + -> (tree, line, column -> ast.AST)
1417 +
1418 + <<< a.b
1419 + "Module(body=[Expr(value=Attribute(value=Name(id='a', ctx=Load()), attr='b', ctx=Load()))])"
1420 + <<< a.b.c
1421 + "Module(body=[Expr(value=Attribute(value=Attribute(value=Name(id='a', ctx=Load()), attr='b', ctx=Load()), attr='c', ctx=Load()))])"
1422 + <<< a.b[c].d
1423 + "Module(body=[Expr(value=Attribute(value=Subscript(value=Attribute(value=Name(id='a', ctx=Load()), attr='b', ctx=Load()), slice=Index(value=Name(id='c', ctx=Load())), ctx=Load()), attr='d', ctx=Load()))])"
1424 + <<< f()
1425 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None))])"
1426 + <<< f(x)
1427 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[], starargs=None, kwargs=None))])"
1428 + <<< f(x, *l, y=2)
1429 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[keyword(arg='y', value=Num(n=2))], starargs=Name(id='l', ctx=Load()), kwargs=None))])"
1430 + <<< f(x, *l, y=2, **d)
1431 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[keyword(arg='y', value=Num(n=2))], starargs=Name(id='l', ctx=Load()), kwargs=Name(id='d', ctx=Load())))])"
1432 + <<< f(*l)
1433 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[], starargs=Name(id='l', ctx=Load()), kwargs=None))])"
1434 + <<< f(**d)
1435 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=Name(id='d', ctx=Load())))])"
1436 + <<< f(x)(a=1, b=2)
1437 + "Module(body=[Expr(value=Call(func=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[], starargs=None, kwargs=None), args=[], keywords=[keyword(arg='a', value=Num(n=1)), keyword(arg='b', value=Num(n=2))], starargs=None, kwargs=None))])"
1438 + """
1439 + hd = st[0].text
1440 + if hd == "." :
1441 + def trail (tree, lineno, col_offset) :
1442 + return self.ST.Attribute(lineno=lineno,
1443 + col_offset=col_offset,
1444 + value=tree,
1445 + attr=st[1].text,
1446 + ctx=ctx())
1447 + elif hd == "[" :
1448 + subscript = self.do(st[1], ctx)
1449 + if len(subscript) == 1 :
1450 + subscript = subscript[0]
1451 + else :
1452 + subscript = self.ST.ExtSlice(lineno=st[1].srow,
1453 + col_offset=st[1].scol,
1454 + dims=subscript,
1455 + ctx=ctx())
1456 + def trail (tree, lineno, col_offset) :
1457 + return self.ST.Subscript(lineno=lineno,
1458 + col_offset=col_offset,
1459 + value=tree,
1460 + slice=subscript,
1461 + ctx=ctx())
1462 + elif len(st) == 2 : # hd = "("
1463 + def trail (tree, lineno, col_offset) :
1464 + return self.ST.Call(lineno=lineno, col_offset=col_offset,
1465 + func=tree, args=[], keywords=[],
1466 + starargs=None, kwargs=None)
1467 + else : # hd = "("
1468 + def trail (tree, lineno, col_offset) :
1469 + args, keywords, starargs, kwargs = self.do(st[1], ctx)
1470 + return self.ST.Call(lineno=lineno, col_offset=col_offset,
1471 + func=tree, args=args, keywords=keywords,
1472 + starargs=starargs, kwargs=kwargs)
1473 + return trail
1474 + def do_subscriptlist (self, st, ctx) :
1475 + """subscriptlist: subscript (',' subscript)* [',']
1476 + -> ast.Slice+
1477 +
1478 + <<< l[:]
1479 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=None), ctx=Load()))])"
1480 + <<< l[1:]
1481 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=None, step=None), ctx=Load()))])"
1482 + <<< l[1::]
1483 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=None, step=Name(id='None', ctx=Load())), ctx=Load()))])"
1484 + <<< l[1:2:]
1485 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=Num(n=2), step=Name(id='None', ctx=Load())), ctx=Load()))])"
1486 + <<< l[1:2:3]
1487 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=Num(n=2), step=Num(n=3)), ctx=Load()))])"
1488 + <<< l[::]
1489 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=Name(id='None', ctx=Load())), ctx=Load()))])"
1490 + <<< l[:2:]
1491 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=Num(n=2), step=Name(id='None', ctx=Load())), ctx=Load()))])"
1492 + <<< l[:2:3]
1493 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=Num(n=2), step=Num(n=3)), ctx=Load()))])"
1494 + <<< l[::3]
1495 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=Num(n=3)), ctx=Load()))])"
1496 + <<< l[1:2:3,4:5:6]
1497 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=ExtSlice(dims=[Slice(lower=Num(n=1), upper=Num(n=2), step=Num(n=3)), Slice(lower=Num(n=4), upper=Num(n=5), step=Num(n=6))]), ctx=Load()))])"
1498 + """
1499 + return [self.do(child, ctx) for child in st[::2]]
1500 + def do_subscript (self, st, ctx) :
1501 + """subscript: test | [test] ':' [test] [sliceop]
1502 + -> ast.Slice | ast.Index
1503 +
1504 + <<< l[:]
1505 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=None), ctx=Load()))])"
1506 + <<< l[1:]
1507 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=None, step=None), ctx=Load()))])"
1508 + <<< l[1::]
1509 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=None, step=Name(id='None', ctx=Load())), ctx=Load()))])"
1510 + <<< l[1:2:]
1511 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=Num(n=2), step=Name(id='None', ctx=Load())), ctx=Load()))])"
1512 + <<< l[1:2:3]
1513 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=Num(n=2), step=Num(n=3)), ctx=Load()))])"
1514 + <<< l[::]
1515 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=Name(id='None', ctx=Load())), ctx=Load()))])"
1516 + <<< l[:2:]
1517 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=Num(n=2), step=Name(id='None', ctx=Load())), ctx=Load()))])"
1518 + <<< l[:2:3]
1519 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=Num(n=2), step=Num(n=3)), ctx=Load()))])"
1520 + <<< l[::3]
1521 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=Num(n=3)), ctx=Load()))])"
1522 + """
1523 + count = len(st)
1524 + if count == 1 and st[0].text == ":" :
1525 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1526 + lower=None, upper=None, step=None)
1527 + elif count == 1 :
1528 + return self.ST.Index(lineno=st.srow, col_offset=st.scol,
1529 + value=self.do(st[0], ctx))
1530 + elif count == 4 :
1531 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1532 + lower=self.do(st[0], ctx),
1533 + upper=self.do(st[2], ctx),
1534 + step=self.do(st[3], ctx))
1535 + elif count == 3 and st[-1].symbol == "test" :
1536 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1537 + lower=self.do(st[0], ctx),
1538 + upper=self.do(st[2], ctx),
1539 + step=None)
1540 + elif count == 3 and st[0].text == ":" :
1541 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1542 + lower=None,
1543 + upper=self.do(st[1], ctx),
1544 + step=self.do(st[2], ctx))
1545 + elif count == 3 :
1546 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1547 + lower=self.do(st[0], ctx),
1548 + upper=None,
1549 + step=self.do(st[2], ctx))
1550 + elif count == 2 and st[-1].symbol == "sliceop" :
1551 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1552 + lower=None,
1553 + upper=None,
1554 + step=self.do(st[1], ctx))
1555 + elif count == 2 and st[0].text == ":" :
1556 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1557 + lower=None,
1558 + upper=self.do(st[1], ctx),
1559 + step=None)
1560 + else :
1561 + return self.ST.Slice(lineno=st.srow, col_offset=st.scol,
1562 + lower=self.do(st[0], ctx),
1563 + upper=None,
1564 + step=None)
1565 + def do_sliceop (self, st, ctx) :
1566 + """sliceop: ':' [test]
1567 + -> ast.AST
1568 +
1569 + <<< l[1::]
1570 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=Num(n=1), upper=None, step=Name(id='None', ctx=Load())), ctx=Load()))])"
1571 + <<< l[::3]
1572 + "Module(body=[Expr(value=Subscript(value=Name(id='l', ctx=Load()), slice=Slice(lower=None, upper=None, step=Num(n=3)), ctx=Load()))])"
1573 + """
1574 + if len(st) == 1 :
1575 + return self.ST.Name(lineno=st.srow, col_offset=st.scol,
1576 + id="None", ctx=ctx())
1577 + else :
1578 + return self.do(st[1], ctx)
1579 + def do_exprlist (self, st, ctx) :
1580 + """exprlist: star_expr (',' star_expr)* [',']
1581 + -> ast.AST+
1582 +
1583 + <<< del x
1584 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del())])])"
1585 + <<< del x, y
1586 + "Module(body=[Delete(targets=[Name(id='x', ctx=Del()), Name(id='y', ctx=Del())])])"
1587 + """
1588 + tree = self.do_testlist(st, ctx)
1589 + tree.st = st
1590 + return tree
1591 + def do_testlist (self, st, ctx) :
1592 + """testlist: test (',' test)* [',']
1593 + -> ast.AST | ast.Tuple
1594 +
1595 + <<< 1
1596 + 'Module(body=[Expr(value=Num(n=1))])'
1597 + <<< 1, 2
1598 + 'Module(body=[Expr(value=Tuple(elts=[Num(n=1), Num(n=2)], ctx=Load()))])'
1599 + """
1600 + lst = [self.do(child, ctx) for child in st[::2]]
1601 + if len(lst) == 1 :
1602 + return lst[0]
1603 + else :
1604 + return self.ST.Tuple(lineno=st.srow, col_offset=st.scol,
1605 + elts=lst, ctx=ctx())
1606 + def do_dictorsetmaker (self, st, ctx) :
1607 + """dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
1608 + (test (comp_for | (',' test)* [','])) )
1609 + -> ast.Dict | ast.DictComp | ast.Set | ast.SetComp
1610 +
1611 + <<< {1, 2, 3}
1612 + 'Module(body=[Expr(value=Set(elts=[Num(n=1), Num(n=2), Num(n=3)]))])'
1613 + <<< {x for x in l}
1614 + "Module(body=[Expr(value=SetComp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1615 + <<< {1:2, 3:4}
1616 + 'Module(body=[Expr(value=Dict(keys=[Num(n=1), Num(n=3)], values=[Num(n=2), Num(n=4)]))])'
1617 + <<< {x:y for x, y in l}
1618 + "Module(body=[Expr(value=DictComp(key=Name(id='x', ctx=Load()), value=Name(id='y', ctx=Load()), generators=[comprehension(target=Tuple(elts=[Name(id='x', ctx=Store()), Name(id='y', ctx=Store())], ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])]))])"
1619 + <<< {42}
1620 + 'Module(body=[Expr(value=Set(elts=[Num(n=42)]))])'
1621 + """
1622 + if len(st) == 1 :
1623 + return self.ST.Set(lineno=st.srow, col_offset=st.scol,
1624 + elts=[self.do(st[0], ctx)])
1625 + elif st[1].text == ":" :
1626 + if st[3].text == "," :
1627 + return self.ST.Dict(lineno=st.srow, col_offset=st.scol,
1628 + keys=[self.do(child, ctx)
1629 + for child in st[::4]],
1630 + values=[self.do(child, ctx)
1631 + for child in st[2::4]])
1632 + else :
1633 + return self.ST.DictComp(lineno=st.srow, col_offset=st.scol,
1634 + key=self.do(st[0], ctx),
1635 + value=self.do(st[2], ctx),
1636 + generators=self.do(st[3], ctx)[0])
1637 + else :
1638 + loop, elts, atom = self.do_testlist_comp(st, ctx)
1639 + if loop is None :
1640 + return self.ST.Set(lineno=st.srow, col_offset=st.scol,
1641 + elts=elts)
1642 + else :
1643 + return self.ST.SetComp(lineno=st.srow, col_offset=st.scol,
1644 + elt=loop, generators=elts)
1645 + def do_classdef (self, st, ctx) :
1646 + """classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
1647 + -> ast.ClassDef
1648 +
1649 + <<< class c : pass
1650 + "Module(body=[ClassDef(name='c', bases=[], keywords=[], starargs=None, kwargs=None, body=[Pass()], decorator_list=[])])"
1651 + <<< class c () : pass
1652 + "Module(body=[ClassDef(name='c', bases=[], keywords=[], starargs=None, kwargs=None, body=[Pass()], decorator_list=[])])"
1653 + <<< class c (object, foo=bar) : pass
1654 + "Module(body=[ClassDef(name='c', bases=[Name(id='object', ctx=Load())], keywords=[keyword(arg='foo', value=Name(id='bar', ctx=Load()))], starargs=None, kwargs=None, body=[Pass()], decorator_list=[])])"
1655 + """
1656 + if len(st) <= 6 :
1657 + return self.ST.ClassDef(lineno=st.srow, col_offset=st.scol,
1658 + name=st[1].text,
1659 + bases=[],
1660 + keywords=[],
1661 + starargs=None,
1662 + kwargs=None,
1663 + body=self.do(st[-1], ctx),
1664 + decorator_list=[])
1665 + else :
1666 + args, keywords, starargs, kwargs = self.do(st[3], ctx)
1667 + return self.ST.ClassDef(lineno=st.srow, col_offset=st.scol,
1668 + name=st[1].text,
1669 + bases=args,
1670 + keywords=keywords,
1671 + starargs=starargs,
1672 + kwargs=kwargs,
1673 + body=self.do(st[-1], ctx),
1674 + decorator_list=[])
1675 + def do_arglist (self, st, ctx) :
1676 + """arglist: (argument ',')* (argument [',']
1677 + |'*' test (',' argument)* [',' '**' test]
1678 + |'**' test)
1679 + -> args=ast.AST*, keywords=ast.keyword*, starargs=ast.AST?, kwargs=ast.AST?
1680 +
1681 + <<< f(x)
1682 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[], starargs=None, kwargs=None))])"
1683 + <<< f(x, *l, y=2)
1684 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[keyword(arg='y', value=Num(n=2))], starargs=Name(id='l', ctx=Load()), kwargs=None))])"
1685 + <<< f(x, *l, y=2, **d)
1686 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[keyword(arg='y', value=Num(n=2))], starargs=Name(id='l', ctx=Load()), kwargs=Name(id='d', ctx=Load())))])"
1687 + <<< f(*l)
1688 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[], starargs=Name(id='l', ctx=Load()), kwargs=None))])"
1689 + <<< f(**d)
1690 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=Name(id='d', ctx=Load())))])"
1691 + <<< f(x=1, y=2, x=3)
1692 + Traceback (most recent call last):
1693 + ...
1694 + ParseError: ... keyword argument repeated
1695 + """
1696 + args = []
1697 + keywords = []
1698 + allkw = set()
1699 + starargs = None
1700 + kwargs = None
1701 + nodes = [n for n in st if n.text != ","]
1702 + while nodes :
1703 + if nodes[0].text == "*" :
1704 + starargs = self.do(nodes[1], ctx)
1705 + del nodes[:2]
1706 + elif nodes[0].text == "**" :
1707 + kwargs = self.do(nodes[1], ctx)
1708 + del nodes[:2]
1709 + else :
1710 + arg = self.do(nodes[0], ctx)
1711 + if isinstance(arg, self.ST.keyword) :
1712 + if arg.arg in allkw :
1713 + raise ParseError(nodes[0].text,
1714 + reason="keyword argument repeated")
1715 + keywords.append(arg)
1716 + allkw.add(arg.arg)
1717 + elif starargs is not None :
1718 + raise ParseError(nodes[0].text, reason="only named"
1719 + " arguments may follow *expression")
1720 + else :
1721 + args.append(arg)
1722 + del nodes[0]
1723 + return args, keywords, starargs, kwargs
1724 + def do_argument (self, st, ctx) :
1725 + """argument: test [comp_for] | test '=' test
1726 + -> ast.keyword | ast.GeneratorExp
1727 +
1728 + <<< f(x)
1729 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Name(id='x', ctx=Load())], keywords=[], starargs=None, kwargs=None))])"
1730 + <<< f(x=1)
1731 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[], keywords=[keyword(arg='x', value=Num(n=1))], starargs=None, kwargs=None))])"
1732 + <<< f(x for x in l)
1733 + "Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[GeneratorExp(elt=Name(id='x', ctx=Load()), generators=[comprehension(target=Name(id='x', ctx=Store()), iter=Name(id='l', ctx=Load()), ifs=[])])], keywords=[], starargs=None, kwargs=None))])"
1734 + """
1735 + test = self.do(st[0], ctx)
1736 + if len(st) == 1 :
1737 + return test
1738 + elif len(st) == 3 :
1739 + if not isinstance(test, self.ST.Name) :
1740 + raise ParseError(st[0].text, reason="keyword can't be an"
1741 + " expression")
1742 + return self.ST.keyword(lineno=st.srow, col_offset=st.scol,
1743 + arg=test.id,
1744 + value=self.do(st[2], ctx))
1745 + else :
1746 + comp, ifs = self.do(st[1], ctx)
1747 + return self.ST.GeneratorExp(lineno=st.srow, col_offset=st.scol,
1748 + elt=test, generators=comp)
1749 + def do_comp_iter (self, st, ctx) :
1750 + """comp_iter: comp_for | comp_if
1751 + -> comprehension*, ast.AST*
1752 +
1753 + <<< [a for b in c if d if e for f in g if h]
1754 + "Module(body=[Expr(value=ListComp(elt=Name(id='a', ctx=Load()), generators=[comprehension(target=Name(id='b', ctx=Store()), iter=Name(id='c', ctx=Load()), ifs=[Name(id='d', ctx=Load()), Name(id='e', ctx=Load())]), comprehension(target=Name(id='f', ctx=Store()), iter=Name(id='g', ctx=Load()), ifs=[Name(id='h', ctx=Load())])]))])"
1755 + """
1756 + return self.do(st[0], ctx)
1757 + def do_comp_for (self, st, ctx) :
1758 + """comp_for: 'for' exprlist 'in' or_test [comp_iter]
1759 + -> comprehension+, []
1760 +
1761 + <<< [a for b in c if d if e for f in g if h]
1762 + "Module(body=[Expr(value=ListComp(elt=Name(id='a', ctx=Load()), generators=[comprehension(target=Name(id='b', ctx=Store()), iter=Name(id='c', ctx=Load()), ifs=[Name(id='d', ctx=Load()), Name(id='e', ctx=Load())]), comprehension(target=Name(id='f', ctx=Store()), iter=Name(id='g', ctx=Load()), ifs=[Name(id='h', ctx=Load())])]))])"
1763 + """
1764 + if len(st) == 4 :
1765 + return [self.ST.comprehension(lineno=st.srow,
1766 + col_offset=st.scol,
1767 + target=self.do(st[1], ast.Store),
1768 + iter=self.do(st[3], ctx),
1769 + ifs=[])], []
1770 + else :
1771 + comp, ifs = self.do(st[4], ctx)
1772 + return [self.ST.comprehension(lineno=st.srow,
1773 + col_offset=st.scol,
1774 + target=self.do(st[1], ast.Store),
1775 + iter=self.do(st[3], ctx),
1776 + ifs=ifs)] + comp, []
1777 + def do_comp_if (self, st, ctx) :
1778 + """comp_if: 'if' test_nocond [comp_iter]
1779 + -> comprehension*, ast.AST+
1780 +
1781 + <<< [a for b in c if d if e for f in g if h]
1782 + "Module(body=[Expr(value=ListComp(elt=Name(id='a', ctx=Load()), generators=[comprehension(target=Name(id='b', ctx=Store()), iter=Name(id='c', ctx=Load()), ifs=[Name(id='d', ctx=Load()), Name(id='e', ctx=Load())]), comprehension(target=Name(id='f', ctx=Store()), iter=Name(id='g', ctx=Load()), ifs=[Name(id='h', ctx=Load())])]))])"
1783 + """
1784 + if len(st) == 2 :
1785 + return [], [self.do(st[1], ctx)]
1786 + else :
1787 + comp, ifs = self.do(st[2], ctx)
1788 + return comp, [self.do(st[1], ctx)] + ifs
1789 + def do_yield_expr (self, st, ctx) :
1790 + """yield_expr: 'yield' [testlist]
1791 + -> ast.Yield
1792 +
1793 + <<< yield
1794 + 'Module(body=[Expr(value=Yield(value=None))])'
1795 + <<< yield 42
1796 + 'Module(body=[Expr(value=Yield(value=Num(n=42)))])'
1797 + <<< yield 42, 43
1798 + 'Module(body=[Expr(value=Yield(value=Tuple(elts=[Num(n=42), Num(n=43)], ctx=Load())))])'
1799 + """
1800 + if len(st) == 2 :
1801 + return self.ST.Yield(lineno=st.srow, col_offset=st.scol,
1802 + value=self.do(st[1], ctx))
1803 + else :
1804 + return self.ST.Yield(lineno=st.srow, col_offset=st.scol,
1805 + value=None)
1806 + @classmethod
1807 + def parse (cls, expr, mode="exec", filename="<string>") :
1808 + tree = cls(cls.parser.parseString(expr.strip() + "\n",
1809 + filename=filename)).ast
1810 + if mode == "exec" :
1811 + return tree
1812 + elif mode == "eval" :
1813 + if len(tree.body) > 1 or not isinstance(tree.body[0], cls.ST.Expr) :
1814 + raise ParseError(None, reason="invalid syntax")
1815 + return cls.ST.Expression(body=tree.body[0].value)
1816 + elif mode == "single" :
1817 + return cls.ST.Interactive(body=tree.body)
1818 + else :
1819 + raise ValueError("arg 2 must be 'exec', 'eval' or 'single'")
1820 +
1821 +parse = Translator.parse
1822 +
1823 +class ParseTestParser (doctest.DocTestParser) :
1824 + _EXAMPLE_RE = re.compile(r'''
1825 + # Source consists of a PS1 line followed by zero or more PS2 lines.
1826 + (?P<source>
1827 + (?:^(?P<indent> [ ]*) <<< .*) # PS1 line
1828 + (?:\n [ ]* \.\.\. .*)*) # PS2 lines
1829 + \n?
1830 + # Want consists of any non-blank lines that do not start with PS1.
1831 + (?P<want> (?:(?![ ]*$) # Not a blank line
1832 + (?![ ]*<<<) # Not a line starting with PS1
1833 + .*$\n? # But any other line
1834 + )*)
1835 + ''', re.MULTILINE | re.VERBOSE)
1836 + def __init__ (self, translator) :
1837 + self.Translator = translator
1838 + def parse (self, string, name="<string>") :
1839 + examples = doctest.DocTestParser.parse(self, string, name)
1840 + try :
1841 + rule = name.split(".do_", 1)[1]
1842 + except :
1843 + rule = None
1844 + for i, exple in enumerate(examples) :
1845 + if isinstance(exple, str) :
1846 + continue
1847 + elif name.split(".")[1] not in self.Translator.__dict__ :
1848 + examples[i] = ("skipping example for another language: %r"
1849 + % exple.source)
1850 + continue
1851 + if rule is not None :
1852 + source = exple.source.strip() + "\n"
1853 + try :
1854 + tree = self.Translator.ParseTree(self.Translator.parser.parseString(source))
1855 + except :
1856 + print("could not parse %r at %s:%s" % (source, name,
1857 + exple.lineno))
1858 + raise
1859 + if not tree.involve(self.Translator.parser.stringMap[rule]) :
1860 + print(("test at %s:%s does not involve rule %s"
1861 + % (name, exple.lineno, rule)))
1862 + examples[i] = "<test skipped>"
1863 + continue
1864 + examples[i] = doctest.Example(
1865 + source=("ast.dump(parse(%r))") % exple.source,
1866 + want=exple.want,
1867 + exc_msg=exple.exc_msg,
1868 + lineno=exple.lineno,
1869 + indent=exple.indent,
1870 + options=exple.options)
1871 + return examples
1872 +
1873 +def testparser (translator) :
1874 + for rule in translator.parser.stringMap :
1875 + try :
1876 + assert "<<<" in getattr(translator, "do_" + rule).__doc__
1877 + except AttributeError :
1878 + print("no handler for rule %r" % rule)
1879 + continue
1880 + except TypeError :
1881 + print("missing doc for rule %r" % rule)
1882 + continue
1883 + except AssertionError :
1884 + print("missing test for rule %r" % rule)
1885 + continue
1886 + finder = doctest.DocTestFinder(parser=ParseTestParser(translator))
1887 + runner = doctest.DocTestRunner(optionflags=doctest.NORMALIZE_WHITESPACE
1888 + | doctest.ELLIPSIS)
1889 + for name, method in inspect.getmembers(translator, inspect.ismethod) :
1890 + if not name.startswith("do_") :
1891 + continue
1892 + for test in finder.find(method, "%s.%s" % (translator.__name__, name)) :
1893 + runner.run(test)
1894 + runner.summarize()
1895 +
1896 +if __name__ == "__main__" :
1897 + testparser(Translator)
This diff could not be displayed because it is too large.
1 +module Python version "$Revision: SNAKES $"
2 +{
3 + mod = Module(stmt* body)
4 + | Interactive(stmt* body)
5 + | Expression(expr body)
6 + | Suite(stmt* body)
7 +
8 + stmt = FunctionDef(identifier name, arguments args,
9 + stmt* body, expr* decorator_list, expr? returns)
10 + | ClassDef(identifier name,
11 + expr* bases,
12 + keyword* keywords,
13 + expr? starargs,
14 + expr? kwargs,
15 + stmt* body,
16 + expr *decorator_list)
17 + | Return(expr? value)
18 +
19 + | Delete(expr* targets)
20 + | Assign(expr* targets, expr value)
21 + | AugAssign(expr target, operator op, expr value)
22 +
23 + | For(expr target, expr iter, stmt* body, stmt* orelse)
24 + | While(expr test, stmt* body, stmt* orelse)
25 + | If(expr test, stmt* body, stmt* orelse)
26 + | With(expr context_expr, expr? optional_vars, stmt* body)
27 +
28 + | Raise(expr? exc, expr? cause)
29 + | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
30 + | TryFinally(stmt* body, stmt* finalbody)
31 + | Assert(expr test, expr? msg)
32 +
33 + | Import(alias* names)
34 + | ImportFrom(identifier module, alias* names, int? level)
35 +
36 + | Exec(expr body, expr? globals, expr? locals)
37 +
38 + | Global(identifier* names)
39 + | Nonlocal(identifier* names)
40 + | Expr(expr value)
41 + | Pass | Break | Continue
42 +
43 + attributes (int lineno, int col_offset)
44 +
45 + expr = BoolOp(boolop op, expr* values)
46 + | BinOp(expr left, operator op, expr right)
47 + | UnaryOp(unaryop op, expr operand)
48 + | Lambda(arguments args, expr body)
49 + | IfExp(expr test, expr body, expr orelse)
50 + | Dict(expr* keys, expr* values)
51 + | Set(expr* elts)
52 + | ListComp(expr elt, comprehension* generators)
53 + | SetComp(expr elt, comprehension* generators)
54 + | DictComp(expr key, expr value, comprehension* generators)
55 + | GeneratorExp(expr elt, comprehension* generators)
56 + | Yield(expr? value)
57 + | Compare(expr left, cmpop* ops, expr* comparators)
58 + | Call(expr func, expr* args, keyword* keywords,
59 + expr? starargs, expr? kwargs)
60 + | Num(object n)
61 + | Str(string s)
62 + | Ellipsis
63 +
64 + | Attribute(expr value, identifier attr, expr_context ctx)
65 + | Subscript(expr value, slice slice, expr_context ctx)
66 + | Starred(expr value, expr_context ctx)
67 + | Name(identifier id, expr_context ctx)
68 + | List(expr* elts, expr_context ctx)
69 + | Tuple(expr* elts, expr_context ctx)
70 +
71 + attributes (int lineno, int col_offset)
72 +
73 + expr_context = Load | Store | Del | AugLoad | AugStore | Param
74 +
75 + slice = Slice(expr? lower, expr? upper, expr? step)
76 + | ExtSlice(slice* dims)
77 + | Index(expr value)
78 +
79 + boolop = And | Or
80 +
81 + operator = Add | Sub | Mult | Div | Mod | Pow | LShift
82 + | RShift | BitOr | BitXor | BitAnd | FloorDiv
83 +
84 + unaryop = Invert | Not | UAdd | USub
85 +
86 + cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
87 +
88 + comprehension = (expr target, expr iter, expr* ifs)
89 +
90 + excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
91 + attributes (int lineno, int col_offset)
92 +
93 + arguments = (arg* args, identifier? vararg, expr? varargannotation,
94 + arg* kwonlyargs, identifier? kwarg,
95 + expr? kwargannotation, expr* defaults,
96 + expr* kw_defaults)
97 + arg = (identifier arg, expr? annotation)
98 +
99 + keyword = (identifier arg, expr value)
100 +
101 + alias = (identifier name, identifier? asname)
102 +}
1 +# Grammar for Python in SNAKES
2 +# This is a mixture of the grammars from various Python versions
3 +
4 +$ELLIPSIS '...'
5 +
6 +file_input: (NEWLINE | stmt)* ENDMARKER
7 +
8 +decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
9 +decorators: decorator+
10 +decorated: decorators (classdef | funcdef)
11 +funcdef: 'def' NAME parameters ['-' '>' test] ':' suite
12 +parameters: '(' [typedargslist] ')'
13 +typedargslist: ((tfpdef ['=' test] ',')*
14 + ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef]
15 + | '**' tfpdef)
16 + | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
17 +tfpdef: NAME [':' test]
18 +varargslist: ((vfpdef ['=' test] ',')*
19 + ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef]
20 + | '**' vfpdef)
21 + | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
22 +vfpdef: NAME
23 +
24 +stmt: simple_stmt | compound_stmt
25 +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
26 +small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
27 + import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
28 +expr_stmt: testlist (augassign (yield_expr|testlist) |
29 + ('=' (yield_expr|testlist))*)
30 +augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
31 + '<<=' | '>>=' | '**=' | '//=')
32 +del_stmt: 'del' exprlist
33 +pass_stmt: 'pass'
34 +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
35 +break_stmt: 'break'
36 +continue_stmt: 'continue'
37 +return_stmt: 'return' [testlist]
38 +yield_stmt: yield_expr
39 +raise_stmt: 'raise' [test ['from' test]]
40 +import_stmt: import_name | import_from
41 +import_name: 'import' dotted_as_names
42 +import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
43 + 'import' ('*' | '(' import_as_names ')' | import_as_names))
44 +import_as_name: NAME ['as' NAME]
45 +dotted_as_name: dotted_name ['as' NAME]
46 +import_as_names: import_as_name (',' import_as_name)* [',']
47 +dotted_as_names: dotted_as_name (',' dotted_as_name)*
48 +dotted_name: NAME ('.' NAME)*
49 +global_stmt: 'global' NAME (',' NAME)*
50 +nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
51 +assert_stmt: 'assert' test [',' test]
52 +
53 +compound_stmt: (if_stmt | while_stmt | for_stmt | try_stmt | with_stmt
54 + | funcdef | classdef | decorated)
55 +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
56 +while_stmt: 'while' test ':' suite ['else' ':' suite]
57 +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
58 +try_stmt: ('try' ':' suite
59 + ((except_clause ':' suite)+
60 + ['else' ':' suite]
61 + ['finally' ':' suite] |
62 + 'finally' ':' suite))
63 +with_stmt: 'with' test [ with_var ] ':' suite
64 +with_var: 'as' expr
65 +except_clause: 'except' [test ['as' NAME]]
66 +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
67 +
68 +test: or_test ['if' or_test 'else' test] | lambdef
69 +test_nocond: or_test | lambdef_nocond
70 +lambdef: 'lambda' [varargslist] ':' test
71 +lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
72 +or_test: and_test ('or' and_test)*
73 +and_test: not_test ('and' not_test)*
74 +not_test: 'not' not_test | comparison
75 +comparison: star_expr (comp_op star_expr)*
76 +comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'<>'|'in'|'not' 'in'|'is'|'is' 'not'
77 +star_expr: ['*'] expr
78 +expr: xor_expr ('|' xor_expr)*
79 +xor_expr: and_expr ('^' and_expr)*
80 +and_expr: shift_expr ('&' shift_expr)*
81 +shift_expr: arith_expr (('<<'|'>>') arith_expr)*
82 +arith_expr: term (('+'|'-') term)*
83 +term: factor (('*'|'/'|'%'|'//') factor)*
84 +factor: ('+'|'-'|'~') factor | power
85 +power: atom trailer* ['**' factor]
86 +atom: ('(' [yield_expr|testlist_comp] ')' |
87 + '[' [testlist_comp] ']' |
88 + '{' [dictorsetmaker] '}' |
89 + NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
90 +testlist_comp: test ( comp_for | (',' test)* [','] )
91 +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
92 +subscriptlist: subscript (',' subscript)* [',']
93 +subscript: test | [test] ':' [test] [sliceop]
94 +sliceop: ':' [test]
95 +exprlist: star_expr (',' star_expr)* [',']
96 +testlist: test (',' test)* [',']
97 +dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
98 + (test (comp_for | (',' test)* [','])) )
99 +
100 +classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
101 +
102 +arglist: (argument ',')* (argument [',']
103 + |'*' test (',' argument)* [',' '**' test]
104 + |'**' test)
105 +argument: test [comp_for] | test '=' test
106 +
107 +comp_iter: comp_for | comp_if
108 +comp_for: 'for' exprlist 'in' or_test [comp_iter]
109 +comp_if: 'if' test_nocond [comp_iter]
110 +
111 +yield_expr: 'yield' [testlist]
1 +"Usage: unparse.py <path to source file>"
2 +import sys
3 +import _ast
4 +from snakes.compat import *
5 +
6 +def interleave(inter, f, seq):
7 + """Call f on each item in seq, calling inter() in between.
8 + """
9 + seq = iter(seq)
10 + try:
11 + f(next(seq))
12 + except StopIteration:
13 + pass
14 + else:
15 + for x in seq:
16 + inter()
17 + f(x)
18 +
19 +class Unparser:
20 + """Methods in this class recursively traverse an AST and
21 + output source code for the abstract syntax; original formatting
22 + is disregarged. """
23 +
24 + def __init__(self, tree, file = sys.stdout):
25 + """Unparser(tree, file=sys.stdout) -> None.
26 + Print the source for tree to file."""
27 + self.f = file
28 + self._indent = 0
29 + self.dispatch(tree)
30 + self.f.write("\n")
31 + self.f.flush()
32 +
33 + def fill(self, text = ""):
34 + "Indent a piece of text, according to the current indentation level"
35 + self.f.write("\n" + " "*self._indent + text)
36 +
37 + def write(self, text):
38 + "Append a piece of text to the current line."
39 + self.f.write(text)
40 +
41 + def enter(self):
42 + "Print ':', and increase the indentation."
43 + self.write(":")
44 + self._indent += 1
45 +
46 + def leave(self):
47 + "Decrease the indentation level."
48 + self._indent -= 1
49 +
50 + def dispatch(self, tree):
51 + "Dispatcher function, dispatching tree type T to method _T."
52 + if isinstance(tree, list):
53 + for t in tree:
54 + self.dispatch(t)
55 + return
56 + meth = getattr(self, "_"+tree.__class__.__name__)
57 + meth(tree)
58 +
59 +
60 + ############### Unparsing methods ######################
61 + # There should be one method per concrete grammar type #
62 + # Constructors should be grouped by sum type. Ideally, #
63 + # this would follow the order in the grammar, but #
64 + # currently doesn't. #
65 + ########################################################
66 +
67 + def _Module(self, tree):
68 + for stmt in tree.body:
69 + self.dispatch(stmt)
70 +
71 + # stmt
72 + def _Expr(self, tree):
73 + self.fill()
74 + self.dispatch(tree.value)
75 +
76 + def _Import(self, t):
77 + self.fill("import ")
78 + interleave(lambda: self.write(", "), self.dispatch, t.names)
79 +
80 + def _ImportFrom(self, t):
81 + self.fill("from ")
82 + self.write(t.module)
83 + self.write(" import ")
84 + interleave(lambda: self.write(", "), self.dispatch, t.names)
85 + # XXX(jpe) what is level for?
86 +
87 + def _Assign(self, t):
88 + self.fill()
89 + for target in t.targets:
90 + self.dispatch(target)
91 + self.write(" = ")
92 + self.dispatch(t.value)
93 +
94 + def _AugAssign(self, t):
95 + self.fill()
96 + self.dispatch(t.target)
97 + self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
98 + self.dispatch(t.value)
99 +
100 + def _Return(self, t):
101 + self.fill("return")
102 + if t.value:
103 + self.write(" ")
104 + self.dispatch(t.value)
105 +
106 + def _Pass(self, t):
107 + self.fill("pass")
108 +
109 + def _Break(self, t):
110 + self.fill("break")
111 +
112 + def _Continue(self, t):
113 + self.fill("continue")
114 +
115 + def _Delete(self, t):
116 + self.fill("del ")
117 + self.dispatch(t.targets)
118 +
119 + def _Assert(self, t):
120 + self.fill("assert ")
121 + self.dispatch(t.test)
122 + if t.msg:
123 + self.write(", ")
124 + self.dispatch(t.msg)
125 +
126 + def _Exec(self, t):
127 + self.fill("exec ")
128 + self.dispatch(t.body)
129 + if t.globals:
130 + self.write(" in ")
131 + self.dispatch(t.globals)
132 + if t.locals:
133 + self.write(", ")
134 + self.dispatch(t.locals)
135 +
136 + def _Print(self, t):
137 + self.fill("print ")
138 + do_comma = False
139 + if t.dest:
140 + self.write(">>")
141 + self.dispatch(t.dest)
142 + do_comma = True
143 + for e in t.values:
144 + if do_comma:self.write(", ")
145 + else:do_comma=True
146 + self.dispatch(e)
147 + if not t.nl:
148 + self.write(",")
149 +
150 + def _Global(self, t):
151 + self.fill("global ")
152 + interleave(lambda: self.write(", "), self.write, t.names)
153 +
154 + def _Yield(self, t):
155 + self.write("(")
156 + self.write("yield")
157 + if t.value:
158 + self.write(" ")
159 + self.dispatch(t.value)
160 + self.write(")")
161 +
162 + def _Raise(self, t):
163 + self.fill('raise ')
164 + if t.type:
165 + self.dispatch(t.type)
166 + if t.inst:
167 + self.write(", ")
168 + self.dispatch(t.inst)
169 + if t.tback:
170 + self.write(", ")
171 + self.dispatch(t.tback)
172 +
173 + def _TryExcept(self, t):
174 + self.fill("try")
175 + self.enter()
176 + self.dispatch(t.body)
177 + self.leave()
178 +
179 + for ex in t.handlers:
180 + self.dispatch(ex)
181 + if t.orelse:
182 + self.fill("else")
183 + self.enter()
184 + self.dispatch(t.orelse)
185 + self.leave()
186 +
187 + def _TryFinally(self, t):
188 + self.fill("try")
189 + self.enter()
190 + self.dispatch(t.body)
191 + self.leave()
192 +
193 + self.fill("finally")
194 + self.enter()
195 + self.dispatch(t.finalbody)
196 + self.leave()
197 +
198 + def _ExceptHandler(self, t):
199 + self.fill("except")
200 + if t.type:
201 + self.write(" ")
202 + self.dispatch(t.type)
203 + if t.name:
204 + self.write(", ")
205 + self.dispatch(t.name)
206 + self.enter()
207 + self.dispatch(t.body)
208 + self.leave()
209 +
210 + def _ClassDef(self, t):
211 + self.write("\n")
212 + self.fill("class "+t.name)
213 + if t.bases:
214 + self.write("(")
215 + for a in t.bases:
216 + self.dispatch(a)
217 + self.write(", ")
218 + self.write(")")
219 + self.enter()
220 + self.dispatch(t.body)
221 + self.leave()
222 +
223 + def _FunctionDef(self, t):
224 + self.write("\n")
225 + for deco in t.decorator_list:
226 + self.fill("@")
227 + self.dispatch(deco)
228 + self.fill("def "+t.name + "(")
229 + self.dispatch(t.args)
230 + self.write(")")
231 + self.enter()
232 + self.dispatch(t.body)
233 + self.leave()
234 +
235 + def _For(self, t):
236 + self.fill("for ")
237 + self.dispatch(t.target)
238 + self.write(" in ")
239 + self.dispatch(t.iter)
240 + self.enter()
241 + self.dispatch(t.body)
242 + self.leave()
243 + if t.orelse:
244 + self.fill("else")
245 + self.enter()
246 + self.dispatch(t.orelse)
247 + self.leave
248 +
249 + def _If(self, t):
250 + self.fill("if ")
251 + self.dispatch(t.test)
252 + self.enter()
253 + # XXX elif?
254 + self.dispatch(t.body)
255 + self.leave()
256 + if t.orelse:
257 + self.fill("else")
258 + self.enter()
259 + self.dispatch(t.orelse)
260 + self.leave()
261 +
262 + def _While(self, t):
263 + self.fill("while ")
264 + self.dispatch(t.test)
265 + self.enter()
266 + self.dispatch(t.body)
267 + self.leave()
268 + if t.orelse:
269 + self.fill("else")
270 + self.enter()
271 + self.dispatch(t.orelse)
272 + self.leave
273 +
274 + def _With(self, t):
275 + self.fill("with ")
276 + self.dispatch(t.context_expr)
277 + if t.optional_vars:
278 + self.write(" as ")
279 + self.dispatch(t.optional_vars)
280 + self.enter()
281 + self.dispatch(t.body)
282 + self.leave()
283 +
284 + # expr
285 + def _Str(self, tree):
286 + self.write(repr(tree.s))
287 +
288 + def _Name(self, t):
289 + self.write(t.id)
290 +
291 + def _Repr(self, t):
292 + self.write("`")
293 + self.dispatch(t.value)
294 + self.write("`")
295 +
296 + def _Num(self, t):
297 + self.write(repr(t.n))
298 +
299 + def _List(self, t):
300 + self.write("[")
301 + interleave(lambda: self.write(", "), self.dispatch, t.elts)
302 + self.write("]")
303 +
304 + def _ListComp(self, t):
305 + self.write("[")
306 + self.dispatch(t.elt)
307 + for gen in t.generators:
308 + self.dispatch(gen)
309 + self.write("]")
310 +
311 + def _GeneratorExp(self, t):
312 + self.write("(")
313 + self.dispatch(t.elt)
314 + for gen in t.generators:
315 + self.dispatch(gen)
316 + self.write(")")
317 +
318 + def _comprehension(self, t):
319 + self.write(" for ")
320 + self.dispatch(t.target)
321 + self.write(" in ")
322 + self.dispatch(t.iter)
323 + for if_clause in t.ifs:
324 + self.write(" if ")
325 + self.dispatch(if_clause)
326 +
327 + def _IfExp(self, t):
328 + self.write("(")
329 + self.dispatch(t.body)
330 + self.write(" if ")
331 + self.dispatch(t.test)
332 + self.write(" else ")
333 + self.dispatch(t.orelse)
334 + self.write(")")
335 +
336 + def _Dict(self, t):
337 + self.write("{")
338 + def writem(arg):
339 + (k, v) = arg
340 + self.dispatch(k)
341 + self.write(": ")
342 + self.dispatch(v)
343 + interleave(lambda: self.write(", "), writem, zip(t.keys, t.values))
344 + self.write("}")
345 +
346 + def _Set(self, t) :
347 + self.write("set([")
348 + if len(t.elts) == 1:
349 + (elt,) = t.elts
350 + self.dispatch(elt)
351 + self.write(",")
352 + else:
353 + interleave(lambda: self.write(", "), self.dispatch, t.elts)
354 + self.write("])")
355 +
356 + def _Tuple(self, t):
357 + self.write("(")
358 + if len(t.elts) == 1:
359 + (elt,) = t.elts
360 + self.dispatch(elt)
361 + self.write(",")
362 + else:
363 + interleave(lambda: self.write(", "), self.dispatch, t.elts)
364 + self.write(")")
365 +
366 + unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
367 + def _UnaryOp(self, t):
368 + self.write(self.unop[t.op.__class__.__name__])
369 + self.write("(")
370 + self.dispatch(t.operand)
371 + self.write(")")
372 +
373 + binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%",
374 + "LShift":">>", "RShift":"<<", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
375 + "FloorDiv":"//", "Pow": "**"}
376 + def _BinOp(self, t):
377 + self.write("(")
378 + self.dispatch(t.left)
379 + self.write(" " + self.binop[t.op.__class__.__name__] + " ")
380 + self.dispatch(t.right)
381 + self.write(")")
382 +
383 + cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
384 + "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
385 + def _Compare(self, t):
386 + self.write("(")
387 + self.dispatch(t.left)
388 + for o, e in zip(t.ops, t.comparators):
389 + self.write(" " + self.cmpops[o.__class__.__name__] + " ")
390 + self.dispatch(e)
391 + self.write(")")
392 +
393 + boolops = {"And": 'and', "Or": 'or'}
394 + def _BoolOp(self, t):
395 + self.write("(")
396 + s = " %s " % self.boolops[t.op.__class__.__name__]
397 + interleave(lambda: self.write(s), self.dispatch, t.values)
398 + self.write(")")
399 +
400 + def _Attribute(self,t):
401 + self.dispatch(t.value)
402 + self.write(".")
403 + self.write(t.attr)
404 +
405 + def _Call(self, t):
406 + self.dispatch(t.func)
407 + self.write("(")
408 + comma = False
409 + for e in t.args:
410 + if comma: self.write(", ")
411 + else: comma = True
412 + self.dispatch(e)
413 + for e in t.keywords:
414 + if comma: self.write(", ")
415 + else: comma = True
416 + self.dispatch(e)
417 + if t.starargs:
418 + if comma: self.write(", ")
419 + else: comma = True
420 + self.write("*")
421 + self.dispatch(t.starargs)
422 + if t.kwargs:
423 + if comma: self.write(", ")
424 + else: comma = True
425 + self.write("**")
426 + self.dispatch(t.kwargs)
427 + self.write(")")
428 +
429 + def _Subscript(self, t):
430 + self.dispatch(t.value)
431 + self.write("[")
432 + self.dispatch(t.slice)
433 + self.write("]")
434 +
435 + # slice
436 + def _Ellipsis(self, t):
437 + self.write("...")
438 +
439 + def _Index(self, t):
440 + self.dispatch(t.value)
441 +
442 + def _Slice(self, t):
443 + if t.lower:
444 + self.dispatch(t.lower)
445 + self.write(":")
446 + if t.upper:
447 + self.dispatch(t.upper)
448 + if t.step:
449 + self.write(":")
450 + self.dispatch(t.step)
451 +
452 + def _ExtSlice(self, t):
453 + interleave(lambda: self.write(', '), self.dispatch, t.dims)
454 +
455 + # others
456 + def _arguments(self, t):
457 + first = True
458 + nonDef = len(t.args)-len(t.defaults)
459 + for a in t.args[0:nonDef]:
460 + if first:first = False
461 + else: self.write(", ")
462 + self.dispatch(a)
463 + for a,d in zip(t.args[nonDef:], t.defaults):
464 + if first:first = False
465 + else: self.write(", ")
466 + self.dispatch(a),
467 + self.write("=")
468 + self.dispatch(d)
469 + if t.vararg:
470 + if first:first = False
471 + else: self.write(", ")
472 + self.write("*"+t.vararg)
473 + if t.kwarg:
474 + if first:first = False
475 + else: self.write(", ")
476 + self.write("**"+t.kwarg)
477 +
478 + def _keyword(self, t):
479 + self.write(t.arg)
480 + self.write("=")
481 + self.dispatch(t.value)
482 +
483 + def _Lambda(self, t):
484 + self.write("lambda ")
485 + self.dispatch(t.args)
486 + self.write(": ")
487 + self.dispatch(t.body)
488 +
489 + def _alias(self, t):
490 + self.write(t.name)
491 + if t.asname:
492 + self.write(" as "+t.asname)
493 +
494 +def roundtrip(filename, output=sys.stdout):
495 + source = open(filename).read()
496 + tree = compile(source, filename, "exec", _ast.PyCF_ONLY_AST)
497 + Unparser(tree, output)
498 +
499 +
500 +
501 +def testdir(a):
502 + try:
503 + names = [n for n in os.listdir(a) if n.endswith('.py')]
504 + except OSError:
505 + sys.stderr.write("Directory not readable: %s\n" % a)
506 + else:
507 + for n in names:
508 + fullname = os.path.join(a, n)
509 + if os.path.isfile(fullname):
510 + output = io.StringIO()
511 + print('Testing %s' % fullname)
512 + try:
513 + roundtrip(fullname, output)
514 + except Exception:
515 + e = sys.exc_info()[1]
516 + print(' Failed to compile, exception is %r' % e)
517 + elif os.path.isdir(fullname):
518 + testdir(fullname)
519 +
520 +def main(args):
521 + if args[0] == '--testdir':
522 + for a in args[1:]:
523 + testdir(a)
524 + else:
525 + for a in args:
526 + roundtrip(a)
527 +
528 +if __name__=='__main__':
529 + main(sys.argv[1:])
This diff could not be displayed because it is too large.
1 +"""A plugins system.
2 +
3 +The first example shows how to load a plugin: we load
4 +C{snakes.plugins.hello} and plug it into C{snakes.nets}, which results
5 +in a new module that actually C{snakes.nets} extended by
6 +C{snakes.plugins.hello}.
7 +
8 +>>> import snakes.plugins as plugins
9 +>>> hello_nets = plugins.load('hello', 'snakes.nets')
10 +>>> n = hello_nets.PetriNet('N')
11 +>>> n.hello()
12 +Hello from N
13 +>>> n = hello_nets.PetriNet('N', hello='Hi, this is %s!')
14 +>>> n.hello()
15 +Hi, this is N!
16 +
17 +The next example shows how to simulate the effect of C{import module}:
18 +we give to C{load} a thrid argument that is the name of the created
19 +module, from which it becomes possible to import names or C{*}.
20 +
21 +B{Warning:} this feature will not work C{load} is not called from the
22 +module where we then do the C{from ... import ...}. This is exactly
23 +the same when, from a module C{foo} that you load a module C{bar}: if
24 +C{bar} loads other modules they will not be imported in C{foo}.
25 +
26 +>>> plugins.load('hello', 'snakes.nets', 'another_version')
27 +<module ...>
28 +>>> from another_version import PetriNet
29 +>>> n = PetriNet('another net')
30 +>>> n.hello()
31 +Hello from another net
32 +>>> n = PetriNet('yet another net', hello='Hi, this is %s!')
33 +>>> n.hello()
34 +Hi, this is yet another net!
35 +
36 +How to define a plugin is explained in the example C{hello}.
37 +"""
38 +
39 +import imp, sys, inspect
40 +from functools import wraps
41 +
42 +def update (module, objects) :
43 + """Update a module content
44 + """
45 + for obj in objects :
46 + if isinstance(obj, tuple) :
47 + try :
48 + n, o = obj
49 + except :
50 + raise ValueError("expected (name, object) and got '%r'" % obj)
51 + setattr(module, n, o)
52 + elif inspect.isclass(obj) or inspect.isfunction(obj) :
53 + setattr(module, obj.__name__, obj)
54 + else :
55 + raise ValueError("cannot plug '%r'" % obj)
56 +
57 +def build (name, module, *objects) :
58 + """Builds an extended module.
59 +
60 + The parameter C{module} is exactly that taken by the function
61 + C{extend} of a plugin. This list argument C{objects} holds all the
62 + objects, constructed in C{extend}, that are extensions of objects
63 + from C{module}. The resulting value should be returned by
64 + C{extend}.
65 +
66 + @param name: the name of the constructed module
67 + @type name: C{str}
68 + @param module: the extended module
69 + @type module: C{module}
70 + @param objects: the sub-objects
71 + @type objects: each is a class object
72 + @return: the new module
73 + @rtype: C{module}
74 + """
75 + result = imp.new_module(name)
76 + result.__dict__.update(module.__dict__)
77 + update(result, objects)
78 + result.__plugins__ = (module.__dict__.get("__plugins__",
79 + (module.__name__,))
80 + + (name,))
81 + for obj in objects :
82 + if inspect.isclass(obj) :
83 + obj.__plugins__ = result.__plugins__
84 + return result
85 +
86 +def load (plugins, base, name=None) :
87 + """Load plugins.
88 +
89 + C{plugins} can be a single plugin name or module or a list of such
90 + values. If C{name} is not C{None}, the extended module is loaded
91 + ad C{name} in C{sys.modules} as well as in the global environment
92 + from which C{load} was called.
93 +
94 + @param plugins: the module that implements the plugin, or its name,
95 + or a collection of such values
96 + @type plugins: C{str} or C{module}, or a C{list}/C{tuple}/... of
97 + such values
98 + @param base: the module being extended or its name
99 + @type base: C{str} or C{module}
100 + @param name: the name of the created module
101 + @type name: C{str}
102 + @return: the extended module
103 + @rtype: C{module}
104 + """
105 + if type(base) is str :
106 + result = __import__(base, fromlist=["__name__"])
107 + else :
108 + result = base
109 + if isinstance(plugins, str) :
110 + plugins = [plugins]
111 + else :
112 + try :
113 + plugins = list(plugins)
114 + except TypeError :
115 + plugins = [plugins]
116 + for i, p in enumerate(plugins) :
117 + if isinstance(p, str) and not p.startswith("snakes.plugins.") :
118 + plugins[i] = "snakes.plugins." + p
119 + for plug in plugins :
120 + if type(plug) is str :
121 + plug = __import__(plug, fromlist=["__name__"])
122 + result = plug.extend(result)
123 + if name is not None :
124 + result.__name__ = name
125 + sys.modules[name] = result
126 + inspect.stack()[1][0].f_globals[name] = result
127 + return result
128 +
129 +def plugin (base, depends=[], conflicts=[]) :
130 + """Decorator for extension functions
131 +
132 + @param base: name of base module (usually 'snakes.nets')
133 + @type base: str
134 + @param depends: list of plugins on which this one depends
135 + @type depends: list of str
136 + @param conflicts: list of plugins with which this one conflicts
137 + @type conflicts: list of str
138 + @return: the appropriate decorator
139 + @rtype: function
140 + """
141 + def wrapper (fun) :
142 + @wraps(fun)
143 + def extend (module) :
144 + try :
145 + loaded = set(module.__plugins__)
146 + except AttributeError :
147 + loaded = set()
148 + for name in depends :
149 + if name not in loaded :
150 + module = load(name, module)
151 + loaded.update(module.__plugins__)
152 + conf = set(conflicts) & loaded
153 + if len(conf) > 0 :
154 + raise ValueError("plugin conflict (%s)" % ", ".join(conf))
155 + objects = fun(module)
156 + if type(objects) is not tuple :
157 + objects = (objects,)
158 + return build(fun.__module__, module, *objects)
159 + module = sys.modules[fun.__module__]
160 + module.__test__ = {"extend" : extend}
161 + objects = fun(__import__(base, fromlist=["__name__"]))
162 + if type(objects) is not tuple :
163 + objects = (objects,)
164 + update(module, objects)
165 + return extend
166 + return wrapper
167 +
168 +def new_instance (cls, obj) :
169 + """Create a copy of C{obj} which is an instance of C{cls}
170 + """
171 + result = object.__new__(cls)
172 + result.__dict__.update(obj.__dict__)
173 + return result
1 +import snakes.plugins
2 +from snakes.plugins import new_instance
3 +from snakes.pnml import Tree
4 +from snakes.data import iterate
5 +
6 +class Cluster (object) :
7 + def __init__ (self, nodes=[], children=[]) :
8 + """
9 + >>> Cluster(['a', 'b'],
10 + ... [Cluster(['1', '2'],
11 + ... [Cluster(['A'])]),
12 + ... Cluster(['3', '4', '5'],
13 + ... [Cluster(['C', 'D'])])])
14 + Cluster(...)
15 +
16 + """
17 + self._nodes = set(nodes)
18 + self._children = []
19 + self._cluster = {}
20 + for child in children :
21 + self.add_child(child)
22 + __pnmltag__ = "clusters"
23 + def __pnmldump__ (self) :
24 + """
25 + >>> Cluster(['a', 'b'],
26 + ... [Cluster(['1', '2'],
27 + ... [Cluster(['A'])]),
28 + ... Cluster(['3', '4', '5'],
29 + ... [Cluster(['C', 'D'])])]).__pnmldump__()
30 + <?xml version="1.0" encoding="utf-8"?>
31 + <pnml>...
32 + <clusters>
33 + <node>
34 + ...
35 + </node>
36 + <node>
37 + ...
38 + </node>
39 + <clusters>
40 + ...
41 + </clusters>
42 + </clusters>
43 + </pnml>
44 + """
45 + result = Tree(self.__pnmltag__, None)
46 + for node in self._nodes :
47 + result.add_child(Tree("node", node))
48 + for child in self._children :
49 + result.add_child(Tree.from_obj(child))
50 + return result
51 + @classmethod
52 + def __pnmlload__ (cls, tree) :
53 + """
54 + >>> t = Cluster(['a', 'b'],
55 + ... [Cluster(['1', '2'],
56 + ... [Cluster(['A'])]),
57 + ... Cluster(['3', '4', '5'],
58 + ... [Cluster(['C', 'D'])])]).__pnmldump__()
59 + >>> Cluster.__pnmlload__(t)
60 + Cluster(['...', '...'],
61 + [Cluster(['...', '...'],
62 + [Cluster(['A'], [])]),
63 + Cluster(['...', '...', '...'],
64 + [Cluster(['...', '...'], [])])])
65 + """
66 +
67 + result = cls()
68 + for child in tree.children :
69 + if child.name == "node" :
70 + result.add_node(child.data)
71 + else :
72 + result.add_child(child.to_obj())
73 + return result
74 + def __str__ (self) :
75 + return "cluster_%s" % str(id(self)).replace("-", "m")
76 + def __repr__ (self) :
77 + """
78 + >>> Cluster(['a', 'b'],
79 + ... [Cluster(['1', '2'],
80 + ... [Cluster(['A'])]),
81 + ... Cluster(['3', '4', '5'],
82 + ... [Cluster(['C', 'D'])])])
83 + Cluster(['...', '...'],
84 + [Cluster(['...', '...'],
85 + [Cluster(['A'], [])]),
86 + Cluster(['...', '...', '...'],
87 + [Cluster(['...', '...'], [])])])
88 + """
89 + return "%s([%s], [%s])" % (self.__class__.__name__,
90 + ", ".join(repr(n) for n in self.nodes()),
91 + ", ".join(repr(c) for c in self.children()))
92 + def copy (self) :
93 + """
94 + >>> Cluster(['a', 'b'],
95 + ... [Cluster(['1', '2'],
96 + ... [Cluster(['A'])]),
97 + ... Cluster(['3', '4', '5'],
98 + ... [Cluster(['C', 'D'])])]).copy()
99 + Cluster(['...', '...'],
100 + [Cluster(['...', '...'],
101 + [Cluster(['A'], [])]),
102 + Cluster(['...', '...', '...'],
103 + [Cluster(['...', '...'], [])])])
104 + """
105 + return self.__class__(self._nodes,
106 + (child.copy() for child in self._children))
107 + def get_path (self, name) :
108 + """
109 + >>> Cluster(['a', 'b'],
110 + ... [Cluster(['1', '2'],
111 + ... [Cluster(['A'])]),
112 + ... Cluster(['3', '4', '5'],
113 + ... [Cluster(['C', 'D'])])]).get_path('C')
114 + [1, 0]
115 + """
116 + if name in self._nodes :
117 + return []
118 + else :
119 + for num, child in enumerate(self._children) :
120 + if name in child :
121 + return [num] + child.get_path(name)
122 + def add_node (self, name, path=None) :
123 + """
124 + >>> c = Cluster(['a', 'b'],
125 + ... [Cluster(['1', '2'],
126 + ... [Cluster(['A'])]),
127 + ... Cluster(['3', '4', '5'],
128 + ... [Cluster(['C', 'D'])])])
129 + >>> c.add_node('c')
130 + Cluster([...'c'...], ...)
131 + >>> c.add_node('E', [1, 0])
132 + Cluster([...'E'...], [])
133 + >>> c
134 + Cluster([...'c'...],
135 + [Cluster(['...', '...'],
136 + [Cluster(['A'], [])]),
137 + Cluster(['...', '...', '...'],
138 + [Cluster([...'E'...], [])])])
139 + """
140 + if path in (None, [], ()) :
141 + self._nodes.add(name)
142 + return self
143 + else :
144 + while len(self._children) <= path[0] :
145 + self._children.append(self.__class__())
146 + target = self._children[path[0]].add_node(name, path[1:])
147 + self._cluster[name] = target
148 + return target
149 + def remove_node (self, name) :
150 + """
151 + >>> c = Cluster(['a', 'b'],
152 + ... [Cluster(['1', '2'],
153 + ... [Cluster(['A'])]),
154 + ... Cluster(['3', '4', '5'],
155 + ... [Cluster(['C', 'D'])])])
156 + >>> c.remove_node('4')
157 + >>> c
158 + Cluster(['...', '...'],
159 + [Cluster(['...', '...'],
160 + [Cluster(['A'], [])]),
161 + Cluster(['...', '...'],
162 + [Cluster(['...', '...'], [])])])
163 +
164 + """
165 + if name in self._cluster :
166 + self._cluster[name].remove_node(name)
167 + else :
168 + self._nodes.remove(name)
169 + def rename_node (self, old, new) :
170 + """
171 + >>> c = Cluster(['a', 'b'],
172 + ... [Cluster(['1', '2'],
173 + ... [Cluster(['A'])]),
174 + ... Cluster(['3', '4', '5'],
175 + ... [Cluster(['C', 'D'])])])
176 + >>> c.rename_node('4', '42')
177 + >>> c
178 + Cluster(['...', '...'],
179 + [Cluster(['...', '...'],
180 + [Cluster(['A'], [])]),
181 + Cluster([...'42'...],
182 + [Cluster(['...', '...'], [])])])
183 + """
184 + if old in self._cluster :
185 + self._cluster[old].rename_node(old, new)
186 + self._cluster[new] = self._cluster[old]
187 + del self._cluster[old]
188 + elif old in self._nodes :
189 + self._nodes.remove(old)
190 + self._nodes.add(new)
191 + else :
192 + for child in self.children() :
193 + child.rename_node(old, new)
194 + def add_child (self, cluster=None) :
195 + """
196 + >>> c = Cluster(['a', 'b'],
197 + ... [Cluster(['1', '2'],
198 + ... [Cluster(['A'])]),
199 + ... Cluster(['3', '4', '5'],
200 + ... [Cluster(['C', 'D'])])])
201 + >>> c.add_child(c.copy())
202 + >>> c
203 + Cluster(['...', '...'],
204 + [Cluster(['...', '...'],
205 + [Cluster(['A'], [])]),
206 + Cluster(['...', '...', '...'],
207 + [Cluster(['...', '...'], [])]),
208 + Cluster(['...', '...'],
209 + [Cluster(['...', '...'],
210 + [Cluster(['A'], [])]),
211 + Cluster(['...', '...', '...'],
212 + [Cluster(['...', '...'], [])])])])
213 + """
214 + if cluster is None :
215 + cluster = Cluster()
216 + self._cluster.update(cluster._cluster)
217 + for node in cluster._nodes :
218 + self._cluster[node] = cluster
219 + self._children.append(cluster)
220 + def nodes (self, all=False) :
221 + """
222 + >>> list(sorted(Cluster(['a', 'b'],
223 + ... [Cluster(['1', '2'],
224 + ... [Cluster(['A'])]),
225 + ... Cluster(['3', '4', '5'],
226 + ... [Cluster(['C', 'D'])])]).nodes()))
227 + ['a', 'b']
228 + >>> list(sorted(Cluster(['a', 'b'],
229 + ... [Cluster(['1', '2'],
230 + ... [Cluster(['A'])]),
231 + ... Cluster(['3', '4', '5'],
232 + ... [Cluster(['C', 'D'])])]).nodes(True)))
233 + ['1', '2', '3', '4', '5', 'A', 'C', 'D', 'a', 'b']
234 + """
235 + if all :
236 + result = set()
237 + for cluster in self :
238 + result.update(cluster.nodes())
239 + return result
240 + else :
241 + return set(self._nodes)
242 + def children (self) :
243 + """
244 + >>> Cluster(['a', 'b'],
245 + ... [Cluster(['1', '2'],
246 + ... [Cluster(['A'])]),
247 + ... Cluster(['3', '4', '5'],
248 + ... [Cluster(['C', 'D'])])]).children()
249 + (Cluster(['...', '...'],
250 + [Cluster(['A'], [])]),
251 + Cluster(['...', '...', '...'],
252 + [Cluster(['...', '...'], [])]))
253 + """
254 + return tuple(self._children)
255 + def __contains__ (self, name) :
256 + """
257 + >>> c = Cluster(['a', 'b'],
258 + ... [Cluster(['1', '2'],
259 + ... [Cluster(['A'])]),
260 + ... Cluster(['3', '4', '5'],
261 + ... [Cluster(['C', 'D'])])])
262 + >>> 'a' in c
263 + True
264 + >>> 'x' in c
265 + False
266 + >>> '4' in c
267 + True
268 + """
269 + if name in self._nodes :
270 + return True
271 + for child in self._children :
272 + if name in child :
273 + return True
274 + return False
275 + def __iter__ (self) :
276 + """
277 + >>> c = Cluster(['a', 'b'],
278 + ... [Cluster(['1', '2'],
279 + ... [Cluster(['A'])]),
280 + ... Cluster(['3', '4', '5'],
281 + ... [Cluster(['C', 'D'])])])
282 + >>> for cluster in c :
283 + ... print(list(sorted(cluster.nodes())))
284 + ['a', 'b']
285 + ['1', '2']
286 + ['A']
287 + ['3', '4', '5']
288 + ['C', 'D']
289 + """
290 + yield self
291 + for child in self._children :
292 + for item in child :
293 + yield item
294 +
295 +@snakes.plugins.plugin("snakes.nets")
296 +def extend (module) :
297 + class PetriNet (module.PetriNet) :
298 + def __init__ (self, name, **options) :
299 + module.PetriNet.__init__(self, name, **options)
300 + self.clusters = Cluster()
301 + def copy (self, name=None, **options) :
302 + result = module.PetriNet.copy(self, name, **options)
303 + result.clusters = self.clusters.copy()
304 + return result
305 + def __pnmldump__ (self) :
306 + result = module.PetriNet.__pnmldump__(self)
307 + result.add_child(Tree.from_obj(self.clusters))
308 + return result
309 + @classmethod
310 + def __pnmlload__ (cls, tree) :
311 + result = new_instance(cls, module.PetriNet.__pnmlload__(tree))
312 + result.clusters = tree.child(Cluster.__pnmltag__).to_obj()
313 + return result
314 + def add_place (self, place, **options) :
315 + path = options.pop("cluster", None)
316 + module.PetriNet.add_place(self, place, **options)
317 + self.clusters.add_node(place.name, path)
318 + def remove_place (self, name, **options) :
319 + module.PetriNet.remove_place(self, name, **options)
320 + self.clusters.remove_node(name)
321 + def add_transition (self, trans, **options) :
322 + path = options.pop("cluster", None)
323 + module.PetriNet.add_transition(self, trans, **options)
324 + self.clusters.add_node(trans.name, path)
325 + def remove_transition (self, name, **options) :
326 + module.PetriNet.remove_transition(self, name, **options)
327 + self.clusters.remove_node(name)
328 + def rename_node (self, old, new, **options) :
329 + module.PetriNet.rename_node(self, old, new, **options)
330 + self.clusters.rename_node(old, new)
331 + return PetriNet, Cluster
1 +"""Draw Petri nets using PyGraphViz
2 +
3 + - adds a method C{draw} to C{PetriNet} and C{StateGraph} that creates
4 + a drawing of the object in a file.
5 +
6 +>>> import snakes.plugins
7 +>>> snakes.plugins.load('gv', 'snakes.nets', 'nets')
8 +<module ...>
9 +>>> from nets import *
10 +>>> n = PetriNet('N')
11 +>>> n.add_place(Place('p00', [0]))
12 +>>> n.add_transition(Transition('t10'))
13 +>>> n.add_place(Place('p11'))
14 +>>> n.add_transition(Transition('t01'))
15 +>>> n.add_input('p00', 't10', Variable('x'))
16 +>>> n.add_output('p11', 't10', Expression('x+1'))
17 +>>> n.add_input('p11', 't01', Variable('y'))
18 +>>> n.add_output('p00', 't01', Expression('y-1'))
19 +
20 +>>> for engine in ('neato', 'dot', 'circo', 'twopi', 'fdp') :
21 +... n.draw(',test-gv-%s.png' % engine, engine=engine)
22 +
23 +>>> s = StateGraph(n)
24 +>>> s.build()
25 +>>> s.draw(',test-gv-graph.png')
26 +
27 +>>> for node in sorted(n.node(), key=str) :
28 +... node.pos.moveto(-100, -100)
29 +>>> n.layout()
30 +>>> any(node.pos == (-100, -100) for node in sorted(n.node(), key=str))
31 +False
32 +"""
33 +
34 +import os, os.path, subprocess, collections
35 +import snakes.plugins
36 +from snakes.plugins.clusters import Cluster
37 +from snakes.compat import *
38 +
39 +class Graph (Cluster) :
40 + def __init__ (self, attr) :
41 + Cluster.__init__(self)
42 + self.attributes = {}
43 + self.attr = dict(style="invis")
44 + self.attr.update(attr)
45 + self.edges = collections.defaultdict(list)
46 + def add_node (self, node, attr) :
47 + self.attributes[node] = attr
48 + Cluster.add_node(self, node)
49 + def add_edge (self, src, dst, attr) :
50 + self.edges[src, dst].append(attr)
51 + def has_edge (self, src, dst) :
52 + if (src, dst) in self.edges :
53 + return True
54 + for child in self.children() :
55 + if child.has_edge(src, dst) :
56 + return True
57 + return False
58 + def _dot_attr (self, attr, tag=None) :
59 + if tag is None :
60 + tag = ""
61 + else :
62 + tag = "%s " % tag
63 + return (["%s[" % tag,
64 + ["%s=%s" % (key, self.escape(str(val)))
65 + for key, val in attr.items()],
66 + "];"])
67 + def _dot (self) :
68 + body = []
69 + lines = ["subgraph %s {" % self, self._dot_attr(self.attr, "graph"),
70 + body, "}"]
71 + for node in self.nodes() :
72 + body.append(node)
73 + body.append(self._dot_attr(self.attributes[node]))
74 + for child in self.children() :
75 + body.extend(child._dot())
76 + for (src, dst), lst in self.edges.items() :
77 + for attr in lst :
78 + body.append("%s -> %s" % (src, dst))
79 + body.append(self._dot_attr(attr))
80 + return lines
81 + def _dot_text (self, lines, indent=0) :
82 + for l in lines :
83 + if isinstance(l, str) :
84 + yield " "*indent*2 + l
85 + else :
86 + for x in self._dot_text(l, indent+1) :
87 + yield x
88 + def dot (self) :
89 + self.done = set()
90 + return "\n".join(self._dot_text(["digraph {",
91 + ['node [label="N",'
92 + ' fillcolor="#FFFFFF",'
93 + ' fontcolor="#000000",'
94 + ' style=filled];',
95 + 'edge [style="solid"];',
96 + 'graph [splines="true",'
97 + ' overlap="false"];'],
98 + self._dot(),
99 + "}"]))
100 + def escape (self, text) :
101 + return '"%s"' % text.replace('"', r'\"')
102 + def render (self, filename, engine="dot", debug=False) :
103 + if engine not in ("dot", "neato", "twopi", "circo", "fdp") :
104 + raise ValueError("unknown GraphViz engine %r" % engine)
105 + outfile = open(filename + ".dot", "w")
106 + outfile.write(self.dot())
107 + outfile.close()
108 + if debug :
109 + dot = subprocess.Popen([engine, "-T" + filename.rsplit(".", 1)[-1],
110 + "-o" + filename, outfile.name],
111 + stdin=subprocess.PIPE)
112 + else :
113 + dot = subprocess.Popen([engine, "-T" + filename.rsplit(".", 1)[-1],
114 + "-o" + filename, outfile.name],
115 + stdin=subprocess.PIPE,
116 + stdout=subprocess.PIPE,
117 + stderr=subprocess.PIPE)
118 + dot.communicate()
119 + if not debug :
120 + os.unlink(outfile.name)
121 + if dot.returncode != 0 :
122 + raise IOError("%s exited with status %s" % (engine, dot.returncode))
123 + def layout (self, engine="dot", debug=False) :
124 + if engine not in ("dot", "neato", "twopi", "circo", "fdp") :
125 + raise ValueError("unknown GraphViz engine %r" % engine)
126 + if debug :
127 + dot = subprocess.Popen([engine, "-Tplain"],
128 + stdin=subprocess.PIPE,
129 + stdout=subprocess.PIPE)
130 + else :
131 + dot = subprocess.Popen([engine, "-Tplain"],
132 + stdin=subprocess.PIPE,
133 + stdout=subprocess.PIPE,
134 + stderr=subprocess.PIPE)
135 + if PY3 :
136 + out, err = dot.communicate(bytes(self.dot(),
137 + snakes.defaultencoding))
138 + out = out.decode(snakes.defaultencoding)
139 + else :
140 + out, err = dot.communicate(self.dot())
141 + if dot.returncode != 0 :
142 + raise IOError("%s exited with status %s"
143 + % (engine, dot.returncode))
144 + for line in (l.strip() for l in out.splitlines()
145 + if l.strip().startswith("node")) :
146 + node, name, x, y, rest = line.split(None, 4)
147 + yield name, float(x), float(y)
148 +
149 +@snakes.plugins.plugin("snakes.nets",
150 + depends=["snakes.plugins.clusters",
151 + "snakes.plugins.pos"])
152 +def extend (module) :
153 + class PetriNet (module.PetriNet) :
154 + "An extension with a method C{draw}"
155 + def draw (self, filename=None, engine="dot", debug=False,
156 + graph_attr=None, cluster_attr=None,
157 + place_attr=None, trans_attr=None, arc_attr=None) :
158 + """
159 + @param filename: the name of the image file to create or
160 + C{None} if only the computed graph is needed
161 + @type filename: C{None} or C{str}
162 + @param engine: the layout engine to use: 'dot' (default),
163 + 'neato', 'circo', 'twopi' or 'fdp'
164 + @type engine: C{str}
165 + @param place_attr: a function to format places, it will be
166 + called with the place and its attributes dict as
167 + parameters
168 + @type place_attr: C{function(Place,dict)->None}
169 + @param trans_attr: a function to format transitions, it
170 + will be called with the transition and its attributes dict
171 + as parameters
172 + @type trans_attr: C{function(Transition,dict)->None}
173 + @param arc_attr: a function to format arcs, it will be
174 + called with the label and its attributes dict as
175 + parameters
176 + @type arc_attr: C{function(ArcAnnotation,dict)->None}
177 + @param cluster_attr: a function to format clusters of
178 + nodes, it will be called with the cluster and its
179 + attributes dict as parameters
180 + @type cluster_attr: C{function(snakes.plugins.clusters.Cluster,dict)->None}
181 + @return: C{None} if C{filename} is not C{None}, the
182 + computed graph otherwise
183 + @rtype: C{None} or C{pygraphviz.AGraph}
184 + """
185 + nodemap = dict((node.name, "node_%s" % num)
186 + for num, node in enumerate(self.node()))
187 + g = self._copy(nodemap, self.clusters, cluster_attr,
188 + place_attr, trans_attr)
189 + self._copy_edges(nodemap, g, arc_attr)
190 + if graph_attr :
191 + graph_attr(self, g.attr)
192 + if filename is None :
193 + g.nodemap = nodemap
194 + return g
195 + else :
196 + g.render(filename, engine, debug)
197 + def _copy (self, nodemap, sub, cluster_attr, place_attr, trans_attr) :
198 + attr = dict(style="invis")
199 + if cluster_attr :
200 + cluster_attr(sub, attr)
201 + graph = Graph(attr)
202 + for name in sub.nodes() :
203 + if self.has_place(name) :
204 + node = self.place(name)
205 + attr = dict(shape="ellipse",
206 + label="%s\\n%s" % (node.name, node.tokens))
207 + if place_attr :
208 + place_attr(node, attr)
209 + else :
210 + node = self.transition(name)
211 + attr = dict(shape="rectangle",
212 + label="%s\\n%s" % (node.name, str(node.guard)))
213 + if trans_attr :
214 + trans_attr(node, attr)
215 + graph.add_node(nodemap[name], attr)
216 + for child in sub.children() :
217 + graph.add_child(self._copy(nodemap, child, cluster_attr,
218 + place_attr, trans_attr))
219 + return graph
220 + def _copy_edges (self, nodemap, graph, arc_attr) :
221 + for trans in self.transition() :
222 + for place, label in trans.input() :
223 + attr = dict(arrowhead="normal",
224 + label=" %s " % label)
225 + if arc_attr :
226 + arc_attr(label, attr)
227 + graph.add_edge(nodemap[place.name], nodemap[trans.name],
228 + attr)
229 + for place, label in trans.output() :
230 + attr = dict(arrowhead="normal",
231 + label=" %s " % label)
232 + if arc_attr :
233 + arc_attr(label, attr)
234 + graph.add_edge(nodemap[trans.name], nodemap[place.name],
235 + attr)
236 + def layout (self, xscale=1.0, yscale=1.0, engine="dot",
237 + debug=False, graph_attr=None, cluster_attr=None,
238 + place_attr=None, trans_attr=None, arc_attr=None) :
239 + g = self.draw(None, engine, debug, graph_attr, cluster_attr,
240 + place_attr, trans_attr, arc_attr)
241 + node = dict((v, k) for k, v in g.nodemap.items())
242 + for n, x, y in g.layout(engine, debug) :
243 + self.node(node[n]).pos.moveto(x*xscale, y*yscale)
244 +
245 + class StateGraph (module.StateGraph) :
246 + "An extension with a method C{draw}"
247 + def draw (self, filename=None, engine="dot", debug=False,
248 + node_attr=None, edge_attr=None, graph_attr=None) :
249 + """
250 + @param filename: the name of the image file to create or
251 + C{None} if only the computed graph is needed
252 + @type filename: C{None} or C{str}
253 + @param engine: the layout engine to use: 'dot' (default),
254 + 'neato', 'circo', 'twopi' or 'fdp'
255 + @type engine: C{str}
256 + @param node_attr: a function to format nodes, it will be
257 + called with the state number, the C{StateGraph} object
258 + and attributes dict as parameters
259 + @type node_attr: C{function(int,StateGraph,dict)->None}
260 + @param edge_attr: a function to format edges, it
261 + will be called with the transition, its mode and
262 + attributes dict as parameters
263 + @type trans_attr: C{function(Transition,Substitution,dict)->None}
264 + @param graph_attr: a function to format grapg, it
265 + will be called with the state graphe and attributes dict
266 + as parameters
267 + @type graph_attr: C{function(StateGraph,dict)->None}
268 + @return: C{None} if C{filename} is not C{None}, the
269 + computed graph otherwise
270 + @rtype: C{None} or C{pygraphviz.AGraph}
271 + """
272 + attr = dict(style="invis",
273 + splines="true")
274 + if graph_attr :
275 + graph_attr(self, attr)
276 + graph = Graph(attr)
277 + for state in self._done :
278 + self.goto(state)
279 + attr = dict(shape="rectangle")
280 + if state == 0 :
281 + attr["shape"] = ""
282 + if node_attr :
283 + node_attr(state, self, attr)
284 + graph.add_node(str(state), attr)
285 + for succ, (trans, mode) in self.successors().items() :
286 + attr = dict(arrowhead="normal",
287 + label="%s\\n%s" % (trans.name, mode))
288 + if edge_attr :
289 + edge_attr(trans, mode, attr)
290 + graph.add_edge(str(state), str(succ), attr)
291 + if filename is None :
292 + return graph
293 + else :
294 + graph.render(filename, engine, debug)
295 + return PetriNet, StateGraph
1 +"""An example plugin that allows instances class C{PetriNet} to say hello.
2 +
3 +A new method C{hello} is added. The constructor is added a keyword
4 +argument C{hello} that must be the C{str} to print when calling
5 +C{hello}, with one C{%s} that will be replaced by the name of the net
6 +when C{hello} is called.
7 +
8 +Defining a plugins need writing a module with a single function called
9 +C{extend} that takes a single argument that is the module to be
10 +extended.
11 +
12 +Inside the function, extensions of the classes in the module are
13 +defined as normal sub-classes.
14 +
15 +The function C{extend} should return the extended module created by
16 +C{snakes.plugins.build} that takes as arguments: the name of the
17 +extended module, the module taken as argument and the sub-classes
18 +defined (expected as a list argument C{*args} in no special order).
19 +
20 +If the plugin depends on other plugins, for instance C{foo} and
21 +C{bar}, the function C{extend} should be decorated by
22 +C{@depends('foo', 'bar')}.
23 +
24 +Read the source code of this module to have an example
25 +"""
26 +
27 +import snakes.plugins
28 +
29 +@snakes.plugins.plugin("snakes.nets")
30 +def extend (module) :
31 + """Extends C{module}
32 + """
33 + class PetriNet (module.PetriNet) :
34 + """Extension of the class C{PetriNet} in C{module}
35 + """
36 + def __init__ (self, name, **args) :
37 + """When extending an existing method, take care that you
38 + may be working on an already extended class, so you so not
39 + know how its arguments have been changed. So, always use
40 + those from the unextended class plus C{**args}, remove
41 + from it what your plugin needs and pass it to the method
42 + of the extended class if you need to call it.
43 +
44 + >>> PetriNet('N').hello()
45 + Hello from N
46 + >>> PetriNet('N', hello='Hi! This is %s...').hello()
47 + Hi! This is N...
48 +
49 + @param args: plugin options
50 + @keyword hello: the message to print, with C{%s} where the
51 + net name should appear.
52 + @type hello: C{str}
53 + """
54 + self._hello = args.pop("hello", "Hello from %s")
55 + module.PetriNet.__init__(self, name, **args)
56 + def hello (self) :
57 + """A new method C{hello}
58 +
59 + >>> n = PetriNet('N')
60 + >>> n.hello()
61 + Hello from N
62 + """
63 + print(self._hello % self.name)
64 + return PetriNet
1 +"""A plugin to add labels to nodes and nets.
2 +
3 +"""
4 +
5 +from snakes.plugins import plugin, new_instance
6 +from snakes.pnml import Tree
7 +
8 +@plugin("snakes.nets")
9 +def extend (module) :
10 + class Transition (module.Transition) :
11 + def label (self, *get, **set) :
12 + if not hasattr(self, "_labels") :
13 + self._labels = {}
14 + result = tuple(self._labels[g] for g in get)
15 + self._labels.update(set)
16 + if len(get) == 1 :
17 + return result[0]
18 + elif len(get) > 1 :
19 + return result
20 + elif len(set) == 0 :
21 + return self._labels.copy()
22 + def has_label (self, name, *names) :
23 + if len(names) == 0 :
24 + return name in self._labels
25 + else :
26 + return tuple(n in self._labels for n in (name,) + names)
27 + def copy (self, name=None, **options) :
28 + if not hasattr(self, "_labels") :
29 + self._labels = {}
30 + result = module.Transition.copy(self, name, **options)
31 + result._labels = self._labels.copy()
32 + return result
33 + def __pnmldump__ (self) :
34 + """
35 + >>> t = Transition('t')
36 + >>> t.label(foo='bar', spam=42)
37 + >>> t.__pnmldump__()
38 + <?xml version="1.0" encoding="utf-8"?>
39 + <pnml>
40 + <transition id="t">
41 + <label name="foo">
42 + <object type="str">
43 + bar
44 + </object>
45 + </label>
46 + <label name="spam">
47 + <object type="int">
48 + 42
49 + </object>
50 + </label>
51 + </transition>
52 + </pnml>
53 + """
54 + t = module.Transition.__pnmldump__(self)
55 + if hasattr(self, "_labels") :
56 + for key, val in self._labels.items() :
57 + t.add_child(Tree("label", None,
58 + Tree.from_obj(val),
59 + name=key))
60 + return t
61 + @classmethod
62 + def __pnmlload__ (cls, tree) :
63 + """
64 + >>> old = Transition('t')
65 + >>> old.label(foo='bar', spam=42)
66 + >>> p = old.__pnmldump__()
67 + >>> new = Transition.__pnmlload__(p)
68 + >>> new
69 + Transition('t', Expression('True'))
70 + >>> new.__class__
71 + <class 'snakes.plugins.labels.Transition'>
72 + >>> new.label('foo', 'spam')
73 + ('bar', 42)
74 + """
75 + t = new_instance(cls, module.Transition.__pnmlload__(tree))
76 + t._labels = dict((lbl["name"], lbl.child().to_obj())
77 + for lbl in tree.get_children("label"))
78 + return t
79 + class Place (module.Place) :
80 + def label (self, *get, **set) :
81 + if not hasattr(self, "_labels") :
82 + self._labels = {}
83 + result = tuple(self._labels[g] for g in get)
84 + self._labels.update(set)
85 + if len(get) == 1 :
86 + return result[0]
87 + elif len(get) > 1 :
88 + return result
89 + elif len(set) == 0 :
90 + return self._labels.copy()
91 + def has_label (self, name, *names) :
92 + if len(names) == 0 :
93 + return name in self._labels
94 + else :
95 + return tuple(n in self._labels for n in (name,) + names)
96 + def copy (self, name=None, **options) :
97 + if not hasattr(self, "_labels") :
98 + self._labels = {}
99 + result = module.Place.copy(self, name, **options)
100 + result._labels = self._labels.copy()
101 + return result
102 + def __pnmldump__ (self) :
103 + """
104 + >>> p = Place('p')
105 + >>> p.label(foo='bar', spam=42)
106 + >>> p.__pnmldump__()
107 + <?xml version="1.0" encoding="utf-8"?>
108 + <pnml>
109 + <place id="p">
110 + <type domain="universal"/>
111 + <initialMarking>
112 + <multiset/>
113 + </initialMarking>
114 + <label name="foo">
115 + <object type="str">
116 + bar
117 + </object>
118 + </label>
119 + <label name="spam">
120 + <object type="int">
121 + 42
122 + </object>
123 + </label>
124 + </place>
125 + </pnml>
126 + """
127 + t = module.Place.__pnmldump__(self)
128 + if hasattr(self, "_labels") :
129 + for key, val in self._labels.items() :
130 + t.add_child(Tree("label", None,
131 + Tree.from_obj(val),
132 + name=key))
133 + return t
134 + @classmethod
135 + def __pnmlload__ (cls, tree) :
136 + """
137 + >>> old = Place('p')
138 + >>> old.label(foo='bar', spam=42)
139 + >>> p = old.__pnmldump__()
140 + >>> new = Place.__pnmlload__(p)
141 + >>> new
142 + Place('p', MultiSet([]), tAll)
143 + >>> new.__class__
144 + <class 'snakes.plugins.labels.Place'>
145 + >>> new.label('foo', 'spam')
146 + ('bar', 42)
147 + """
148 + p = new_instance(cls, module.Place.__pnmlload__(tree))
149 + p._labels = dict((lbl["name"], lbl.child().to_obj())
150 + for lbl in tree.get_children("label"))
151 + return p
152 + class PetriNet (module.PetriNet) :
153 + def label (self, *get, **set) :
154 + if not hasattr(self, "_labels") :
155 + self._labels = {}
156 + result = tuple(self._labels[g] for g in get)
157 + self._labels.update(set)
158 + if len(get) == 1 :
159 + return result[0]
160 + elif len(get) > 1 :
161 + return result
162 + elif len(set) == 0 :
163 + return self._labels.copy()
164 + def has_label (self, name, *names) :
165 + if len(names) == 0 :
166 + return name in self._labels
167 + else :
168 + return tuple(n in self._labels for n in (name,) + names)
169 + def copy (self, name=None, **options) :
170 + if not hasattr(self, "_labels") :
171 + self._labels = {}
172 + result = module.PetriNet.copy(self, name, **options)
173 + result._labels = self._labels.copy()
174 + return result
175 + def __pnmldump__ (self) :
176 + """
177 + >>> n = PetriNet('n')
178 + >>> n.label(foo='bar', spam=42)
179 + >>> n.__pnmldump__()
180 + <?xml version="1.0" encoding="utf-8"?>
181 + <pnml>
182 + <net id="n">
183 + <label name="foo">
184 + <object type="str">
185 + bar
186 + </object>
187 + </label>
188 + <label name="spam">
189 + <object type="int">
190 + 42
191 + </object>
192 + </label>
193 + </net>
194 + </pnml>
195 + """
196 + t = module.PetriNet.__pnmldump__(self)
197 + if hasattr(self, "_labels") :
198 + for key, val in self._labels.items() :
199 + t.add_child(Tree("label", None,
200 + Tree.from_obj(val),
201 + name=key))
202 + return t
203 + @classmethod
204 + def __pnmlload__ (cls, tree) :
205 + """
206 + >>> old = PetriNet('n')
207 + >>> old.label(foo='bar', spam=42)
208 + >>> p = old.__pnmldump__()
209 + >>> new = PetriNet.__pnmlload__(p)
210 + >>> new
211 + PetriNet('n')
212 + >>> new.__class__
213 + <class 'snakes.plugins.labels.PetriNet'>
214 + >>> new.label('foo', 'spam')
215 + ('bar', 42)
216 + """
217 + n = new_instance(cls, module.PetriNet.__pnmlload__(tree))
218 + n._labels = dict((lbl["name"], lbl.child().to_obj())
219 + for lbl in tree.get_children("label"))
220 + return n
221 + def merge_places (self, target, sources, **options) :
222 + module.PetriNet.merge_places(self, target, sources, **options)
223 + new = self.place(target)
224 + for place in sources :
225 + new.label(**dict(self.place(place).label()))
226 + def merge_transitions (self, target, sources, **options) :
227 + module.PetriNet.merge_transitions(self, target, sources, **options)
228 + new = self.transition(target)
229 + for trans in sources :
230 + new.label(**dict(self.transition(trans).label()))
231 + return Transition, Place, PetriNet
1 +"""A plugin to compose nets.
2 +
3 +The compositions are based on place status and automatically merge
4 +some nodes (buffers and variables, tick transitions).
5 +
6 +>>> import snakes.plugins
7 +>>> snakes.plugins.load('ops', 'snakes.nets', 'nets')
8 +<module ...>
9 +>>> from nets import *
10 +>>> from snakes.plugins.status import entry, internal, exit, buffer
11 +>>> basic = PetriNet('basic')
12 +>>> basic.add_place(Place('e', status=entry))
13 +>>> basic.add_place(Place('x', status=exit))
14 +>>> basic.add_transition(Transition('t'))
15 +>>> basic.add_input('e', 't', Value(1))
16 +>>> basic.add_output('x', 't', Value(2))
17 +>>> basic.add_place(Place('b', [1], status=buffer('buf')))
18 +
19 +>>> n = basic.copy()
20 +>>> n.hide(entry)
21 +>>> n.node('e').status
22 +Status(None)
23 +>>> n.hide(buffer('buf'), buffer(None))
24 +>>> n.node('b').status
25 +Buffer('buffer')
26 +
27 +>>> n = basic / 'buf'
28 +>>> n.node('[b/buf]').status
29 +Buffer('buffer')
30 +
31 +>>> n = basic & basic
32 +>>> n.status(internal)
33 +('[x&e]',)
34 +>>> n.place('[x&e]').pre
35 +{'[t&]': Value(2)}
36 +>>> n.place('[x&e]').post
37 +{'[&t]': Value(1)}
38 +>>> n.status(buffer('buf'))
39 +('[b&b]',)
40 +
41 +>>> n = basic + basic
42 +>>> n.status(entry)
43 +('[e+e]',)
44 +>>> list(sorted(n.place('[e+e]').post.items()))
45 +[('[+t]', Value(1)), ('[t+]', Value(1))]
46 +>>> n.status(exit)
47 +('[x+x]',)
48 +>>> list(sorted(n.place('[x+x]').pre.items()))
49 +[('[+t]', Value(2)), ('[t+]', Value(2))]
50 +
51 +>>> n = basic * basic
52 +>>> n.status(entry)
53 +('[e,x*e]',)
54 +>>> n.place('[e,x*e]').post
55 +{'[t*]': Value(1), '[*t]': Value(1)}
56 +>>> n.place('[e,x*e]').pre
57 +{'[t*]': Value(2)}
58 +
59 +>>> n1 = basic.copy()
60 +>>> n1.declare('global x; x=1')
61 +>>> n2 = basic.copy()
62 +>>> n2.globals['y'] = 2
63 +>>> n = n1 + n2
64 +>>> n.globals['x'], n.globals['y']
65 +(1, 2)
66 +>>> n._declare
67 +['global x; x=1']
68 +"""
69 +
70 +import snakes.plugins
71 +from snakes.plugins.status import Status, entry, exit, internal
72 +from snakes.data import cross
73 +from snakes.plugins.clusters import Cluster
74 +
75 +def _glue (op, one, two) :
76 + result = one.__class__("(%s%s%s)" % (one.name, op, two.name))
77 + def new (name) :
78 + return "[%s%s]" % (name, op)
79 + for net in (one, two) :
80 + result.clusters.add_child(Cluster())
81 + result._declare = list(set(result._declare) | set(net._declare))
82 + result.globals.update(net.globals)
83 + for place in net.place() :
84 + result.add_place(place.copy(new(place.name)),
85 + cluster=[-1]+net.clusters.get_path(place.name))
86 + for trans in net.transition() :
87 + result.add_transition(trans.copy(new(trans.name)),
88 + cluster=[-1]+net.clusters.get_path(trans.name))
89 + for place, label in trans.input() :
90 + result.add_input(new(place.name),
91 + new(trans.name),
92 + label.copy())
93 + for place, label in trans.output() :
94 + result.add_output(new(place.name),
95 + new(trans.name),
96 + label.copy())
97 + def new (name) :
98 + return "[%s%s]" % (op, name)
99 + for status in result.status :
100 + result.status.merge(status)
101 + new = result.status(status)
102 + if len(new) == 1 :
103 + name = "[%s%s%s]" % (",".join(sorted(one.status(status))),
104 + op,
105 + ",".join(sorted(two.status(status))))
106 + if name != new[0] :
107 + result.rename_node(new[0], name)
108 + return result
109 +
110 +@snakes.plugins.plugin("snakes.nets",
111 + depends=["snakes.plugins.clusters",
112 + "snakes.plugins.status"])
113 +def extend (module) :
114 + "Build the extended module"
115 + class PetriNet (module.PetriNet) :
116 + def __or__ (self, other) :
117 + "Parallel"
118 + return _glue("|", self, other)
119 + def __and__ (self, other) :
120 + "Sequence"
121 + result = _glue("&", self, other)
122 + remove = set()
123 + for x, e in cross((self.status(exit), other.status(entry))) :
124 + new = "[%s&%s]" % (x, e)
125 + new_x, new_e = "[%s&]" % x, "[&%s]" % e
126 + result.merge_places(new, (new_x, new_e), status=internal)
127 + remove.update((new_x, new_e))
128 + for p in remove :
129 + result.remove_place(p)
130 + return result
131 + def __add__ (self, other) :
132 + "Choice"
133 + result = _glue("+", self, other)
134 + for status in (entry, exit) :
135 + remove = set()
136 + for l, r in cross((self.status(status),
137 + other.status(status))) :
138 + new = "[%s+%s]" % (l, r)
139 + new_l, new_r = "[%s+]" % l, "[+%s]" % r
140 + result.merge_places(new, (new_l, new_r), status=status)
141 + remove.update((new_l, new_r))
142 + for p in remove :
143 + result.remove_place(p)
144 + return result
145 + def __mul__ (self, other) :
146 + "Iteration"
147 + result = _glue("*", self, other)
148 + remove = set()
149 + for e1, x1, e2 in cross((self.status(entry),
150 + self.status(exit),
151 + other.status(entry))) :
152 + new = "[%s,%s*%s]" % (e1, x1, e2)
153 + new_e1, new_x1 = "[%s*]" % e1, "[%s*]" % x1
154 + new_e2 = "[*%s]" % e2
155 + result.merge_places(new, (new_e1, new_x1, new_e2),
156 + status=entry)
157 + remove.update((new_e1, new_x1, new_e2))
158 + for p in remove :
159 + result.remove_place(p)
160 + return result
161 + def hide (self, old, new=None) :
162 + if new is None :
163 + new = Status(None)
164 + for node in self.status(old) :
165 + self.set_status(node, new)
166 + def __div__ (self, name) :
167 + result = self.copy()
168 + for node in result.node() :
169 + result.rename_node(node.name, "[%s/%s]" % (node, name))
170 + for status in result.status :
171 + if status._value == name :
172 + result.hide(status, status.__class__(status._name, None))
173 + return result
174 + def __truediv__ (self, other) :
175 + return self.__div__(other)
176 + return PetriNet
1 +"""A plugin to add positions to the nodes.
2 +
3 + - C{Place} and C{Transition} constructors are added an optional
4 + argument C{pos=(x,y)} to set their position
5 +
6 + - C{Place} and C{Transition} are added an attribute C{pos} that is
7 + pair of numbers with attributes C{x} and C{y} and methods
8 + C{shift(dx, dy)} and C{moveto(x, y)}
9 +
10 + - Petri nets are added methods C{bbox()} that returns a pair of
11 + extrema C{((xmin, ymin), (xmax, ymax))}, a method C{shift(dx, dy)}
12 + that shift all the nodes, and a method C{transpose()} that rotates
13 + the net in such a way that the top-down direction becomes
14 + left-right
15 +
16 +>>> import snakes.plugins
17 +>>> snakes.plugins.load('pos', 'snakes.nets', 'nets')
18 +<module ...>
19 +>>> from nets import PetriNet, Place, Transition
20 +>>> n = PetriNet('N')
21 +>>> n.add_place(Place('p00'))
22 +>>> n.add_transition(Transition('t10', pos=(1, 0)))
23 +>>> n.add_place(Place('p11', pos=(1, 1)))
24 +>>> n.add_transition(Transition('t01', pos=(0, 1)))
25 +>>> n.node('t10').pos
26 +Position(1, 0)
27 +>>> n.node('t10').pos.x
28 +1
29 +>>> n.node('t10').pos.y
30 +0
31 +>>> n.node('t10').pos.y = 1
32 +Traceback (most recent call last):
33 + ...
34 +AttributeError: readonly attribute
35 +>>> n.node('t10').pos()
36 +(1, 0)
37 +>>> n.bbox()
38 +((0, 0), (1, 1))
39 +>>> n.shift(1, 2)
40 +>>> n.bbox()
41 +((1, 2), (2, 3))
42 +>>> n.node('t01').copy().pos
43 +Position(1, 3)
44 +>>> n.transpose()
45 +>>> n.node('t01').pos
46 +Position(-3, 1)
47 +"""
48 +
49 +from snakes import SnakesError
50 +from snakes.compat import *
51 +from snakes.plugins import plugin, new_instance
52 +from snakes.pnml import Tree
53 +
54 +class Position (object) :
55 + "The position of a node"
56 + def __init__ (self, x, y) :
57 + self.__dict__["x"] = x
58 + self.__dict__["y"] = y
59 + def __str__ (self) :
60 + return "(%s, %s)" % (str(self.x), str(self.y))
61 + def __repr__ (self) :
62 + return "Position(%s, %s)" % (str(self.x), str(self.y))
63 + def __setattr__ (self, name, value) :
64 + if name in ("x", "y") :
65 + raise AttributeError("readonly attribute")
66 + else :
67 + self.__dict__[name] = value
68 + def moveto (self, x, y) :
69 + self.__init__(x, y)
70 + def shift (self, dx, dy) :
71 + self.__init__(self.x + dx, self.y + dy)
72 + def __getitem__ (self, rank) :
73 + if rank == 0 :
74 + return self.x
75 + elif rank == 1 :
76 + return self.y
77 + else :
78 + raise IndexError("Position index out of range")
79 + def __iter__ (self) :
80 + yield self.x
81 + yield self.y
82 + def __call__ (self) :
83 + return (self.x, self.y)
84 +
85 +@plugin("snakes.nets")
86 +def extend (module) :
87 + class Place (module.Place) :
88 + def __init__ (self, name, tokens=[], check=None, **args) :
89 + x, y = args.pop("pos", (0, 0))
90 + self.pos = Position(x, y)
91 + module.Place.__init__(self, name, tokens, check, **args)
92 + def copy (self, name=None, **args) :
93 + x, y = args.pop("pos", self.pos())
94 + result = module.Place.copy(self, name, **args)
95 + result.pos.moveto(x, y)
96 + return result
97 + def __pnmldump__ (self) :
98 + """
99 + >>> p = Place('p', pos=(1, 2))
100 + >>> p.__pnmldump__()
101 + <?xml version="1.0" encoding="utf-8"?>
102 + <pnml>
103 + <place id="p">
104 + <type domain="universal"/>
105 + <initialMarking>
106 + <multiset/>
107 + </initialMarking>
108 + <graphics>
109 + <position x="1" y="2"/>
110 + </graphics>
111 + </place>
112 + </pnml>
113 + """
114 + t = module.Place.__pnmldump__(self)
115 + try :
116 + gfx = t.child("graphics")
117 + except SnakesError :
118 + gfx = Tree("graphics", None)
119 + t.add_child(gfx)
120 + gfx.add_child(Tree("position", None,
121 + x=str(self.pos.x),
122 + y=str(self.pos.y)))
123 + return t
124 + @classmethod
125 + def __pnmlload__ (cls, tree) :
126 + """
127 + >>> old = Place('p', pos=(1, 2))
128 + >>> p = old.__pnmldump__()
129 + >>> new = Place.__pnmlload__(p)
130 + >>> new.pos
131 + Position(1, 2)
132 + >>> new
133 + Place('p', MultiSet([]), tAll)
134 + >>> new.__class__
135 + <class 'snakes.plugins.pos.Place'>
136 + """
137 + result = new_instance(cls, module.Place.__pnmlload__(tree))
138 + try :
139 + p = tree.child("graphics").child("position")
140 + x, y = eval(p["x"]), eval(p["y"])
141 + result.pos = Position(x, y)
142 + except SnakesError :
143 + result.pos = Position(0, 0)
144 + return result
145 + class Transition (module.Transition) :
146 + def __init__ (self, name, guard=None, **args) :
147 + x, y = args.pop("pos", (0, 0))
148 + self.pos = Position(x, y)
149 + module.Transition.__init__(self, name, guard, **args)
150 + def copy (self, name=None, **args) :
151 + x, y = args.pop("pos", self.pos())
152 + result = module.Transition.copy(self, name, **args)
153 + result.pos.moveto(x, y)
154 + return result
155 + def __pnmldump__ (self) :
156 + """
157 + >>> t = Transition('t', pos=(2, 1))
158 + >>> t.__pnmldump__()
159 + <?xml version="1.0" encoding="utf-8"?>
160 + <pnml>
161 + <transition id="t">
162 + <graphics>
163 + <position x="2" y="1"/>
164 + </graphics>
165 + </transition>
166 + </pnml>
167 + """
168 + t = module.Transition.__pnmldump__(self)
169 + t.add_child(Tree("graphics", None,
170 + Tree("position", None,
171 + x=str(self.pos.x),
172 + y=str(self.pos.y))))
173 + return t
174 + @classmethod
175 + def __pnmlload__ (cls, tree) :
176 + """
177 + >>> old = Transition('t', pos=(2, 1))
178 + >>> p = old.__pnmldump__()
179 + >>> new = Transition.__pnmlload__(p)
180 + >>> new.pos
181 + Position(2, 1)
182 + >>> new
183 + Transition('t', Expression('True'))
184 + >>> new.__class__
185 + <class 'snakes.plugins.pos.Transition'>
186 + """
187 + result = new_instance(cls, module.Transition.__pnmlload__(tree))
188 + try :
189 + p = tree.child("graphics").child("position")
190 + x, y = eval(p["x"]), eval(p["y"])
191 + result.pos = Position(x, y)
192 + except SnakesError :
193 + result.pos = Position(0, 0)
194 + return result
195 + class PetriNet (module.PetriNet) :
196 + def add_place (self, place, **args) :
197 + if "pos" in args :
198 + x, y = args.pop("pos")
199 + place.pos.moveto(x, y)
200 + module.PetriNet.add_place(self, place, **args)
201 + def add_transition (self, trans, **args) :
202 + if "pos" in args :
203 + x, y = args.pop("pos")
204 + trans.pos.moveto(x, y)
205 + module.PetriNet.add_transition(self, trans, **args)
206 + def merge_places (self, target, sources, **args) :
207 + pos = args.pop("pos", None)
208 + module.PetriNet.merge_places(self, target, sources, **args)
209 + if pos is None :
210 + pos = reduce(complex.__add__,
211 + (complex(*self._place[name].pos())
212 + for name in sources)) / len(sources)
213 + x, y = pos.real, pos.imag
214 + else :
215 + x, y = pos
216 + self._place[target].pos.moveto(x, y)
217 + def merge_transitions (self, target, sources, **args) :
218 + pos = args.pop("pos", None)
219 + module.PetriNet.merge_transitions(self, target, sources, **args)
220 + if pos is None :
221 + pos = reduce(complex.__add__,
222 + (complex(*self._trans[name].pos())
223 + for name in sources)) / len(sources)
224 + x, y = pos.real, pos.imag
225 + else :
226 + x, y = pos
227 + self._trans[target].pos.moveto(x, y)
228 + def bbox (self) :
229 + if len(self._node) == 0 :
230 + return (0, 0), (0, 0)
231 + else :
232 + nodes = iter(self._node.values())
233 + xmin, ymin = next(nodes).pos()
234 + xmax, ymax = xmin, ymin
235 + for n in nodes :
236 + x, y = n.pos()
237 + xmin = min(xmin, x)
238 + xmax = max(xmax, x)
239 + ymin = min(ymin, y)
240 + ymax = max(ymax, y)
241 + return (xmin, ymin), (xmax, ymax)
242 + def shift (self, dx, dy) :
243 + for node in self.node() :
244 + node.pos.shift(dx, dy)
245 + def transpose (self) :
246 + for node in self.node() :
247 + x, y = node.pos()
248 + node.pos.moveto(-y, x)
249 + return Place, Transition, PetriNet, Position
1 +from snakes.plugins import plugin
2 +from snakes.pnml import Tree, loads, dumps
3 +import imp, sys, socket, traceback, operator
4 +
5 +class QueryError (Exception) :
6 + pass
7 +
8 +class Query (object) :
9 + def __init__ (self, name, *larg, **karg) :
10 + self._name = name
11 + self._larg = tuple(larg)
12 + self._karg = dict(karg)
13 + __pnmltag__ = "query"
14 + def __pnmldump__ (self) :
15 + """
16 + >>> Query('set', 'x', 42).__pnmldump__()
17 + <?xml version="1.0" encoding="utf-8"?>
18 + <pnml>
19 + <query name="set">
20 + <argument>
21 + <object type="str">
22 + x
23 + </object>
24 + </argument>
25 + <argument>
26 + <object type="int">
27 + 42
28 + </object>
29 + </argument>
30 + </query>
31 + </pnml>
32 + >>> Query('test', x=1).__pnmldump__()
33 + <?xml version="1.0" encoding="utf-8"?>
34 + <pnml>
35 + <query name="test">
36 + <keyword name="x">
37 + <object type="int">
38 + 1
39 + </object>
40 + </keyword>
41 + </query>
42 + </pnml>
43 + >>> Query('test', 'x', 42, y=1).__pnmldump__()
44 + <?xml version="1.0" encoding="utf-8"?>
45 + <pnml>
46 + <query name="test">
47 + <argument>
48 + <object type="str">
49 + x
50 + </object>
51 + </argument>
52 + <argument>
53 + <object type="int">
54 + 42
55 + </object>
56 + </argument>
57 + <keyword name="y">
58 + <object type="int">
59 + 1
60 + </object>
61 + </keyword>
62 + </query>
63 + </pnml>
64 + >>> Query('set', 'x', Query('call', 'x.upper')).__pnmldump__()
65 + <?xml version="1.0" encoding="utf-8"?>
66 + <pnml>
67 + <query name="set">
68 + <argument>
69 + <object type="str">
70 + x
71 + </object>
72 + </argument>
73 + <argument>
74 + <query name="call">
75 + <argument>
76 + <object type="str">
77 + x.upper
78 + </object>
79 + </argument>
80 + </query>
81 + </argument>
82 + </query>
83 + </pnml>
84 + """
85 + children = []
86 + for arg in self._larg :
87 + children.append(Tree("argument", None,
88 + Tree.from_obj(arg)))
89 + for name, value in self._karg.items() :
90 + children.append(Tree("keyword", None,
91 + Tree.from_obj(value),
92 + name=name))
93 + return Tree(self.__pnmltag__, None, *children, **{"name": self._name})
94 + @classmethod
95 + def __pnmlload__ (cls, tree) :
96 + """
97 + >>> Tree._tag2obj = {'query': Query}
98 + >>> t = Query('test', 'x', 42, y=1).__pnmldump__()
99 + >>> q = Query.__pnmlload__(t)
100 + >>> q._name
101 + 'test'
102 + >>> q._larg
103 + ('x', 42)
104 + >>> q._karg
105 + {'y': 1}
106 + """
107 + larg = (child.child().to_obj()
108 + for child in tree.get_children("argument"))
109 + karg = dict((child["name"], child.child().to_obj())
110 + for child in tree.get_children("keyword"))
111 + return cls(tree["name"], *larg, **karg)
112 + def run (self, envt) :
113 + """
114 + >>> import imp
115 + >>> env = imp.new_module('environment')
116 + >>> Query('set', 'x', 'hello').run(env)
117 + >>> env.x
118 + 'hello'
119 + >>> Query('set', 'x', Query('call', 'x.upper')).run(env)
120 + >>> env.x
121 + 'HELLO'
122 + >>> Query('test', 1, 2, 3).run(env)
123 + Traceback (most recent call last):
124 + ...
125 + QueryError: unknown query 'test'
126 + """
127 + try :
128 + handler = getattr(self, "_run_%s" % self._name)
129 + except AttributeError :
130 + raise QueryError("unknown query %r" % self._name)
131 + self._envt = envt
132 + larg = tuple(a.run(envt) if isinstance(a, self.__class__) else a
133 + for a in self._larg)
134 + karg = dict((n, v.run(envt) if isinstance(v, self.__class__) else v)
135 + for n, v in self._karg.items())
136 + try :
137 + return handler(*larg, **karg)
138 + except TypeError :
139 + cls, val, tb = sys.exc_info()
140 + try :
141 + fun, msg = str(val).strip().split("()", 1)
142 + except :
143 + raise val
144 + if fun.startswith("_run_") and hasattr(self, fun) :
145 + raise TypeError(fun[5:] + "()" + msg)
146 + raise val
147 + def _get_object (self, path) :
148 + obj = self._envt
149 + for n in path :
150 + obj = getattr(obj, n)
151 + return obj
152 + def _run_set (self, name, value) :
153 + """
154 + >>> import imp
155 + >>> env = imp.new_module('environment')
156 + >>> Query('set', 'x', 1).run(env)
157 + >>> env.x
158 + 1
159 + """
160 + path = name.split(".")
161 + setattr(self._get_object(path[:-1]), path[-1], value)
162 + def _run_get (self, name) :
163 + """
164 + >>> import imp
165 + >>> env = imp.new_module('environment')
166 + >>> env.x = 2
167 + >>> Query('get', 'x').run(env)
168 + 2
169 + """
170 + path = name.split(".")
171 + return self._get_object(path)
172 + def _run_del (self, name) :
173 + """
174 + >>> import imp
175 + >>> env = imp.new_module('environment')
176 + >>> env.x = 2
177 + >>> Query('del', 'x').run(env)
178 + >>> env.x
179 + Traceback (most recent call last):
180 + ...
181 + AttributeError: 'module' object has no attribute 'x'
182 + """
183 + path = name.split(".")
184 + delattr(self._get_object(path[:-1]), path[-1])
185 + def _run_call (self, fun, *larg, **karg) :
186 + """
187 + >>> import imp
188 + >>> env = imp.new_module('environment')
189 + >>> env.x = 'hello'
190 + >>> Query('call', 'x.center', 7).run(env)
191 + ' hello '
192 + >>> env.__dict__.update(__builtins__)
193 + >>> Query('call', Query('call', 'getattr',
194 + ... Query('call', 'x.center', 7),
195 + ... 'upper')).run(env)
196 + ' HELLO '
197 + """
198 + if isinstance(fun, str) :
199 + fun = self._get_object(fun.split("."))
200 + return fun(*larg, **karg)
201 +
202 +@plugin("snakes.nets")
203 +def extend (module) :
204 + class UDPServer (object) :
205 + def __init__ (self, port, size=2**20, verbose=0) :
206 + self._size = size
207 + self._verbose = verbose
208 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
209 + self._sock.bind(("", port))
210 + self._env = imp.new_module("snk")
211 + self._env.__dict__.update(__builtins__)
212 + self._env.__dict__.update(operator.__dict__)
213 + self._env.__dict__.update(module.__dict__)
214 + def recvfrom (self) :
215 + return self._sock.recvfrom(self._size)
216 + def sendto (self, data, address) :
217 + self._sock.sendto(data.strip() + "\n", address)
218 + def run (self) :
219 + while True :
220 + data, address = self.recvfrom()
221 + data = data.strip()
222 + if self._verbose :
223 + print("# query from %s:%u" % address)
224 + try :
225 + if self._verbose > 1 :
226 + print(data)
227 + res = loads(data).run(self._env)
228 + if res is None :
229 + res = Tree("answer", None, status="ok")
230 + else :
231 + res = Tree("answer", None, Tree.from_obj(res),
232 + status="ok")
233 + except :
234 + cls, val, tb = sys.exc_info()
235 + res = Tree("answer", str(val).strip(),
236 + error=cls.__name__, status="error")
237 + if self._verbose > 1 :
238 + print("# error")
239 + for entry in traceback.format_exception(cls, val, tb) :
240 + for line in entry.splitlines() :
241 + print("## %s" % line)
242 + if self._verbose :
243 + if self._verbose > 1 :
244 + print("# answer")
245 + print(res.to_pnml())
246 + elif res["status"] == "error" :
247 + print("# answer: %s: %s" % (res["error"], res.data))
248 + else :
249 + print("# answer: %s" % res["status"])
250 + self.sendto(res.to_pnml(), address)
251 + class TCPServer (UDPServer) :
252 + def __init__ (self, port, size=2**20, verbose=0) :
253 + self._size = size
254 + self._verbose = verbose
255 + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
256 + self._sock.bind(("", port))
257 + self._sock.listen(1)
258 + self._env = imp.new_module("snk")
259 + self._env.__dict__.update(__builtins__)
260 + self._env.__dict__.update(operator.__dict__)
261 + self._env.__dict__.update(module.__dict__)
262 + self._connection = {}
263 + def recvfrom (self) :
264 + connection, address = self._sock.accept()
265 + self._connection[address] = connection
266 + parts = []
267 + while True :
268 + parts.append(connection.recv(self._size))
269 + if len(parts[-1]) < self._size :
270 + break
271 + return "".join(parts), address
272 + def sendto (self, data, address) :
273 + self._connection[address].send(data.rstrip() + "\n")
274 + self._connection[address].close()
275 + del self._connection[address]
276 + return Query, UDPServer, TCPServer
1 +"""A plugin to add nodes status.
2 +
3 +Several status are defined by default: C{entry}, C{internal}, C{exit},
4 +C{buffer}, C{safebuffer} for places and {tick} for transitions.
5 +
6 +>>> import snakes.plugins
7 +>>> snakes.plugins.load('status', 'snakes.nets', 'nets')
8 +<module ...>
9 +>>> from nets import *
10 +>>> import snakes.plugins.status as status
11 +>>> n = PetriNet('N')
12 +>>> n.add_place(Place('p1'), status=status.entry)
13 +>>> n.place('p1')
14 +Place('p1', MultiSet([]), tAll, status=Status('entry'))
15 +"""
16 +
17 +import operator, weakref
18 +import snakes.plugins
19 +from snakes import ConstraintError
20 +from snakes.plugins import new_instance
21 +from snakes.compat import *
22 +from snakes.data import iterate
23 +from snakes.pnml import Tree
24 +
25 +class Status (object) :
26 + "The status of a node"
27 + def __init__ (self, name, value=None) :
28 + """Initialize with a status name and an optional value
29 +
30 + @param name: the name of the status
31 + @type name: C{str}
32 + @param value: an optional additional value to make a
33 + difference between status with te same name
34 + @type value: hashable
35 + """
36 + self._name = name
37 + self._value = value
38 + __pnmltag__ = "status"
39 + def __pnmldump__ (self) :
40 + """Dump a C{Status} as a PNML tree
41 +
42 + @return: PNML tree
43 + @rtype: C{pnml.Tree}
44 +
45 + >>> Status('foo', 42).__pnmldump__()
46 + <?xml version="1.0" encoding="utf-8"?>
47 + <pnml>...
48 + <status>
49 + <name>
50 + foo
51 + </name>
52 + <value>
53 + <object type="int">
54 + 42
55 + </object>
56 + </value>
57 + </status>
58 + </pnml>
59 + """
60 + return Tree(self.__pnmltag__, None,
61 + Tree("name", self._name),
62 + Tree("value", None, Tree.from_obj(self._value)))
63 + @classmethod
64 + def __pnmlload__ (cls, tree) :
65 + """Create a C{Status} from a PNML tree
66 +
67 + @param tree: the tree to convert
68 + @type tree: C{pnml.Tree}
69 + @return: the status built
70 + @rtype: C{Status}
71 +
72 + >>> t = Status('foo', 42).__pnmldump__()
73 + >>> Status.__pnmlload__(t)
74 + Status('foo',42)
75 + """
76 + return cls(tree.child("name").data,
77 + tree.child("value").child().to_obj())
78 + def copy (self) :
79 + """Return a copy of the status
80 +
81 + A status is normally never muted, so this may be useless,
82 + unless the user decides to store additional data in status.
83 +
84 + @return: a copy of the status
85 + @rtype: C{Status}
86 + """
87 + return self.__class__(self._name, self._value)
88 + def __str__ (self) :
89 + """Short textual representation
90 +
91 + >>> str(internal)
92 + 'internal'
93 + >>> str(buffer('buf'))
94 + 'buffer(buf)'
95 +
96 + @return: a textual representation
97 + @rtype: C{str}
98 + """
99 + if self._value is None :
100 + return str(self._name)
101 + else :
102 + return "%s(%s)" % (self._name, self._value)
103 + def __repr__ (self) :
104 + """Detailed textual representation
105 +
106 + >>> repr(internal)
107 + "Status('internal')"
108 + >>> repr(buffer('buf'))
109 + "Buffer('buffer','buf')"
110 +
111 + @return: a textual representation suitable for C{eval}
112 + @rtype: C{str}
113 + """
114 + if self._value is None :
115 + return "%s(%s)" % (self.__class__.__name__, repr(self._name))
116 + else :
117 + return "%s(%s,%s)" % (self.__class__.__name__,
118 + repr(self._name), repr(self._value))
119 + def __hash__ (self) :
120 + """Hash a status
121 +
122 + @return: the hash value
123 + @rtype: C{int}
124 + """
125 + return hash((self._name, self._value))
126 + def __eq__ (self, other) :
127 + """Compares two status for equality
128 +
129 + They are equal if they have the same name and value
130 +
131 + >>> internal == Status('internal')
132 + True
133 + >>> Status('a', 1) == Status('a', 2)
134 + False
135 + >>> Status('a', 1) == Status('b', 1)
136 + False
137 +
138 + @param other: a status
139 + @type other: C{Status}
140 + @return: C{True} is they are equal, C{False} otherwise
141 + @rtype: C{bool}
142 + """
143 + try :
144 + return (self._name, self._value) == (other._name, other._value)
145 + except :
146 + return False
147 + def __ne__ (self, other) :
148 + return not(self == other)
149 + def __add__ (self, other) :
150 + if self == other :
151 + return self.copy()
152 + else :
153 + raise ConstraintError("incompatible status")
154 + def name (self) :
155 + return self._name
156 + def value (self) :
157 + return self._value
158 + def merge (self, net, nodes, name=None) :
159 + """Merge C{nodes} in C{net} into a new node called C{name}
160 +
161 + This does nothing by default, other status will refine this
162 + method. Merged nodes are removed, only the newly created one
163 + remains.
164 +
165 + @param net: the Petri net where nodes should be merged
166 + @type net: C{PetriNet}
167 + @param nodes: a collection of node names to be merged
168 + @type nodes: iterable of C{str}
169 + @param name: the name of the new node or C{Node} if it should
170 + be generated
171 + @type name: C{str}
172 + """
173 + pass
174 +
175 +entry = Status('entry')
176 +exit = Status('exit')
177 +internal = Status('internal')
178 +
179 +class Buffer (Status) :
180 + "A status for buffer places"
181 + def merge (self, net, nodes, name=None) :
182 + """Merge C{nodes} in C{net}
183 +
184 + Buffer places with the status status C{Buffer('buffer', None)}
185 + are not merged. Other buffer places are merged exactly has
186 + C{PetriNet.merge_places} does.
187 +
188 + If C{name} is C{None} the name generated is a concatenation of
189 + the nodes names separated by '+', with parenthesis outside.
190 +
191 + >>> import snakes.plugins
192 + >>> snakes.plugins.load('status', 'snakes.nets', 'nets')
193 + <module ...>
194 + >>> from nets import *
195 + >>> n = PetriNet('N')
196 + >>> import snakes.plugins.status as status
197 + >>> buf = status.buffer('buf')
198 + >>> n.add_place(Place('p3', range(2), status=buf))
199 + >>> n.add_place(Place('p4', range(3), status=buf))
200 + >>> n.status.merge(buf, 'b')
201 + >>> p = n.place('b')
202 + >>> p
203 + Place('b', MultiSet([...]), tAll, status=Buffer('buffer','buf'))
204 + >>> p.tokens == MultiSet([0, 0, 1, 1, 2])
205 + True
206 +
207 + @param net: the Petri net where places should be merged
208 + @type net: C{PetriNet}
209 + @param nodes: a collection of place names to be merged
210 + @type nodes: iterable of C{str}
211 + @param name: the name of the new place or C{Node} if it should
212 + be generated
213 + @type name: C{str}
214 + """
215 + if self._value is None :
216 + return
217 + if name is None :
218 + name = "(%s)" % "+".join(sorted(nodes))
219 + net.merge_places(name, nodes, status=self)
220 + for src in nodes :
221 + net.remove_place(src)
222 +
223 +def buffer (name) :
224 + """Generate a buffer status called C{name}
225 +
226 + @param name: the name of the buffer
227 + @type name: C{str}
228 + @return: C{Buffer('buffer', name)}
229 + @rtype: C{Buffer}
230 + """
231 + return Buffer('buffer', name)
232 +
233 +class Safebuffer (Buffer) :
234 + "A status for safe buffers (ie, variables) places"
235 + def merge (self, net, nodes, name=None) :
236 + """Merge C{nodes} in C{net}
237 +
238 + Safe buffers places with the status C{Safebuffer('safebuffer',
239 + None)} are not merged. Other safe buffers places are merged if
240 + they all have the same marking, which becomes the marking of
241 + the resulting place. Otherwise, C{ConstraintError} is raised.
242 +
243 + If C{name} is C{None} the name generated is a concatenation of
244 + the nodes names separated by '+', with parenthesis outside.
245 +
246 + >>> import snakes.plugins
247 + >>> snakes.plugins.load('status', 'snakes.nets', 'nets')
248 + <module ...>
249 + >>> from nets import *
250 + >>> import snakes.plugins.status as status
251 + >>> n = PetriNet('N')
252 + >>> var = status.safebuffer('var')
253 + >>> n.add_place(Place('p5', [1], status=var))
254 + >>> n.add_place(Place('p6', [1], status=var))
255 + >>> n.add_place(Place('p7', [1], status=var))
256 + >>> n.status.merge(var, 'v')
257 + >>> n.place('v')
258 + Place('v', MultiSet([1]), tAll, status=Safebuffer('safebuffer','var'))
259 + >>> n.add_place(Place('p8', [3], status=var))
260 + >>> try : n.status.merge(var, 'vv')
261 + ... except ConstraintError : print(sys.exc_info()[1])
262 + incompatible markings
263 +
264 + @param net: the Petri net where places should be merged
265 + @type net: C{PetriNet}
266 + @param nodes: a collection of place names to be merged
267 + @type nodes: iterable of C{str}
268 + @param name: the name of the new place or C{Node} if it should
269 + be generated
270 + @type name: C{str}
271 + """
272 + if self._value is None :
273 + return
274 + marking = net.place(nodes[0]).tokens
275 + for node in nodes[1:] :
276 + if net.place(node).tokens != marking :
277 + raise ConstraintError("incompatible markings")
278 + if name is None :
279 + name = "(%s)" % "+".join(sorted(nodes))
280 + net.merge_places(name, nodes, status=self)
281 + for src in nodes :
282 + net.remove_place(src)
283 + net.set_status(name, self)
284 + net.place(name).reset(marking)
285 +
286 +def safebuffer (name) :
287 + """Generate a safebuffer status called C{name}
288 +
289 + @param name: the name of the safebuffer
290 + @type name: C{str}
291 + @return: C{Safebuffer('safebuffer', name)}
292 + @rtype: C{Safebuffer}
293 + """
294 + return Safebuffer('safebuffer', name)
295 +
296 +class Tick (Status) :
297 + "A status for tick transition"
298 + def merge (self, net, nodes, name=None) :
299 + """Merge C{nodes} in C{net}
300 +
301 + Tick transitions are merged exactly as
302 + C{PetriNet.merge_transitions} does.
303 +
304 + If C{name} is C{None} the name generated is a concatenation of
305 + the nodes names separated by '+', with parenthesis outside.
306 +
307 + >>> import snakes.plugins
308 + >>> snakes.plugins.load('status', 'snakes.nets', 'nets')
309 + <module ...>
310 + >>> from nets import *
311 + >>> import snakes.plugins.status as status
312 + >>> n = PetriNet('N')
313 + >>> tick = status.tick('tick')
314 + >>> n.add_transition(Transition('t1', Expression('x==1'), status=tick))
315 + >>> n.add_transition(Transition('t2', Expression('y==2'), status=tick))
316 + >>> n.add_transition(Transition('t3', Expression('z==3'), status=tick))
317 + >>> n.status.merge(tick, 't')
318 + >>> n.transition('t')
319 + Transition('t', Expression('((...) and (...)) and (...)'), status=Tick('tick','tick'))
320 +
321 + @param net: the Petri net where transitions should be merged
322 + @type net: C{PetriNet}
323 + @param nodes: a collection of transition names to be merged
324 + @type nodes: iterable of C{str}
325 + @param name: the name of the new transition or C{Node} if it
326 + should be generated
327 + @type name: C{str}
328 + """
329 + if self._value is None :
330 + return
331 + if name is None :
332 + name = "(%s)" % "+".join(nodes)
333 + net.merge_transitions(name, nodes, status=self)
334 + for src in nodes :
335 + net.remove_transition(src)
336 +
337 +def tick (name) :
338 + """Generate a tick status called C{name}
339 +
340 + @param name: the name of the tick
341 + @type name: C{str}
342 + @return: C{Tick('tick', name)}
343 + @rtype: C{Tick}
344 + """
345 + return Tick('tick', name)
346 +
347 +class StatusDict (object) :
348 + "A container to access the nodes of a net by their status"
349 + def __init__ (self, net) :
350 + """
351 + @param net: the Petri net for which nodes will be recorded
352 + @type net: C{PetriNet}
353 + """
354 + self._nodes = {}
355 + self._net = weakref.ref(net)
356 + def copy (self, net=None) :
357 + """
358 + @param net: the Petri net for which nodes will be recorded
359 + (C{None} if it is the same as the copied object)
360 + @type net: C{PetriNet}
361 + """
362 + if net is None :
363 + net = self._net()
364 + result = self.__class__(net)
365 + for status in self._nodes :
366 + result._nodes[status.copy()] = self._nodes[status].copy()
367 + return result
368 + def __iter__ (self) :
369 + return iter(self._nodes)
370 + def record (self, node) :
371 + """Called when C{node} is added to the net
372 +
373 + @param node: the added node
374 + @type node: C{Node}
375 + """
376 + if node.status not in self._nodes :
377 + self._nodes[node.status] = set([node.name])
378 + else :
379 + self._nodes[node.status].add(node.name)
380 + def remove (self, node) :
381 + """Called when C{node} is removed from the net
382 +
383 + @param node: the added node
384 + @type node: C{Node}
385 + """
386 + if node.status in self._nodes :
387 + self._nodes[node.status].discard(node.name)
388 + if len(self._nodes[node.status]) == 0 :
389 + del self._nodes[node.status]
390 + def __call__ (self, status) :
391 + """Return the nodes having C{status}
392 +
393 + @param status: the searched status
394 + @type status: C{Status}
395 + @return: the node names in the net having this status
396 + @rtype: C{tuple} of C{str}
397 + """
398 + return tuple(self._nodes.get(status, tuple()))
399 + def merge (self, status, name=None) :
400 + """Merge the nodes in the net having C{status}
401 +
402 + This is a shortcut to call C{status.merge} with the right
403 + parameters.
404 +
405 + @param status: the status for which nodes have to be merged
406 + """
407 + if status :
408 + nodes = self(status)
409 + if len(nodes) > 1 :
410 + status.merge(self._net(), nodes, name)
411 +
412 +@snakes.plugins.plugin("snakes.nets")
413 +def extend (module) :
414 + "Build the extended module"
415 + class Place (module.Place) :
416 + def __init__ (self, name, tokens=[], check=None, **args) :
417 + self.status = args.pop("status", Status(None))
418 + module.Place.__init__(self, name, tokens, check, **args)
419 + def copy (self, name=None, **args) :
420 + result = module.Place.copy(self, name, **args)
421 + result.status = self.status.copy()
422 + return result
423 + def __repr__ (self) :
424 + if self.status == Status(None) :
425 + return module.Place.__repr__(self)
426 + else :
427 + return "%s, status=%s)" % (module.Place.__repr__(self)[:-1],
428 + repr(self.status))
429 + def __pnmldump__ (self) :
430 + """
431 + >>> p = Place('p', status=Status('foo', 42))
432 + >>> p.__pnmldump__()
433 + <?xml version="1.0" encoding="utf-8"?>
434 + <pnml>...
435 + <place id="p">
436 + <type domain="universal"/>
437 + <initialMarking>
438 + <multiset/>
439 + </initialMarking>
440 + <status>
441 + <name>
442 + foo
443 + </name>
444 + <value>
445 + <object type="int">
446 + 42
447 + </object>
448 + </value>
449 + </status>
450 + </place>
451 + </pnml>
452 + """
453 + t = module.Place.__pnmldump__(self)
454 + t.add_child(Tree.from_obj(self.status))
455 + return t
456 + @classmethod
457 + def __pnmlload__ (cls, tree) :
458 + """
459 + >>> t = Place('p', status=Status('foo', 42)).__pnmldump__()
460 + >>> Place.__pnmlload__(t).status
461 + Status('foo',42)
462 + """
463 + result = new_instance(cls, module.Place.__pnmlload__(tree))
464 + try :
465 + result.status = tree.child("status").to_obj()
466 + except SnakesError :
467 + result.status = Status(None)
468 + return result
469 + class Transition (module.Transition) :
470 + def __init__ (self, name, guard=None, **args) :
471 + self.status = args.pop("status", Status(None)).copy()
472 + module.Transition.__init__(self, name, guard, **args)
473 + def __pnmldump__ (self) :
474 + """
475 + >>> p = Transition('p', status=Status('foo', 42))
476 + >>> p.__pnmldump__()
477 + <?xml version="1.0" encoding="utf-8"?>
478 + <pnml>...
479 + <transition id="p">
480 + <status>
481 + <name>
482 + foo
483 + </name>
484 + <value>
485 + <object type="int">
486 + 42
487 + </object>
488 + </value>
489 + </status>
490 + </transition>
491 + </pnml>
492 + """
493 + t = module.Transition.__pnmldump__(self)
494 + t.add_child(Tree.from_obj(self.status))
495 + return t
496 + @classmethod
497 + def __pnmlload__ (cls, tree) :
498 + """
499 + >>> t = Transition('p', status=Status('foo', 42)).__pnmldump__()
500 + >>> Transition.__pnmlload__(t).status
501 + Status('foo',42)
502 + """
503 + result = new_instance(cls, module.Transition.__pnmlload__(tree))
504 + try :
505 + result.status = tree.child("status").to_obj()
506 + except SnakesError :
507 + result.status = Status(None)
508 + return result
509 + def copy (self, name=None, **args) :
510 + result = module.Transition.copy(self, name, **args)
511 + result.status = self.status.copy()
512 + return result
513 + def __repr__ (self) :
514 + if self.status == Status(None) :
515 + return module.Transition.__repr__(self)
516 + else :
517 + return "%s, status=%s)" % (module.Transition.__repr__(self)[:-1],
518 + repr(self.status))
519 + class PetriNet (module.PetriNet) :
520 + def __init__ (self, name, **args) :
521 + module.PetriNet.__init__(self, name, **args)
522 + self.status = StatusDict(self)
523 + @classmethod
524 + def __pnmlload__ (cls, tree) :
525 + t = new_instance(cls, module.PetriNet.__pnmlload__(tree))
526 + t.status = StatusDict(t)
527 + return t
528 + def copy (self, name=None, **args) :
529 + result = module.PetriNet.copy(self, name, **args)
530 + result.status = self.status.copy(result)
531 + return result
532 + def add_place (self, place, **args) :
533 + place.status = args.pop("status", place.status)
534 + module.PetriNet.add_place(self, place, **args)
535 + self.status.record(place)
536 + def remove_place (self, name, **args) :
537 + place = self.place(name)
538 + self.status.remove(place)
539 + module.PetriNet.remove_place(self, name, **args)
540 + def add_transition (self, trans, **args) :
541 + trans.status = args.pop("status", trans.status)
542 + module.PetriNet.add_transition(self, trans, **args)
543 + self.status.record(trans)
544 + def remove_transition (self, name, **args) :
545 + trans = self.transition(name)
546 + self.status.remove(trans)
547 + module.PetriNet.remove_transition(self, name, **args)
548 + def set_status (self, node, status) :
549 + node = self.node(node)
550 + self.status.remove(node)
551 + node.status = status
552 + self.status.record(node)
553 + def rename_node (self, old, new, **args) :
554 + old_node = self.node(old).copy()
555 + module.PetriNet.rename_node(self, old, new, **args)
556 + self.status.remove(old_node)
557 + self.status.record(self.node(new))
558 + def copy_place (self, source, targets, **args) :
559 + status = args.pop("status", self.place(source).status)
560 + module.PetriNet.copy_place(self, source, targets, **args)
561 + for new in iterate(targets) :
562 + self.set_status(new, status)
563 + def copy_transition (self, source, targets, **args) :
564 + status = args.pop("status", self.transition(source).status)
565 + module.PetriNet.copy_transition(self, source, targets, **args)
566 + for new in iterate(targets) :
567 + self.set_status(new, status)
568 + def merge_places (self, target, sources, **args) :
569 + if "status" in args :
570 + status = args.pop("status")
571 + else :
572 + status = reduce(operator.add,
573 + (self.place(s).status for s in sources))
574 + module.PetriNet.merge_places(self, target, sources, **args)
575 + self.set_status(target, status)
576 + def merge_transitions (self, target, sources, **args) :
577 + if "status" in args :
578 + status = args.pop("status")
579 + else :
580 + status = reduce(operator.add,
581 + (self.place(s).status for s in sources))
582 + module.PetriNet.merge_transitions(self, target, sources, **args)
583 + self.set_status(target, status)
584 + return Place, Transition, PetriNet, Status, \
585 + ("entry", entry), ("exit", exit), ("internal", internal), \
586 + Buffer, buffer, Safebuffer, safebuffer, Tick, tick
1 +"""An implementation of the M-nets synchronisation.
2 +
3 +This plugins extends the basic Petri net model in order to provide an
4 +action-based synchronisation scheme that implements that of M-nets.
5 +The plugin proposes a generalisation of the M-nets synchronisation in
6 +that it does not impose a fixed correspondence between action names
7 +and action arities.
8 +
9 + - class C{Action} corresponds to a synchronisable action, it has a
10 + name, a send/receive flag and a list of parameters. Actions have no
11 + predetermined arities, only conjugated actions with the same arity
12 + will be able to synchronise.
13 +
14 + - class C{MultiAction} corresponds to a multiset of actions. It is
15 + forbidden to build a multiaction that holds a pair of conjugated
16 + actions (this leads to infinite nets when synchronising).
17 +
18 + - Transition.__init__ accepts a parameter C{actions} that is a
19 + collection of instances of C{Action}, this multiaction is added in
20 + the attribute C{actions} of the transition.
21 +
22 + - PetriNet is given new methods: C{synchronise(action_name)} to
23 + perform the M-net synchronisation, C{restrict(action_name)} to
24 + perform the restriction and C{scope(action_name)} for the scoping.
25 +
26 +B{Remark:} the instances of C{Substitution} used in this plugins must
27 +map variable names to instances of C{Variable} or C{Value}, but not to
28 +other variable names.
29 +
30 +>>> import snakes.plugins
31 +>>> snakes.plugins.load('synchro', 'snakes.nets', 'nets')
32 +<module ...>
33 +>>> from nets import PetriNet, Place, Transition, Expression
34 +>>> n = PetriNet('N')
35 +>>> n.add_place(Place('e1'))
36 +>>> n.add_place(Place('x1'))
37 +>>> n.add_transition(Transition('t1', guard=Expression('x!=y'),
38 +... actions=[Action('a', True, [Variable('x'), Value(2)]),
39 +... Action('a', True, [Value(3), Variable('y')]),
40 +... Action('b', False, [Variable('x'), Variable('y')])]))
41 +>>> n.add_input('e1', 't1', Variable('x'))
42 +>>> n.add_output('x1', 't1', Variable('z'))
43 +>>> n.add_place(Place('e2'))
44 +>>> n.add_place(Place('x2'))
45 +>>> n.add_transition(Transition('t2', guard=Expression('z>0'),
46 +... actions=[Action('a', False, [Variable('w'), Variable('y')]),
47 +... Action('c', False, [Variable('z')])]))
48 +>>> n.add_input('e2', 't2', Variable('w'))
49 +>>> n.add_output('x2', 't2', Variable('z'))
50 +>>> n.transition('t1').vars() == set(['x', 'y', 'z'])
51 +True
52 +>>> n.transition('t2').copy().vars() == set(['w', 'y', 'z'])
53 +True
54 +>>> n.synchronise('a')
55 +>>> for t in sorted(n.transition(), key=str) :
56 +... print('%s %s' % (t, t.guard))
57 +... for place, label in sorted(t.input(), key=str) :
58 +... print(' %s >> %s' % (place, label))
59 +... for place, label in sorted(t.output(), key=str) :
60 +... print(' %s << %s' % (place, label))
61 +((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)] (...)
62 +...
63 +t2 z>0
64 + e2 >> w
65 + x2 << z
66 +>>> n.restrict('a')
67 +>>> [t.name for t in sorted(n.transition(), key=str)]
68 +["((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]",
69 + "((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]"]
70 +"""
71 +
72 +from snakes import ConstraintError
73 +from snakes.data import Substitution, WordSet, iterate
74 +from snakes.nets import Value, Variable
75 +from snakes.pnml import Tree
76 +import snakes.plugins
77 +from snakes.plugins import new_instance
78 +from snakes.compat import *
79 +
80 +class Action (object) :
81 + def __init__ (self, name, send, params) :
82 + """
83 + @param name: the name of the action
84 + @type name: C{str}
85 + @param send: a flag indicating whether this is a send or
86 + receive action
87 + @type send: C{bool}
88 + @param params: the list of parameters
89 + @type params: C{list} of C{Variable} or C{Value}
90 + """
91 + self.name = name
92 + self.send = send
93 + self.params = list(params)
94 + __pnmltag__ = "action"
95 + def __pnmldump__ (self) :
96 + """
97 + >>> Action('a', True, [Value(1), Variable('x')]).__pnmldump__()
98 + <?xml version="1.0" encoding="utf-8"?>
99 + <pnml>...
100 + <action name="a" send="True">
101 + <value>
102 + <object type="int">
103 + 1
104 + </object>
105 + </value>
106 + <variable>
107 + x
108 + </variable>
109 + </action>
110 + </pnml>
111 + """
112 + result = Tree(self.__pnmltag__, None,
113 + name=self.name,
114 + send=str(self.send))
115 + for param in self.params :
116 + result.add_child(Tree.from_obj(param))
117 + return result
118 + @classmethod
119 + def __pnmlload__ (cls, tree) :
120 + """
121 + >>> t = Action('a', True, [Value(1), Variable('x')]).__pnmldump__()
122 + >>> Action.__pnmlload__(t)
123 + Action('a', True, [Value(1), Variable('x')])
124 + """
125 + params = [Tree.to_obj(child) for child in tree.children]
126 + return cls(tree["name"], tree["send"] == "True", params)
127 + def __str__ (self) :
128 + """
129 + >>> a = Action('a', True, [Value(1), Variable('x')])
130 + >>> str(a)
131 + 'a!(1,x)'
132 + >>> a.send = False
133 + >>> str(a)
134 + 'a?(1,x)'
135 + """
136 + if self.send :
137 + return "%s!(%s)" % (self.name, ",".join([str(p) for p in self]))
138 + else :
139 + return "%s?(%s)" % (self.name, ",".join([str(p) for p in self]))
140 + def __repr__ (self) :
141 + """
142 + >>> a = Action('a', True, [Value(1), Variable('x')])
143 + >>> repr(a)
144 + "Action('a', True, [Value(1), Variable('x')])"
145 + >>> a.send = False
146 + >>> repr(a)
147 + "Action('a', False, [Value(1), Variable('x')])"
148 + """
149 + return "%s(%s, %s, [%s])" % (self.__class__.__name__, repr(self.name),
150 + str(self.send),
151 + ", ".join([repr(p) for p in self]))
152 + def __len__ (self) :
153 + """Return the number of parameters, aka the arity of the
154 + action.
155 +
156 + >>> len(Action('a', True, [Value(1), Variable('x')]))
157 + 2
158 +
159 + @return: the arity of the action
160 + @rtype: non negative C{int}
161 + """
162 + return len(self.params)
163 + def __iter__ (self) :
164 + """Iterate on the parameters
165 +
166 + >>> list(Action('a', True, [Value(1), Variable('x')]))
167 + [Value(1), Variable('x')]
168 + """
169 + for action in self.params :
170 + yield action
171 + def __eq__ (self, other) :
172 + """Two actions are equal if they have the same name, same send
173 + flags and same parameters.
174 +
175 + >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1), Variable('x')])
176 + True
177 + >>> Action('a', True, [Value(1), Variable('x')]) == Action('b', True, [Value(1), Variable('x')])
178 + False
179 + >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', False, [Value(1), Variable('x')])
180 + False
181 + >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(2), Variable('x')])
182 + False
183 + >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1)])
184 + False
185 +
186 + @param other: the action to compare
187 + @type other: C{Action}
188 + @return: C{True} if the two actions are equal, C{False}
189 + otherwise
190 + @rtype: C{bool}
191 + """
192 + if self.name != other.name :
193 + return False
194 + elif self.send != other.send :
195 + return False
196 + elif len(self.params) != len(other.params) :
197 + return False
198 + for p, q in zip(self.params, other.params) :
199 + if p != q :
200 + return False
201 + return True
202 + def __ne__ (self, other) :
203 + return not (self == other)
204 + def copy (self, subst=None) :
205 + """Copy the action, optionally substituting its parameters.
206 +
207 + >>> a = Action('a', True, [Variable('x'), Value(2)])
208 + >>> a.copy()
209 + Action('a', True, [Variable('x'), Value(2)])
210 + >>> a = Action('a', True, [Variable('x'), Value(2)])
211 + >>> a.copy(Substitution(x=Value(3)))
212 + Action('a', True, [Value(3), Value(2)])
213 +
214 + @param subst: if not C{None}, a substitution to apply to the
215 + parameters of the copy
216 + @type subst: C{None} or C{Substitution} mapping variables
217 + names to C{Value} or C{Variable}
218 + @return: a copy of the action, substituted by C{subst} if not
219 + C{None}
220 + @rtype: C{Action}
221 + """
222 + result = self.__class__(self.name, self.send,
223 + [p.copy() for p in self.params])
224 + if subst is not None :
225 + result.substitute(subst)
226 + return result
227 + def substitute (self, subst) :
228 + """Substitute the parameters according to C{subst}
229 +
230 + >>> a = Action('a', True, [Variable('x'), Value(2)])
231 + >>> a.substitute(Substitution(x=Value(3)))
232 + >>> a
233 + Action('a', True, [Value(3), Value(2)])
234 +
235 + @param subst: a substitution to apply to the parameters
236 + @type subst: C{Substitution} mapping variables names to
237 + C{Value} or C{Variable}
238 + """
239 + for i, p in enumerate(self.params) :
240 + if isinstance(p, Variable) and p.name in subst :
241 + self.params[i] = subst(p.name)
242 + def vars (self) :
243 + """
244 + >>> Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')]).vars() == set(['x', 'y'])
245 + True
246 +
247 + @return: the set of variable names appearing in the parameters
248 + of the action
249 + @rtype: C{set} of C{str}
250 + """
251 + return set(p.name for p in self.params if isinstance(p, Variable))
252 + def __and__ (self, other) :
253 + """Compute an unification of two conjugated actions.
254 +
255 + An unification is a C{Substitution} that maps variable names
256 + to C{Variable} or C{Values}. If both actions are substituted
257 + by this unification, their parameters lists become equal. If
258 + no unification can be found, C{ConstraintError} is raised (or,
259 + rarely, C{DomainError} depending on the cause of the failure).
260 +
261 + >>> s = Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')])
262 + >>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')])
263 + >>> u = s & r
264 + >>> u == Substitution(y=Variable('t'), x=Value(2), z=Value(2))
265 + True
266 + >>> s.substitute(u)
267 + >>> r.substitute(u)
268 + >>> s.params == r.params
269 + True
270 + >>> s.params
271 + [Value(3), Value(2), Variable('t'), Value(2)]
272 +
273 + >>> s = Action('a', True, [Value(2), Variable('x'), Variable('y'), Variable('x')])
274 + >>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')])
275 + >>> try : s & r
276 + ... except ConstraintError : print(sys.exc_info()[1])
277 + incompatible values
278 + >>> r = Action('a', False, [Value(2), Value(2), Variable('t')])
279 + >>> try : s & r
280 + ... except ConstraintError : print(sys.exc_info()[1])
281 + arities do not match
282 + >>> r = Action('b', False, [Value(3), Value(2), Variable('t'), Variable('z')])
283 + >>> try : s & r
284 + ... except ConstraintError : print(sys.exc_info()[1])
285 + actions not conjugated
286 + >>> r = Action('a', True, [Value(3), Value(2), Variable('t'), Variable('z')])
287 + >>> try : s & r
288 + ... except ConstraintError : print(sys.exc_info()[1])
289 + actions not conjugated
290 +
291 + @param other: the other action to unify with
292 + @type other: C{Action}
293 + @return: a substitution that unify both actions
294 + @rtype: C{Substitution}
295 + """
296 + if (self.name != other.name) or (self.send == other.send) :
297 + raise ConstraintError("actions not conjugated")
298 + elif len(self) != len(other) :
299 + raise ConstraintError("arities do not match")
300 + result = Substitution()
301 + for x, y in zip(self.params, other.params) :
302 + # apply the unifier already computed
303 + if isinstance(x, Variable) and x.name in result :
304 + x = result(x.name)
305 + if isinstance(y, Variable) and y.name in result :
306 + y = result(y.name)
307 + # unify the current pair of parameters
308 + if isinstance(x, Value) and isinstance(y, Value) :
309 + if x.value != y.value :
310 + raise ConstraintError("incompatible values")
311 + elif isinstance(x, Variable) and isinstance(y, Value) :
312 + result += Substitution({x.name : y.copy()})
313 + elif isinstance(x, Value) and isinstance(y, Variable) :
314 + result += Substitution({y.name : x.copy()})
315 + elif isinstance(x, Variable) and isinstance(y, Variable) :
316 + if x.name != y.name :
317 + result += Substitution({x.name : y.copy()})
318 + else :
319 + raise ConstraintError("unexpected action parameter")
320 + return result
321 +
322 +class MultiAction (object) :
323 + def __init__ (self, actions) :
324 + """
325 + >>> try : MultiAction([Action('a', True, [Variable('x')]),
326 + ... Action('a', False, [Value(2)])])
327 + ... except ConstraintError : print(sys.exc_info()[1])
328 + conjugated actions in the same multiaction
329 +
330 + @param actions: a collection of actions with no conjugated
331 + actions in it
332 + @type actions: C{list} of C{Action}
333 + """
334 + self._actions = []
335 + self._sndrcv = {}
336 + self._count = {}
337 + for act in actions :
338 + self.add(act)
339 + __pnmltag__ = "multiaction"
340 + def __pnmldump__ (self) :
341 + """
342 + >>> MultiAction([Action('a', True, [Variable('x')]),
343 + ... Action('b', False, [Variable('y'), Value(2)])
344 + ... ]).__pnmldump__()
345 + <?xml version="1.0" encoding="utf-8"?>
346 + <pnml>...
347 + <multiaction>
348 + <action name="a" send="True">
349 + <variable>
350 + x
351 + </variable>
352 + </action>
353 + <action name="b" send="False">
354 + <variable>
355 + y
356 + </variable>
357 + <value>
358 + <object type="int">
359 + 2
360 + </object>
361 + </value>
362 + </action>
363 + </multiaction>
364 + </pnml>
365 + """
366 + return Tree(self.__pnmltag__, None,
367 + *(Tree.from_obj(action) for action in self._actions))
368 + @classmethod
369 + def __pnmlload__ (cls, tree) :
370 + """
371 + >>> t = MultiAction([Action('a', True, [Variable('x')]),
372 + ... Action('b', False, [Variable('y'), Value(2)])
373 + ... ]).__pnmldump__()
374 + >>> MultiAction.__pnmlload__(t)
375 + MultiAction([Action('a', True, [Variable('x')]),
376 + Action('b', False, [Variable('y'), Value(2)])])
377 + """
378 + return cls(child.to_obj() for child in tree.children)
379 + def __repr__ (self) :
380 + """
381 + >>> MultiAction([Action('a', True, [Variable('x')]),
382 + ... Action('b', False, [Variable('y'), Value(2)])])
383 + MultiAction([Action('a', True, [Variable('x')]),
384 + Action('b', False, [Variable('y'), Value(2)])])
385 + """
386 + return "%s([%s])" % (self.__class__.__name__,
387 + ", ".join(repr(act) for act in self._actions))
388 + def __str__ (self) :
389 + """
390 + >>> str(MultiAction([Action('a', True, [Variable('x')]),
391 + ... Action('b', False, [Variable('y'), Value(2)])]))
392 + '[a!(x), b?(y,2)]'
393 + """
394 + return "[%s]" % ", ".join(str(act) for act in self._actions)
395 + def send (self, name) :
396 + """Returns the send flag of the action C{name} in this
397 + multiaction.
398 +
399 + This value is unique as conjugated actions are forbidden in
400 + the same multiaction.
401 +
402 + >>> m = MultiAction([Action('a', True, [Variable('x')]),
403 + ... Action('b', False, [Variable('y'), Value(2)])])
404 + >>> m.send('a'), m.send('b')
405 + (True, False)
406 + """
407 + return self._sndrcv[name]
408 + def add (self, action) :
409 + """Add an action to the multiaction.
410 +
411 + This may raise C{ConstraintError} if the added action is
412 + conjugated to one that already belongs to the multiaction.
413 +
414 + @param action: the action to add
415 + @type action: C{Action}
416 + """
417 + if self._sndrcv.get(action.name, action.send) != action.send :
418 + raise ConstraintError("conjugated actions in the same multiaction")
419 + self._sndrcv[action.name] = action.send
420 + self._count[action.name] = self._count.get(action.name, 0) + 1
421 + self._actions.append(action)
422 + def remove (self, action) :
423 + """Remove an action from the multiaction.
424 +
425 + This may raise C{ValueError} if the removed action does
426 + belongs to the multiaction.
427 +
428 + @param action: the action to remove
429 + @type action: C{Action}
430 + """
431 + self._actions.remove(action)
432 + self._count[action.name] -= 1
433 + if self._count[action.name] == 0 :
434 + del self._count[action.name]
435 + del self._sndrcv[action.name]
436 + def __iter__ (self) :
437 + """Iterate over the actions in the multiaction.
438 +
439 + >>> list(MultiAction([Action('a', True, [Variable('x')]),
440 + ... Action('b', False, [Variable('y'), Value(2)])]))
441 + [Action('a', True, [Variable('x')]),
442 + Action('b', False, [Variable('y'), Value(2)])]
443 + """
444 + for action in self._actions :
445 + yield action
446 + def __len__ (self) :
447 + """Return the number of actions in a multiaction.
448 +
449 + >>> len(MultiAction([Action('a', True, [Variable('x')]),
450 + ... Action('b', False, [Variable('y'), Value(2)])]))
451 + 2
452 +
453 + @return: the number of contained actions
454 + @rtype: non negative C{int}
455 + """
456 + return len(self._actions)
457 + def substitute (self, subst) :
458 + """Substitute bu C{subt} all the actions in the multiaction.
459 +
460 + >>> m = MultiAction([Action('a', True, [Variable('x')]),
461 + ... Action('b', False, [Variable('y'), Variable('x')])])
462 + >>> m.substitute(Substitution(x=Value(4)))
463 + >>> m
464 + MultiAction([Action('a', True, [Value(4)]),
465 + Action('b', False, [Variable('y'), Value(4)])])
466 + """
467 + for action in self._actions :
468 + action.substitute(subst)
469 + def copy (self, subst=None) :
470 + """ Copy the multiaction (and the actions is contains)
471 + optionally substituting it.
472 +
473 + @param subst: if not C{None}, the substitution to apply to the
474 + copy.
475 + @type subst: C{None} or C{Substitution}
476 + @return: a copy of the multiaction, optionally substituted
477 + @rtype: C{MultiAction}
478 + """
479 + result = self.__class__(act.copy() for act in self._actions)
480 + if subst is not None :
481 + result.substitute(subst)
482 + return result
483 + def __contains__ (self, action) :
484 + """Search an action in the multiaction.
485 +
486 + The searched action may be a complete C{Action}, just an
487 + action name, or a pair C{(name, send_flag)}.
488 +
489 + >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
490 + ... Action('a', True, [Value(3), Variable('y')]),
491 + ... Action('b', False, [Variable('x'), Variable('y')])])
492 + >>> 'a' in m, 'b' in m, 'c' in m
493 + (True, True, False)
494 + >>> ('a', True) in m, ('a', False) in m
495 + (True, False)
496 + >>> Action('a', True, [Variable('x'), Value(2)]) in m
497 + True
498 + >>> Action('a', True, [Variable('x')]) in m
499 + False
500 + >>> Action('a', False, [Variable('x'), Value(2)]) in m
501 + False
502 + >>> Action('c', True, [Variable('x'), Value(2)]) in m
503 + False
504 +
505 + @param action: an complete action, or its name or its name and
506 + send flag
507 + @type action: C{Action} or C{str} or C{tuple(str, bool)}
508 + @return: C{True} if the specified action was found, C{False}
509 + otherwise
510 + @rtype: C{bool}
511 + """
512 + if isinstance(action, Action) :
513 + return action in self._actions
514 + elif isinstance(action, tuple) and len(action) == 2 :
515 + return (action[0] in self._sndrcv
516 + and self._sndrcv[action[0]] == action[1])
517 + elif isinstance(action, str) :
518 + return action in self._count
519 + else :
520 + raise ValueError("invalid action specification")
521 + def __add__ (self, other) :
522 + """Create a multiaction by adding the actions of two others.
523 +
524 + >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
525 + ... Action('a', True, [Value(3), Variable('y')]),
526 + ... Action('b', False, [Variable('x'), Variable('y')])])
527 + >>> m + m
528 + MultiAction([Action('a', True, [Variable('x'), Value(2)]),
529 + Action('a', True, [Value(3), Variable('y')]),
530 + Action('b', False, [Variable('x'), Variable('y')]),
531 + Action('a', True, [Variable('x'), Value(2)]),
532 + Action('a', True, [Value(3), Variable('y')]),
533 + Action('b', False, [Variable('x'), Variable('y')])])
534 + >>> m + Action('c', True, [])
535 + MultiAction([Action('a', True, [Variable('x'), Value(2)]),
536 + Action('a', True, [Value(3), Variable('y')]),
537 + Action('b', False, [Variable('x'), Variable('y')]),
538 + Action('c', True, [])])
539 +
540 + @param other: the other multiaction to combine or a single
541 + action
542 + @type other: C{MultiAction} or C{Action}
543 + @return: the concatenated multiaction
544 + @rtype: C{MultiAction}
545 + """
546 + if isinstance(other, Action) :
547 + other = self.__class__([other])
548 + result = self.copy()
549 + for action in other._actions :
550 + result.add(action)
551 + return result
552 + def __sub__ (self, other) :
553 + """Create a multiaction by substracting the actions of two others.
554 +
555 + >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
556 + ... Action('a', True, [Value(3), Variable('y')]),
557 + ... Action('b', False, [Variable('x'), Variable('y')])])
558 + >>> m - m
559 + MultiAction([])
560 + >>> m - Action('b', False, [Variable('x'), Variable('y')])
561 + MultiAction([Action('a', True, [Variable('x'), Value(2)]),
562 + Action('a', True, [Value(3), Variable('y')])])
563 +
564 + @param other: the other multiaction to combine or a single
565 + action
566 + @type other: C{MultiAction} or C{Action}
567 + @return: the resulting multiaction
568 + @rtype: C{MultiAction}
569 + """
570 + if isinstance(other, Action) :
571 + other = self.__class__([other])
572 + result = self.copy()
573 + for action in other._actions :
574 + result.remove(action)
575 + return result
576 + def vars (self) :
577 + """Return the set of variable names used in all the actions of
578 + the multiaction.
579 +
580 + >>> MultiAction([Action('a', True, [Variable('x'), Value(2)]),
581 + ... Action('a', True, [Value(3), Variable('y')]),
582 + ... Action('b', False, [Variable('x'), Variable('z')])]).vars() == set(['x', 'y', 'z'])
583 + True
584 +
585 + @return: the set of variable names
586 + @rtype: C{set} of C{str}
587 + """
588 + result = set()
589 + for action in self._actions :
590 + result.update(action.vars())
591 + return result
592 + def synchronise (self, other, name) :
593 + """Search all the possible synchronisation on an action name
594 + with another multiaction.
595 +
596 + This method returns an iterator that yields for each possible
597 + synchronisation a 4-tuple whose components are:
598 +
599 + 1. The sending action that did synchronise, it is already
600 + unified, so the corresponding receiving action is just the
601 + same with the reversed send flag.
602 +
603 + 2. The multiaction resulting from the synchronisation that is
604 + also unified.
605 +
606 + 3. The substitution that must be applied to the transition
607 + that provided the sending action.
608 +
609 + 4. The substitution that must be applied to the transition
610 + that provided the receiving action.
611 +
612 + >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
613 + ... Action('a', True, [Value(3), Variable('y')]),
614 + ... Action('b', False, [Variable('x'), Variable('y')])])
615 + >>> n = MultiAction([Action('a', False, [Variable('w'), Variable('y')]),
616 + ... Action('c', False, [Variable('y')])])
617 + >>> for a, x, u, v in m.synchronise(n, 'a') :
618 + ... print('%s %s %s %s' % (str(a), str(x), list(sorted(u.items())), list(sorted(v.items()))))
619 + a!(w,2) [a!(3,y), b?(w,y), c?(a)] [('a', Value(2)), ('x', Variable('w'))] [('a', Value(2)), ('x', Variable('w')), ('y', Variable('a'))]
620 + a!(3,a) [a!(x,2), b?(x,a), c?(a)] [('w', Value(3)), ('y', Variable('a'))] [('w', Value(3)), ('y', Variable('a'))]
621 +
622 + @param other: the other multiaction to synchronise with
623 + @type other: C{MultiAction}
624 + @param name: the name of the action to synchronise on
625 + @type name: C{str}
626 + @return: an iterator over the possible synchronisations
627 + @rtype: iterator of C{tuple(Action, MultiAction, Substitution, Substitution)}
628 + """
629 + renamer = Substitution()
630 + common = self.vars() & other.vars()
631 + if len(common) > 0 :
632 + names = WordSet(common)
633 + for var in common :
634 + renamer += Substitution({var : Variable(names.fresh(add=True))})
635 + for left in (act for act in self._actions if act.name == name) :
636 + for right in (act for act in other._actions if act.name == name
637 + if act.send != left.send) :
638 + _right = right.copy(renamer)
639 + try :
640 + unifier = left & _right
641 + except :
642 + continue
643 + _unifier = unifier * renamer
644 + _self = self - left
645 + _self.substitute(unifier)
646 + _other = other - right
647 + _other.substitute(_unifier)
648 + yield left.copy(unifier), _self + _other, unifier, _unifier
649 +
650 +@snakes.plugins.plugin("snakes.nets")
651 +def extend (module) :
652 + class Transition (module.Transition) :
653 + def __init__ (self, name, guard=None, **args) :
654 + self.actions = MultiAction(args.pop("actions", []))
655 + module.Transition.__init__(self, name, guard, **args)
656 + def vars (self) :
657 + return module.Transition.vars(self) | self.actions.vars()
658 + def substitute (self, subst) :
659 + module.Transition.substitute(self, subst)
660 + self.actions.substitute(subst)
661 + def copy (self, name=None, **args) :
662 + actions = args.pop("actions", None)
663 + result = module.Transition.copy(self, name, **args)
664 + if actions is None :
665 + result.actions = self.actions.copy()
666 + else :
667 + result.actions = MultiAction(actions)
668 + return result
669 + def __pnmldump__ (self) :
670 + """
671 + >>> m = MultiAction([Action('a', True, [Variable('x')]),
672 + ... Action('b', False, [Variable('y'), Value(2)])])
673 + >>> Transition('t', actions=m).__pnmldump__()
674 + <?xml version="1.0" encoding="utf-8"?>
675 + <pnml>...
676 + <transition id="t">
677 + <multiaction>
678 + <action name="a" send="True">
679 + <variable>
680 + x
681 + </variable>
682 + </action>
683 + <action name="b" send="False">
684 + <variable>
685 + y
686 + </variable>
687 + <value>
688 + <object type="int">
689 + 2
690 + </object>
691 + </value>
692 + </action>
693 + </multiaction>
694 + </transition>
695 + </pnml>
696 + """
697 + result = module.Transition.__pnmldump__(self)
698 + result.add_child(Tree.from_obj(self.actions))
699 + return result
700 + @classmethod
701 + def __pnmlload__ (cls, tree) :
702 + """
703 + >>> m = MultiAction([Action('a', True, [Variable('x')]),
704 + ... Action('b', False, [Variable('y'), Value(2)])])
705 + >>> t = Transition('t', actions=m).__pnmldump__()
706 + >>> Transition.__pnmlload__(t).actions
707 + MultiAction([Action('a', True, [Variable('x')]),
708 + Action('b', False, [Variable('y'), Value(2)])])
709 + """
710 + result = new_instance(cls, module.Transition.__pnmlload__(tree))
711 + result.actions = Tree.to_obj(tree.child(MultiAction.__pnmltag__))
712 + return result
713 + class PetriNet (module.PetriNet) :
714 + def synchronise (self, name) :
715 + snd = []
716 + rcv = []
717 + for trans in self.transition() :
718 + if (name, True) in trans.actions :
719 + snd.append(trans)
720 + elif (name, False) in trans.actions :
721 + rcv.append(trans)
722 + loop = True
723 + done = set()
724 + while loop :
725 + loop = False
726 + for _snd in snd :
727 + for _rcv in rcv :
728 + if (_snd.name, _rcv.name) in done :
729 + continue
730 + try :
731 + new = _snd.actions.synchronise(_rcv.actions, name)
732 + except ConstraintError :
733 + continue
734 + for a, m, s, r in new :
735 + t = self._synchronise(_snd, s, _rcv, r, m, a)
736 + if (name, True) in t.actions :
737 + snd.append(t)
738 + loop = True
739 + elif (name, False) in t.actions :
740 + rcv.append(t)
741 + loop = True
742 + done.add((_snd.name, _rcv.name))
743 + def _synchronise (self, snd, s, rcv, r, actions, sync) :
744 + collect = []
745 + varset = WordSet()
746 + for trans, subst in ((snd, s), (rcv, r)) :
747 + new = "%s%s" % (trans.name, str(subst))
748 + self.copy_transition(trans.name, new)
749 + collect.append(new)
750 + new = self.transition(new)
751 + nv = new.vars()
752 + for v in varset & nv :
753 + new.substitute(Substitution({v : varset.fresh(add=True)}))
754 + varset.update(nv)
755 + for var, val in subst.items() :
756 + if isinstance(val, Variable) :
757 + new.substitute(Substitution({var : val.name}))
758 + for place, label in new.input() :
759 + if var in label.vars() :
760 + self.remove_input(place.name, new.name)
761 + self.add_input(place.name, new.name,
762 + label.replace(Variable(var), val))
763 + for place, label in new.output() :
764 + if var in label.vars() :
765 + self.remove_output(place.name, new.name)
766 + self.add_output(place.name, new.name,
767 + label.replace(Variable(var), val))
768 + merged = "(%s%s+%s%s)[%s]" % (snd.name, str(s), rcv.name, str(s),
769 + str(sync).replace("?", "").replace("!", ""))
770 + self.merge_transitions(merged, collect, actions=actions)
771 + for name in collect :
772 + self.remove_transition(name)
773 + return self.transition(merged)
774 + def restrict (self, action) :
775 + removed = [trans.name for trans in self.transition()
776 + if action in trans.actions]
777 + for trans in removed :
778 + self.remove_transition(trans)
779 + def scope (self, action) :
780 + self.synchronise(action)
781 + self.restrict(action)
782 + def merge_transitions (self, target, sources, **args) :
783 + actions = args.pop("actions", None)
784 + module.PetriNet.merge_transitions(self, target, sources, **args)
785 + if actions is None :
786 + actions = MultiAction()
787 + for src in sources :
788 + actions += self.transition(src).actions
789 + self.transition(target).actions = actions
790 + else :
791 + self.transition(target).actions = MultiAction(actions)
792 + def copy_transition (self, source, targets, **args) :
793 + actions = args.pop("actions", None)
794 + module.PetriNet.copy_transition(self, source, targets, **args)
795 + if actions is None :
796 + actions = self.transition(source).actions
797 + else :
798 + actions = MultiAction(actions)
799 + old = self.transition(source)
800 + for trans in iterate(targets) :
801 + self.transition(trans).actions = actions.copy()
802 + return PetriNet, Transition, Action, MultiAction
1 +"""A module to save and load objects in PNML.
2 +
3 +Petri nets objects are saved in PNML, other Python objects are saved
4 +in a readable format when possible and pickled as a last solution.
5 +This should result in a complete PNML serialization of any object.
6 +"""
7 +
8 +import xml.dom.minidom
9 +import pickle
10 +import sys, inspect, os, os.path, imp, pkgutil
11 +import snakes, snakes.plugins
12 +from snakes import SnakesError
13 +from snakes.compat import *
14 +if PY3 :
15 + import ast
16 +
17 +try :
18 + builtins = sys.modules["__builtin__"]
19 +except KeyError :
20 + builtins = sys.modules["builtins"]
21 +
22 +def _snk_import (name) :
23 + "Properly import a module, including a plugin"
24 + if name.startswith("snakes.plugins.") :
25 + return snakes.plugins.load(name, "snakes.nets")
26 + else :
27 + return __import__(name, fromlist=["__name__"])
28 +
29 +def _snk_modules () :
30 + "List all SNAKES' modules"
31 + queue = ["snakes"]
32 + while len(queue) > 0 :
33 + modname = queue.pop(0)
34 + try :
35 + mod = _snk_import(modname)
36 + except :
37 + continue
38 + yield modname, mod
39 + importer = pkgutil.ImpImporter(mod.__path__[0])
40 + for name, ispkg in importer.iter_modules(prefix=mod.__name__ + ".") :
41 + if ispkg :
42 + queue.append(name)
43 + else :
44 + try :
45 + yield name, _snk_import(name)
46 + except :
47 + pass
48 +
49 +def _snk_tags () :
50 + "Lists all PNML tags found in SNAKES"
51 + for modname, mod in _snk_modules() :
52 + for clsname, cls in inspect.getmembers(mod, inspect.isclass) :
53 + if cls.__module__ == modname and "__pnmltag__" in cls.__dict__ :
54 + yield cls.__pnmltag__, modname, clsname
55 +
56 +class _set (object) :
57 + """Set where items are iterated by order of insertion
58 + """
59 + def __init__ (self, elements=[]) :
60 + """
61 + >>> _set([4, 5, 1, 2, 4])
62 + _set([4, 5, 1, 2])
63 + """
64 + self._data = {}
65 + self._last = 0
66 + for e in elements :
67 + self.add(e)
68 + def __repr__ (self) :
69 + """
70 + >>> _set([4, 5, 1, 2, 4])
71 + _set([4, 5, 1, 2])
72 + """
73 + return "%s([%s])" % (self.__class__.__name__,
74 + ", ".join(repr(x) for x in self))
75 + def add (self, element) :
76 + """
77 + >>> s = _set([4, 5, 1, 2, 4])
78 + >>> s.add(0)
79 + >>> s
80 + _set([4, 5, 1, 2, 0])
81 + >>> s.add(5)
82 + >>> s
83 + _set([4, 5, 1, 2, 0])
84 + """
85 + if element not in self._data :
86 + self._data[element] = self._last
87 + self._last += 1
88 + def __contains__ (self, element) :
89 + """
90 + >>> 4 in _set([4, 5, 1, 2, 4])
91 + True
92 + >>> 0 in _set([4, 5, 1, 2, 4])
93 + False
94 + """
95 + return element in self._data
96 + def _2nd (self, pair) :
97 + """
98 + >>> _set()._2nd((1, 2))
99 + 2
100 + """
101 + return pair[1]
102 + def __iter__ (self) :
103 + """
104 + >>> list(_set([4, 5, 1, 2, 4]))
105 + [4, 5, 1, 2]
106 + """
107 + return (k for k, v in sorted(self._data.items(), key=self._2nd))
108 + def discard (self, element) :
109 + """
110 + >>> s = _set([4, 5, 1, 2, 4])
111 + >>> s.discard(0)
112 + >>> s.discard(4)
113 + >>> s
114 + _set([5, 1, 2])
115 + """
116 + if element in self._data :
117 + del self._data[element]
118 + def remove (self, element) :
119 + """
120 + >>> s = _set([4, 5, 1, 2, 4])
121 + >>> s.remove(0)
122 + Traceback (most recent call last):
123 + ...
124 + KeyError: ...
125 + >>> s.remove(4)
126 + >>> s
127 + _set([5, 1, 2])
128 + """
129 + del self._data[element]
130 + def copy (self) :
131 + """
132 + >>> _set([4, 5, 1, 2, 4]).copy()
133 + _set([4, 5, 1, 2])
134 + """
135 + return self.__class__(self)
136 + def __iadd__ (self, other) :
137 + """
138 + >>> s = _set([4, 5, 1, 2, 4])
139 + >>> s += range(7)
140 + >>> s
141 + _set([4, 5, 1, 2, 0, 3, 6])
142 + """
143 + for element in other :
144 + self.add(element)
145 + return self
146 + def __add__ (self, other) :
147 + """
148 + >>> _set([4, 5, 1, 2, 4]) + range(7)
149 + _set([4, 5, 1, 2, 0, 3, 6])
150 + """
151 + result = self.copy()
152 + result += other
153 + return result
154 + def __len__ (self) :
155 + """
156 + >>> len(_set([4, 5, 1, 2, 4]))
157 + 4
158 + """
159 + return len(self._data)
160 +
161 +class Tree (object) :
162 + """Abstraction of a PNML tree
163 +
164 + >>> Tree('tag', 'data', Tree('child', None), attr='attribute value')
165 + <?xml version="1.0" encoding="utf-8"?>
166 + <pnml>
167 + <tag attr="attribute value">
168 + <child/>
169 + data
170 + </tag>
171 + </pnml>
172 + """
173 + def __init__ (self, _name, _data, *_children, **_attributes) :
174 + """Initialize a PNML tree
175 +
176 + >>> Tree('tag', 'data',
177 + ... Tree('first_child', None),
178 + ... Tree('second_child', None),
179 + ... first_attr='attribute value',
180 + ... second_attr='another value')
181 + <?xml version="1.0" encoding="utf-8"?>
182 + <pnml>
183 + <tag first_attr="attribute value" second_attr="another value">
184 + <first_child/>
185 + <second_child/>
186 + data
187 + </tag>
188 + </pnml>
189 +
190 + Note: parameters names start with a '_' in order to allow for
191 + using them as attributes.
192 +
193 + @param _name: the name of the tag
194 + @type _name: C{str}
195 + @param _data: the text held by the tag or C{None}
196 + @type _data: C{str} or C{None}
197 + @param _children: children nodes
198 + @type _children: C{Tree}
199 + @param _attributes: attributes and values of the tag
200 + @type _attributes: C{str}
201 + """
202 + self.name = _name
203 + if _data is not None and _data.strip() == "" :
204 + _data = None
205 + self.data = _data
206 + self.children = list(_children)
207 + self.attributes = _attributes
208 + @classmethod
209 + def _load_tags (_class) :
210 + if not hasattr(_class, "_tags") :
211 + _class._tags = {}
212 + for tag, mod, cls in _snk_tags() :
213 + if tag not in _class._tags :
214 + _class._tags[tag] = (mod, cls)
215 + def _update_node (self, doc, node) :
216 + for key, val in self.attributes.items() :
217 + node.setAttribute(key, val)
218 + for child in self.children :
219 + node.appendChild(child._to_dom(doc))
220 + if self.data is not None :
221 + node.appendChild(doc.createTextNode(self.data))
222 + def _to_dom (self, doc) :
223 + result = doc.createElement(self.name)
224 + self._update_node(doc, result)
225 + return result
226 + def to_pnml (self) :
227 + """Dumps a PNML tree to an XML string
228 +
229 + >>> print(Tree('tag', 'data', Tree('child', None), attr='value').to_pnml())
230 + <?xml version="1.0" encoding="utf-8"?>
231 + <pnml>
232 + <tag attr="value">
233 + <child/>
234 + data
235 + </tag>
236 + </pnml>
237 +
238 + @return: the XML string that represents the PNML tree
239 + @rtype: C{str}
240 + """
241 + if self.name == "pnml" :
242 + tree = self
243 + else :
244 + tree = self.__class__("pnml", None, self)
245 + try :
246 + plugins = _set(self.__plugins__)
247 + except AttributeError :
248 + plugins = _set()
249 + for node in self.nodes() :
250 + if hasattr(node, "_plugins") :
251 + plugins += node._plugins
252 + if len(plugins) > 0 :
253 + tree.children.insert(0, Tree("snakes", None,
254 + Tree("plugins", None,
255 + Tree.from_obj(tuple(plugins))),
256 + version=snakes.version))
257 + impl = xml.dom.minidom.getDOMImplementation()
258 + doc = impl.createDocument(None, "pnml", None)
259 + node = tree._to_dom(doc)
260 + tree._update_node(doc, doc.documentElement)
261 + if len(plugins) > 0 :
262 + del tree.children[0]
263 + r = doc.toprettyxml(indent=" ",
264 + encoding=snakes.defaultencoding).strip()
265 + if PY3 :
266 + return r.decode()
267 + else :
268 + return r
269 + @classmethod
270 + def from_dom (cls, node) :
271 + """Load a PNML tree from an XML DOM representation
272 +
273 + >>> src = Tree('object', '42', type='int').to_pnml()
274 + >>> dom = xml.dom.minidom.parseString(src)
275 + >>> Tree.from_dom(dom.documentElement)
276 + <?xml version="1.0" encoding="utf-8"?>
277 + <pnml>
278 + <object type="int">
279 + 42
280 + </object>
281 + </pnml>
282 +
283 + @param node: the DOM node to load
284 + @type node: C{xml.dom.minidom.Element}
285 + @return: the loaded PNML tree
286 + @rtype: C{Tree}
287 + """
288 + result = cls(node.tagName, node.nodeValue)
289 + for i in range(node.attributes.length) :
290 + name = node.attributes.item(i).localName
291 + result[name] = str(node.getAttribute(name))
292 + if node.hasChildNodes() :
293 + for child in node.childNodes :
294 + if child.nodeType == child.TEXT_NODE :
295 + result.add_data(str(child.data))
296 + elif child.nodeType == child.ELEMENT_NODE :
297 + result.add_child(cls.from_dom(child))
298 + return result
299 + @classmethod
300 + def from_pnml (cls, source, plugins=[]) :
301 + """Load a PNML tree from an XML string representation
302 +
303 + >>> src = Tree('object', '42', type='int').to_pnml()
304 + >>> Tree.from_pnml(src)
305 + <?xml version="1.0" encoding="utf-8"?>
306 + <pnml>
307 + <object type="int">
308 + 42
309 + </object>
310 + </pnml>
311 +
312 + @param source: the XML string to load or an opened file that
313 + contains it
314 + @type source: C{str} or C{file}
315 + @return: the loaded PNML tree
316 + @rtype: C{Tree}
317 + """
318 + try :
319 + doc = xml.dom.minidom.parse(source)
320 + except :
321 + doc = xml.dom.minidom.parseString(source)
322 + result = cls.from_dom(doc.documentElement)
323 + plugins = _set(plugins)
324 + cls._load_tags()
325 + tag2obj = {}
326 + trash = []
327 + for node in result.nodes() :
328 + node._tag2obj = tag2obj
329 + if node.has_child("snakes") :
330 + snk = node.child("snakes")
331 + trash.append((node, snk))
332 + plugins += snk.child("plugins").child("object").to_obj()
333 + if node.name in cls._tags :
334 + modname, clsname = cls._tags[node.name]
335 + if modname.startswith("snakes.plugins.") :
336 + plugins.add(modname)
337 + elif node.name not in tag2obj :
338 + tag2obj[node.name] = getattr(_snk_import(modname), clsname)
339 + for parent, child in trash :
340 + parent.children.remove(child)
341 + plugins.discard("snakes.nets")
342 + nets = snakes.plugins.load(plugins, "snakes.nets")
343 + for name, obj in inspect.getmembers(nets, inspect.isclass) :
344 + # Skip classes that cannot be serialised to PNML
345 + try :
346 + tag = obj.__pnmltag__
347 + except AttributeError :
348 + continue
349 + # Get the last class in the hierarchy that has the same
350 + # "__pnmltag__" and is in the same module. This is useful
351 + # for instance for snakes.typing.Type and its subclasses:
352 + # the former should be called used of the laters because
353 + # it dispatches the call to "__pnmlload__" according to
354 + # "__pnmltype__".
355 + bases = [obj] + [c for c in inspect.getmro(obj)
356 + if (c.__module__ == obj.__module__)
357 + and hasattr(c, "__pnmltag__")
358 + and c.__pnmltag__ == tag]
359 + tag2obj[tag] = bases[-1]
360 + return result
361 + def nodes (self) :
362 + """Iterate over all the nodes (top-down) in a tree
363 +
364 + >>> t = Tree('foo', None,
365 + ... Tree('bar', None),
366 + ... Tree('egg', None,
367 + ... Tree('spam', None)))
368 + >>> for node in t.nodes() :
369 + ... print(str(node))
370 + <PNML tree 'foo'>
371 + <PNML tree 'bar'>
372 + <PNML tree 'egg'>
373 + <PNML tree 'spam'>
374 +
375 + @return: an iterator on all the nodes in the tree, including
376 + this one
377 + @rtype: C{generator}
378 + """
379 + yield self
380 + for child in self.children :
381 + for node in child.nodes() :
382 + yield node
383 + def update (self, other) :
384 + """Incorporates children, attributes and data from another
385 + PNML tree
386 +
387 + >>> t = Tree('foo', 'hello',
388 + ... Tree('bar', None),
389 + ... Tree('egg', None,
390 + ... Tree('spam', None)))
391 + >>> o = Tree('foo', 'world',
392 + ... Tree('python', None),
393 + ... attr='value')
394 + >>> t.update(o)
395 + >>> t
396 + <?xml version="1.0" encoding="utf-8"?>
397 + <pnml>
398 + <foo attr="value">
399 + <bar/>
400 + <egg>
401 + <spam/>
402 + </egg>
403 + <python/>
404 + hello
405 + world
406 + </foo>
407 + </pnml>
408 + >>> o = Tree('oops', None,
409 + ... Tree('hello', None),
410 + ... attr='value')
411 + >>> try : t.update(o)
412 + ... except SnakesError : print(sys.exc_info()[1])
413 + tag mismatch 'foo', 'oops'
414 +
415 + @param other: the other tree to get data from
416 + @type other: C{Tree}
417 + @raise SnakesError: when C{other} has not the same tag as C{self}
418 + """
419 + if self.name != other.name :
420 + raise SnakesError("tag mismatch '%s', '%s'" % (self.name, other.name))
421 + self.children.extend(other.children)
422 + self.attributes.update(other.attributes)
423 + self.add_data(other.data)
424 + def add_child (self, child) :
425 + """Add a child to a PNML tree
426 +
427 + >>> t = Tree('foo', None)
428 + >>> t.add_child(Tree('bar', None))
429 + >>> t
430 + <?xml version="1.0" encoding="utf-8"?>
431 + <pnml>
432 + <foo>
433 + <bar/>
434 + </foo>
435 + </pnml>
436 +
437 + @param child: the PNML tree to append
438 + @type child: C{Tree}
439 + """
440 + self.children.append(child)
441 + def add_data (self, data, sep='\n') :
442 + """Appends data to the current node
443 +
444 + >>> t = Tree('foo', None)
445 + >>> t.add_data('hello')
446 + >>> t
447 + <?xml version="1.0" encoding="utf-8"?>
448 + <pnml>
449 + <foo>
450 + hello
451 + </foo>
452 + </pnml>
453 + >>> t.add_data('world')
454 + >>> t
455 + <?xml version="1.0" encoding="utf-8"?>
456 + <pnml>
457 + <foo>
458 + hello
459 + world
460 + </foo>
461 + </pnml>
462 + >>> t.add_data('!', '')
463 + >>> t
464 + <?xml version="1.0" encoding="utf-8"?>
465 + <pnml>
466 + <foo>
467 + hello
468 + world!
469 + </foo>
470 + </pnml>
471 +
472 + @param data: the data to add
473 + @type data: C{str}
474 + @param sep: separator to insert between pieces of data
475 + @type sep: C{str}
476 + """
477 + try :
478 + data = data.strip()
479 + if data != "" :
480 + if self.data is None :
481 + self.data = data
482 + else :
483 + self.data += sep + data
484 + except :
485 + pass
486 + def __getitem__ (self, name) :
487 + """Returns one attribute
488 +
489 + >>> Tree('foo', None, x='egg', y='spam')['x']
490 + 'egg'
491 + >>> Tree('foo', None, x='egg', y='spam')['z']
492 + Traceback (most recent call last):
493 + ...
494 + KeyError: 'z'
495 +
496 + @param name: the name of the attribute
497 + @type name: C{str}
498 + @return: the value of the attribute
499 + @rtype: C{str}
500 + @raise KeyError: if no such attribute is found
501 + """
502 + return self.attributes[name]
503 + def __setitem__ (self, name, value) :
504 + """Sets an attribute
505 +
506 + >>> t = Tree('foo', None)
507 + >>> t['egg'] = 'spam'
508 + >>> t
509 + <?xml version="1.0" encoding="utf-8"?>
510 + <pnml>
511 + <foo egg="spam"/>
512 + </pnml>
513 +
514 + @param name: the name of the attribute
515 + @type name: C{str}
516 + @param value: the value of the attribute
517 + @type value: C{str}
518 + """
519 + self.attributes[name] = value
520 + def __iter__ (self) :
521 + """Iterate over children nodes
522 +
523 + >>> [str(node) for node in Tree('foo', None,
524 + ... Tree('egg', None),
525 + ... Tree('spam', None,
526 + ... Tree('bar', None)))]
527 + ["<PNML tree 'egg'>", "<PNML tree 'spam'>"]
528 +
529 + @return: an iterator over direct children of the node
530 + @rtype: C{generator}
531 + """
532 + return iter(self.children)
533 + def has_child (self, name) :
534 + """Test if the tree has the given tag as a direct child
535 +
536 + >>> t = Tree('foo', None,
537 + ... Tree('egg', None),
538 + ... Tree('spam', None,
539 + ... Tree('bar', None)))
540 + >>> t.has_child('egg')
541 + True
542 + >>> t.has_child('bar')
543 + False
544 + >>> t.has_child('python')
545 + False
546 +
547 + @param name: tag name to search for
548 + @type name: C{str}
549 + @return: a Boolean value indicating wether such a child was
550 + found or not
551 + @rtype: C{bool}
552 + """
553 + for child in self :
554 + if child.name == name :
555 + return True
556 + return False
557 + def child (self, name=None) :
558 + """Return the direct child that as the given tag
559 +
560 + >>> t = Tree('foo', None,
561 + ... Tree('egg', 'first'),
562 + ... Tree('egg', 'second'),
563 + ... Tree('spam', None,
564 + ... Tree('bar', None)))
565 + >>> t.child('spam')
566 + <?xml version="1.0" encoding="utf-8"?>
567 + <pnml>
568 + <spam>
569 + <bar/>
570 + </spam>
571 + </pnml>
572 + >>> try : t.child('python')
573 + ... except SnakesError : print(sys.exc_info()[1])
574 + no child 'python'
575 + >>> try : t.child('bar')
576 + ... except SnakesError : print(sys.exc_info()[1])
577 + no child 'bar'
578 + >>> try : t.child('egg')
579 + ... except SnakesError : print(sys.exc_info()[1])
580 + multiple children 'egg'
581 + >>> try : t.child()
582 + ... except SnakesError : print(sys.exc_info()[1])
583 + multiple children
584 +
585 + @param name: name of the tag to search for, if C{None}, the
586 + fisrt child is returned if it is the only child
587 + @type name: C{str} or C{None}
588 + @return: the only child with the given name, or the only child
589 + if no name is given
590 + @rtype: C{Tree}
591 + @raise SnakesError: when no child or more than one child could
592 + be returned
593 + """
594 + result = None
595 + for child in self :
596 + if name is None or child.name == name :
597 + if result is None :
598 + result = child
599 + elif name is None :
600 + raise SnakesError("multiple children")
601 + else :
602 + raise SnakesError("multiple children '%s'" % name)
603 + if result is None :
604 + raise SnakesError("no child '%s'" % name)
605 + return result
606 + def get_children (self, name=None) :
607 + """Iterates over direct children having the given tag
608 +
609 + >>> t = Tree('foo', None,
610 + ... Tree('egg', 'first'),
611 + ... Tree('egg', 'second'),
612 + ... Tree('spam', None,
613 + ... Tree('bar', None)))
614 + >>> [str(n) for n in t.get_children()]
615 + ["<PNML tree 'egg'>", "<PNML tree 'egg'>", "<PNML tree 'spam'>"]
616 + >>> [str(n) for n in t.get_children('egg')]
617 + ["<PNML tree 'egg'>", "<PNML tree 'egg'>"]
618 + >>> [str(n) for n in t.get_children('python')]
619 + []
620 + >>> [str(n) for n in t.get_children('bar')]
621 + []
622 +
623 + @param name: tag to search for or C{None}
624 + @type name: C{str} or C{None}
625 + @return: iterator over all the children if C{name} is C{None},
626 + or over the children with tag C{name} otherwise
627 + @rtype: C{generator}
628 + """
629 + for child in self :
630 + if name is None or child.name == name :
631 + yield child
632 + def __str__ (self) :
633 + """Return a simple string representation of the node
634 +
635 + >>> str(Tree('foo', None, Tree('child', None)))
636 + "<PNML tree 'foo'>"
637 +
638 + @return: simple string representation of the node
639 + @rtype: C{str}
640 + """
641 + return "<PNML tree %r>" % self.name
642 + def __repr__ (self) :
643 + """Return a detailed representation of the node.
644 +
645 + This is actually the XML text that corresponds to the C{Tree},
646 + as returned by C{Tree.to_pnml}.
647 +
648 + >>> print(repr(Tree('foo', None, Tree('child', None))))
649 + <?xml version="1.0" encoding="utf-8"?>
650 + <pnml>
651 + <foo>
652 + <child/>
653 + </foo>
654 + </pnml>
655 +
656 + @return: XML string representation of the node
657 + @rtype: C{str}
658 + """
659 + return self.to_pnml()
660 + _elementary = set(("str", "int", "float", "bool"))
661 + _collection = set(("list", "tuple", "set"))
662 + @classmethod
663 + def from_obj (cls, obj) :
664 + """Builds a PNML tree from an object.
665 +
666 + Objects defined in SNAKES usually have a method
667 + C{__pnmldump__} that handles the conversion, for instance:
668 +
669 + >>> import snakes.nets
670 + >>> Tree.from_obj(snakes.nets.Place('p'))
671 + <?xml version="1.0" encoding="utf-8"?>
672 + <pnml>
673 + <place id="p">
674 + <type domain="universal"/>
675 + <initialMarking>
676 + <multiset/>
677 + </initialMarking>
678 + </place>
679 + </pnml>
680 +
681 + Most basic Python classes are handled has readable XML:
682 +
683 + >>> Tree.from_obj(42)
684 + <?xml version="1.0" encoding="utf-8"?>
685 + <pnml>
686 + <object type="int">
687 + 42
688 + </object>
689 + </pnml>
690 + >>> Tree.from_obj([1, 2, 3])
691 + <?xml version="1.0" encoding="utf-8"?>
692 + <pnml>
693 + <object type="list">
694 + <object type="int">
695 + 1
696 + </object>
697 + <object type="int">
698 + 2
699 + </object>
700 + <object type="int">
701 + 3
702 + </object>
703 + </object>
704 + </pnml>
705 +
706 + Otherwise, the object is serialised using module C{pickle},
707 + which allows to embed almost anything into PNML.
708 +
709 + >>> import re
710 + >>> Tree.from_obj(re.compile('foo|bar')) # serialized data replaced with '...'
711 + <?xml version="1.0" encoding="utf-8"?>
712 + <pnml>
713 + <object type="pickle">
714 + ...
715 + </object>
716 + </pnml>
717 +
718 + @param obj: the object to convert to PNML
719 + @type obj: C{object}
720 + @return: the corresponding PNML tree
721 + @rtype: C{Tree}
722 + """
723 + try :
724 + result = obj.__pnmldump__()
725 + result._tag2obj = {result.name : obj}
726 + if hasattr(obj, "__plugins__") :
727 + result._plugins = obj.__plugins__
728 + return result
729 + except AttributeError :
730 + pass
731 + result = Tree("object", None)
732 + _type = type(obj)
733 + _name = _type.__name__
734 + if _name in cls._elementary :
735 + handler = result._set_elementary
736 + elif _name in cls._collection :
737 + handler = result._set_collection
738 + elif inspect.ismethod(obj) :
739 + handler = result._set_method
740 + _name = "method"
741 + elif inspect.isclass(obj) :
742 + handler = result._set_class
743 + _name = "class"
744 + elif inspect.isroutine(obj) :
745 + handler = result._set_function
746 + _name = "function"
747 + elif inspect.ismodule(obj) :
748 + handler = result._set_module
749 + _name = "module"
750 + else :
751 + try :
752 + handler = getattr(result, "_set_" + _name)
753 + except AttributeError :
754 + handler = result._set_pickle
755 + _name = "pickle"
756 + result["type"] = _name
757 + handler(obj)
758 + return result
759 + def _set_NoneType (self, value) :
760 + pass
761 + def _get_NoneType (self) :
762 + pass
763 + def _set_elementary (self, value) :
764 + self.data = str(value)
765 + def _get_elementary (self) :
766 + if self["type"] == "bool" :
767 + return self.data.strip() == "True"
768 + return getattr(builtins, self["type"])(self.data)
769 + def _set_collection (self, value) :
770 + for v in value :
771 + self.add_child(self.from_obj(v))
772 + def _get_collection (self) :
773 + return getattr(builtins, self["type"])(child.to_obj()
774 + for child in self)
775 + def _set_dict (self, value) :
776 + for k, v in value.items() :
777 + self.add_child(Tree("item", None,
778 + Tree("key", None, self.from_obj(k)),
779 + Tree("value", None, self.from_obj(v))))
780 + def _get_dict (self) :
781 + return dict((child.child("key").child("object").to_obj(),
782 + child.child("value").child("object").to_obj())
783 + for child in self)
784 + def _native (self, obj) :
785 + try :
786 + if (obj.__module__ in ("__builtin__", "__builtins__", "builtins")
787 + or obj.__module__ == "snakes"
788 + or obj.__module__.startswith("snakes")
789 + or inspect.isbuiltin(obj)) :
790 + return True
791 + except :
792 + pass
793 + try :
794 + lib = os.path.dirname(inspect.getfile(inspect.getfile))
795 + if os.path.isfile(lib) :
796 + lib = os.path.dirname(lib)
797 + lib += os.sep
798 + try :
799 + path = inspect.getfile(obj)
800 + except :
801 + path = inspect.getmodule(obj).__file__
802 + return path.startswith(lib)
803 + except :
804 + return False
805 + def _name (self, obj) :
806 + try :
807 + name = obj.__module__
808 + except :
809 + name = inspect.getmodule(obj).__name__
810 + if name in ("__builtin__", "__builtins__", "builtins") :
811 + return obj.__name__
812 + else :
813 + return name + "." + obj.__name__
814 + def _set_class (self, value) :
815 + if self._native(value) :
816 + self["name"] = self._name(value)
817 + else :
818 + self._set_pickle(value)
819 + def _get_class (self) :
820 + if self.data :
821 + return self._get_pickle()
822 + elif "." in self["name"] :
823 + module, name = self["name"].rsplit(".", 1)
824 + return getattr(__import__(module, fromlist=[name]), name)
825 + else :
826 + return getattr(builtins, self["name"])
827 + def _set_function (self, value) :
828 + self._set_class(value)
829 + def _get_function (self) :
830 + return self._get_class()
831 + def _set_method (self, value) :
832 + self._set_function(value)
833 + self["name"] = "%s.%s" % (self["name"], value.__name__)
834 + def _get_method (self) :
835 + if self.data :
836 + return self._get_pickle()
837 + module, cls, name = self["name"].rsplit(".", 2)
838 + cls = getattr(__import__(module, fromlist=[name]), name)
839 + return getattr(cls, name)
840 + def _set_module (self, value) :
841 + self["name"] = value.__name__
842 + def _get_module (self) :
843 + return __import__(self["name"], fromlist=["__name__"])
844 + def _set_pickle (self, value) :
845 + self["type"] = "pickle"
846 + self.data = pickle.dumps(value)
847 + if PY3 :
848 + self.data = repr(self.data)
849 + def _get_pickle (self) :
850 + if PY3 :
851 + return pickle.loads(ast.literal_eval(self.data))
852 + else :
853 + return pickle.loads(self.data)
854 + def to_obj (self) :
855 + """Build an object from its PNML representation
856 +
857 + This is just the reverse as C{Tree.from_obj}, objects that
858 + have a C{__pnmldump__} method should also have a
859 + C{__pnmlload__} class method to perform the reverse operation,
860 + together with an attribute C{__pnmltag__}. Indeed, when a PNML
861 + node with tag name 'foo' has to be converted to an object, a
862 + class C{C} such that C{C.__pnmltag__ == 'foo'} is searched in
863 + module C{snakes.nets} and C{C.__pnmlload__(tree)} is called to
864 + rebuild the object.
865 +
866 + Standard Python objects and pickled ones are also recognised
867 + and correctly rebuilt.
868 +
869 + >>> import snakes.nets
870 + >>> Tree.from_obj(snakes.nets.Place('p')).to_obj()
871 + Place('p', MultiSet([]), tAll)
872 + >>> Tree.from_obj(42).to_obj()
873 + 42
874 + >>> Tree.from_obj([1, 2, 3]).to_obj()
875 + [1, 2, 3]
876 + >>> import re
877 + >>> Tree.from_obj(re.compile('foo|bar')).to_obj()
878 + <... object at ...>
879 +
880 + @return: the Python object encoded by the PNML tree
881 + @rtype: C{object}
882 + """
883 + if self.name == "pnml" :
884 + if len(self.children) == 0 :
885 + raise SnakesError("empty PNML content")
886 + elif len(self.children) == 1 :
887 + return self.child().to_obj()
888 + else :
889 + return tuple(child.to_obj() for child in self.children
890 + if child.name != "snakes")
891 + elif self.name == "object" :
892 + if self["type"] in self._elementary :
893 + handler = self._get_elementary
894 + elif self["type"] in self._collection :
895 + handler = self._get_collection
896 + else :
897 + try :
898 + handler = getattr(self, "_get_" + self["type"])
899 + except AttributeError :
900 + handler = self._get_pickle
901 + return handler()
902 + elif self.name != "snakes" :
903 + try :
904 + if self.name in self._tag2obj :
905 + return self._tag2obj[self.name].__pnmlload__(self)
906 + except AttributeError :
907 + pass
908 + raise SnakesError("unsupported PNML tag '%s'" % self.name)
909 +
910 +def dumps (obj) :
911 + """Dump an object to a PNML string
912 +
913 + >>> print(dumps(42))
914 + <?xml version="1.0" encoding="utf-8"?>
915 + <pnml>
916 + <object type="int">
917 + 42
918 + </object>
919 + </pnml>
920 +
921 + @param obj: the object to dump
922 + @type obj: C{object}
923 + @return: the PNML that represents the object
924 + @rtype: C{str}
925 + """
926 + return Tree.from_obj(obj).to_pnml()
927 +
928 +def loads (source, plugins=[]) :
929 + """Load an object from a PNML string
930 +
931 + >>> loads(dumps(42))
932 + 42
933 +
934 + @param source: the data to parse
935 + @type source: C{str}
936 + @return: the object represented by the source
937 + @rtype: C{object}
938 + """
939 + return Tree.from_pnml(source, plugins).to_obj()
1 +"""Typing system for places (and transitions guards).
2 +
3 +Types are contraint checkers to verify whether a value is in the type
4 +or not. For instance:
5 +
6 +>>> 5 in tInteger
7 +True
8 +>>> 4.3 in tInteger
9 +False
10 +
11 +The typechecking of several values is possible using the type as a
12 +function. It fails if one value is not in the type.
13 +
14 +>>> tInteger(5, 4, 6)
15 +True
16 +>>> tInteger(5, 4, 6.0)
17 +False
18 +
19 +Types can be composed in order to build more complexe types. For
20 +example:
21 +
22 +>>> 8 in (tInteger & ~Range(1, 5))
23 +True
24 +>>> 3 in (tInteger & ~Range(1, 5))
25 +False
26 +
27 +The various compositions are the same as sets operations, plus the
28 +complement: C{&}, C{|}, C{-}, C{^} and C{~} (complement).
29 +
30 +Various types are predefined (like C{tInteger}) the others can be
31 +constructed using the various classes in the module. Prefefined types
32 +are:
33 +
34 + - C{tAll}: any value is in the type
35 + - C{tNothing}: empty type
36 + - C{tString}: string values
37 + - C{tList}: lists values
38 + - C{tInteger}: integer values
39 + - C{tNatural}: non-negative integers
40 + - C{tPositive}: strictly positive integers
41 + - C{tFloat}: float values
42 + - C{tNumber}: integers or float values
43 + - C{tDict}: Python's C{dict} values
44 + - C{tNone}: the single value C{None}
45 + - C{tBoolean}: C{True} or C{False}
46 + - C{tTuple}: tuple values
47 + - C{tPair}: tuples of length two
48 +
49 +Types with finitely many elements can be iterated:
50 +
51 +>>> list(sorted(iter(CrossProduct(Range(0, 10, 2), tBoolean) & ~OneOf((4, True), (6, False)))))
52 +[(0, False), (0, True), (2, False), (2, True), (4, False), (6, True), (8, False), (8, True)]
53 +>>> list(iter(OneOf(1, 2, 3)))
54 +[1, 2, 3]
55 +>>> iter(tInteger)
56 +Traceback (most recent call last):
57 +...
58 +TypeError: ...
59 +>>> iter(~OneOf(1, 2, 3))
60 +Traceback (most recent call last):
61 +...
62 +TypeError: ...
63 +"""
64 +
65 +import inspect, sys
66 +from snakes.compat import *
67 +from snakes.data import cross
68 +from snakes.pnml import Tree
69 +
70 +def _iterable (obj, *types) :
71 + for t in types :
72 + try :
73 + iter(t)
74 + except :
75 + def __iterable__ () :
76 + raise ValueError("iteration over non-sequence")
77 + obj.__iterable__ = __iterable__
78 + break
79 +
80 +class Type (object) :
81 + """Base class for all types.
82 +
83 + Implement operations C{&}, C{|}, C{-}, C{^} and C{~} to build new
84 + types. Also implement the typechecking of several values. All the
85 + subclasses should implement the method C{__contains__} to
86 + typecheck a single object.
87 + """
88 + def __init__ (self) :
89 + """Abstract method
90 +
91 + >>> Type()
92 + Traceback (most recent call last):
93 + ...
94 + NotImplementedError: abstract class
95 +
96 + @raise NotImplementedError: when called
97 + """
98 + raise NotImplementedError("abstract class")
99 + def __eq__ (self, other) :
100 + return (self.__class__ == other.__class__
101 + and self.__dict__ == other.__dict__)
102 + def __hash__ (self) :
103 + return hash(repr(self))
104 + def __and__ (self, other) :
105 + """Intersection type.
106 +
107 + >>> Instance(int) & Greater(0)
108 + (Instance(int) & Greater(0))
109 +
110 + @param other: the other type in the intersection
111 + @type other: C{Type}
112 + @return: the intersection of both types
113 + @rtype: C{Type}
114 + """
115 + if other is self :
116 + return self
117 + else :
118 + return _And(self, other)
119 + def __or__ (self, other) :
120 + """Union type.
121 +
122 + >>> Instance(int) | Instance(bool)
123 + (Instance(int) | Instance(bool))
124 +
125 + @param other: the other type in the union
126 + @type other: C{Type}
127 + @return: the union of both types
128 + @rtype: C{Type}
129 + """
130 + if other is self :
131 + return self
132 + else :
133 + return _Or(self, other)
134 + def __sub__ (self, other) :
135 + """Substraction type.
136 +
137 + >>> Instance(int) - OneOf([0, 1, 2])
138 + (Instance(int) - OneOf([0, 1, 2]))
139 +
140 + @param other: the other type in the substraction
141 + @type other: C{Type}
142 + @return: the type C{self} minus the type C{other}
143 + @rtype: C{Type}
144 + """
145 + if other is self :
146 + return tNothing
147 + else :
148 + return _Sub(self, other)
149 + def __xor__ (self, other) :
150 + """Disjoint union type.
151 +
152 + >>> Greater(0) ^ Instance(float)
153 + (Greater(0) ^ Instance(float))
154 +
155 + @param other: the other type in the disjoint union
156 + @type other: C{Type}
157 + @return: the disjoint union of both types
158 + @rtype: C{Type}
159 + """
160 + if other is self :
161 + return tNothing
162 + else :
163 + return _Xor(self, other)
164 + def __invert__ (self) :
165 + """Complementary type.
166 +
167 + >>> ~ Instance(int)
168 + (~Instance(int))
169 +
170 + @return: the complementary type
171 + @rtype: C{Type}
172 + """
173 + return _Invert(self)
174 + def __iterable__ (self) :
175 + """Called to test if a type is iterable
176 +
177 + Should be replaced in subclasses that are not iterable
178 +
179 + @raise ValueError: if not iterable
180 + """
181 + pass
182 + def __call__ (self, *values) :
183 + """Typecheck values.
184 +
185 + >>> Instance(int)(3, 4, 5)
186 + True
187 + >>> Instance(int)(3, 4, 5.0)
188 + False
189 +
190 + @param values: values that have to be checked
191 + @type values: C{object}
192 + @return: C{True} if all the values are in the types, C{False}
193 + otherwise
194 + @rtype: C{bool}
195 + """
196 + for v in values :
197 + if v not in self :
198 + return False
199 + return True
200 + __pnmltag__ = "type"
201 + _typemap = None
202 + @classmethod
203 + def __pnmlload__ (cls, tree) :
204 + """Load a C{Type} from a PNML tree
205 +
206 + Uses the attribute C{__pnmltype__} to know which type
207 + corresponds to a tag "<type domain='xxx'>"
208 +
209 + >>> s = List(tNatural | tBoolean).__pnmldump__()
210 + >>> Type.__pnmlload__(s)
211 + Collection(Instance(list),
212 + ((Instance(int) & GreaterOrEqual(0))
213 + | OneOf(True, False)))
214 +
215 + @param tree: the PNML tree to load
216 + @type tree: C{snakes.pnml.Tree}
217 + @return: the loaded type
218 + @rtype: C{Type}
219 + """
220 + if cls._typemap is None :
221 + cls._typemap = {}
222 + for n, c in inspect.getmembers(sys.modules[cls.__module__],
223 + inspect.isclass) :
224 + try :
225 + cls._typemap[c.__pnmltype__] = c
226 + except AttributeError :
227 + pass
228 + return cls._typemap[tree["domain"]].__pnmlload__(tree)
229 +
230 +class _BinaryType (Type) :
231 + """A type build from two other ones
232 +
233 + This class allows to factorize the PNML related code for various
234 + binary types.
235 + """
236 + def __pnmldump__ (self) :
237 + return Tree(self.__pnmltag__, None,
238 + Tree("left", None, Tree.from_obj(self._left)),
239 + Tree("right", None, Tree.from_obj(self._right)),
240 + domain=self.__pnmltype__)
241 + @classmethod
242 + def __pnmlload__ (cls, tree) :
243 + return cls(tree.child("left").child().to_obj(),
244 + tree.child("right").child().to_obj())
245 +
246 +class _And (_BinaryType) :
247 + "Intersection of two types"
248 + __pnmltype__ = "intersection"
249 + def __init__ (self, left, right) :
250 + self._left = left
251 + self._right = right
252 + _iterable(self, left)
253 + def __repr__ (self) :
254 + return "(%s & %s)" % (repr(self._left), repr(self._right))
255 + def __contains__ (self, value) :
256 + """Check wether a value is in the type.
257 +
258 + @param value: the value to check
259 + @type value: C{object}
260 + @return: C{True} if C{value} is in the type, C{False} otherwise
261 + @rtype: C{bool}
262 + """
263 + return (value in self._left) and (value in self._right)
264 + def __iter__ (self) :
265 + self.__iterable__()
266 + for value in self._left :
267 + if value in self._right :
268 + yield value
269 +
270 +class _Or (_BinaryType) :
271 + "Union of two types"
272 + __pnmltype__ = "union"
273 + def __init__ (self, left, right) :
274 + self._left = left
275 + self._right = right
276 + _iterable(self, left, right)
277 + def __repr__ (self) :
278 + return "(%s | %s)" % (repr(self._left), repr(self._right))
279 + def __contains__ (self, value) :
280 + """Check wether a value is in the type.
281 +
282 + @param value: the value to check
283 + @type value: C{object}
284 + @return: C{True} if C{value} is in the type, C{False} otherwise
285 + @rtype: C{bool}
286 + """
287 + return (value in self._left) or (value in self._right)
288 + def __iter__ (self) :
289 + self.__iterable__()
290 + for value in (self._left & self._right) :
291 + yield value
292 + for value in (self._left ^ self._right) :
293 + yield value
294 +
295 +class _Sub (_BinaryType) :
296 + "Subtyping by difference"
297 + __pnmltype__ = "difference"
298 + def __init__ (self, left, right) :
299 + self._left = left
300 + self._right = right
301 + _iterable(self, left)
302 + def __repr__ (self) :
303 + return "(%s - %s)" % (repr(self._left), repr(self._right))
304 + def __contains__ (self, value) :
305 + """Check wether a value is in the type.
306 +
307 + @param value: the value to check
308 + @type value: C{object}
309 + @return: C{True} if C{value} is in the type, C{False} otherwise
310 + @rtype: C{bool}
311 + """
312 + return (value in self._left) and (value not in self._right)
313 + def __iter__ (self) :
314 + self.__iterable__()
315 + for value in self._left :
316 + if value not in self._right :
317 + yield value
318 +
319 +class _Xor (_BinaryType) :
320 + "Exclusive union of two types"
321 + __pnmltype__ = "xor"
322 + def __init__ (self, left, right) :
323 + self._left = left
324 + self._right = right
325 + _iterable(self, left, right)
326 + def __repr__ (self) :
327 + return "(%s ^ %s)" % (repr(self._left), repr(self._right))
328 + def __contains__ (self, value) :
329 + """Check wether a value is in the type.
330 +
331 + @param value: the value to check
332 + @type value: C{object}
333 + @return: C{True} if C{value} is in the type, C{False} otherwise
334 + @rtype: C{bool}
335 + """
336 + if value in self._left :
337 + return value not in self._right
338 + else :
339 + return value in self._right
340 + def __iter__ (self) :
341 + self.__iterable__()
342 + for value in self._left :
343 + if value not in self._right :
344 + yield value
345 + for value in self._right :
346 + if value not in self._left :
347 + yield value
348 +
349 +class _Invert (Type) :
350 + "Complement of a type"
351 + def __init__ (self, base) :
352 + self._base = base
353 + _iterable(self, None)
354 + def __repr__ (self) :
355 + return "(~%s)" % repr(self._base)
356 + def __contains__ (self, value) :
357 + """Check wether a value is in the type.
358 +
359 + @param value: the value to check
360 + @type value: C{object}
361 + @return: C{True} if C{value} is in the type, C{False} otherwise
362 + @rtype: C{bool}
363 + """
364 + return (value not in self._base)
365 + __pnmltype__ = "complement"
366 + def __pnmldump__ (self) :
367 + return Tree(self.__pnmltag__, None,
368 + Tree.from_obj(self._base),
369 + domain=self.__pnmltype__)
370 + @classmethod
371 + def __pnmlload__ (cls, tree) :
372 + return cls(tree.child().to_obj())
373 +
374 +class _All (Type) :
375 + "A type allowing for any value"
376 + def __init__ (self) :
377 + pass
378 + def __and__ (self, other) :
379 + return other
380 + def __or__ (self, other) :
381 + return self
382 + def __sub__ (self, other) :
383 + return ~other
384 + def __xor__ (self, other) :
385 + return ~other
386 + def __invert__ (self) :
387 + return tNothing
388 + def __repr__ (self) :
389 + return "tAll"
390 + def __contains__ (self, value) :
391 + """Check wether a value is in the type.
392 +
393 + @param value: the value to check
394 + @type value: C{object}
395 + @return: C{True}
396 + @rtype: C{bool}
397 + """
398 + return True
399 + def __call__ (self, *values) :
400 + """Typecheck values.
401 +
402 + @param values: values that have to be checked
403 + @type values: any objet
404 + @return: C{True}
405 + """
406 + return True
407 + __pnmltype__ = "universal"
408 + def __pnmldump__ (self) :
409 + """
410 + >>> tAll.__pnmldump__()
411 + <?xml version="1.0" encoding="utf-8"?>
412 + <pnml>
413 + <type domain="universal"/>
414 + </pnml>
415 + """
416 + return Tree(self.__pnmltag__, None, domain=self.__pnmltype__)
417 + @classmethod
418 + def __pnmlload__ (cls, tree) :
419 + """
420 + >>> Type.__pnmlload__(tAll.__pnmldump__())
421 + tAll
422 + """
423 + return cls()
424 +
425 +class _Nothing (Type) :
426 + "A types with no value"
427 + def __init__ (self) :
428 + pass
429 + def __and__ (self, other) :
430 + return self
431 + def __or__ (self, other) :
432 + return other
433 + def __sub__ (self, other) :
434 + return self
435 + def __xor__ (self, other) :
436 + return other
437 + def __invert__ (self) :
438 + return tAll
439 + def __repr__ (self) :
440 + return "tNothing"
441 + def __contains__ (self, value) :
442 + """Check wether a value is in the type.
443 +
444 + @param value: the value to check
445 + @type value: C{object}
446 + @return: C{False}
447 + @rtype: C{bool}
448 + """
449 + return False
450 + def __call__ (self, *values) :
451 + """Typecheck values.
452 +
453 + @param values: values that have to be checked
454 + @type values: any objet
455 + @return: C{False}
456 + """
457 + return False
458 + def __iter__ (self) :
459 + pass
460 + __pnmltype__ = "empty"
461 + def __pnmldump__ (self) :
462 + return Tree(self.__pnmltag__, None, domain=self.__pnmltype__)
463 + @classmethod
464 + def __pnmlload__ (cls, tree) :
465 + return cls()
466 +
467 +class Instance (Type) :
468 + """A type whose values are all instances of one class.
469 +
470 + >>> [1, 2] in Instance(list)
471 + True
472 + >>> (1, 2) in Instance(list)
473 + False
474 + """
475 + def __init__ (self, _class) :
476 + """Initialize the type
477 +
478 + >>> Instance(int)
479 + Instance(int)
480 +
481 + @param _class: the class of instance
482 + @type _class: any class
483 + @return: initialized object
484 + @rtype: C{Instance}
485 + """
486 + self._class = _class
487 + def __contains__ (self, value) :
488 + """Check wether a value is in the type.
489 +
490 + >>> 5 in Instance(int)
491 + True
492 + >>> 5.0 in Instance(int)
493 + False
494 +
495 + @param value: the value to check
496 + @type value: C{object}
497 + @return: C{True} if C{value} is in the type, C{False} otherwise
498 + @rtype: C{bool}
499 + """
500 + return isinstance(value, self._class)
501 + def __repr__ (self) :
502 + """String representation of the type, suitable for C{eval}
503 +
504 + >>> repr(Instance(str))
505 + 'Instance(str)'
506 +
507 + @return: precise string representation
508 + @rtype: C{str}
509 + """
510 + return "Instance(%s)" % self._class.__name__
511 + __pnmltype__ = "instance"
512 + def __pnmldump__ (self) :
513 + """Dump a type to a PNML tree
514 +
515 + >>> Instance(int).__pnmldump__()
516 + <?xml version="1.0" encoding="utf-8"?>
517 + <pnml>
518 + <type domain="instance">
519 + <object name="int" type="class"/>
520 + </type>
521 + </pnml>
522 +
523 + @return: the PNML representation of the type
524 + @rtype: C{str}
525 + """
526 + return Tree(self.__pnmltag__, None,
527 + Tree.from_obj(self._class),
528 + domain=self.__pnmltype__)
529 + @classmethod
530 + def __pnmlload__ (cls, tree) :
531 + """Builds a type from its PNML representation
532 +
533 + >>> t = Instance(int).__pnmldump__()
534 + >>> Instance.__pnmlload__(t)
535 + Instance(int)
536 +
537 + @param tree: the PNML tree to load
538 + @type tree: C{snakes.pnml.Tree}
539 + @return: the loaded type
540 + @rtype: C{Instance}
541 + """
542 + return cls(tree.child().to_obj())
543 +
544 +def _full_name (fun) :
545 + if fun.__module__ is None :
546 + funname = fun.__name__
547 + for modname in sys.modules :
548 + try :
549 + if sys.modules[modname].__dict__.get(funname, None) is fun :
550 + return ".".join([modname, funname])
551 + except :
552 + pass
553 + return funname
554 + else :
555 + return ".".join([fun.__module__, fun.__name__])
556 +
557 +class TypeCheck (Type) :
558 + """A type whose values are accepted by a given function.
559 +
560 + >>> def odd (val) :
561 + ... return type(val) is int and (val % 2) == 1
562 + >>> 3 in TypeCheck(odd)
563 + True
564 + >>> 4 in TypeCheck(odd)
565 + False
566 + >>> 3.0 in TypeCheck(odd)
567 + False
568 + """
569 + def __init__ (self, checker, iterate=None) :
570 + """Initialize the type
571 +
572 + >>> import operator
573 + >>> TypeCheck(operator.truth)
574 + TypeCheck(...truth)
575 + >>> def true_values () : # enumerates only choosen values
576 + ... yield True
577 + ... yield 42
578 + ... yield '42'
579 + ... yield [42]
580 + ... yield (42,)
581 + ... yield {True: 42}
582 + >>> TypeCheck(operator.truth, true_values)
583 + TypeCheck(...truth, snakes.typing.true_values)
584 +
585 + @param checker: a function that checks one value and returns
586 + C{True} if it is in te type and C{False} otherwise
587 + @type checker: C{function(value)->bool}
588 + @param iterate: C{None} or an iterator over the values of the
589 + type
590 + @type iterate: C{None} or C{iterator}
591 + """
592 + self._check = checker
593 + self._iterate = iterate
594 + def __iter__ (self) :
595 + """
596 + >>> def odd (val) :
597 + ... return type(val) is int and (val % 2) == 1
598 + >>> i = iter(TypeCheck(odd))
599 + Traceback (most recent call last):
600 + ...
601 + ValueError: type not iterable
602 + >>> def odd_iter () :
603 + ... i = 1
604 + ... while True :
605 + ... yield i
606 + ... yield -i
607 + ... i += 2
608 + >>> i = iter(TypeCheck(odd, odd_iter))
609 + >>> next(i), next(i), next(i)
610 + (1, -1, 3)
611 + """
612 + try :
613 + return iter(self._iterate())
614 + except TypeError :
615 + raise ValueError("type not iterable")
616 + def __contains__ (self, value) :
617 + """Check wether a value is in the type.
618 +
619 + >>> def odd (val) :
620 + ... return type(val) is int and (val % 2) == 1
621 + >>> 3 in TypeCheck(odd)
622 + True
623 + >>> 4 in TypeCheck(odd)
624 + False
625 + >>> 3.0 in TypeCheck(odd)
626 + False
627 +
628 + @param value: the value to check
629 + @type value: C{object}
630 + @return: C{True} if C{value} is in the type, C{False} otherwise
631 + @rtype: C{bool}
632 + """
633 + return self._check(value)
634 + def __repr__ (self) :
635 + """
636 + >>> def odd (val) :
637 + ... return type(val) is int and (val % 2) == 1
638 + >>> repr(TypeCheck(odd))
639 + 'TypeCheck(snakes.typing.odd)'
640 + >>> def odd_iter () :
641 + ... i = 1
642 + ... while True :
643 + ... yield i
644 + ... yield -i
645 + ... i += 2
646 + >>> repr(TypeCheck(odd, odd_iter))
647 + 'TypeCheck(snakes.typing.odd, snakes.typing.odd_iter)'
648 + """
649 + if self._iterate is None :
650 + return "%s(%s)" % (self.__class__.__name__,
651 + _full_name(self._check))
652 + else :
653 + return "%s(%s, %s)" % (self.__class__.__name__,
654 + _full_name(self._check),
655 + _full_name(self._iterate))
656 + __pnmltype__ = "checker"
657 + def __pnmldump__ (self) :
658 + """Dump type to a PNML tree
659 +
660 + >>> import operator
661 + >>> TypeCheck(operator.truth).__pnmldump__()
662 + <?xml version="1.0" encoding="utf-8"?>
663 + <pnml>
664 + <type domain="checker">
665 + <checker>
666 + ...
667 + </checker>
668 + <iterator>
669 + <object type="NoneType"/>
670 + </iterator>
671 + </type>
672 + </pnml>
673 + >>> def true_values () : # enumerates only choosen values
674 + ... yield True
675 + ... yield 42
676 + ... yield '42'
677 + ... yield [42]
678 + ... yield (42,)
679 + ... yield {True: 42}
680 + >>> TypeCheck(operator.truth, true_values).__pnmldump__()
681 + <?xml version="1.0" encoding="utf-8"?>
682 + <pnml>
683 + <type domain="checker">
684 + <checker>
685 + ...
686 + </checker>
687 + <iterator>
688 + <object name="snakes.typing.true_values" type="function"/>
689 + </iterator>
690 + </type>
691 + </pnml>
692 +
693 + Note that this last example would not work as C{true_value} as
694 + been defined inside a docstring. In order to allow it to be
695 + re-loaded from PNML, it should be defined at module level.
696 +
697 + @return: the type serialized to PNML
698 + @rtype: C{snakes.pnml.Tree}
699 + """
700 + return Tree(self.__pnmltag__, None,
701 + Tree("checker", None, Tree.from_obj(self._check)),
702 + Tree("iterator", None, Tree.from_obj(self._iterate)),
703 + domain=self.__pnmltype__)
704 + @classmethod
705 + def __pnmlload__ (cls, tree) :
706 + """Build type from a PNML tree
707 +
708 + >>> import operator
709 + >>> TypeCheck.__pnmlload__(TypeCheck(operator.truth).__pnmldump__())
710 + TypeCheck(....truth)
711 + >>> def true_values () : # enumerates only choosen values
712 + ... yield True
713 + ... yield 42
714 + ... yield '42'
715 + ... yield [42]
716 + ... yield (42,)
717 + ... yield {True: 42}
718 +
719 + @param tree: the PNML tree to load
720 + @type tree: C{snakes.pnml.Tree}
721 + @return: the loaded type
722 + @rtype: C{TypeChecker}
723 + """
724 + return cls(tree.child("checker").child().to_obj(),
725 + tree.child("iterator").child().to_obj())
726 +
727 +class OneOf (Type) :
728 + """A type whose values are explicitely enumerated.
729 +
730 + >>> 3 in OneOf(1, 2, 3, 4, 5)
731 + True
732 + >>> 0 in OneOf(1, 2, 3, 4, 5)
733 + False
734 + """
735 + def __init__ (self, *values) :
736 + """
737 + @param values: the enumeration of the values in the type
738 + @type values: any objects
739 + """
740 + self._values = values
741 + def __contains__ (self, value) :
742 + """Check wether a value is in the type.
743 +
744 + >>> 3 in OneOf(1, 2, 3, 4, 5)
745 + True
746 + >>> 0 in OneOf(1, 2, 3, 4, 5)
747 + False
748 +
749 + @param value: the value to check
750 + @type value: C{object}
751 + @return: C{True} if C{value} is in the type, C{False} otherwise
752 + @rtype: C{bool}
753 + """
754 + return value in self._values
755 + def __repr__ (self) :
756 + """
757 + >>> repr(OneOf(1, 2, 3, 4, 5))
758 + 'OneOf(1, 2, 3, 4, 5)'
759 + """
760 + return "OneOf(%s)" % ", ".join([repr(val) for val in self._values])
761 + def __iter__ (self) :
762 + """
763 + >>> list(iter(OneOf(1, 2, 3, 4, 5)))
764 + [1, 2, 3, 4, 5]
765 + """
766 + return iter(self._values)
767 + __pnmltype__ = "enum"
768 + def __pnmldump__ (self) :
769 + """Dump type to its PNML representation
770 +
771 + >>> OneOf(1, 2, 3, 4, 5).__pnmldump__()
772 + <?xml version="1.0" encoding="utf-8"?>
773 + <pnml>
774 + <type domain="enum">
775 + <object type="int">
776 + 1
777 + </object>
778 + <object type="int">
779 + 2
780 + </object>
781 + <object type="int">
782 + 3
783 + </object>
784 + <object type="int">
785 + 4
786 + </object>
787 + <object type="int">
788 + 5
789 + </object>
790 + </type>
791 + </pnml>
792 +
793 + @return: PNML representation of the type
794 + @rtype: C{snakes.pnml.Tree}
795 + """
796 + return Tree(self.__pnmltag__, None,
797 + *(Tree.from_obj(val) for val in self._values),
798 + **dict(domain=self.__pnmltype__))
799 + @classmethod
800 + def __pnmlload__ (cls, tree) :
801 + """Build type from its PNML representation
802 +
803 + >>> OneOf.__pnmlload__(OneOf(1, 2, 3, 4, 5).__pnmldump__())
804 + OneOf(1, 2, 3, 4, 5)
805 +
806 + @param tree: PNML tree to load
807 + @type tree: C{snakes.pnml.Tree}
808 + @return: loaded type
809 + @rtype: C{OneOf}
810 + """
811 + return cls(*(child.to_obj() for child in tree.children))
812 +
813 +class Collection (Type) :
814 + """A type whose values are a given container, holding items of a
815 + given type and ranging in a given interval.
816 +
817 + >>> [0, 1.1, 2, 3.3, 4] in Collection(Instance(list), tNumber, 3, 10)
818 + True
819 + >>> [0, 1.1] in Collection(Instance(list), tNumber, 3, 10) #too short
820 + False
821 + >>> [0, '1.1', 2, 3.3, 4] in Collection(Instance(list), tNumber, 3, 10) #wrong item
822 + False
823 + """
824 + def __init__ (self, collection, items, min=None, max=None) :
825 + """Initialise the type
826 +
827 + >>> Collection(Instance(list), tNumber, 3, 10)
828 + Collection(Instance(list), (Instance(int) | Instance(float)), min=3, max=10)
829 +
830 + @param collection: the collection type
831 + @type collection: any container type
832 + @param items: the type of the items
833 + @type items: any type
834 + @param min: the smallest allowed value
835 + @type min: any value in C{items}
836 + @param max: the greatest allowed value
837 + @type max: any value in C{items}
838 + """
839 + self._collection = collection
840 + self._class = collection._class
841 + self._items = items
842 + self._max = max
843 + self._min = min
844 + def __contains__ (self, value) :
845 + """Check wether a value is in the type.
846 +
847 + >>> [0, 1.1, 2, 3.3, 4] in Collection(Instance(list), tNumber, 3, 10)
848 + True
849 + >>> [0, 1.1] in Collection(Instance(list), tNumber, 3, 10) #too short
850 + False
851 + >>> [0, '1.1', 2, 3.3, 4] in Collection(Instance(list), tNumber, 3, 10) #wrong item
852 + False
853 +
854 + @param value: the value to check
855 + @type value: C{object}
856 + @return: C{True} if C{value} is in the type, C{False} otherwise
857 + @rtype: C{bool}
858 + """
859 + if value not in self._collection :
860 + return False
861 + try :
862 + len(value)
863 + iter(value)
864 + except TypeError :
865 + return False
866 + if (self._min is not None) and (len(value) < self._min) :
867 + return False
868 + if (self._max is not None) and (len(value) > self._max) :
869 + return False
870 + for item in value :
871 + if item not in self._items :
872 + return False
873 + return True
874 + def __repr__ (self) :
875 + """
876 + >>> repr(Collection(Instance(list), tNumber, 3, 10))
877 + 'Collection(Instance(list), (Instance(int) | Instance(float)), min=3, max=10)'
878 + """
879 + if (self._min is None) and (self._max is None) :
880 + return "Collection(%s, %s)" % (repr(self._collection),
881 + repr(self._items))
882 + elif self._min is None :
883 + return "Collection(%s, %s, max=%s)" % (repr(self._collection),
884 + repr(self._items),
885 + repr(self._max))
886 + elif self._max is None :
887 + return "Collection(%s, %s, min=%s)" % (repr(self._collection),
888 + repr(self._items),
889 + repr(self._min))
890 + else :
891 + return "Collection(%s, %s, min=%s, max=%s)" % (repr(self._collection),
892 + repr(self._items),
893 + repr(self._min),
894 + repr(self._max))
895 + __pnmltype__ = "collection"
896 + def __pnmldump__ (self) :
897 + """Dump type to a PNML tree
898 +
899 + >>> Collection(Instance(list), tNumber, 3, 10).__pnmldump__()
900 + <?xml version="1.0" encoding="utf-8"?>
901 + <pnml>
902 + <type domain="collection">
903 + <container>
904 + <type domain="instance">
905 + <object name="list" type="class"/>
906 + </type>
907 + </container>
908 + <items>
909 + <type domain="union">
910 + <left>
911 + <type domain="instance">
912 + <object name="int" type="class"/>
913 + </type>
914 + </left>
915 + <right>
916 + <type domain="instance">
917 + <object name="float" type="class"/>
918 + </type>
919 + </right>
920 + </type>
921 + </items>
922 + <min>
923 + <object type="int">
924 + 3
925 + </object>
926 + </min>
927 + <max>
928 + <object type="int">
929 + 10
930 + </object>
931 + </max>
932 + </type>
933 + </pnml>
934 +
935 + @return: the PNML representation of the type
936 + @rtype: C{snakes.pnml.Tree}
937 + """
938 + return Tree(self.__pnmltag__, None,
939 + Tree("container", None, Tree.from_obj(self._collection)),
940 + Tree("items", None, Tree.from_obj(self._items)),
941 + Tree("min", None, Tree.from_obj(self._min)),
942 + Tree("max", None, Tree.from_obj(self._max)),
943 + domain=self.__pnmltype__)
944 + @classmethod
945 + def __pnmlload__ (cls, tree) :
946 + """Load type from its PNML representation
947 +
948 + >>> t = Collection(Instance(list), tNumber, 3, 10).__pnmldump__()
949 + >>> Collection.__pnmlload__(t)
950 + Collection(Instance(list), (Instance(int) | Instance(float)),
951 + min=3, max=10)
952 +
953 + @param tree: the PNML tree to load
954 + @type tree: C{snakes.pnml.Tree}
955 + @return: the loaded type
956 + @rtype: C{Collection}
957 + """
958 + return cls(tree.child("container").child().to_obj(),
959 + tree.child("items").child().to_obj(),
960 + tree.child("min").child().to_obj(),
961 + tree.child("max").child().to_obj())
962 +
963 +def List (items, min=None, max=None) :
964 + """Shorthand for instantiating C{Collection}
965 +
966 + >>> List(tNumber, min=3, max=10)
967 + Collection(Instance(list), (Instance(int) | Instance(float)), min=3, max=10)
968 +
969 + @param items: the type of the elements in the collection
970 + @type items: C{Type}
971 + @param min: the minimum number of elements in the collection
972 + @type min: C{int} or C{None}
973 + @param max: the maximum number of elements in the collection
974 + @type max: C{int} or C{None}
975 + @return: a type that checks the given constraints
976 + @rtype: C{Collection}
977 + """
978 + return Collection(Instance(list), items, min, max)
979 +
980 +def Tuple (items, min=None, max=None) :
981 + """Shorthand for instantiating C{Collection}
982 +
983 + >>> Tuple(tNumber, min=3, max=10)
984 + Collection(Instance(tuple), (Instance(int) | Instance(float)), min=3, max=10)
985 +
986 + @param items: the type of the elements in the collection
987 + @type items: C{Type}
988 + @param min: the minimum number of elements in the collection
989 + @type min: C{int} or C{None}
990 + @param max: the maximum number of elements in the collection
991 + @type max: C{int} or C{None}
992 + @return: a type that checks the given constraints
993 + @rtype: C{Collection}
994 + """
995 + return Collection(Instance(tuple), items, min, max)
996 +
997 +def Set (items, min=None, max=None) :
998 + """Shorthand for instantiating C{Collection}
999 +
1000 + >>> Set(tNumber, min=3, max=10)
1001 + Collection(Instance(set), (Instance(int) | Instance(float)), min=3, max=10)
1002 +
1003 + @param items: the type of the elements in the collection
1004 + @type items: C{Type}
1005 + @param min: the minimum number of elements in the collection
1006 + @type min: C{int} or C{None}
1007 + @param max: the maximum number of elements in the collection
1008 + @type max: C{int} or C{None}
1009 + @return: a type that checks the given constraints
1010 + @rtype: C{Collection}
1011 + """
1012 + return Collection(Instance(set), items, min, max)
1013 +
1014 +class Mapping (Type) :
1015 + """A type whose values are mapping (eg, C{dict})
1016 +
1017 + >>> {'Yes': True, 'No': False} in Mapping(tString, tAll)
1018 + True
1019 + >>> {True: 1, False: 0} in Mapping(tString, tAll)
1020 + False
1021 + """
1022 + def __init__ (self, keys, items, _dict=Instance(dict)) :
1023 + """Initialise a mapping type
1024 +
1025 + >>> Mapping(tInteger, tFloat)
1026 + Mapping(Instance(int), Instance(float), Instance(dict))
1027 + >>> from snakes.data import hdict
1028 + >>> Mapping(tInteger, tFloat, Instance(hdict))
1029 + Mapping(Instance(int), Instance(float), Instance(hdict))
1030 +
1031 + @param keys: the type for the keys
1032 + @type keys: any type
1033 + @param items: the type for the items
1034 + @type items: any type
1035 + @param _dict: the class that mapping must be instances of
1036 + @type _dict: any C{dict} like class
1037 + """
1038 + self._keys = keys
1039 + self._items = items
1040 + self._dict = _dict
1041 + def __contains__ (self, value) :
1042 + """Check wether a value is in the type.
1043 +
1044 + >>> {'Yes': True, 'No': False} in Mapping(tString, tAll)
1045 + True
1046 + >>> {True: 1, False: 0} in Mapping(tString, tAll)
1047 + False
1048 +
1049 + @param value: the value to check
1050 + @type value: C{object}
1051 + @return: C{True} if C{value} is in the type, C{False} otherwise
1052 + @rtype: C{bool}
1053 + """
1054 + if not self._dict(value) :
1055 + return False
1056 + for key, item in value.items() :
1057 + if key not in self._keys :
1058 + return False
1059 + if item not in self._items :
1060 + return True
1061 + return True
1062 + def __repr__ (self) :
1063 + """Return a string representation of the type suitable for C{eval}
1064 +
1065 + >>> repr(Mapping(tString, tAll))
1066 + 'Mapping(Instance(str), tAll, Instance(dict))'
1067 +
1068 + @return: precise string representation
1069 + @rtype: C{str}
1070 + """
1071 + return "Mapping(%s, %s, %s)" % (repr(self._keys),
1072 + repr(self._items),
1073 + repr(self._dict))
1074 + __pnmltype__ = "mapping"
1075 + def __pnmldump__ (self) :
1076 + """Dump type to a PNML tree
1077 +
1078 + >>> from snakes.hashables import hdict
1079 + >>> Mapping(tString, tAll, Instance(hdict)).__pnmldump__()
1080 + <?xml version="1.0" encoding="utf-8"?>
1081 + <pnml>
1082 + <type domain="mapping">
1083 + <keys>
1084 + <type domain="instance">
1085 + <object name="str" type="class"/>
1086 + </type>
1087 + </keys>
1088 + <items>
1089 + <type domain="universal"/>
1090 + </items>
1091 + <container>
1092 + <type domain="instance">
1093 + <object name="snakes.hashables.hdict" type="class"/>
1094 + </type>
1095 + </container>
1096 + </type>
1097 + </pnml>
1098 +
1099 + @return: PNML representation of the type
1100 + @rtype: C{snakes.pnml.Tree}
1101 + """
1102 + return Tree(self.__pnmltag__, None,
1103 + Tree("keys", None, Tree.from_obj(self._keys)),
1104 + Tree("items", None, Tree.from_obj(self._items)),
1105 + Tree("container", None, Tree.from_obj(self._dict)),
1106 + domain=self.__pnmltype__)
1107 + @classmethod
1108 + def __pnmlload__ (cls, tree) :
1109 + """Load type from its PNML representation
1110 +
1111 + >>> from snakes.hashables import hdict
1112 + >>> t = Mapping(tString, tAll, Instance(hdict)).__pnmldump__()
1113 + >>> Mapping.__pnmlload__(t)
1114 + Mapping(Instance(str), tAll, Instance(hdict))
1115 +
1116 + @param tree: PNML representation of the type
1117 + @type tree: C{snakes.pnml.Tree}
1118 + @return: the loaded type
1119 + @rtype: C{Mapping}
1120 + """
1121 + return cls(tree.child("keys").child().to_obj(),
1122 + tree.child("items").child().to_obj(),
1123 + tree.child("container").child().to_obj())
1124 +
1125 +class Range (Type) :
1126 + """A type whose values are in a given range
1127 +
1128 + Notice that ranges are not built into the memory so that huge
1129 + values can be used.
1130 +
1131 + >>> 3 in Range(1, 2**128, 2)
1132 + True
1133 + >>> 4 in Range(1, 2**128, 2)
1134 + False
1135 + """
1136 + def __init__ (self, first, last, step=1) :
1137 + """The values are those that the builtin C{range(first, last, step)}
1138 + would return.
1139 +
1140 + >>> Range(1, 10)
1141 + Range(1, 10)
1142 + >>> Range(1, 10, 2)
1143 + Range(1, 10, 2)
1144 +
1145 + @param first: first element in the range
1146 + @type first: C{int}
1147 + @param last: upper bound of the range, not belonging to it
1148 + @type last: C{int}
1149 + @param step: step between elements in the range
1150 + @type step: C{int}
1151 + """
1152 + self._first, self._last, self._step = first, last, step
1153 + def __contains__ (self, value) :
1154 + """Check wether a value is in the type.
1155 +
1156 + >>> 1 in Range(1, 10, 2)
1157 + True
1158 + >>> 2 in Range(1, 10, 2)
1159 + False
1160 + >>> 9 in Range(1, 10, 2)
1161 + True
1162 + >>> 10 in Range(1, 10, 2)
1163 + False
1164 +
1165 + @param value: the value to check
1166 + @type value: C{object}
1167 + @return: C{True} if C{value} is in the type, C{False} otherwise
1168 + @rtype: C{bool}
1169 + """
1170 + return ((self._first <= value < self._last)
1171 + and ((value - self._first) % self._step == 0))
1172 + def __repr__ (self) :
1173 + """Return a string representation of the type suitable for C{eval}
1174 +
1175 + >>> repr(Range(1, 2**128, 2))
1176 + 'Range(1, 340282366920938463463374607431768211456, 2)'
1177 +
1178 + @return: precise string representation
1179 + @rtype: C{str}
1180 + """
1181 + if self._step == 1 :
1182 + return "Range(%s, %s)" % (self._first, self._last)
1183 + else :
1184 + return "Range(%s, %s, %s)" % (self._first, self._last, self._step)
1185 + def __iter__ (self) :
1186 + """Iterate over the elements of the type
1187 +
1188 + >>> list(iter(Range(1, 10, 3)))
1189 + [1, 4, 7]
1190 +
1191 + @return: an iterator over the values belonging to the range
1192 + @rtype: C{generator}
1193 + """
1194 + return iter(xrange(self._first, self._last, self._step))
1195 + __pnmltype__ = "range"
1196 + def __pnmldump__ (self) :
1197 + """Dump type to a PNML tree
1198 +
1199 + >>> Range(1, 10, 2).__pnmldump__()
1200 + <?xml version="1.0" encoding="utf-8"?>
1201 + <pnml>
1202 + <type domain="range">
1203 + <first>
1204 + <object type="int">
1205 + 1
1206 + </object>
1207 + </first>
1208 + <last>
1209 + <object type="int">
1210 + 10
1211 + </object>
1212 + </last>
1213 + <step>
1214 + <object type="int">
1215 + 2
1216 + </object>
1217 + </step>
1218 + </type>
1219 + </pnml>
1220 +
1221 + @return: PNML representation of the type
1222 + @rtype: C{snakes.pnml.Tree}
1223 + """
1224 + return Tree(self.__pnmltag__, None,
1225 + Tree("first", None, Tree.from_obj(self._first)),
1226 + Tree("last", None, Tree.from_obj(self._last)),
1227 + Tree("step", None, Tree.from_obj(self._step)),
1228 + domain=self.__pnmltype__)
1229 + @classmethod
1230 + def __pnmlload__ (cls, tree) :
1231 + """Build type from its PNML representation
1232 +
1233 + >>> Range.__pnmlload__(Range(1, 10, 2).__pnmldump__())
1234 + Range(1, 10, 2)
1235 +
1236 + @param tree: PNML tree to load
1237 + @type tree: C{snakes.pnml.Tree}
1238 + @return: the loaded type
1239 + @rtype: C{Range}
1240 + """
1241 + return cls(tree.child("first").child().to_obj(),
1242 + tree.child("last").child().to_obj(),
1243 + tree.child("step").child().to_obj())
1244 +
1245 +class Greater (Type) :
1246 + """A type whose values are greater than a minimum.
1247 +
1248 + The minimum and the checked values can be of any type as soon as
1249 + they can be compared with C{>}.
1250 +
1251 + >>> 6 in Greater(3)
1252 + True
1253 + >>> 3 in Greater(3)
1254 + False
1255 + """
1256 + def __init__ (self, min) :
1257 + """Initialises the type
1258 +
1259 + >>> Greater(5)
1260 + Greater(5)
1261 +
1262 + @param min: the greatest value not included in the type
1263 + @type min: any C{object} that support comparison
1264 + """
1265 + self._min = min
1266 + def __contains__ (self, value) :
1267 + """Check wether a value is in the type.
1268 +
1269 + >>> 5 in Greater(3)
1270 + True
1271 + >>> 5 in Greater(3.0)
1272 + True
1273 + >>> 3 in Greater(3.0)
1274 + False
1275 + >>> 1.0 in Greater(5)
1276 + False
1277 +
1278 + @param value: the value to check
1279 + @type value: C{object}
1280 + @return: C{True} if C{value} is in the type, C{False} otherwise
1281 + @rtype: C{bool}
1282 + """
1283 + try :
1284 + return value > self._min
1285 + except :
1286 + return False
1287 + def __repr__ (self) :
1288 + """Return a string representation of the type suitable for C{eval}
1289 +
1290 + >>> repr(Greater(3))
1291 + 'Greater(3)'
1292 +
1293 + @return: precise string representation
1294 + @rtype: C{str}
1295 + """
1296 + return "Greater(%s)" % repr(self._min)
1297 + __pnmltype__ = "greater"
1298 + def __pnmldump__ (self) :
1299 + """Dump type to its PNML representation
1300 +
1301 + >>> Greater(42).__pnmldump__()
1302 + <?xml version="1.0" encoding="utf-8"?>
1303 + <pnml>
1304 + <type domain="greater">
1305 + <object type="int">
1306 + 42
1307 + </object>
1308 + </type>
1309 + </pnml>
1310 +
1311 + @return: PNML representation of the type
1312 + @rtype: C{snakes.pnml.Tree}
1313 + """
1314 + return Tree(self.__pnmltag__, None,
1315 + Tree.from_obj(self._min),
1316 + domain=self.__pnmltype__)
1317 + @classmethod
1318 + def __pnmlload__ (cls, tree) :
1319 + """Build type from its PNLM representation
1320 +
1321 + >>> Greater.__pnmlload__(Greater(42).__pnmldump__())
1322 + Greater(42)
1323 +
1324 + @param tree: PNML representation to load
1325 + @type tree: C{snakes.pnml.Tree}
1326 + @return: loaded type
1327 + @rtype: C{Greater}
1328 + """
1329 + return cls(tree.child().to_obj())
1330 +
1331 +class GreaterOrEqual (Type) :
1332 + """A type whose values are greater or equal than a minimum.
1333 +
1334 + See the description of C{Greater}
1335 + """
1336 + def __init__ (self, min) :
1337 + """Initialises the type
1338 +
1339 + >>> GreaterOrEqual(5)
1340 + GreaterOrEqual(5)
1341 +
1342 + @param min: the minimal allowed value
1343 + @type min: any C{object} that support comparison
1344 + """
1345 + self._min = min
1346 + def __contains__ (self, value) :
1347 + """Check wether a value is in the type.
1348 +
1349 + >>> 5 in GreaterOrEqual(3)
1350 + True
1351 + >>> 5 in GreaterOrEqual(3.0)
1352 + True
1353 + >>> 3 in GreaterOrEqual(3.0)
1354 + True
1355 + >>> 1.0 in GreaterOrEqual(5)
1356 + False
1357 +
1358 + @param value: the value to check
1359 + @type value: C{object}
1360 + @return: C{True} if C{value} is in the type, C{False} otherwise
1361 + @rtype: C{bool}
1362 + """
1363 + try :
1364 + return value >= self._min
1365 + except :
1366 + False
1367 + def __repr__ (self) :
1368 + """Return a strign representation of the type suitable for C{eval}
1369 +
1370 + >>> repr(GreaterOrEqual(3))
1371 + 'GreaterOrEqual(3)'
1372 +
1373 + @return: precise string representation
1374 + @rtype: C{str}
1375 + """
1376 + return "GreaterOrEqual(%s)" % repr(self._min)
1377 + __pnmltype__ = "greatereq"
1378 + def __pnmldump__ (self) :
1379 + """Dump type to its PNML representation
1380 +
1381 + >>> GreaterOrEqual(42).__pnmldump__()
1382 + <?xml version="1.0" encoding="utf-8"?>
1383 + <pnml>
1384 + <type domain="greatereq">
1385 + <object type="int">
1386 + 42
1387 + </object>
1388 + </type>
1389 + </pnml>
1390 +
1391 + @return: PNML representation of the type
1392 + @rtype: C{snakes.pnml.Tree}
1393 + """
1394 + return Tree(self.__pnmltag__, None,
1395 + Tree.from_obj(self._min),
1396 + domain=self.__pnmltype__)
1397 + @classmethod
1398 + def __pnmlload__ (cls, tree) :
1399 + """Build type from its PNLM representation
1400 +
1401 + >>> GreaterOrEqual.__pnmlload__(GreaterOrEqual(42).__pnmldump__())
1402 + GreaterOrEqual(42)
1403 +
1404 + @param tree: PNML representation to load
1405 + @type tree: C{snakes.pnml.Tree}
1406 + @return: loaded type
1407 + @rtype: C{GreaterOrEqual}
1408 + """
1409 + return cls(tree.child().to_obj())
1410 +
1411 +class Less (Type) :
1412 + """A type whose values are less than a maximum.
1413 +
1414 + See the description of C{Greater}
1415 + """
1416 + def __init__ (self, max) :
1417 + """Initialises the type
1418 +
1419 + >>> Less(5)
1420 + Less(5)
1421 +
1422 + @param min: the smallest value not included in the type
1423 + @type min: any C{object} that support comparison
1424 + """
1425 + self._max = max
1426 + def __contains__ (self, value) :
1427 + """Check wether a value is in the type.
1428 +
1429 + >>> 5.0 in Less(5)
1430 + False
1431 + >>> 4.9 in Less(5)
1432 + True
1433 + >>> 4 in Less(5.0)
1434 + True
1435 +
1436 + @param value: the value to check
1437 + @type value: C{object}
1438 + @return: C{True} if C{value} is in the type, C{False} otherwise
1439 + @rtype: C{bool}
1440 + """
1441 + return value < self._max
1442 + def __repr__ (self) :
1443 + """Return a string representation of the type suitable for C{eval}
1444 +
1445 + >>> repr(Less(3))
1446 + 'Less(3)'
1447 +
1448 + @return: precise string representation
1449 + @rtype: C{str}
1450 + """
1451 + return "Less(%s)" % repr(self._max)
1452 + __pnmltype__ = "less"
1453 + def __pnmldump__ (self) :
1454 + """Dump type to its PNML representation
1455 +
1456 + >>> Less(3).__pnmldump__()
1457 + <?xml version="1.0" encoding="utf-8"?>
1458 + <pnml>
1459 + <type domain="less">
1460 + <object type="int">
1461 + 3
1462 + </object>
1463 + </type>
1464 + </pnml>
1465 +
1466 + @return: PNML representation of the type
1467 + @rtype: C{snakes.pnml.Tree}
1468 + """
1469 + return Tree(self.__pnmltag__, None,
1470 + Tree.from_obj(self._max),
1471 + domain=self.__pnmltype__)
1472 + @classmethod
1473 + def __pnmlload__ (cls, tree) :
1474 + """Build type from its PNML representation
1475 +
1476 + >>> Less.__pnmlload__(Less(3).__pnmldump__())
1477 + Less(3)
1478 +
1479 + @param tree: PNML tree to load
1480 + @type tree: C{snakes.pnml.Tree}
1481 + @return: loaded type
1482 + @rtype: C{Less}
1483 + """
1484 + return cls(tree.child().to_obj())
1485 +
1486 +class LessOrEqual (Type) :
1487 + """A type whose values are less than or equal to a maximum.
1488 +
1489 + See the description of C{Greater}
1490 + """
1491 + def __init__ (self, max) :
1492 + """Initialises the type
1493 +
1494 + >>> LessOrEqual(5)
1495 + LessOrEqual(5)
1496 +
1497 + @param min: the greatest value the type
1498 + @type min: any C{object} that support comparison
1499 + """
1500 +
1501 + self._max = max
1502 + def __contains__ (self, value) :
1503 + """Check wether a value is in the type.
1504 +
1505 + >>> 5 in LessOrEqual(5.0)
1506 + True
1507 + >>> 5.1 in LessOrEqual(5)
1508 + False
1509 + >>> 1.0 in LessOrEqual(5)
1510 + True
1511 +
1512 + @param value: the value to check
1513 + @type value: C{object}
1514 + @return: C{True} if C{value} is in the type, C{False} otherwise
1515 + @rtype: C{bool}
1516 + """
1517 + return value <= self._max
1518 + def __repr__ (self) :
1519 + """Return a string representation of the type suitable for C{eval}
1520 +
1521 + >>> repr(LessOrEqual(3))
1522 + 'LessOrEqual(3)'
1523 +
1524 + @return: precise string representation
1525 + @rtype: C{str}
1526 + """
1527 + return "LessOrEqual(%s)" % repr(self._max)
1528 + __pnmltype__ = "lesseq"
1529 + def __pnmldump__ (self) :
1530 + """Dump type to its PNML representation
1531 +
1532 + >>> LessOrEqual(4).__pnmldump__()
1533 + <?xml version="1.0" encoding="utf-8"?>
1534 + <pnml>
1535 + <type domain="lesseq">
1536 + <object type="int">
1537 + 4
1538 + </object>
1539 + </type>
1540 + </pnml>
1541 +
1542 + @return: PNML representation of the type
1543 + @rtype: C{snakes.pnml.Tree}
1544 + """
1545 + return Tree(self.__pnmltag__, None,
1546 + Tree.from_obj(self._max),
1547 + domain=self.__pnmltype__)
1548 + @classmethod
1549 + def __pnmlload__ (cls, tree) :
1550 + """Build type from its PNML representation
1551 +
1552 + >>> LessOrEqual.__pnmlload__(LessOrEqual(4).__pnmldump__())
1553 + LessOrEqual(4)
1554 +
1555 + @param tree: PNML tree to load
1556 + @type tree: C{snakes.pnml.Tree}
1557 + @return: loaded type
1558 + @rtype: C{LessOrEqual}
1559 + """
1560 + return cls(tree.child().to_obj())
1561 +
1562 +class CrossProduct (Type) :
1563 + """A type whose values are tuples, each component of them being in
1564 + given types. The resulting type is the cartesian cross product of
1565 + the compound types.
1566 +
1567 + >>> (1, 3, 5) in CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))
1568 + True
1569 + >>> (2, 4, 6) in CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))
1570 + False
1571 + """
1572 + def __init__ (self, *types) :
1573 + """Initialise the type
1574 +
1575 + >>> CrossProduct(Instance(int), Instance(float))
1576 + CrossProduct(Instance(int), Instance(float))
1577 +
1578 + @param types: the types of each component of the allowed tuples
1579 + @type types: C{Type}
1580 + """
1581 + self._types = types
1582 + _iterable(self, *types)
1583 + def __repr__ (self) :
1584 + """Return a string representation of the type suitable for C{eval}
1585 +
1586 + >>> repr(CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10)))
1587 + 'CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))'
1588 +
1589 + @return: precise string representation
1590 + @rtype: C{str}
1591 + """
1592 + return "CrossProduct(%s)" % ", ".join([repr(t) for t in self._types])
1593 + def __contains__ (self, value) :
1594 + """Check wether a value is in the type.
1595 +
1596 + >>> (1, 3, 5) in CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))
1597 + True
1598 + >>> (2, 4, 6) in CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))
1599 + False
1600 +
1601 + @param value: the value to check
1602 + @type value: C{object}
1603 + @return: C{True} if C{value} is in the type, C{False} otherwise
1604 + @rtype: C{bool}
1605 + """
1606 + if not isinstance(value, tuple) :
1607 + return False
1608 + elif len(self._types) != len(value) :
1609 + return False
1610 + for item, t in zip(value, self._types) :
1611 + if not item in t :
1612 + return False
1613 + return True
1614 + def __iter__ (self) :
1615 + """A cross product is iterable if so are all its components.
1616 +
1617 + >>> list(iter(CrossProduct(Range(1, 3), Range(3, 5))))
1618 + [(1, 3), (1, 4), (2, 3), (2, 4)]
1619 + >>> iter(CrossProduct(Range(1, 100), tAll))
1620 + Traceback (most recent call last):
1621 + ...
1622 + ValueError: iteration over non-sequence
1623 +
1624 + @return: an iterator over the values in the type
1625 + @rtype: C{generator}
1626 + @raise ValueError: when one component is not iterable
1627 + """
1628 + self.__iterable__()
1629 + return cross(self._types)
1630 + __pnmltype__ = "crossproduct"
1631 + def __pnmldump__ (self) :
1632 + """Dumps type to its PNML representation
1633 +
1634 + >>> CrossProduct(Instance(int), Instance(float)).__pnmldump__()
1635 + <?xml version="1.0" encoding="utf-8"?>
1636 + <pnml>
1637 + <type domain="crossproduct">
1638 + <type domain="instance">
1639 + <object name="int" type="class"/>
1640 + </type>
1641 + <type domain="instance">
1642 + <object name="float" type="class"/>
1643 + </type>
1644 + </type>
1645 + </pnml>
1646 +
1647 + @return: PNML representation of the type
1648 + @rtype: C{snakes.pnml.Tree}
1649 + """
1650 + return Tree(self.__pnmltag__, None,
1651 + *(Tree.from_obj(t) for t in self._types),
1652 + **dict(domain=self.__pnmltype__))
1653 + @classmethod
1654 + def __pnmlload__ (cls, tree) :
1655 + """Build type from its PNML representation
1656 +
1657 + >>> t = CrossProduct(Instance(int), Instance(float)).__pnmldump__()
1658 + >>> CrossProduct.__pnmlload__(t)
1659 + CrossProduct(Instance(int), Instance(float))
1660 +
1661 + @param tree: PNML tree to load
1662 + @type tree: C{snakes.pnml.Tree}
1663 + @return: loaded type
1664 + @rtype: C{CrossProduct}
1665 + """
1666 + return cls(*(child.to_obj() for child in tree.children))
1667 +
1668 +tAll = _All()
1669 +tNothing = _Nothing()
1670 +tString = Instance(str)
1671 +tList = List(tAll)
1672 +tInteger = Instance(int)
1673 +tNatural = tInteger & GreaterOrEqual(0)
1674 +tPositive = tInteger & Greater(0)
1675 +tFloat = Instance(float)
1676 +tNumber = tInteger|tFloat
1677 +tDict = Instance(dict)
1678 +tNone = OneOf(None)
1679 +tBoolean = OneOf(True, False)
1680 +tTuple = Tuple(tAll)
1681 +tPair = Tuple(tAll, min=2, max=2)
File mode changed
1 +from snakes import SnakesError
2 +
3 +class CompilationError (SnakesError) :
4 + pass
5 +
6 +class DeclarationError (SnakesError) :
7 + pass
1 +import sys, operator, inspect, re, collections
2 +from snakes.utils.abcd import CompilationError, DeclarationError
3 +from snakes.lang.abcd.parser import ast
4 +from snakes.lang import unparse
5 +import snakes.utils.abcd.transform as transform
6 +from snakes.data import MultiSet
7 +from snakes import *
8 +
9 +class Decl (object) :
10 + OBJECT = "object"
11 + TYPE = "type"
12 + BUFFER = "buffer"
13 + SYMBOL = "symbol"
14 + CONST = "const"
15 + NET = "net"
16 + TASK = "task"
17 + IMPORT = "import"
18 + def __init__ (self, node, kind=None, **data) :
19 + self.node = node
20 + classname = node.__class__.__name__
21 + if kind is not None :
22 + self.kind = kind
23 + elif classname == "AbcdTypedef" :
24 + self.kind = self.TYPE
25 + elif classname == "AbcdBuffer" :
26 + self.kind = self.BUFFER
27 + elif classname == "AbcdSymbol" :
28 + self.kind = self.SYMBOL
29 + elif classname == "AbcdConst" :
30 + self.kind = self.CONST
31 + elif classname == "AbcdNet" :
32 + self.kind = self.NET
33 + elif classname == "AbcdTask" :
34 + self.kind = self.TASK
35 + elif classname in ("Import", "ImportFrom") :
36 + self.kind = self.IMPORT
37 + else :
38 + self.kind = self.OBJECT
39 + for key, val in data.items() :
40 + setattr(self, key, val)
41 +
42 +class GetInstanceArgs (object) :
43 + """Bind arguments for a net instance"""
44 + def __init__ (self, node) :
45 + self.argspec = []
46 + self.arg = {}
47 + self.buffer = {}
48 + self.net = {}
49 + self.task = {}
50 + seen = set()
51 + for a in node.args.args + node.args.kwonlyargs :
52 + if a.arg in seen :
53 + self._raise(CompilationError,
54 + "duplicate argument %r" % a.arg)
55 + seen.add(a.arg)
56 + if a.annotation is None :
57 + self.argspec.append((a.arg, "arg"))
58 + else :
59 + self.argspec.append((a.arg, a.annotation.id))
60 + def __call__ (self, *args) :
61 + self.arg.clear()
62 + self.buffer.clear()
63 + self.net.clear()
64 + self.task.clear()
65 + for (name, kind), value in zip(self.argspec, args) :
66 + getattr(self, kind)[name] = value
67 + return self.arg, self.buffer, self.net, self.task
68 +
69 +class Builder (object) :
70 + def __init__ (self, snk, path=[], up=None) :
71 + self.snk = snk
72 + self.path = path
73 + self.up = up
74 + self.env = {"True": Decl(None, kind=Decl.CONST, value=True),
75 + "False": Decl(None, kind=Decl.CONST, value=False),
76 + "None": Decl(None, kind=Decl.CONST, value=None),
77 + "dot": Decl(None, kind=Decl.CONST, value=self.snk.dot),
78 + "BlackToken": Decl(None, kind=Decl.TYPE,
79 + type=self.snk.Instance(self.snk.BlackToken))}
80 + self.stack = []
81 + if up :
82 + self.globals = up.globals
83 + else :
84 + self.globals = snk.Evaluator(dot=self.snk.dot,
85 + BlackToken=self.snk.BlackToken)
86 + self.instances = MultiSet()
87 + # utilities
88 + def _raise (self, error, message) :
89 + """raise an exception with appropriate location
90 + """
91 + if self.stack :
92 + pos = "[%s:%s]: " % (self.stack[-1].lineno,
93 + self.stack[-1].col_offset)
94 + else :
95 + pos = ""
96 + raise error(pos+message)
97 + def _eval (self, expr, *largs, **kwargs) :
98 + env = self.globals.copy()
99 + if isinstance(expr, ast.AST) :
100 + expr = unparse(expr)
101 + return env(expr, dict(*largs, **kwargs))
102 + # declarations management
103 + def __setitem__ (self, name, value) :
104 + if name in self.env :
105 + self._raise(DeclarationError, "duplicated declaration of %r" % name)
106 + self.env[name] = value
107 + def __getitem__ (self, name) :
108 + if name in self.env :
109 + return self.env[name]
110 + elif self.up is None :
111 + self._raise(DeclarationError, "%r not declared" % name)
112 + else :
113 + return self.up[name]
114 + def __contains__ (self, name) :
115 + if name in self.env :
116 + return True
117 + elif self.up is None :
118 + return False
119 + else :
120 + return name in self.up
121 + def goto (self, name) :
122 + if name in self.env :
123 + return self
124 + elif self.up is None :
125 + self._raise(DeclarationError, "%r not declared" % name)
126 + else :
127 + return self.up.goto(name)
128 + def get_buffer (self, name) :
129 + if name not in self :
130 + self._raise(DeclarationError,
131 + "buffer %r not declared" % name)
132 + decl = self[name]
133 + if decl.kind != Decl.BUFFER :
134 + self._raise(DeclarationError,
135 + "%r declared as %s but used as buffer"
136 + % (name, decl.kind))
137 + elif decl.capacity is not None :
138 + pass
139 + #self._raise(NotImplementedError, "capacities not (yet) supported")
140 + return decl
141 + def get_net (self, name) :
142 + if name not in self :
143 + self._raise(DeclarationError,
144 + "net %r not declared" % name)
145 + decl = self[name]
146 + if decl.kind != Decl.NET :
147 + self._raise(DeclarationError,
148 + "%r declared as %s but used as net"
149 + % (name, decl.kind))
150 + return decl
151 + def get_task (self, name) :
152 + if name not in self :
153 + self._raise(DeclarationError,
154 + "task %r not declared" % name)
155 + decl = self[name]
156 + if decl.kind != Decl.TASK :
157 + self._raise(DeclarationError,
158 + "%r declared as %s but used as task"
159 + % (name, decl.kind))
160 + return decl
161 + # main compiler entry point
162 + def build (self, node, prefix="", fallback=None) :
163 + self.stack.append(node)
164 + if prefix :
165 + prefix += "_"
166 + method = "build_" + prefix + node.__class__.__name__
167 + visitor = getattr(self, method, fallback or self.build_fail)
168 + try :
169 + return visitor(node)
170 + finally :
171 + self.stack.pop(-1)
172 + def build_fail (self, node) :
173 + self._raise(CompilationError, "do not know how to compile %s"
174 + % node.__class__.__name__)
175 + def build_arc (self, node) :
176 + return self.build(node, "arc", self.build_arc_expr)
177 + # specification
178 + def build_AbcdSpec (self, node) :
179 + for decl in node.context :
180 + self.build(decl)
181 + tasks = [self._build_TaskNet(decl.node)
182 + for name, decl in self.env.items()
183 + if decl.kind == Decl.TASK and decl.used]
184 + net = reduce(operator.or_, tasks, self.build(node.body))
185 + # set local buffers marking, and hide them
186 + for name, decl in ((n, d) for n, d in self.env.items()
187 + if d.kind == Decl.BUFFER) :
188 + status = self.snk.buffer(name)
189 + for place in net.status(status) :
190 + place = net.place(place)
191 + try :
192 + place.reset(decl.marking)
193 + except ValueError as err :
194 + self._raise(CompilationError,
195 + "invalid initial marking (%s)" % err)
196 + if decl.capacity is None :
197 + cap = None
198 + else :
199 + #cap = [c.n if c else None for c in decl.capacity]
200 + # TODO: accept more than integers as capacities
201 + cap = []
202 + for c in decl.capacity :
203 + if c is None :
204 + cap.append(None)
205 + else :
206 + try :
207 + cap.append(self._eval(c))
208 + except :
209 + err = sys.exc_info()[1]
210 + self._raise(CompilationError,
211 + "could not evaluate %r, %s"
212 + % (unparse(c), err))
213 + place.label(path=self.path,
214 + capacity=cap)
215 + # TODO: check capacity
216 + net.hide(status)
217 + if self.up is None :
218 + # set entry marking
219 + for place in net.status(self.snk.entry) :
220 + net.place(place).reset(self.snk.dot)
221 + # rename nodes
222 + self._rename_nodes(net)
223 + # copy global declarations
224 + net.globals.update(self.globals)
225 + # add info about source file
226 + net.label(srcfile=str(node.st.text.filename))
227 + # add assertions
228 + net.label(asserts=node.asserts)
229 + return net
230 + def _build_TaskNet (self, node) :
231 + self._raise(NotImplementedError, "tasks not (yet) supported")
232 + def _rename_nodes (self, net) :
233 + # generate unique names
234 + total = collections.defaultdict(int)
235 + count = collections.defaultdict(int)
236 + def ren (node) :
237 + if net.has_transition(node.name) :
238 + status = node.label("srctext")
239 + else :
240 + if node.status == self.snk.entry :
241 + status = "e"
242 + elif node.status == self.snk.internal :
243 + status = "i"
244 + elif node.status == self.snk.exit :
245 + status = "x"
246 + else :
247 + status = node.label("buffer")
248 + name = ".".join(node.label("path") + [status])
249 + if total[name] > 1 :
250 + count[name] += 1
251 + name = "%s#%s" % (name, count[name])
252 + return name
253 + # count occurrences of each name base
254 + _total = collections.defaultdict(int)
255 + for node in net.node() :
256 + _total[ren(node)] += 1
257 + total = _total
258 + # rename nodes using a depth-first traversal
259 + done = set(net.status(self.snk.entry))
260 + todo = [net.node(n) for n in done]
261 + while todo :
262 + node = todo.pop(-1)
263 + new = ren(node)
264 + if new != node.name :
265 + net.rename_node(node.name, new)
266 + done.add(new)
267 + for n in net.post(new) - done :
268 + todo.append(net.node(n))
269 + done.add(n)
270 + # rename isolated nodes
271 + for letter, method in (("p", net.place), ("t", net.transition)) :
272 + for node in method() :
273 + if node.name not in done :
274 + net.rename_node(node.name, ren(node))
275 + # declarations
276 + def build_AbcdTypedef (self, node) :
277 + """
278 + >>> import snakes.nets
279 + >>> b = Builder(snakes.nets)
280 + >>> b.build(ast.AbcdTypedef(name='number', type=ast.UnionType(types=[ast.NamedType(name='int'), ast.NamedType(name='float')])))
281 + >>> b.env['number'].type
282 + (Instance(int) | Instance(float))
283 + >>> b.build(ast.ImportFrom(module='inspect', names=[ast.alias(name='isbuiltin')]))
284 + >>> b.build(ast.AbcdTypedef(name='builtin', type=ast.NamedType(name='isbuiltin')))
285 + >>> b.env['builtin'].type
286 + TypeCheck(inspect.isbuiltin)
287 + """
288 + self[node.name] = Decl(node, type=self.build(node.type))
289 + def build_AbcdBuffer (self, node) :
290 + self[node.name] = Decl(node,
291 + type=self.build(node.type),
292 + capacity=node.capacity,
293 + marking=self._eval(node.content))
294 + def build_AbcdSymbol (self, node) :
295 + for name in node.symbols :
296 + value = self.snk.Symbol(name, False)
297 + self[name] = Decl(node, value=value)
298 + self.globals[name] = value
299 + def build_AbcdConst (self, node) :
300 + value = self._eval(node.value)
301 + self[node.name] = Decl(node, value=value)
302 + self.globals[node.name] = value
303 + def build_AbcdNet (self, node) :
304 + self[node.name] = Decl(node, getargs=GetInstanceArgs(node))
305 + def build_AbcdTask (self, node) :
306 + self._raise(NotImplementedError, "tasks not (yet) supported")
307 + self[node.name] = Decl(node, used=False)
308 + def build_Import (self, node) :
309 + for alias in node.names :
310 + self[alias.asname or alias.name] = Decl(node)
311 + self.globals.declare(unparse(node))
312 + def build_ImportFrom (self, node) :
313 + self.build_Import(node)
314 + # processes
315 + def build_AbcdAction (self, node) :
316 + if node.guard is True :
317 + return self._build_True(node)
318 + elif node.guard is False :
319 + return self._build_False(node)
320 + else :
321 + return self._build_action(node)
322 + def _build_True (self, node) :
323 + net = self.snk.PetriNet("true")
324 + e = self.snk.Place("e", [], self.snk.tBlackToken,
325 + status=self.snk.entry)
326 + e.label(path=self.path)
327 + net.add_place(e)
328 + x = self.snk.Place("x", [], self.snk.tBlackToken,
329 + status=self.snk.exit)
330 + x.label(path=self.path)
331 + net.add_place(x)
332 + t = self.snk.Transition("t")
333 + t.label(srctext=node.st.source(),
334 + srcloc=(node.st.srow, node.st.scol,
335 + node.st.erow, node.st.ecol),
336 + path=self.path)
337 + net.add_transition(t)
338 + net.add_input("e", "t", self.snk.Value(self.snk.dot))
339 + net.add_output("x", "t", self.snk.Value(self.snk.dot))
340 + return net
341 + def _build_False (self, node) :
342 + net = self.snk.PetriNet("false")
343 + e = self.snk.Place("e", [], self.snk.tBlackToken,
344 + status=self.snk.entry)
345 + e.label(path=self.path)
346 + net.add_place(e)
347 + x = self.snk.Place("x", [], self.snk.tBlackToken,
348 + status=self.snk.exit)
349 + x.label(path=self.path)
350 + net.add_place(x)
351 + return net
352 + def _build_action (self, node) :
353 + net = self.snk.PetriNet("flow")
354 + e = self.snk.Place("e", [], self.snk.tBlackToken,
355 + status=self.snk.entry)
356 + e.label(path=self.path)
357 + net.add_place(e)
358 + x = self.snk.Place("x", [], self.snk.tBlackToken,
359 + status=self.snk.exit)
360 + x.label(path=self.path)
361 + net.add_place(x)
362 + t = self.snk.Transition("t", self.snk.Expression(unparse(node.guard)),
363 + status=self.snk.tick("action"))
364 + t.label(srctext=node.st.source(),
365 + srcloc=(node.st.srow, node.st.scol,
366 + node.st.erow, node.st.ecol),
367 + path=self.path)
368 + net.add_transition(t)
369 + net.add_input("e", "t", self.snk.Value(self.snk.dot))
370 + net.add_output("x", "t", self.snk.Value(self.snk.dot))
371 + net = reduce(operator.or_, [self.build(a) for a in node.accesses],
372 + net)
373 + net.hide(self.snk.tick("action"))
374 + return net
375 + def build_AbcdFlowOp (self, node) :
376 + return self.build(node.op)(self.build(node.left),
377 + self.build(node.right))
378 + def _get_instance_arg (self, arg) :
379 + if arg.__class__.__name__ == "Name" and arg.id in self :
380 + return self[arg.id]
381 + else :
382 + try :
383 + self._eval(arg)
384 + except :
385 + self._raise(CompilationError,
386 + "could not evaluate argument %r"
387 + % arg.st.source())
388 + return arg
389 + def build_AbcdInstance (self, node) :
390 + if node.net not in self :
391 + self._raise(DeclarationError, "%r not declared" % node.net)
392 + elif node.starargs :
393 + self._raise(CompilationError, "* argument not allowed here")
394 + elif node.kwargs :
395 + self._raise(CompilationError, "** argument not allowed here")
396 + decl = self[node.net]
397 + if decl.kind != Decl.NET :
398 + self._raise(DeclarationError,
399 + "%r declared as %s but used as net"
400 + % (name, decl.kind))
401 + # unpack args
402 + posargs, kwargs = [], {}
403 + for arg in node.args :
404 + posargs.append(self._get_instance_arg(arg))
405 + for kw in node.keywords :
406 + kwargs[kw.arg] = self._get_instance_arg(kw.value)
407 + # bind args
408 + try :
409 + args, buffers, nets, tasks = decl.getargs(*posargs, **kwargs)
410 + except TypeError :
411 + c, v, t = sys.exc_info()
412 + self._raise(CompilationError, str(v))
413 + for d, kind in ((buffers, Decl.BUFFER),
414 + (nets, Decl.NET),
415 + (tasks, Decl.TASK)) :
416 + for k, v in d.items() :
417 + if v.kind != kind :
418 + self._raise(DeclarationError,
419 + "%r declared as %s but used as %s"
420 + % (k, v.kind, kind))
421 + d[k] = v.node.name
422 + # build sub-net
423 + binder = transform.ArgsBinder(args, buffers, nets, tasks)
424 + spec = binder.visit(decl.node.body)
425 + if node.asname :
426 + name = str(node.asname)
427 + else :
428 + name = node.st.source()
429 + if name in self.instances :
430 + name = "%s#%s" % (name, self.instances(name))
431 + self.instances.add(name)
432 + path = self.path + [name]
433 + builder = self.__class__(self.snk, path, self)
434 + return builder.build(spec)
435 + # control flow operations
436 + def build_Sequence (self, node) :
437 + return self.snk.PetriNet.__and__
438 + def build_Choice (self, node) :
439 + return self.snk.PetriNet.__add__
440 + def build_Parallel (self, node) :
441 + return self.snk.PetriNet.__or__
442 + def build_Loop (self, node) :
443 + return self.snk.PetriNet.__mul__
444 + # accesses :
445 + def build_SimpleAccess (self, node) :
446 + decl = self.get_buffer(node.buffer)
447 + net = self.snk.PetriNet("access")
448 + net.add_transition(self.snk.Transition("t", status=self.snk.tick("action")))
449 + b = self.snk.Place(str(node.buffer), [], decl.type,
450 + status=self.snk.buffer(node.buffer))
451 + b.label(path=self.path,
452 + buffer=str(node.buffer),
453 + srctext=decl.node.st.source(),
454 + srcloc=(decl.node.st.srow, decl.node.st.scol,
455 + decl.node.st.erow, decl.node.st.ecol))
456 + net.add_place(b)
457 + self.build(node.arc)(net, node.buffer, "t", self.build_arc(node.tokens))
458 + return net
459 + def build_FlushAccess (self, node) :
460 + decl = self.get_buffer(node.buffer)
461 + net = self.snk.PetriNet("access")
462 + net.add_transition(self.snk.Transition("t", status=self.snk.tick("action")))
463 + b = self.snk.Place(str(node.buffer), [], decl.type,
464 + status=self.snk.buffer(node.buffer))
465 + b.label(path=self.path,
466 + buffer=str(node.buffer),
467 + srctext=decl.node.st.source(),
468 + srcloc=(decl.node.st.srow, decl.node.st.scol,
469 + decl.node.st.erow, decl.node.st.ecol))
470 + net.add_place(b)
471 + net.add_input(node.buffer, "t", self.snk.Flush(node.target))
472 + return net
473 + def build_SwapAccess (self, node) :
474 + decl = self.get_buffer(node.buffer)
475 + net = self.snk.PetriNet("access")
476 + net.add_transition(self.snk.Transition("t", status=self.snk.tick("action")))
477 + b = self.snk.Place(node.buffer, [], decl.type,
478 + status=self.snk.buffer(node.buffer))
479 + b.label(path=self.path,
480 + buffer=str(node.buffer),
481 + srctext=decl.node.st.source(),
482 + srcloc=(decl.node.st.srow, decl.node.st.scol,
483 + decl.node.st.erow, decl.node.st.ecol))
484 + net.add_place(b)
485 + net.add_input(node.buffer, "t", self.build_arc(node.target))
486 + net.add_output(node.buffer, "t", self.build_arc(node.tokens))
487 + return net
488 + def build_Spawn (self, node) :
489 + self._raise(NotImplementedError, "tasks not (yet) supported")
490 + def build_Wait (self, node) :
491 + self._raise(NotImplementedError, "tasks not (yet) supported")
492 + def build_Suspend (self, node) :
493 + self._raise(NotImplementedError, "tasks not (yet) supported")
494 + def build_Resume (self, node) :
495 + self._raise(NotImplementedError, "tasks not (yet) supported")
496 + # arc labels
497 + def build_arc_Name (self, node) :
498 + if node.id in self :
499 + decl = self[node.id]
500 + if decl.kind in (Decl.CONST, Decl.SYMBOL) :
501 + return self.snk.Value(decl.value)
502 + return self.snk.Variable(node.id)
503 + def build_arc_Num (self, node) :
504 + return self.snk.Value(node.n)
505 + def build_arc_Str (self, node) :
506 + return self.snk.Value(node.s)
507 + def build_arc_Tuple (self, node) :
508 + return self.snk.Tuple([self.build_arc(elt) for elt in node.elts])
509 + def build_arc_expr (self, node) :
510 + return self.snk.Expression(unparse(node))
511 + # arcs
512 + def build_Produce (self, node) :
513 + def arc (net, place, trans, label) :
514 + net.add_output(place, trans, label)
515 + return arc
516 + def build_Test (self, node) :
517 + def arc (net, place, trans, label) :
518 + net.add_input(place, trans, self.snk.Test(label))
519 + return arc
520 + def build_Consume (self, node) :
521 + def arc (net, place, trans, label) :
522 + net.add_input(place, trans, label)
523 + return arc
524 + def build_Fill (self, node) :
525 + def arc (net, place, trans, label) :
526 + net.add_output(place, trans, self.snk.Flush(str(label)))
527 + return arc
528 + # types
529 + def build_UnionType (self, node) :
530 + return reduce(operator.or_, (self.build(child)
531 + for child in node.types))
532 + def build_IntersectionType (self, node) :
533 + return reduce(operator.and_, (self.build(child)
534 + for child in node.types))
535 + def build_CrossType (self, node) :
536 + return self.snk.CrossProduct(*(self.build(child)
537 + for child in node.types))
538 + def build_ListType (self, node) :
539 + return self.snk.List(self.build(node.items))
540 + def build_TupleType (self, node) :
541 + return self.snk.Collection(self.snk.Instance(tuple),
542 + (self.build(node.items)))
543 + def build_SetType (self, node) :
544 + return self.snk.Set(self.build(node.items))
545 + def build_DictType (self, node) :
546 + return self.snk.Mapping(keys=self.build(node.keys),
547 + items=self.build(node.items),
548 + _dict=self.snk.Instance(self.snk.hdict))
549 + def build_EnumType (self, node) :
550 + return self.snk.OneOf(*(self._eval(child) for child in node.items))
551 + def build_NamedType (self, node) :
552 + name = node.name
553 + if name in self and self[name].kind == Decl.TYPE :
554 + return self[name].type
555 + elif name in self.globals :
556 + obj = self.globals[name]
557 + if inspect.isclass(obj) :
558 + return self.snk.Instance(obj)
559 + elif inspect.isroutine(obj) :
560 + return self.snk.TypeCheck(obj)
561 + elif hasattr(sys.modules["__builtin__"], name) :
562 + obj = getattr(sys.modules["__builtin__"], name)
563 + if inspect.isclass(obj) :
564 + return self.snk.Instance(obj)
565 + elif inspect.isroutine(obj) :
566 + return self.snk.TypeCheck(obj)
567 + self._raise(CompilationError,
568 + "invalid type %r" % name)
569 +
570 +if __name__ == "__main__" :
571 + import doctest
572 + doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE
573 + | doctest.REPORT_ONLY_FIRST_FAILURE
574 + | doctest.ELLIPSIS)
575 + from snakes.lang.abcd.parser import parse
576 + node = parse(open(sys.argv[1]).read())
577 + import snakes.plugins
578 + snk = snakes.plugins.load(["ops", "gv", "labels"], "snakes.nets", "snk")
579 + build = Builder(snk)
580 + net = build.build(node)
581 + net.draw(sys.argv[1] + ".png")
1 +import heapq
2 +from snakes.nets import StateGraph
3 +import snakes.lang
4 +import snkast as ast
5 +
6 +class Checker (object) :
7 + def __init__ (self, net) :
8 + self.g = StateGraph(net)
9 + self.f = [self.build(f) for f in net.label("asserts")]
10 + def build (self, tree) :
11 + src = """
12 +def check (_) :
13 + return %s
14 +""" % tree.st.source()[7:]
15 + ctx = dict(self.g.net.globals)
16 + ctx["bounded"] = self.bounded
17 + exec(src, ctx)
18 + fun = ctx["check"]
19 + fun.lineno = tree.lineno
20 + return fun
21 + def bounded (self, marking, max) :
22 + return all(len(marking(p)) == 1 for p in marking)
23 + def run (self) :
24 + for state in self.g :
25 + marking = self.g.net.get_marking()
26 + for place in marking :
27 + if max(marking(place).values()) > 1 :
28 + return None, self.trace(state)
29 + for check in self.f :
30 + try :
31 + if not check(marking) :
32 + return check.lineno, self.trace(state)
33 + except :
34 + pass
35 + return None, None
36 + def path (self, tgt, src=0) :
37 + q = [(0, src, ())]
38 + visited = set()
39 + while True :
40 + (c, v1, path) = heapq.heappop(q)
41 + if v1 not in visited :
42 + path = path + (v1,)
43 + if v1 == tgt :
44 + return path
45 + visited.add(v1)
46 + for v2 in self.g.successors(v1) :
47 + if v2 not in visited :
48 + heapq.heappush(q, (c+1, v2, path))
49 + def trace (self, state) :
50 + path = self.path(state)
51 + return tuple(self.g.successors(i)[j]
52 + for i, j in zip(path[:-1], path[1:]))
1 +import sys, optparse, os.path
2 +import pdb, traceback
3 +import snakes.plugins
4 +from snakes.utils.abcd.build import Builder
5 +from snakes.lang.abcd.parser import parse
6 +from snakes.lang.pgen import ParseError
7 +from snakes.utils.abcd import CompilationError, DeclarationError
8 +from snakes.utils.abcd.simul import Simulator
9 +from snakes.utils.abcd.checker import Checker
10 +
11 +##
12 +## error messages
13 +##
14 +
15 +ERR_ARG = 1
16 +ERR_OPT = 2
17 +ERR_IO = 3
18 +ERR_PARSE = 4
19 +ERR_PLUGIN = 5
20 +ERR_COMPILE = 6
21 +ERR_OUTPUT = 7
22 +ERR_BUG = 255
23 +
24 +def err (message) :
25 + sys.stderr.write("abcd: %s\n" % message.strip())
26 +
27 +def die (code, message=None) :
28 + if message :
29 + err(message)
30 + if options.debug :
31 + pdb.post_mortem(sys.exc_info()[2])
32 + else :
33 + sys.exit(code)
34 +
35 +def bug () :
36 + sys.stderr.write("""
37 + ********************************************************************
38 + *** An unexpected error ocurred. Please report this bug to ***
39 + *** <franck.pommereau@gmail.com>, together with the execution ***
40 + *** trace below and, if possible, a stripped-down version of the ***
41 + *** ABCD source code that caused this bug. Thank you for your ***
42 + *** help in improving SNAKES! ***
43 + ********************************************************************
44 +
45 +""")
46 + traceback.print_exc()
47 + if options.debug :
48 + pdb.post_mortem(sys.exc_info()[2])
49 + else :
50 + sys.exit(ERR_BUG)
51 +
52 +##
53 +## options parsing
54 +##
55 +
56 +gv_engines = ("dot", "neato", "twopi", "circo", "fdp")
57 +
58 +opt = optparse.OptionParser(prog="abcd",
59 + usage="%prog [OPTION]... FILE")
60 +opt.add_option("-l", "--load",
61 + dest="plugins", action="append", default=[],
62 + help="load plugin (this option can be repeated)",
63 + metavar="PLUGIN")
64 +opt.add_option("-p", "--pnml",
65 + dest="pnml", action="store", default=None,
66 + help="save net as PNML",
67 + metavar="OUTFILE")
68 +for engine in gv_engines :
69 + opt.add_option("-" + engine[0], "--" + engine,
70 + dest="gv" + engine, action="store", default=None,
71 + help="draw net using '%s' (from GraphViz)" % engine,
72 + metavar="OUTFILE")
73 +opt.add_option("-a", "--all-names",
74 + dest="allnames", action="store_true", default=False,
75 + help="draw control-flow places names (default: hide)")
76 +opt.add_option("--debug",
77 + dest="debug", action="store_true", default=False,
78 + help="launch debugger on compiler error (default: no)")
79 +opt.add_option("-s", "--simul",
80 + dest="simul", action="store_true", default=False,
81 + help="launch interactive code simulator")
82 +opt.add_option("--check",
83 + dest="check", action="store_true", default=False,
84 + help="check assertions")
85 +
86 +def getopts (args) :
87 + global options, abcd
88 + (options, args) = opt.parse_args(args)
89 + plugins = []
90 + for p in options.plugins :
91 + plugins.extend(t.strip() for t in p.split(","))
92 + if "ops" not in options.plugins :
93 + plugins.append("ops")
94 + if "labels" not in plugins :
95 + plugins.append("labels")
96 + for engine in gv_engines :
97 + gvopt = getattr(options, "gv%s" % engine)
98 + if gvopt and "gv" not in plugins :
99 + plugins.append("gv")
100 + break
101 + options.plugins = plugins
102 + if len(args) < 1 :
103 + err("no input file provided")
104 + opt.print_help()
105 + die(ERR_ARG)
106 + elif len(args) > 1 :
107 + err("more than one input file provided")
108 + opt.print_help()
109 + die(ERR_ARG)
110 + abcd = args[0]
111 + if options.pnml == abcd :
112 + err("input file also used as output (--pnml)")
113 + opt.print_help()
114 + die(ERR_ARG)
115 + for engine in gv_engines :
116 + if getattr(options, "gv%s" % engine) == abcd :
117 + err("input file also used as output (--%s)" % engine)
118 + opt.print_help()
119 + die(ERR_ARG)
120 +
121 +##
122 +## drawing nets
123 +##
124 +
125 +def place_attr (place, attr) :
126 + # fix color
127 + if place.status == snk.entry :
128 + attr["fillcolor"] = "green"
129 + elif place.status == snk.internal :
130 + pass
131 + elif place.status == snk.exit :
132 + attr["fillcolor"] = "yellow"
133 + else :
134 + attr["fillcolor"] = "lightblue"
135 + # fix shape
136 + if (not options.allnames
137 + and place.status in (snk.entry, snk.internal, snk.exit)) :
138 + attr["shape"] = "circle"
139 + # render marking
140 + if place._check == snk.tBlackToken :
141 + count = len(place.tokens)
142 + if count == 0 :
143 + marking = " "
144 + elif count == 1 :
145 + marking = "@"
146 + else :
147 + marking = "%s@" % count
148 + else :
149 + marking = str(place.tokens)
150 + # node label
151 + if (options.allnames
152 + or place.status not in (snk.entry, snk.internal, snk.exit)) :
153 + attr["label"] = "%s\\n%s" % (place.name, marking)
154 + else :
155 + attr["label"] = "%s" % marking
156 +
157 +def trans_attr (trans, attr) :
158 + pass
159 +
160 +def arc_attr (label, attr) :
161 + if label == snk.Value(snk.dot) :
162 + del attr["label"]
163 + elif isinstance(label, snk.Test) :
164 + attr["arrowhead"] = "none"
165 + attr["label"] = " %s " % label._annotation
166 + elif isinstance(label, snk.Flush) :
167 + attr["arrowhead"] = "box"
168 + attr["label"] = " %s " % label._annotation
169 +
170 +def draw (net, engine, target) :
171 + try :
172 + net.draw(target, engine=engine,
173 + place_attr=place_attr,
174 + trans_attr=trans_attr,
175 + arc_attr=arc_attr)
176 + except :
177 + die(ERR_OUTPUT, str(sys.exc_info()[1]))
178 +
179 +##
180 +## save pnml
181 +##
182 +
183 +def save_pnml (net, target) :
184 + try :
185 + out = open(target, "w")
186 + out.write(snk.dumps(net))
187 + out.close()
188 + except :
189 + die(ERR_OUTPUT, str(sys.exc_info()[1]))
190 +
191 +##
192 +## main
193 +##
194 +
195 +def main (args=sys.argv[1:], src=None) :
196 + global snk
197 + # get options
198 + try:
199 + if src is None :
200 + getopts(args)
201 + else :
202 + getopts(list(args) + ["<string>"])
203 + except SystemExit :
204 + raise
205 + except :
206 + die(ERR_OPT, str(sys.exc_info()[1]))
207 + # read source
208 + if src is not None :
209 + source = src
210 + else :
211 + try :
212 + source = open(abcd).read()
213 + except :
214 + die(ERR_IO, "could not read input file %r" % abcd)
215 + # parse
216 + try :
217 + node = parse(source, filename=abcd)
218 + except ParseError :
219 + die(ERR_PARSE, str(sys.exc_info()[1]))
220 + except :
221 + bug()
222 + # compile
223 + dirname = os.path.dirname(abcd)
224 + if dirname and dirname not in sys.path :
225 + sys.path.append(dirname)
226 + elif "." not in sys.path :
227 + sys.path.append(".")
228 + try :
229 + snk = snakes.plugins.load(options.plugins, "snakes.nets", "snk")
230 + except :
231 + die(ERR_PLUGIN, str(sys.exc_info()[1]))
232 + build = Builder(snk)
233 + try :
234 + net = build.build(node)
235 + net.label(srcfile=abcd)
236 + except (CompilationError, DeclarationError) :
237 + die(ERR_COMPILE, str(sys.exc_info()[1]))
238 + except :
239 + bug()
240 + # output
241 + if options.pnml :
242 + save_pnml(net, options.pnml)
243 + for engine in gv_engines :
244 + target = getattr(options, "gv%s" % engine)
245 + if target :
246 + draw(net, engine, target)
247 + trace, lineno = [], None
248 + if options.check :
249 + lineno, trace = Checker(net).run()
250 + if options.simul :
251 + Simulator(snk, source, net, trace, lineno).run()
252 + elif trace :
253 + if lineno is None :
254 + print("unsafe execution:")
255 + else :
256 + asserts = dict((a.lineno, a) for a in net.label("asserts"))
257 + print("line %s, %r failed:"
258 + % (lineno, asserts[lineno].st.source()))
259 + for trans, mode in trace :
260 + print(" %s %s" % (trans, mode))
261 + return net
262 +
263 +if __name__ == "__main__" :
264 + main()
1 +import math, operator, collections
2 +import Tkinter as tk
3 +import tkMessageBox as popup
4 +try :
5 + import Tkinter.scrolledtext as ScrolledText
6 +except ImportError :
7 + import ScrolledText
8 +
9 +class Action (object) :
10 + def __init__ (self, trans, mode, shift) :
11 + self.trans = trans
12 + self.mode = mode
13 + self.net = trans.net
14 + self.pre = self.net.get_marking()
15 + self.post = None
16 + srow, scol, erow, ecol = trans.label("srcloc")
17 + self.start = "%s.%s" % (srow, scol + shift)
18 + self.stop = "%s.%s" % (erow, ecol + shift)
19 + self.line = srow
20 + def fire (self) :
21 + self.trans.fire(self.mode)
22 + self.post = self.net.get_marking()
23 + def __eq__ (self, other) :
24 + try :
25 + return (self.trans == other.trans and self.mode == other.mode
26 + and self.net == other.net)
27 + except AttributeError :
28 + return False
29 +
30 +class Trace (object) :
31 + def __init__ (self, net) :
32 + self.net = net
33 + self.actions = []
34 + def add (self, action) :
35 + self.actions.append(action)
36 + def back (self) :
37 + self.net.set_marking(self.actions[-1].pre)
38 + self.actions.pop()
39 + def empty (self) :
40 + return not self.actions
41 + def __getitem__ (self, idx) :
42 + return self.actions[idx]
43 + def __len__ (self) :
44 + return len(self.actions)
45 +
46 +class Simulator (object) :
47 + def __init__ (self, snk, src, net, trace=None, errline=None) :
48 + self.snk = snk
49 + self.src = src
50 + self.net = net
51 + self.srclength = len(src.splitlines())
52 + self.width = int(math.ceil(math.log10(self.srclength)))
53 + self.shift = self.width + 2
54 + self.modes = []
55 + self.trans2modes = collections.defaultdict(set)
56 + self.trace = Trace(self.net)
57 + self.build_gui(errline)
58 + if trace is not None :
59 + for trans, mode in trace :
60 + trans = self.net.transition(trans.name)
61 + action = Action(trans, mode, self.shift)
62 + action.fire()
63 + self.extend_trace(action)
64 + self.trace.add(action)
65 + self.update()
66 + if trace :
67 + self._back.configure(state=tk.NORMAL)
68 + def update (self) :
69 + self.update_modes()
70 + self.update_state()
71 + def build_gui (self, errline) :
72 + self._win = tk.Tk()
73 + self._win.title("ABCD simulator")
74 + self._win.bind("<Return>", self.fire)
75 + self._win.bind("<BackSpace>", self.back)
76 + self._win.bind("<Escape>", self.quit)
77 + # paned windows and frames
78 + self._pan_main = tk.PanedWindow(self._win, orient=tk.HORIZONTAL)
79 + self._pan_main.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
80 + self.__pan_left = tk.PanedWindow(self._pan_main, orient=tk.VERTICAL)
81 + self._pan_main.add(self.__pan_left)
82 + self._pan_right = tk.PanedWindow(self._pan_main, orient=tk.VERTICAL)
83 + self._pan_main.add(self._pan_right)
84 + self._modes_frame = tk.Frame(self.__pan_left)
85 + self.__pan_left.add(self._modes_frame)
86 + self._trace_frame = tk.Frame(self.__pan_left)
87 + self.__pan_left.add(self._trace_frame)
88 + self._state_frame = tk.Frame(self._pan_right)
89 + self._pan_right.add(self._state_frame)
90 + # modes
91 + self._modes_x = tk.Scrollbar (self._modes_frame,
92 + orient=tk.HORIZONTAL)
93 + self._modes_x.grid(row=1, column=0, sticky=tk.W+tk.E)
94 + self._modes_y = tk.Scrollbar (self._modes_frame, orient=tk.VERTICAL)
95 + self._modes_y.grid(row=0, column=1, sticky=tk.N+tk.S)
96 + self._modes = tk.Listbox(self._modes_frame,
97 + xscrollcommand=self._modes_x.set,
98 + yscrollcommand=self._modes_y.set,
99 + width=50,
100 + font="monospace",
101 + activestyle="none",
102 + selectbackground="green",
103 + selectborderwidth=0,
104 + highlightthickness=0,
105 + selectmode=tk.SINGLE,
106 + disabledforeground="black")
107 + self._modes.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
108 + self._modes_x["command"] = self._modes.xview
109 + self._modes_y["command"] = self._modes.yview
110 + self._modes.bind("<Button-1>", self.select_mode)
111 + self._modes.bind("<Double-Button-1>", self.select_mode_fire)
112 + # fire button
113 + self._fire = tk.Button(self._modes_frame,
114 + text="Fire",
115 + command=self.fire,
116 + state=tk.DISABLED)
117 + self._fire.grid(row=2, column=0, columnspan=2, sticky=tk.W+tk.E)
118 + # back button
119 + self._back = tk.Button(self._modes_frame,
120 + text="Undo last action",
121 + command=self.back,
122 + state=tk.DISABLED)
123 + self._back.grid(row=3, column=0, columnspan=2, sticky=tk.W+tk.E)
124 + # resume button
125 + self._resume = tk.Button(self._modes_frame,
126 + text="Resume simulation",
127 + command=self.resume,
128 + state=tk.DISABLED)
129 + self._resume.grid(row=4, column=0, columnspan=2, sticky=tk.W+tk.E)
130 + # traces
131 + self._trace_x = tk.Scrollbar (self._trace_frame,
132 + orient=tk.HORIZONTAL)
133 + self._trace_x.grid(row=1, column=0, sticky=tk.W+tk.E)
134 + self._trace_y = tk.Scrollbar (self._trace_frame, orient=tk.VERTICAL)
135 + self._trace_y.grid(row=0, column=1, sticky=tk.N+tk.S)
136 + self._trace = tk.Listbox(self._trace_frame,
137 + font="monospace",
138 + width=50,
139 + activestyle="none",
140 + selectbackground="blue",
141 + xscrollcommand=self._trace_x.set,
142 + yscrollcommand=self._trace_y.set,
143 + disabledforeground="black")
144 + self._trace.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
145 + self._trace_x["command"] = self._trace.xview
146 + self._trace_y["command"] = self._trace.yview
147 + self._trace.bind("<Button-1>", self.select_trace)
148 + self._trace.insert(tk.END, "<init>")
149 + # save trace button
150 + # self._save = tk.Button(self._trace_frame,
151 + # text="Save trace",
152 + # command=self.save)
153 + # self._save.grid(row=2, column=0, columnspan=2, sticky=tk.W+tk.E)
154 + # source
155 + self._source = ScrolledText.ScrolledText(self._pan_right,
156 + font="monospace",
157 + width=70,
158 + height=min([self.srclength,
159 + 25]))
160 + self._pan_right.add(self._source)
161 + self._source.tag_config("linenum",
162 + background="#eee",
163 + foreground="#222")
164 + for num, line in enumerate(self.src.splitlines()) :
165 + if num :
166 + self._source.insert(tk.END, "\n")
167 + self._source.insert(tk.END, "%s: %s"
168 + % (str(num+1).rjust(self.width), line))
169 + self._source.tag_add("linenum",
170 + "%s.0" % (num+1),
171 + "%s.%s" % (num+1, self.width+1))
172 + self._source.configure(state=tk.DISABLED)
173 + if errline is not None :
174 + self._source.tag_add("error",
175 + "%s.%s" % (errline, self.shift),
176 + "%s.end" % errline)
177 + self._source.tag_config("error", background="red")
178 + # states
179 + self._state = ScrolledText.ScrolledText(self._pan_right,
180 + font="monospace",
181 + width=70,
182 + state=tk.DISABLED,
183 + height=10)
184 + self._pan_right.add(self._state)
185 + # setup cells expansion
186 + for widget in (self._win.winfo_toplevel(), self._win,
187 + self._modes_frame, self._trace_frame) :
188 + widget.rowconfigure(0, weight=1)
189 + widget.columnconfigure(0, weight=1)
190 + # tag places and transitions
191 + for place in self.net.place() :
192 + if place.status not in (self.snk.entry, self.snk.internal,
193 + self.snk.exit) :
194 + srow, scol, erow, ecol = place.label("srcloc")
195 + self._source.tag_add(place.name,
196 + "%s.%s" % (srow, scol + self.shift),
197 + "%s.%s" % (erow, ecol + self.shift))
198 + self.buffer_bind(self._source, place.name)
199 + for trans in self.net.transition() :
200 + srow, scol, erow, ecol = trans.label("srcloc")
201 + self._source.tag_add(trans.name,
202 + "%s.%s" % (srow, scol + self.shift),
203 + "%s.%s" % (erow, ecol + self.shift))
204 + self.trans_bind(trans.name)
205 + def trans_bind (self, tag) :
206 + def trans_enter (evt) :
207 + self.trans_enter(tag)
208 + self._source.tag_bind(tag, "<Enter>", trans_enter)
209 + def trans_leave (evt) :
210 + self.trans_leave(tag)
211 + self._source.tag_bind(tag, "<Leave>", trans_leave)
212 + def buffer_bind (self, widget, tag) :
213 + def buffer_enter (evt) :
214 + self.buffer_enter(tag)
215 + widget.tag_bind(tag, "<Enter>", buffer_enter)
216 + def buffer_leave (evt) :
217 + self.buffer_leave(tag)
218 + widget.tag_bind(tag, "<Leave>", buffer_leave)
219 + def run (self) :
220 + self._win.mainloop()
221 + def update_modes (self) :
222 + self._modes.delete(0, tk.END)
223 + self.modes = []
224 + self.selected_mode = None
225 + self.trans2modes = collections.defaultdict(set)
226 + for trans in self.net.transition() :
227 + self._source.tag_config(trans.name, background="white")
228 + for mode in trans.modes() :
229 + self.modes.append(Action(trans, mode, self.shift))
230 + self.modes.sort(key=operator.attrgetter("line"))
231 + for idx, action in enumerate(self.modes) :
232 + self.trans2modes[action.trans.name].add(idx)
233 + self._modes.insert(tk.END, "%s @ %s"
234 + % (action.mode, action.trans.name))
235 + self._source.tag_config(action.trans.name, background="yellow")
236 + self._source.tag_raise(action.trans.name)
237 + if not self.modes :
238 + self._fire.configure(state=tk.DISABLED)
239 + def fire (self, evt=None) :
240 + if evt is not None :
241 + self._fire.flash()
242 + action = self.modes[self.selected_mode]
243 + action.fire()
244 + self.trace.add(action)
245 + self.extend_trace(action)
246 + self.update()
247 + self._fire.configure(state=tk.DISABLED)
248 + self._back.configure(state=tk.NORMAL)
249 + def extend_trace (self, action) :
250 + self._trace.insert(tk.END, "%s @ %s"
251 + % (action.mode, action.trans.name))
252 + self._trace.see(self._trace.size()-1)
253 + def trans_enter (self, trans) :
254 + if trans in self.trans2modes :
255 + state = self._modes["state"]
256 + self._modes.configure(state=tk.NORMAL)
257 + for idx in self.trans2modes[trans] :
258 + self._modes.itemconfig(idx,
259 + background="orange",
260 + selectbackground="orange")
261 + self._modes.see(idx)
262 + self._source.tag_config(trans, background="orange")
263 + self._modes.configure(state=state)
264 + def trans_leave (self, trans) :
265 + if trans in self.trans2modes :
266 + state = self._modes["state"]
267 + self._modes.configure(state=tk.NORMAL)
268 + for idx in self.trans2modes[trans] :
269 + self._modes.itemconfig(idx,
270 + background="white",
271 + selectbackground="green")
272 + if self.selected_mode in self.trans2modes[trans] :
273 + self._source.tag_config(trans, background="green")
274 + else :
275 + self._source.tag_config(trans, background="yellow")
276 + self._modes.configure(state=state)
277 + def select_mode (self, evt) :
278 + if not self.modes or self._modes["state"] == "disabled" :
279 + return
280 + if isinstance(evt, int) :
281 + idx = evt
282 + else :
283 + idx = self._modes.nearest(evt.y)
284 + for num in range(len(self.modes)) :
285 + self._source.tag_config(self.modes[num].trans.name,
286 + background="yellow")
287 + self._source.tag_config(self.modes[idx].trans.name,
288 + background="green")
289 + self.selected_mode = idx
290 + self._fire.configure(state=tk.NORMAL)
291 + def select_mode_fire (self, evt) :
292 + if self.modes or self._modes["state"] == "disabled" :
293 + self.select_mode(evt)
294 + self.fire(evt)
295 + def back (self, evt=None) :
296 + if evt is not None :
297 + self._back.flash()
298 + self.trace.back()
299 + self._trace.delete(self._trace.size()-1)
300 + if self._trace.curselection() :
301 + last = self._trace.size() - 1
302 + self._trace.selection_clear(0, last)
303 + self._trace.see(last)
304 + self.update()
305 + if self.trace.empty() :
306 + self._back.configure(state=tk.DISABLED)
307 + def save (self, evt=None) :
308 + if evt is not None :
309 + self._save.flash()
310 + def update_state (self) :
311 + self._state.configure(state=tk.NORMAL)
312 + self._state.delete("1.0", tk.END)
313 + pos = 1
314 + for place in self.net.place() :
315 + if place.status not in (self.snk.entry, self.snk.internal,
316 + self.snk.exit) :
317 + self._state.insert(tk.END, "%s = %s\n"
318 + % (place.name, place.tokens))
319 + self._state.tag_add(place.name,
320 + "%s.0" % pos, "%s.end" % pos)
321 + pos += 1
322 + self.buffer_bind(self._state, place.name)
323 + self._state.configure(state=tk.DISABLED)
324 + def buffer_enter (self, tag) :
325 + self._state.tag_configure(tag, background="#cff")
326 + self._source.tag_configure(tag, background="#cff")
327 + def buffer_leave (self, tag) :
328 + self._state.tag_configure(tag, background="white")
329 + self._source.tag_configure(tag, background="white")
330 + def quit (self, evt=None) :
331 + if popup.askokcancel("Really quit?", "Are you sure you want to"
332 + " quit the simulator?") :
333 + self._win.quit()
334 + def select_trace (self, evt) :
335 + idx = self._trace.nearest(evt.y)
336 + if idx == 0 :
337 + self.net.set_marking(self.trace[0].pre)
338 + else :
339 + self.net.set_marking(self.trace[idx-1].post)
340 + self._modes.configure(state=tk.NORMAL)
341 + self.update()
342 + if idx < len(self.trace) :
343 + self._modes.selection_set(self.modes.index(self.trace[idx]))
344 + self.select_mode(idx)
345 + self._fire.configure(state=tk.DISABLED)
346 + self._modes.configure(state=tk.DISABLED)
347 + self._resume.configure(state=tk.NORMAL)
348 + def resume (self) :
349 + if self._trace.curselection() :
350 + last = self._trace.size() - 1
351 + self._trace.selection_clear(0, last)
352 + self._trace.see(last)
353 + self.net.set_marking(self.trace[-1].post)
354 + self.update_state()
355 + self._resume.configure(state=tk.DISABLED)
356 + self._modes.configure(state=tk.NORMAL)
1 +from snakes.lang.abcd.parser import ast
2 +
3 +class NodeCopier (ast.NodeTransformer) :
4 + def copy (self, node, **replace) :
5 + args = {}
6 + for name in node._fields + node._attributes :
7 + old = getattr(node, name, None)
8 + if name in replace :
9 + new = replace[name]
10 + elif isinstance(old, list):
11 + new = []
12 + for val in old :
13 + if isinstance(val, ast.AST) :
14 + new.append(self.visit(val))
15 + else :
16 + new.append(val)
17 + elif isinstance(old, ast.AST):
18 + new = self.visit(old)
19 + else :
20 + new = old
21 + args[name] = new
22 + if hasattr(node, "st") :
23 + args["st"] = node.st
24 + return node.__class__(**args)
25 + def generic_visit (self, node) :
26 + return self.copy(node)
27 +
28 +class ArgsBinder (NodeCopier) :
29 + def __init__ (self, args, buffers, nets, tasks) :
30 + NodeCopier.__init__(self)
31 + self.args = args
32 + self.buffers = buffers
33 + self.nets = nets
34 + self.tasks = tasks
35 + def visit_Name (self, node) :
36 + if node.id in self.args :
37 + return self.copy(self.args[node.id])
38 + else :
39 + return self.copy(node)
40 + def visit_Instance (self, node) :
41 + if node.net in self.nets :
42 + return self.copy(node, net=self.nets[node.net])
43 + else :
44 + return self.copy(node)
45 + def _visit_access (self, node) :
46 + if node.buffer in self.buffers :
47 + return self.copy(node, buffer=self.buffers[node.buffer])
48 + else :
49 + return self.copy(node)
50 + def visit_SimpleAccess (self, node) :
51 + return self._visit_access(node)
52 + def visit_FlushAccess (self, node) :
53 + return self._visit_access(node)
54 + def visit_SwapAccess (self, node) :
55 + return self._visit_access(node)
56 + def _visit_task (self, node) :
57 + if node.net in self.tasks :
58 + return self.copy(node, net=self.tasks[node.net])
59 + else :
60 + return self.copy(node)
61 + def visit_Spawn (self, node) :
62 + return self._visit_task(node)
63 + def visit_Wait (self, node) :
64 + return self._visit_task(node)
65 + def visit_Suspend (self, node) :
66 + return self._visit_task(node)
67 + def visit_Resume (self, node) :
68 + return self._visit_task(node)
69 + def visit_AbcdNet (self, node) :
70 + args = self.args.copy()
71 + buffers = self.buffers.copy()
72 + nets = self.nets.copy()
73 + tasks = self.tasks.copy()
74 + netargs = ([a.arg for a in node.args.args + node.args.kwonlyargs]
75 + + [node.args.vararg, node.args.kwarg])
76 + copy = True
77 + for a in netargs :
78 + for d in (args, buffers, nets, tasks) :
79 + if a in d :
80 + del d[a]
81 + copy = False
82 + if copy :
83 + return self.copy(node)
84 + else :
85 + return self.__class__(args, buffers, nets, tasks).visit(node)
86 +
87 +if __name__ == "__main__" :
88 + import doctest
89 + doctest.testmod()
1 +import sys, os, os.path
2 +import inspect, fnmatch, collections
3 +import textwrap, doctest, ast
4 +import snakes
5 +from snakes.lang import unparse
6 +
7 +##
8 +## console messages
9 +##
10 +
11 +CLEAR = "\033[0m"
12 +BLUE = "\033[1;34m"
13 +BOLD = "\033[1;38m"
14 +GRAY = "\033[1;30m"
15 +LIGHTGRAY = "\033[0;30m"
16 +GREEN = "\033[1;32m"
17 +CYAN = "\033[1;36m"
18 +MAGENTA = "\033[1;35m"
19 +RED = "\033[1;31m"
20 +YELLOW = "\033[1;33m"
21 +
22 +def log (message, color=None, eol=True) :
23 + if color :
24 + sys.stderr.write(color)
25 + if isinstance(message, (list, tuple)) :
26 + message = " ".join(str(m) for m in message)
27 + sys.stderr.write(message)
28 + if color :
29 + sys.stderr.write(CLEAR)
30 + if eol :
31 + sys.stderr.write("\n")
32 + sys.stderr.flush()
33 +
34 +def debug (message, eol=True) :
35 + log("[debug] ", GRAY, eol=False)
36 + log(message, LIGHTGRAY, eol=eol)
37 +
38 +def info (message, eol=True) :
39 + log("[info] ", BLUE, False)
40 + log(message, eol=eol)
41 +
42 +def warn (message, eol=True) :
43 + log("[warn] ", YELLOW, False)
44 + log(message, eol=eol)
45 +
46 +def err (message, eol=True) :
47 + log("[error] ", RED, False)
48 + log(message, eol=eol)
49 +
50 +def die (message, code=1) :
51 + err(message)
52 + sys.exit(code)
53 +
54 +##
55 +## extract doc
56 +##
57 +
58 +class DocExtract (object) :
59 + def __init__ (self, inpath, outpath, exclude=[]) :
60 + self.path = inpath.rstrip(os.sep)
61 + self.outpath = outpath
62 + self.out = None
63 + self.exclude = exclude
64 + self._last = "\n\n"
65 + def openout (self, path) :
66 + if self.out is not None :
67 + self.out.close()
68 + relpath = path[len(os.path.dirname(self.path)):].strip(os.sep)
69 + parts = relpath.split(os.sep)
70 + relpath = os.path.dirname(relpath.split(os.sep, 1)[-1])
71 + self.package = (parts[-1] == "__init__.py")
72 + if self.package :
73 + self.module = ".".join(parts[:-1])
74 + target = "index.md"
75 + else :
76 + parts[-1] = os.path.splitext(parts[-1])[0]
77 + self.module = ".".join(parts)
78 + target = parts[-1] + ".md"
79 + if any(fnmatch.fnmatch(self.module, glob) for glob in self.exclude) :
80 + warn("skip %s" % self.module)
81 + return False
82 + outdir = os.path.join(self.outpath, relpath)
83 + outpath = os.path.join(outdir, target)
84 + info("%s -> %r" % (self.module, outpath))
85 + if not os.path.exists(outdir) :
86 + os.makedirs(outdir)
87 + self.out = open(outpath, "w")
88 + self.classname = None
89 + return True
90 + def write (self, text) :
91 + if len(text) > 1 :
92 + self._last = text[-2:]
93 + elif len(text) > 0 :
94 + self._last = self._last[-1] + text[-1]
95 + else :
96 + return
97 + self.out.write(text)
98 + def newline (self) :
99 + if self._last != "\n\n" :
100 + self.write("\n")
101 + def writeline (self, text="") :
102 + self.write(text.rstrip() + "\n")
103 + def writetext (self, text, **args) :
104 + br = args.pop("break_on_hyphens", False)
105 + for line in textwrap.wrap(text, break_on_hyphens=br, **args) :
106 + self.writeline(line)
107 + def writelist (self, text, bullet=" * ", **args) :
108 + br = args.pop("break_on_hyphens", False)
109 + for line in textwrap.wrap(text, break_on_hyphens=br,
110 + initial_indent=bullet,
111 + subsequent_indent=" "*len(bullet),
112 + **args) :
113 + self.writeline(line)
114 + def process (self) :
115 + for dirpath, dirnames, filenames in os.walk(self.path) :
116 + for name in sorted(filenames) :
117 + if not name.endswith(".py") :
118 + continue
119 + path = os.path.join(dirpath, name)
120 + if not self.openout(path) :
121 + continue
122 + node = ast.parse(open(path).read())
123 + if ".plugins." in self.module :
124 + self.visit_plugin(node)
125 + else :
126 + self.visit_module(node)
127 + def _pass (self, node) :
128 + pass
129 + def visit (self, node) :
130 + name = getattr(node, "name", "__")
131 + if (name.startswith("_") and not (name.startswith("__")
132 + and name.endswith("__"))) :
133 + return
134 + try :
135 + getattr(self, "visit_" + node.__class__.__name__, self._pass)(node)
136 + except :
137 + src = unparse(node)
138 + if len(src) > 40 :
139 + src = src[:40] + "..."
140 + err("line %s source %r" % (node.lineno, src))
141 + raise
142 + def visit_module (self, node) :
143 + doc = ast.get_docstring(node)
144 + self.write_module()
145 + for child in ast.iter_child_nodes(node) :
146 + self.visit(child)
147 + def visit_plugin (self, node) :
148 + doc = ast.get_docstring(node)
149 + self.write_module()
150 + extend = None
151 + for child in ast.iter_child_nodes(node) :
152 + if (getattr(child, "name", None) == "extend"
153 + and isinstance(child, ast.FunctionDef)) :
154 + extend = child
155 + else :
156 + self.visit(child)
157 + doc = ast.get_docstring(extend)
158 + self.write_plugin()
159 + for child in ast.iter_child_nodes(extend) :
160 + self.visit(child)
161 + def visit_ClassDef (self, node) :
162 + doc = ast.get_docstring(node)
163 + self.write_class(node)
164 + self.classname = node.name
165 + for child in ast.iter_child_nodes(node) :
166 + self.visit(child)
167 + self.classname = None
168 + def visit_FunctionDef (self, node) :
169 + self.write_function(node)
170 + self.args = [n.id for n in node.args.args]
171 + if self.args and self.args[0] == "self" :
172 + del self.args[0]
173 + if node.args.vararg :
174 + self.args.append(node.args.vararg)
175 + if node.args.kwarg :
176 + self.args.append(node.args.kwarg)
177 + self.visit(node.body[0])
178 + self.args = []
179 + def visit_Expr (self, node) :
180 + self.visit(node.value)
181 + def visit_Str (self, node) :
182 + self.write_doc(inspect.cleandoc(node.s))
183 + def write_module (self) :
184 + if self.package :
185 + self.writeline("# Package `%s` #" % self.module)
186 + else :
187 + self.writeline("# Module `%s` #" % self.module)
188 + self.newline()
189 + def write_plugin (self) :
190 + self.writeline("## Extensions ##")
191 + self.newline()
192 + def write_function (self, node) :
193 + self.newline()
194 + if self.classname :
195 + self.writeline("#### Method `%s.%s` ####"
196 + % (self.classname, node.name))
197 + else :
198 + self.writeline("### Function `%s` ###" % node.name)
199 + self.newline()
200 + self.writeline(" :::python")
201 + for line in unparse(node).splitlines() :
202 + if line.startswith("def") :
203 + self.writeline(" %s ..." % line)
204 + break
205 + else :
206 + self.writeline(" " + line)
207 + self.newline
208 + def write_class (self, node) :
209 + self.newline()
210 + self.writeline("### Class `%s` ###" % node.name)
211 + self.newline()
212 + self.writeline(" :::python")
213 + for line in unparse(node).splitlines() :
214 + if line.startswith("class") :
215 + self.writeline(" %s ..." % line)
216 + break
217 + else :
218 + self.writeline(" " + line)
219 + self.newline
220 + parse = doctest.DocTestParser().parse
221 + def write_doc (self, doc) :
222 + if doc is None :
223 + return
224 + docs = self.parse(doc)
225 + test, skip = False, False
226 + for doc in docs :
227 + if isinstance(doc, str) :
228 + doc = doc.strip()
229 + if test :
230 + if not doc :
231 + continue
232 + test, skip = False, False
233 + self.newline()
234 + lines = doc.strip().splitlines()
235 + for num, line in enumerate(lines) :
236 + if line.startswith("@") :
237 + self.write_epydoc("\n".join(lines[num:]))
238 + break
239 + self.writeline(line)
240 + elif not skip :
241 + if doc.source.strip() == "pass" :
242 + skip = True
243 + else :
244 + if not test :
245 + test = True
246 + self.newline()
247 + self.writeline(" :::python")
248 + for i, line in enumerate(doc.source.splitlines()) :
249 + if i > 0 :
250 + self.writeline(" ... %s" % line)
251 + else :
252 + self.writeline(" >>> %s" % line)
253 + for line in doc.want.splitlines() :
254 + self.writeline(" %s" % line)
255 + def write_epydoc (self, doc) :
256 + info = {"param" : {},
257 + "type" : {},
258 + "keyword" : {},
259 + "raise" : {},
260 + "todo" : [],
261 + "note" : [],
262 + "attention": [],
263 + "bug" : [],
264 + "warning" : [],
265 + }
266 + for item in doc.lstrip("@").split("\n@") :
267 + left, text = item.split(":", 1)
268 + left = left.split()
269 + assert 1 <= len(left) <= 2, "unsupported item %r" % item
270 + if len(left) == 1 :
271 + left.append(None)
272 + tag, name = [x.strip() if x else x for x in left]
273 + text = " ".join(text.strip().split())
274 + if isinstance(info.get(tag, None), list) :
275 + assert name is None, "unsupported item %r" % item
276 + info[tag].append(text)
277 + elif isinstance(info.get(tag, None), dict) :
278 + assert name is not None, "unsupported item %r" % item
279 + assert name not in info[tag], "duplicated item %r" % item
280 + info[tag][name] = text
281 + else :
282 + assert name is None, "unsupported item %r" % item
283 + assert tag not in info, "duplicated tag %r" % item
284 + info[tag] = text
285 + if any(k in info for k in ("author", "organization", "copyright",
286 + "license", "contact")) :
287 + self.newline()
288 + self.writeline('<div class="api-info">')
289 + for tag in ("author", "organization", "copyright",
290 + "license", "contact") :
291 + if tag in info :
292 + self.writeline('<div class="api-%s">' % tag)
293 + self.writetext('<span class="api-title">%s:</span> %s'
294 + % (tag.capitalize(), info[tag]),
295 + subsequent_indent=" ")
296 + self.writeline('</div>')
297 + self.writeline('</div>')
298 + if any(info[k] for k in
299 + ("todo", "note", "attention", "bug", "warning")) :
300 + self.newline()
301 + self.writeline('<div class="api-remarks">')
302 + self.writeline("##### Remarks #####")
303 + self.newline()
304 + for tag in ("note", "todo", "attention", "bug", "warning") :
305 + for text in info[tag] :
306 + self.writeline('<div class="api-%s">' % tag)
307 + self.writetext('<span class="api-title">%s:</span> %s'
308 + % (tag.capitalize(), text),
309 + subsequent_indent=" ")
310 + self.writeline('</div>')
311 + self.writeline('</div>')
312 + if (any(info[k] for k in ("param", "type", "keyword"))
313 + or any(k in info for k in ("return", "rtype"))) :
314 + self.newline()
315 + self.writeline('<div class="api-call">')
316 + self.writeline("##### Call API #####")
317 + self.newline()
318 + for arg in self.args :
319 + if arg in info["param"] :
320 + self.writelist("`%s` (%s): %s"
321 + % (arg,
322 + info["type"].get(arg, "`object`"),
323 + info["param"][arg]))
324 + else :
325 + self.writelist("`%s` (%s)"
326 + % (arg,
327 + info["type"].get(arg, "`object`")))
328 + for kw, text in sorted(info["keyword"].items()) :
329 + self.writelist("`%s`: %s" % (kw, text))
330 + if any(k in info for k in ("return", "rtype")) :
331 + if "return" in info :
332 + self.writelist("return %s: %s"
333 + % (info.get("rtype", "`object`"),
334 + info["return"]))
335 + else :
336 + self.writelist("return %s"
337 + % (info.get("rtype", "`object`")))
338 + self.writeline('</div>')
339 + if info["raise"] :
340 + self.newline()
341 + self.writeline('<div class="api-errors">')
342 + self.writeline("##### Exceptions #####")
343 + self.newline()
344 + for exc, reason in sorted(info["raise"].items()) :
345 + self.writelist("`%s`: %s" % (exc, reason))
346 + self.writeline('</div>')
347 +
348 +def main (finder, args) :
349 + try :
350 + source = os.path.dirname(snakes.__file__)
351 + target = args[0]
352 + exclude = args[1:]
353 + if not os.path.isdir(source) :
354 + raise Exception("could not find SNAKES sources")
355 + elif not os.path.isdir(target) :
356 + raise Exception("no directory %r" % target)
357 + except ValueError :
358 + die("""Usage: python %s TARGET [EXCLUDE...]
359 + TARGET target directory to write files
360 + EXCLUDE pattern to exclude modules (not file names)
361 + """ % __file__)
362 + except Exception as error :
363 + die(str(error))
364 + finder(source, target, exclude).process()
365 +
366 +if __name__ == "__main__" :
367 + main(DocExtract, sys.argv[1:])
1 +from snakes.lang.ctlstar.parser import parse
2 +from snakes.lang.ctlstar import asdl as ast
3 +from snakes.lang import getvars, bind
4 +import _ast
5 +
6 +class SpecError (Exception) :
7 + def __init__ (self, node, reason) :
8 + Exception.__init__(self, "[line %s] %s" % (node.lineno, reason))
9 +
10 +def astcopy (node) :
11 + if not isinstance(node, _ast.AST) :
12 + return node
13 + attr = {}
14 + for name in node._fields + node._attributes :
15 + value = getattr(node, name)
16 + if isinstance(value, list) :
17 + attr[name] = [astcopy(child) for child in value]
18 + else :
19 + attr[name] = astcopy(value)
20 + return node.__class__(**attr)
21 +
22 +class Builder (object) :
23 + def __init__ (self, spec) :
24 + self.spec = spec
25 + self.decl = {}
26 + for node in spec.atoms + spec.properties :
27 + if node.name in self.decl :
28 + raise SpecError(node, "%r already declare line %s"
29 + % (name.name, self.decl[name.name].lineno))
30 + self.decl[node.name] = node
31 + self.main = spec.main
32 + def build (self, node) :
33 + node = astcopy(node)
34 + return self._build(node, {})
35 + def _build (self, node, ctx) :
36 + if isinstance(node, ast.atom) :
37 + try :
38 + builder = getattr(self, "_build_%s" % node.__class__.__name__)
39 + except AttributeError :
40 + node.atomic = True
41 + else :
42 + node = builder(node, ctx)
43 + node.atomic = True
44 + elif isinstance(node, ast.CtlBinary) :
45 + node.left = self._build(node.left, ctx)
46 + node.right = self._build(node.right, ctx)
47 + node.atomic = (isinstance(node.op, (ast.boolop, ast.Imply,
48 + ast.Iff))
49 + and node.left.atomic
50 + and node.right.atomic)
51 + elif isinstance(node, ast.CtlUnary) :
52 + node.child = self._build(node.child, ctx)
53 + node.atomic = (isinstance(node.op, ast.Not)
54 + and node.child.atomic)
55 + else :
56 + assert False, "how did we get there?"
57 + return node
58 + def _build_place (self, param, ctx) :
59 + if isinstance(param, ast.Parameter) :
60 + if param.name not in ctx :
61 + raise SpecError(param, "place %r should be instantiated"
62 + % param.name)
63 + return ctx[param.name]
64 + else :
65 + return param
66 + def _build_InPlace (self, node, ctx) :
67 + node.data = [bind(child, ctx) for child in node.data]
68 + node.place = self._build_place(node.place, ctx)
69 + return node
70 + def _build_NotInPlace (self, node, ctx) :
71 + return self._build_InPlace(node, ctx)
72 + def _build_EmptyPlace (self, node, ctx) :
73 + node.place = self._build_place(node.place, ctx)
74 + return node
75 + def _build_MarkedPlace (self, node, ctx) :
76 + return self._build_EmptyPlace(node, ctx)
77 + # skip Deadlock and Boolean: nothing to do
78 + def _build_Quantifier (self, node, ctx) :
79 + node.place = self._build_place(node.place, ctx)
80 + ctx = ctx.copy()
81 + for name in node.vars :
82 + ctx[name] = ast.Token(name, node.place.place)
83 + node.child = self._build(node.child, ctx)
84 + return node
85 + def _build_Instance (self, node, ctx) :
86 + if node.name not in self.decl :
87 + raise SpecError(node, "undeclared object %r" % node.name)
88 + ctx = ctx.copy()
89 + decl = self.decl[node.name]
90 + for arg in decl.args :
91 + ctx[arg.name] = arg
92 + if isinstance(decl, ast.Property) :
93 + return self._build_Instance_Property(node, decl, ctx)
94 + else :
95 + return self._build_Instance_Atom(node, decl, ctx)
96 + def _build_Instance_Property (self, node, prop, ctx) :
97 + bound = set(a.name for a in prop.args)
98 + args = dict((a.arg, a.annotation) for a in node.args)
99 + for param in prop.params :
100 + if param.name in bound :
101 + raise SpecError(node, "argument %r already bound"
102 + % param.name)
103 + elif param.name in args :
104 + arg = args.pop(param.name)
105 + bound.add(param.name)
106 + else :
107 + raise SpecError(node, "missing argument %r" % param.name)
108 + if param.type == "place" :
109 + if not isinstance(arg, ast.Place) :
110 + raise SpecError(node, "expected place for %r"
111 + % param.name)
112 + arg.name = param.name
113 + ctx[param.name] = arg
114 + if args :
115 + raise SpecError(node, "too many arguments (%s)"
116 + % ", ".join(repr(a) for a in args))
117 + return self._build(astcopy(prop.body), ctx)
118 + def _build_Instance_Atom (self, node, atom, ctx) :
119 + bound = set(a.name for a in atom.args)
120 + args = dict((a.arg, a.annotation) for a in node.args)
121 + new = astcopy(atom)
122 + for param in atom.params :
123 + if param.name in bound :
124 + raise SpecError(node, "argument %r already bound"
125 + % param.name)
126 + elif param.name in args :
127 + arg = args.pop(param.name)
128 + bound.add(param.name)
129 + else :
130 + raise SpecError(node, "missing argument %r" % param.name)
131 + if param.type == "place" :
132 + if not isinstance(arg, ast.Place) :
133 + raise SpecError(node, "expected place for %r"
134 + % param.name)
135 + arg.name = param.name
136 + else :
137 + arg = ast.Argument(name=param.name,
138 + value=arg,
139 + type=param.type)
140 + new.args.append(arg)
141 + if args :
142 + raise SpecError(node, "too many arguments (%s)"
143 + % ", ".join(repr(a) for a in args))
144 + del new.params[:]
145 + return new
146 +
147 +def build (spec, main=None) :
148 + b = Builder(spec)
149 + if main is None :
150 + return b.build(spec.main)
151 + else :
152 + return b.build(main)
1 +#!/bin/sh
2 +exec python bin/abcd --dot ,railroad.png \
3 + --pnml ,railroad.pnml \
4 + doc/examples/abcd/railroad.abcd
...\ No newline at end of file ...\ No newline at end of file
1 +import doctest, sys, os, glob
2 +
3 +retcode = 0
4 +
5 +import snakes
6 +version = open("VERSION").read().strip()
7 +if snakes.version != version :
8 + print("Mismatched versions:")
9 + print(" snakes.version = %r" % snakes.version)
10 + print(" VERSION = %r" % version)
11 + sys.exit(1)
12 +
13 +def test (module) :
14 + print(" Testing '%s'" % module.__name__)
15 + f, t = doctest.testmod(module, #verbose=True,
16 + optionflags=doctest.NORMALIZE_WHITESPACE
17 + | doctest.REPORT_ONLY_FIRST_FAILURE
18 + | doctest.ELLIPSIS)
19 + return f
20 +
21 +modules = ["snakes",
22 + "snakes.hashables",
23 + "snakes.lang",
24 + "snakes.lang.python.parser",
25 + "snakes.lang.abcd.parser",
26 + "snakes.lang.ctlstar.parser",
27 + "snakes.data",
28 + "snakes.typing",
29 + "snakes.nets",
30 + "snakes.pnml",
31 + "snakes.plugins",
32 + "snakes.plugins.pos",
33 + "snakes.plugins.status",
34 + "snakes.plugins.ops",
35 + "snakes.plugins.synchro",
36 + "snakes.plugins.hello",
37 + "snakes.plugins.gv",
38 + "snakes.plugins.clusters",
39 + "snakes.plugins.labels",
40 + "snakes.utils.abcd.build",
41 + ]
42 +
43 +stop = False
44 +if len(sys.argv) > 1 :
45 + if sys.argv[1] == "--stop" :
46 + stop = True
47 + del sys.argv[1]
48 +
49 +doscripts = True
50 +if len(sys.argv) > 1 :
51 + modules = sys.argv[1:]
52 + doscripts = False
53 +
54 +for modname in modules :
55 + try :
56 + __import__(modname)
57 + retcode = max(retcode, test(sys.modules[modname]))
58 + if retcode and stop :
59 + break
60 + except :
61 + print(" Could not test %r:" % modname)
62 + c, e, t = sys.exc_info()
63 + print(" %s: %s" % (c.__name__, e))
64 +
65 +if doscripts :
66 + for script in (glob.glob("test-scripts/test*.sh")
67 + + glob.glob("test-scripts/test*.py")) :
68 + print(" Running '%s'" % script)
69 + retcode = max(retcode, os.system(script))
70 + if retcode and stop :
71 + break
72 +
73 +sys.exit(retcode)
1 +(provide 'abcd-mode)
2 +
3 +(define-derived-mode abcd-mode python-mode "ABCD"
4 + (font-lock-add-keywords
5 + nil
6 + `((,(concat "\\<\\(buffer\\|typedef\\|net\\|enum\\|task\\|const\\|symbol\\)\\>")
7 + 1 font-lock-keyword-face t))))
1 +"""Draws a dependency graph of non-terminals in a pgen grammar
2 +(pygraphviz required).
3 +
4 +Usage: python pgen2dot.py INFILE OUTFILE [ENGINE [OPTION=VAL]...]
5 +"""
6 +
7 +import sys, string, os.path
8 +import pygraphviz as gv
9 +import snakes.lang.pgen as pgen
10 +
11 +if len(sys.argv) < 3 :
12 + print("Usage: python pgen2dot.py INFILE OUTFILE [ENGINE [OPTION=VAL]...]")
13 + sys.exit(1)
14 +elif len(sys.argv) >= 4 :
15 + engine = sys.argv[3]
16 +else :
17 + engine = "dot"
18 +
19 +nodes = set()
20 +edges = set()
21 +
22 +def walk (st, lex, rule=None) :
23 + tok, children = st
24 + if tok == pgen.PgenParser.RULE :
25 + rule = children[0][0]
26 + nodes.add(rule)
27 + for child in children[1:] :
28 + walk(child, lex, rule)
29 + elif isinstance(tok, str) and tok.strip() and tok[0] in string.ascii_lowercase :
30 + nodes.add(tok)
31 + if rule is not None :
32 + edges.add((rule, tok))
33 + else :
34 + for child in children :
35 + walk(child, lex, rule)
36 +
37 +st, lex = pgen.PgenParser.parse(sys.argv[1])
38 +walk(st, lex)
39 +
40 +g = gv.AGraph(directed=True)
41 +g.add_edges_from(edges)
42 +g.graph_attr["overlap"] = "false"
43 +g.graph_attr["splines"] = "true"
44 +for arg in sys.argv[4:] :
45 + key, val = arg.split("=", 1)
46 + g.graph_attr[key] = val
47 +g.draw(sys.argv[2], prog=engine)
1 +"""Lists unreachable non-terminals in a pgen grammar
2 +
3 +Usage: python pgen2min.py INFILE
4 +"""
5 +
6 +import sys, string, os.path
7 +import snakes.lang.pgen as pgen
8 +import collections
9 +
10 +if len(sys.argv) < 2 :
11 + print("Usage: python pgen2dot.py INFILE")
12 + sys.exit(1)
13 +
14 +root = None
15 +nodes = set()
16 +edges = collections.defaultdict(set)
17 +
18 +def walk (st, lex, rule=None) :
19 + global root
20 + tok, children = st
21 + if tok == pgen.PgenParser.RULE :
22 + rule = children[0][0]
23 + if root is None :
24 + root = rule
25 + nodes.add(rule)
26 + for child in children[1:] :
27 + walk(child, lex, rule)
28 + elif isinstance(tok, str) and tok.strip() and tok[0] in string.ascii_lowercase :
29 + nodes.add(tok)
30 + if rule is not None :
31 + edges[rule].add(tok)
32 + else :
33 + for child in children :
34 + walk(child, lex, rule)
35 +
36 +st, lex = pgen.PgenParser.parse(sys.argv[1])
37 +walk(st, lex)
38 +
39 +reached = set()
40 +next = set([root])
41 +while not next.issubset(reached) :
42 + reached.update(next)
43 + prev = next
44 + next = set()
45 + for n in prev :
46 + next.update(edges[n])
47 +
48 +for node in sorted(nodes - reached) :
49 + print(node)