[PATCH 13/18] modifies 3rd party code - promela parser

Andrew.Butterfield at scss.tcd.ie Andrew.Butterfield at scss.tcd.ie
Thu Dec 22 11:46:57 UTC 2022



---
 .../src/src/modules/promela_yacc/.gitignore   |  20 -
 .../src/src/modules/promela_yacc/.travis.yml  |  21 -
 .../src/src/modules/promela_yacc/LICENSE      |   1 +
 .../src/src/modules/promela_yacc/MANIFEST.in  |   4 -
 .../src/src/modules/promela_yacc/doc.md       | 100 ---
 .../src/modules/promela_yacc/promela/ast.py   | 120 ++-
 .../src/modules/promela_yacc/promela/lex.py   |  18 +-
 .../src/modules/promela_yacc/promela/yacc.py  | 207 +++--
 .../modules/promela_yacc/tests/yacc_test.py   | 759 ------------------
 9 files changed, 228 insertions(+), 1022 deletions(-)
 delete mode 100644 formal/promela/src/src/modules/promela_yacc/.gitignore
 delete mode 100644 formal/promela/src/src/modules/promela_yacc/.travis.yml
 delete mode 100644 formal/promela/src/src/modules/promela_yacc/MANIFEST.in
 delete mode 100644 formal/promela/src/src/modules/promela_yacc/doc.md
 delete mode 100644 formal/promela/src/src/modules/promela_yacc/tests/yacc_test.py

diff --git a/formal/promela/src/src/modules/promela_yacc/.gitignore b/formal/promela/src/src/modules/promela_yacc/.gitignore
deleted file mode 100644
index f4cd3f85..00000000
--- a/formal/promela/src/src/modules/promela_yacc/.gitignore
+++ /dev/null
@@ -1,20 +0,0 @@
-build/*
-dist/*
-promela/_version.py
-*parsetab.py
-.coverage
-.DS_Store
-Icon?
-
-*.*~
-__pycache__
-*.pyc
-*.swp
-*.pdf
-*.PDF
-*.txt
-*.log
-*.egg-info
-*.html
-*.css
-
diff --git a/formal/promela/src/src/modules/promela_yacc/.travis.yml b/formal/promela/src/src/modules/promela_yacc/.travis.yml
deleted file mode 100644
index 2fee40c9..00000000
--- a/formal/promela/src/src/modules/promela_yacc/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: python
-
-python:
-  - 2.7
-  - 3.4
-  - 3.5
-  - 3.6
-
-install:
-  - sudo apt-get update -qq
-  - sudo apt-get install -y graphviz
-  - pip install --upgrade pip setuptools
-  - pip install -r requirements.txt
-  - pip install .
-
-script:
-  - cd tests/
-  - nosetests --with-coverage --cover-package=promela
-
-after_success:
-  - coveralls
diff --git a/formal/promela/src/src/modules/promela_yacc/LICENSE b/formal/promela/src/src/modules/promela_yacc/LICENSE
index bebe3694..40f0a792 100644
--- a/formal/promela/src/src/modules/promela_yacc/LICENSE
+++ b/formal/promela/src/src/modules/promela_yacc/LICENSE
@@ -1,3 +1,4 @@
+Copyright (c) 2019-2021 by Trinity College Dublin, Ireland
 Copyright (c) 2014-2018 by California Institute of Technology
 Copyright (c) 2014-2018 by Ioannis Filippidis
 All rights reserved.
diff --git a/formal/promela/src/src/modules/promela_yacc/MANIFEST.in b/formal/promela/src/src/modules/promela_yacc/MANIFEST.in
deleted file mode 100644
index bbacf5bf..00000000
--- a/formal/promela/src/src/modules/promela_yacc/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-include tests/*.py
-include README.md
-include LICENSE
-include doc.md
diff --git a/formal/promela/src/src/modules/promela_yacc/doc.md b/formal/promela/src/src/modules/promela_yacc/doc.md
deleted file mode 100644
index 02d73431..00000000
--- a/formal/promela/src/src/modules/promela_yacc/doc.md
+++ /dev/null
@@ -1,100 +0,0 @@
-This package provides a lexer, parser, and abstract syntax tree (AST) for the [Promela](http://en.wikipedia.org/wiki/Promela) modeling language.
-The lexer and parser are generated using [PLY](https://pypi.python.org/pypi/ply/3.4) (Python `lex`-`yacc`).
-The [grammar](http://spinroot.com/spin/Man/grammar.html) is based on that used in the [SPIN](http://spinroot.com/spin/whatispin.html) model checker (in the files `spin.y` and `spinlex.c` of SPIN's source distribution), with modifications where needed.
-
-To instantiate a Promela parser:
-
-```python
-from promela.yacc import Parser
-parser = Parser()
-```
-
-Then Promela code, as a string, can be parsed by:
-
-```python
-s = '''
-active proctype foo(){
-	int x;
-	do
-	:: x = x + 1;
-	od
-}
-'''
-program = parser.parse(s)
-```
-
-then
-
-```python
->>> print(program)
-active [1]  proctype foo(){
-	int x
-	do
-	:: x = (x + 1)
-	od;
-}
-```
-
-The parser returns the result as an abstract syntax tree using classes from the `promela.ast` module.
-The top production rule returns a `Program` instance, which itself is a `list`.
-The contents of this list
-
-There are two categories of AST classes: those that represent control flow constructs:
-
-- `Proctype`, (`Init`, `NeverClaim`), `Node`, (`Expression`, `Assignment`, `Assert`, `Options` (if, do), `Else`, `Break`, `Goto`, `Label`, `Call`, `Return`, `Run`), `Sequence`
-
-and those that represent only syntax inside an expression:
-
-- `Terminal`, (`VarRef`, `Integer`, `Bool`), `Operator`, (`Binary`, `Unary`)
-
-The classes in parentheses are subclasses of the last class preceding the parentheses.
-Each control flow class has a method `to_pg` that recursively converts the abstract syntax tree to a program graph.
-
-A program graph is a directed graph whose edges are labeled with statements from the program.
-Nodes represent states of the program.
-Note the difference with a control flow graph, whose nodes are program statements and edges are program states.
-AST node classes correspond to nodes of the control flow graph and edges of the program graph (possibly with branching).
-
-For some node classes like `Expression` and `Assignment`, the `to_pg` method returns themselves, a.
-Almost all statements are represented as either an `Expression` or an `Assignment`.
-These label edges in the program graph, using the edge attribute `"stmt"`.
-
-The program graph is represented as a [multi-digraph](http://en.wikipedia.org/wiki/Multigraph) using [`networkx.MultiDiGraph`](https://networkx.github.io/documentation/latest/reference/classes.multidigraph.html).
-A multi-digraph is necessary, because two nodes in the program graph may be connected by two edges, each edge labeled with a different statement.
-For example, this is the case in the code fragment:
-
-```promela
-bit x;
-do
-:: x == 0
-:: x == 1
-od
-```
-
-The above defines a program graph with a single node and two self-loops, one labeled with the statement `x == 0` and another with the statement `x == 1`.
-These two statements here are guards, so they only determine whether the edge can be traversed, without affecting the program's data state (variable values).
-
-Program graph nodes are labeled with a `"context"` attribute that can take the values:
-- `"atomic"`
-- `"d_step"`
-- `None`
-The values `"atomic"` and `"d_step"` signify that the state is inside an atomic or deterministic step block.
-
-Continuing our earlier example:
-
-```python
->>> g = program[0].to_pg()
->>> g.nodes(data=True)
-[(0, {'context': None}), (1, {'context': None})]
->>> g.edges(data=True)
-[(0, 1, {'stmt': Assignment(
-    VarRef('x', None, None),
-    Expression(
-        Operator('+',
-            VarRef('x', None, None),
-            Integer('1'))))}),
- (1, 0, {'stmt': Expression(
-    Operator('==',
-        VarRef('x', None, None),
-        Integer('2')))})]
-```
diff --git a/formal/promela/src/src/modules/promela_yacc/promela/ast.py b/formal/promela/src/src/modules/promela_yacc/promela/ast.py
index 16baaa05..77db1fe9 100644
--- a/formal/promela/src/src/modules/promela_yacc/promela/ast.py
+++ b/formal/promela/src/src/modules/promela_yacc/promela/ast.py
@@ -48,19 +48,20 @@ def to_str(x):
 class Proctype(object):
     def __init__(self, name, body, args=None,
                  active=None, d_proc=False,
-                 priority=None, provided=None):
+                 priority=None, provided=None,
+                 disable_negation=False):
         self.name = name
         self.body = body
         self.args = args
         if active is None:
             active = 0
-        else:
-            active = int(active.value)
+        self.d_proc = d_proc
         if priority is not None:
             priority = int(priority.value)
         self.active = active
         self.priority = priority
         self.provided = provided
+        self.disable_negation = disable_negation
 
     def __str__(self):
         return "Proctype('{name}')".format(name=self.name)
@@ -86,7 +87,7 @@ class Proctype(object):
         if self.args is None:
             args = ''
         else:
-            args = to_str(self.args)
+            args = '; '.join(to_str(xx) for x in self.args for xx in x)
         return args
 
     def to_pg(self, syntactic_else=False):
@@ -223,7 +224,7 @@ class Init(Proctype):
 
 class Program(list):
     def __str__(self):
-        return '\n'.join(to_str(x) for x in self)
+        return '\n'.join(to_str(x) for x, _ in self)
 
     def __repr__(self):
         c = super(Program, self).__repr__()
@@ -245,21 +246,25 @@ class Program(list):
 class LTL(object):
     """Used to mark strings as LTL blocks."""
 
-    def __init__(self, formula):
+    def __init__(self, formula, name = None):
         self.formula = formula
+        self.name = name
 
     def __repr__(self):
         return 'LTL({f})'.format(f=repr(self.formula))
 
     def __str__(self):
-        return 'ltl {' + str(self.formula) + '}'
+        return 'ltl ' + (self.name + ' ' if self.name else '') + '{' + str(self.formula) + '}'
 
 
 class Sequence(list):
-    def __init__(self, iterable, context=None, is_option=False):
+    def __init__(self, iterable, context=None, context_for_var=None, context_for_begin=None, context_for_end=None, is_option=False):
         super(Sequence, self).__init__(iterable)
-        # "atomic" or "dstep"
+        # "for" or "atomic" or "dstep"
         self.context = context
+        self.context_for_var = context_for_var
+        self.context_for_begin = context_for_begin
+        self.context_for_end = context_for_end
         self.is_option = is_option
 
     def to_str(self):
@@ -267,20 +272,22 @@ class Sequence(list):
             return '\n'.join(to_str(x) for x in self)
         else:
             return (
-                self.context + '{\n' +
-                _indent(to_str(self)) + '\n}\n')
+                self.context +
+                (' (' + self.context_for_var + ' : ' + to_str (self.context_for_begin) + ' .. ' + to_str (self.context_for_end) + ') ' if self.context == 'for' else '') +
+                '{\n' +
+                '\n'.join(_indent(to_str(x)) for x in self) + '\n}\n')
 
     def __repr__(self):
         l = super(Sequence, self).__repr__()
-        return 'Sequence({l}, context={c}, is_option={isopt})'.format(
-            l=l, c=self.context, isopt=self.is_option)
+        return 'Sequence({l}, context={c}, context_for_var={cfv}, context_for_begin={cfb}, context_for_end={cfe}, is_option={isopt})'.format(
+            l=l, c=self.context, cfv=self.context_for_var, cfb=self.context_for_begin, cfe=self.context_for_end, isopt=self.is_option)
 
     def to_pg(self, g, context=None, option_guard=None, **kw):
         # set context
         if context is None:
             context = self.context
         c = context
-        assert c in {'atomic', 'd_step', None}
+        assert c in {'atomic', 'd_step', None} # TODO: 'for'
         # atomic cannot appear inside d_step
         if context == 'd_step' and c == 'atomic':
             raise Exception('atomic inside d_step')
@@ -541,12 +548,13 @@ class VarDef(Node):
             assert isinstance(l, int), l
         self.length = l
         self.visible = visible
-        if bitwidth is not None:
-            self.bitwidth = int(bitwidth.value)
+        self.bitwidth = int(bitwidth.value) if bitwidth else None
         if vartype == 'bool':
             default_initval = Bool('false')
         else:
             default_initval = Integer('0')
+        self.msg_types = msg_types
+        self.initial_value0 = initval
         if initval is None:
             initval = Expression(default_initval)
         self.initial_value = initval
@@ -556,10 +564,13 @@ class VarDef(Node):
         return 'VarDef({t}, {v})'.format(t=self.type, v=self.name)
 
     def to_str(self):
-        s = '{type} {varname}{len}'.format(
+        s = '{type} {varname}{bitwidth}{len}{initval}{msg_types}'.format(
             type=self._type_str(),
             varname=self.name,
-            len='[{n}]'.format(n=self.len) if self.len else '')
+            bitwidth=' : {n}'.format(n =self.bitwidth) if self.bitwidth else '',
+            len=' [{n}]'.format(n=self.length) if self.length and not self.msg_types else '',
+            initval=' = {i}'.format(i=self.initial_value0) if self.initial_value0 else '',
+            msg_types=' = [{l}] of {{ {m} }}'.format(l=self.length, m=' , '.join(to_str(x) for x in self.msg_types)) if self.msg_types else '')
         return s
 
     def _type_str(self):
@@ -741,8 +752,8 @@ class TypeDef(Node):
         self.decls = decls
 
     def __str__(self):
-        return 'typedef {name} {\ndecls\n}'.format(
-            name=self.name, decls=to_str(self.decls))
+        return 'typedef {name} {{ {decls} }}'.format(
+            name=self.name, decls='; '.join(to_str(x) for x in self.decls))
 
     def exe(self, t):
         t.types[self.name] = self
@@ -752,8 +763,8 @@ class MessageType(Node):
     def __init__(self, values, visible=None):
         self.values = values
 
-    def __str__(self):
-        return 'mtype {{ {values} }}'.format(values=self.values)
+    def to_str(self):
+        return 'mtype = {{ {values} }}'.format(values=' , '.join(to_str(x) for x in self.values))
 
     def exe(self, t):
         t.types[self.name] = self
@@ -774,27 +785,40 @@ class Run(Node):
         self.priority = priority
 
     def __str__(self):
-        return 'run({f})'.format(f=self.func)
+        return 'run {f} ({args})'.format(f=self.func, args='' if self.args is None else ' , '.join(to_str(x) for x in self.args))
 
 
-class Inline(Node):
-    def __init__(self, name, args):
+class InlineDef(Node):
+    def __init__(self, name, decl, body, disable_negation=False):
         self.name = name
-        self.args = args
+        self.decl = decl
+        self.body = body
+        self.disable_negation = disable_negation
 
+    def __str__(self):
+        return ('inline {name} ({decl}) {{\n'
+                '{body}\n'
+                '}}\n\n').format(
+                    name = self.name,
+                    decl = ', '.join(to_str(x) for x in self.decl) if self.decl else '',
+                    body = _indent(to_str(self.body)))
 
 class Call(Node):
-    def __init__(self, func, args):
-        self.func = func
+    def __init__(self, name, args):
+        self.name = name
         self.args = args
 
+    def __str__(self):
+        return '{name} ({args})'.format(name = self.name, args = ', '.join(to_str(x) for x in self.args) if self.args else '')
+
 
 class Assert(Node):
-    def __init__(self, expr):
+    def __init__(self, expr, pos = None):
         self.expr = expr
+        self.pos = pos
 
-    def __repr__(self):
-        return 'assert({expr})'.format(expr=repr(self.expr))
+    def __str__(self):
+        return 'assert({expr})'.format(expr=to_str(self.expr))
 
 
 class Expression(Node):
@@ -872,32 +896,32 @@ class Assignment(Node):
 
 
 class Receive(Node):
-    def __init__(self, varref, args=None):
+    def __init__(self, varref, args):
         self.var = varref
         self.args = args
 
     def __str__(self):
         v = to_str(self.var)
-        return 'Rx({v})'.format(v=v)
+        return '{v} ? {args}'.format(v=v, args = ' , '.join(to_str(x) for x in self.args))
 
 
 class Send(Node):
-    def __init__(self, varref, args=None):
-        self.varref = varref
+    def __init__(self, varref, args):
+        self.var = varref
         self.args = args
 
     def __str__(self):
         v = to_str(self.var)
-        return 'Tx({v})'.format(v=v)
+        return '{v} ! {args}'.format(v=v, args = ' , '.join(to_str(x) for x in self.args))
 
 
 class Printf(Node):
-    def __init__(self, s, args):
+    def __init__(self, s, args=None):
         self.s = s
         self.args = args
 
     def __str__(self):
-        return 'printf()'.format(s=self.s, args=self.args)
+        return 'printf({s}{args})'.format(s=self.s, args='' if self.args is None else ' , ' + ' , '.join(to_str(x) for x in self.args))
 
 
 class Operator(object):
@@ -927,6 +951,14 @@ class Binary(Operator):
 class Unary(Operator):
     pass
 
+class ReceiveExpr(Node):
+    def __init__(self, varref, args):
+        self.var = varref
+        self.args = args
+
+    def __str__(self):
+        v = to_str(self.var)
+        return '({v} ? [{args}])'.format(v=v, args = ' , '.join(to_str(x) for x in self.args))
 
 class Terminal(object):
     def __init__(self, value):
@@ -967,7 +999,7 @@ class VarRef(Terminal):
         return '{name}{index}{ext}'.format(
             name=self.name,
             index=i,
-            ext='' if self.extension is None else self.extension)
+            ext=('.' + to_str(self.extension)) if self.extension else '' )
 
 
 class Integer(Terminal):
@@ -986,7 +1018,7 @@ class Bool(Terminal):
         return 'Bool({value})'.format(value=repr(self.value))
 
     def __str__(self):
-        return str(self.value)
+        return str('true' if self.value else 'false')
 
 
 class RemoteRef(Terminal):
@@ -1008,6 +1040,14 @@ class RemoteRef(Terminal):
             proc=self.proctype, inst=inst, label=self.label)
 
 
+class Timeout(Node):
+    def __init__(self):
+        return
+
+    def __str__(self):
+        return 'timeout'
+
+
 def dump_graph(g, fname='a.pdf', node_label='label',
                edge_label='label', relabel=False):
     """Write the program graph, annotated with formulae, to PDF file."""
diff --git a/formal/promela/src/src/modules/promela_yacc/promela/lex.py b/formal/promela/src/src/modules/promela_yacc/promela/lex.py
index cd3eafe0..e31a51c0 100644
--- a/formal/promela/src/src/modules/promela_yacc/promela/lex.py
+++ b/formal/promela/src/src/modules/promela_yacc/promela/lex.py
@@ -1,7 +1,7 @@
 """Lexer for Promela, using Python Lex-Yacc (PLY)."""
 import logging
 import ply.lex
-
+import re
 
 logger = logging.getLogger(__name__)
 
@@ -30,12 +30,14 @@ class Lexer(object):
         'enabled': 'ENABLED',
         'eval': 'EVAL',
         'fi': 'FI',
+        'for': 'FOR',
         'full': 'FULL',
         'get_priority': 'GET_P',
         'goto': 'GOTO',
         'hidden': 'HIDDEN',
         'if': 'IF',
         'init': 'INIT',
+        'inline': 'INLINE',
         'int': 'INT',
         'len': 'LEN',
         'local': 'ISLOCAL',
@@ -56,7 +58,7 @@ class Lexer(object):
         'return': 'RETURN',
         'run': 'RUN',
         'short': 'SHORT',
-        'skip': 'TRUE',
+        'skip': 'SKIP',
         'show': 'SHOW',
         'timeout': 'TIMEOUT',
         'typedef': 'TYPEDEF',
@@ -67,9 +69,9 @@ class Lexer(object):
         'xr': 'XR',
         'xs': 'XS',
         'W': 'WEAK_UNTIL'}
-    values = {'skip': 'true'}
+    values = {'': ''}
     delimiters = ['LPAREN', 'RPAREN', 'LBRACKET', 'RBRACKET',
-                  'LBRACE', 'RBRACE', 'COMMA', 'PERIOD',
+                  'LBRACE', 'RBRACE', 'COMMA', 'PERIODS', 'PERIOD',
                   'SEMI', 'COLONS', 'COLON', 'ELLIPSIS']
     # remember to check precedence
     operators = ['PLUS', 'INCR', 'MINUS', 'DECR', 'TIMES', 'DIVIDE',
@@ -159,6 +161,7 @@ class Lexer(object):
     t_LBRACE = r'\{'
     t_RBRACE = r'\}'
     t_COMMA = r','
+    t_PERIODS = r'\.\.'
     t_PERIOD = r'\.'
     t_SEMI = r';'
     t_COLONS = r'::'
@@ -184,6 +187,11 @@ class Lexer(object):
 
     t_INITIAL_ARROW = r'->'
 
+    def t_PREPROC_stdin(self, t):
+        r'\# .+ "<stdin>"[0-9 ]*' # WARNING: using '\d+' instead of '.+' does not necessarily result in the same matching
+        t.lexer.lineno = int (re.search (r'\# (\d+) "<stdin>"', t.value).group (1)) - 1
+        pass
+
     def t_PREPROC(self, t):
         r'\#.*'
         pass
@@ -204,7 +212,7 @@ class Lexer(object):
 
     def t_COMMENT(self, t):
         r' /\*(.|\n)*?\*/'
-        t.lineno += t.value.count('\n')
+        t.lexer.lineno += t.value.count('\n')
 
     def t_error(self, t):
         logger.error('Illegal character "{s}"'.format(s=t.value[0]))
diff --git a/formal/promela/src/src/modules/promela_yacc/promela/yacc.py b/formal/promela/src/src/modules/promela_yacc/promela/yacc.py
index d650c063..af96dc99 100644
--- a/formal/promela/src/src/modules/promela_yacc/promela/yacc.py
+++ b/formal/promela/src/src/modules/promela_yacc/promela/yacc.py
@@ -15,6 +15,7 @@ import os
 import subprocess
 import warnings
 import ply.yacc
+from sys import platform as _platform
 # inline
 #
 # import promela.ast as promela_ast
@@ -90,11 +91,11 @@ class Parser(object):
             debuglog=debuglog,
             errorlog=errorlog)
 
-    def parse(self, promela):
+    def parse(self, promela, fic):
         """Parse string of Promela code."""
-        s = cpp(promela)
+        s = cpp(promela, fic)
         program = self.parser.parse(
-            s, lexer=self.lexer.lexer, debug=self.logger)
+            s, lexer=self.lexer.lexer, debug=self.logger, tracking=True)
         return program
 
     def _iter(self, p):
@@ -115,6 +116,10 @@ class Parser(object):
         """program : units"""
         p[0] = self.ast.Program(p[1])
 
+    def p_program_empty(self, p):
+        """program : empty"""
+        p[0] = self.ast.Program([])
+
     def p_units_iter(self, p):
         """units : units unit"""
         p[0] = self._iter(p)
@@ -126,17 +131,18 @@ class Parser(object):
     # TODO: events, c_fcts, ns, error
     def p_unit_proc(self, p):
         """unit : proc
+                | inline
                 | init
                 | claim
                 | ltl
         """
-        p[0] = p[1]
+        p[0] = (p[1], p.lineno(1))
 
     def p_unit_decl(self, p):
         """unit : one_decl
                 | utype
         """
-        p[0] = p[1]
+        p[0] = (p[1], p.lineno(1))
 
     def p_unit_semi(self, p):
         """unit : semi"""
@@ -158,6 +164,13 @@ class Parser(object):
             name, body, args=args, priority=priority,
             provided=enabler, **inst)
 
+    def p_inline(self, p):
+        ("""inline : INLINE NAME"""
+         """         LPAREN var_list0 RPAREN"""
+         """         body
+         """)
+        p[0] = self.ast.InlineDef(name = p[2], decl = p[4], body = p[6])
+
     # instantiator
     def p_inst(self, p):
         """prefix_proctype : ACTIVE opt_index proctype"""
@@ -166,7 +179,7 @@ class Parser(object):
             n_active = self.ast.Integer('1')
         else:
             n_active = p[2]
-        d['active'] = n_active
+        d['active'] = int(n_active.value)
         p[0] = d
 
     def p_inactive_proctype(self, p):
@@ -197,7 +210,11 @@ class Parser(object):
         seq = self.ast.Sequence(p[4])
         p[0] = self.ast.TypeDef(p[2], seq)
 
-    def p_ltl(self, p):
+    def p_ltl1(self, p):
+        """ltl : LTL NAME LBRACE expr RBRACE"""
+        p[0] = self.ast.LTL(p[4], name = p[2])
+
+    def p_ltl2(self, p):
         """ltl : LTL LBRACE expr RBRACE"""
         p[0] = self.ast.LTL(p[3])
 
@@ -219,6 +236,21 @@ class Parser(object):
         """decl_lst : one_decl"""
         p[0] = [p[1]]
 
+    def p_decl0(self, p):
+        """decl0 : decl_lst0"""
+        p[0] = self.ast.Sequence(p[1])
+
+    def p_decl_empty0(self, p):
+        """decl0 : empty"""
+
+    def p_decl_lst_iter0(self, p):
+        """decl_lst0 : expr COMMA decl_lst0"""
+        p[0] = [p[1]] + p[3]
+
+    def p_decl_lst_end0(self, p):
+        """decl_lst0 : expr"""
+        p[0] = [p[1]]
+
     def p_one_decl_visible(self, p):
         """one_decl : vis typename var_list
                     | vis NAME var_list
@@ -251,7 +283,7 @@ class Parser(object):
 
     def p_one_decl_mtype(self, p):
         """one_decl : MTYPE asgn LBRACE name_list RBRACE"""
-        p[0] = self.ast.MessageType(p[3])
+        p[0] = self.ast.MessageType(p[4])
 
     def p_name_list_iter(self, p):
         """name_list : name_list COMMA NAME"""
@@ -270,8 +302,28 @@ class Parser(object):
         """var_list : ivar"""
         p[0] = [p[1]]
 
+    def p_var_list00_iter(self, p):
+        """var_list00 : ivar0 COMMA var_list00"""
+        p[0] = [p[1]] + p[3]
+
+    def p_var_list00_end(self, p):
+        """var_list00 : ivar0"""
+        p[0] = [p[1]]
+
+    def p_var_list0(self, p):
+        """var_list0 : var_list00"""
+        p[0] = p[1]
+
+    def p_var_list0_empty(self, p):
+        """var_list0 : empty"""
+        p[0] = []
+
     # TODO: vardcl asgn LBRACE c_list RBRACE
 
+    def p_ivar0(self, p):
+        """ivar0 : NAME"""
+        p[0] = p[1]
+
     # ivar = initialized variable
     def p_ivar(self, p):
         """ivar : vardcl"""
@@ -342,7 +394,7 @@ class Parser(object):
 
     def p_cmpnd_iter(self, p):
         """cmpnd : cmpnd PERIOD cmpnd %prec DOT"""
-        p[0] = self.ast.VarRef(extension=p[3], **p[1])
+        p[0] = self.ast.VarRef(extension=p[3], name = p[1].name, index = p[1].index)
 
     def p_cmpnd_end(self, p):
         """cmpnd : pfld"""
@@ -467,7 +519,7 @@ class Parser(object):
 
     def p_statement_assert(self, p):
         """statement : ASSERT full_expr"""
-        p[0] = self.ast.Assert(p[2])
+        p[0] = self.ast.Assert(p[2], pos = p.lineno(1))
 
     def p_statement_fifo_receive(self, p):
         """statement : varref RCV rargs"""
@@ -486,7 +538,7 @@ class Parser(object):
         p[0] = self.ast.Receive(p[1], p[4])
 
     def p_statement_tx2(self, p):
-        """statement : varref TX2 margs"""
+        """statement : varref TX2 rargs"""
         p[0] = self.ast.Send(p[1], p[3])
 
     def p_statement_full_expr(self, p):
@@ -497,6 +549,19 @@ class Parser(object):
         """statement : ELSE"""
         p[0] = self.ast.Else()
 
+    def p_statement_for(self, p):
+        """statement : for"""
+        p[0] = p[1]
+
+    def p_for(self, p):
+        """for : FOR LPAREN NAME COLON full_expr PERIODS full_expr RPAREN LBRACE sequence os RBRACE"""
+        s = p[10]
+        s.context = 'for'
+        s.context_for_var = p[3]
+        s.context_for_begin = p[5]
+        s.context_for_end = p[7]
+        p[0] = s
+
     def p_statement_atomic(self, p):
         """statement : atomic"""
         p[0] = p[1]
@@ -523,23 +588,27 @@ class Parser(object):
 
     # the stmt of line 696 in spin.y collects the inline ?
     def p_statement_call(self, p):
-        """statement : NAME LPAREN args RPAREN"""
+        """statement : NAME LPAREN decl0 RPAREN"""
         # NAME = INAME = inline
-        c = self.ast.Inline(p[1], p[3])
+        c = self.ast.Call(p[1], p[3])
         p[0] = self.ast.Sequence([c])
 
     def p_statement_assgn_call(self, p):
-        """statement : varref asgn NAME LPAREN args RPAREN statement"""
-        inline = self.ast.Inline(p[3], p[5])
+        """statement : varref asgn NAME LPAREN decl0 RPAREN statement"""
+        inline = self.ast.Call(p[3], p[5])
         p[0] = self.ast.Assignment(p[1], inline)
 
     def p_statement_return(self, p):
         """statement : RETURN full_expr"""
         p[0] = self.ast.Return(p[2])
 
-    def p_printf(self, p):
-        """statement : PRINT LPAREN STRING prargs RPAREN"""
-        p[0] = self.ast.Printf(p[3], p[4])
+    def p_printf1(self, p):
+        """statement : PRINT LPAREN STRING RPAREN"""
+        p[0] = self.ast.Printf(p[3])
+
+    def p_printf2(self, p):
+        """statement : PRINT LPAREN STRING COMMA decl0 RPAREN"""
+        p[0] = self.ast.Printf(p[3], p[5])
 
     # yet unimplemented for statement:
         # SET_P l_par two_args r_par
@@ -555,8 +624,8 @@ class Parser(object):
         p[0] = self.ast.Receive(p[1])
 
     def p_varref_lnot(self, p):
-        """special : varref LNOT margs"""
-        raise NotImplementedError
+        """special : varref LNOT rargs"""
+        p[0] = self.ast.Send(p[1], p[3])
 
     def p_break(self, p):
         """special : BREAK"""
@@ -609,17 +678,23 @@ class Parser(object):
         p[0] = self.ast.Expression(p[1])
 
     # probe expr = no negation allowed (positive)
-    def p_pexpr(self, p):
-        """pexpr : probe
-                 | LPAREN pexpr RPAREN
-                 | pexpr LAND pexpr
+    def p_pexpr_probe(self, p):
+        """pexpr : probe"""
+        p[0] = p[1]
+
+    def p_pexpr_paren(self, p):
+        """pexpr : LPAREN pexpr RPAREN"""
+        p[0] = p[2]
+
+    def p_pexpr_logical(self, p):
+        """pexpr : pexpr LAND pexpr
                  | pexpr LAND expr
                  | expr LAND pexpr
                  | pexpr LOR pexpr
                  | pexpr LOR expr
                  | expr LOR pexpr
         """
-        p[0] = 'pexpr'
+        p[0] = self.ast.Binary(p[2], p[1], p[3])
 
     def p_probe(self, p):
         """probe : FULL LPAREN varref RPAREN
@@ -627,7 +702,7 @@ class Parser(object):
                  | EMPTY LPAREN varref RPAREN
                  | NEMPTY LPAREN varref RPAREN
         """
-        p[0] = 'probe'
+        p[0] = self.ast.Call(p[1], self.ast.Sequence([p[3]]))
 
     def p_expr_paren(self, p):
         """expr : LPAREN expr RPAREN"""
@@ -676,8 +751,7 @@ class Parser(object):
         """expr : varref RCV LBRACKET rargs RBRACKET
                 | varref R_RCV LBRACKET rargs RBRACKET
         """
-        p[0] = p[1]
-        warnings.warn('not implemented')
+        p[0] = self.ast.ReceiveExpr(p[1], p[4])
 
     def p_expr_other(self, p):
         """expr : LPAREN expr ARROW expr COLON expr RPAREN
@@ -689,12 +763,15 @@ class Parser(object):
         warnings.warn('"{s}" not implemented'.format(s=p[1]))
 
     def p_expr_run(self, p):
-        """expr : RUN aname LPAREN args RPAREN opt_priority"""
+        """expr : RUN aname LPAREN decl0 RPAREN opt_priority"""
         p[0] = self.ast.Run(p[2], p[4], p[6])
 
+    def p_expr_other_1(self, p):
+        """expr : TIMEOUT"""
+        p[0] = self.ast.Timeout()
+
     def p_expr_other_2(self, p):
-        """expr : TIMEOUT
-                | NONPROGRESS
+        """expr : NONPROGRESS
                 | PC_VAL LPAREN expr RPAREN
         """
         raise NotImplementedError()
@@ -764,10 +841,10 @@ class Parser(object):
         p[0] = p[2]
 
     def p_const(self, p):
-        """const : boolean
+        """const : SKIP
+                 | boolean
                  | number
         """
-        # lex maps `skip` to `TRUE`
         p[0] = p[1]
 
     def p_bool(self, p):
@@ -783,47 +860,24 @@ class Parser(object):
     # Auxiliary
     # =========
 
-    def p_two_args(self, p):
-        """two_args : expr COMMA expr"""
-
-    def p_args(self, p):
-        """args : arg"""
-        p[0] = p[1]
-
-    def p_prargs(self, p):
-        """prargs : COMMA arg"""
-        p[0] = p[2]
-
-    def p_prargs_empty(self, p):
-        """prargs : empty"""
-
-    def p_args_empty(self, p):
-        """args : empty"""
-
-    def p_margs(self, p):
-        """margs : arg
-                 | expr LPAREN arg RPAREN
-        """
-
-    def p_arg(self, p):
-        """arg : expr
-               | expr COMMA arg
-        """
-        p[0] = 'arg'
-
     # TODO: CONST, MINUS CONST %prec UMIN
     def p_rarg(self, p):
-        """rarg : varref
+        """rarg : const
+                | varref
                 | EVAL LPAREN expr RPAREN
         """
-        p[0] = 'rarg'
+        # todo: support all cases
+        #       | rarg LPAREN rargs RPAREN
+        #       | LPAREN rargs RPAREN
+        p[0] = p[1]
 
-    def p_rargs(self, p):
-        """rargs : rarg
-                 | rarg COMMA rargs
-                 | rarg LPAREN rargs RPAREN
-                 | LPAREN rargs RPAREN
-        """
+    def p_rargs_iter(self, p):
+        """rargs : rarg COMMA rargs"""
+        p[0] = [p[1]] + p[3]
+
+    def p_rargs_end(self, p):
+        """rargs : rarg"""
+        p[0] = [p[1]]
 
     def p_proctype(self, p):
         """proctype : PROCTYPE
@@ -887,10 +941,14 @@ class Parser(object):
         raise Exception('syntax error at: {p}'.format(p=p))
 
 
-def cpp(s):
+def cpp(code, fic):
     """Call the C{C} preprocessor with input C{s}."""
     try:
-        p = subprocess.Popen(['cpp', '-E', '-x', 'c'],
+        if _platform == "darwin":
+            cppprog = 'clang'
+        else:
+            cppprog = 'cpp'
+        p = subprocess.Popen([cppprog, '-E', '-x', 'c', '-' if code is not None else fic], # NOTE: if code is empty, then '-' must be returned as well
                              stdin=subprocess.PIPE,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
@@ -900,10 +958,13 @@ def cpp(s):
             raise Exception('C preprocessor (cpp) not found in path.')
         else:
             raise
-    logger.debug('cpp input:\n' + s)
-    stdout, stderr = p.communicate(s)
+    if code:
+        logger.debug('cpp input:\n' + code)
+    stdout, stderr = p.communicate(code)
     logger.debug('cpp returned: {c}'.format(c=p.returncode))
     logger.debug('cpp stdout:\n {out}'.format(out=stdout))
+    if p.returncode != 0:
+        raise Exception('C preprocessor return code: {returncode}'.format(returncode=p.returncode))
     return stdout
 
 
diff --git a/formal/promela/src/src/modules/promela_yacc/tests/yacc_test.py b/formal/promela/src/src/modules/promela_yacc/tests/yacc_test.py
deleted file mode 100644
index b26d22aa..00000000
--- a/formal/promela/src/src/modules/promela_yacc/tests/yacc_test.py
+++ /dev/null
@@ -1,759 +0,0 @@
-import logging
-import networkx as nx
-import networkx.algorithms.isomorphism as iso
-from nose.tools import assert_raises
-from promela import ast, yacc
-
-
-logger = logging.getLogger(__name__)
-logger.setLevel('WARNING')
-log = logging.getLogger('promela.yacc')
-log.setLevel(logging.ERROR)
-h = logging.StreamHandler()
-log = logging.getLogger('promela.ast')
-log.setLevel('WARNING')
-log.addHandler(h)
-
-
-parser = yacc.Parser()
-
-
-def parse_proctype_test():
-    s = '''
-    active [3] proctype main(){
-        int x;
-    }
-    '''
-    tree = parser.parse(s)
-    assert isinstance(tree, ast.Program)
-    assert len(tree) == 1
-    p = tree[0]
-    assert isinstance(p, ast.Proctype)
-    assert p.name == 'main'
-    assert isinstance(p.body, ast.Sequence)
-    assert p.active == 3, type(p.active)
-    assert p.args is None
-    assert p.priority is None
-    assert p.provided is None
-
-
-def parse_if_test():
-    s = '''
-    proctype p (){
-        if
-        :: skip
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    assert isinstance(tree, ast.Program)
-    assert len(tree) == 1
-    proc = tree[0]
-    assert isinstance(proc, ast.Proctype)
-    assert isinstance(proc.body, ast.Sequence)
-    assert len(proc.body) == 1
-    if_block = proc.body[0]
-    assert isinstance(if_block, ast.Options)
-    assert if_block.type == 'if'
-    options = if_block.options
-    assert isinstance(options, list)
-    assert len(options) == 1
-    opt0 = options[0]
-    assert isinstance(opt0, ast.Sequence)
-    assert len(opt0) == 1
-    assert isinstance(opt0[0], ast.Expression)
-    e = opt0[0].expr
-    assert isinstance(e, ast.Bool)
-    assert e.value, e
-
-
-def parse_do_multiple_options_test():
-    s = '''
-    proctype p (){
-        do
-        :: x -> x = x + 1;
-        :: (y == 0) -> y = x; y == 1;
-        od
-    }
-    '''
-    tree = parser.parse(s)
-    assert isinstance(tree, ast.Program)
-    assert len(tree) == 1
-    proc = tree[0]
-    assert isinstance(proc, ast.Proctype)
-    assert isinstance(proc.body, ast.Sequence)
-    assert len(proc.body) == 1
-    do_block = proc.body[0]
-    assert isinstance(do_block, ast.Options)
-    assert do_block.type == 'do'
-    options = do_block.options
-    assert isinstance(options, list)
-    assert len(options) == 2
-    opt = options[0]
-    assert isinstance(opt, ast.Sequence)
-    assert len(opt) == 2
-    assert isinstance(opt[0], ast.Expression)
-    assert isinstance(opt[1], ast.Assignment)
-
-    opt = options[1]
-    assert isinstance(opt, ast.Sequence)
-    assert len(opt) == 3
-    assert isinstance(opt[0], ast.Expression)
-    assert isinstance(opt[1], ast.Assignment)
-    assert isinstance(opt[2], ast.Expression)
-
-
-def if_one_option_pg_test():
-    s = '''
-    proctype p (){
-        if
-        :: skip
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1)])
-    assert nx.is_isomorphic(g, h)
-
-
-def if_two_options_pg_test():
-    s = '''
-    proctype p(){
-        if
-        :: true
-        :: false
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 1)])
-    assert nx.is_isomorphic(g, h)
-
-
-def do_one_option_pg_test():
-    s = '''
-    proctype p(){
-        do
-        :: skip
-        od
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 0)])
-    assert nx.is_isomorphic(g, h)
-
-
-def do_two_options_pg_test():
-    s = '''
-    proctype p(){
-        do
-        :: true
-        :: false
-        od
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 0), (0, 0)])
-    assert nx.is_isomorphic(g, h)
-
-
-def nested_if_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: if
-           :: true
-           :: false
-           fi
-        :: x
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 1), (0, 1)])
-    assert nx.is_isomorphic(g, h)
-
-
-def nested_if_not_guard_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: true;
-           if
-           :: true
-           :: false
-           fi
-        :: x
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 2), (2, 1), (2, 1)])
-    assert nx.is_isomorphic(g, h)
-
-
-def doubly_nested_if_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: if
-           :: true
-           :: if
-              :: true
-              :: skip
-              fi
-           :: false
-           fi
-        :: if
-           :: if
-              :: true
-              :: false
-              fi
-           fi
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    for i in range(6):
-        h.add_edge(0, 1)
-    assert nx.is_isomorphic(g, h)
-
-
-def nested_do_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: do
-           :: true
-           :: false
-           od
-        :: x
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 2), (0, 2), (2, 2), (2, 2)])
-    assert nx.is_isomorphic(g, h)
-
-
-def nested_do_not_guard_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: true;
-           do
-           :: true
-           :: false
-           od
-        :: x
-        fi
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 2), (2, 2), (2, 2)])
-    assert nx.is_isomorphic(g, h)
-
-
-def combined_if_do_program_graph_test():
-    s = '''
-    active proctype p(){
-        int x, y, z;
-        if /* 0 */
-        ::  do
-            :: x = 1; /* 2 */
-               y == 5 /* 1 */
-
-            :: z = 3; /* 3 */
-               skip /* 1 */
-
-            ::  if
-                :: z = (3 - x) * y; /* 4 */
-                   true; /* 5 */
-                   y = 3 /* 1 */
-
-                :: true /* 1 */
-                fi
-            od
-          /* 1 */
-
-        :: true; /* 6 */
-           if
-           :: true /* 7 */
-
-           :: true -> /* 8 */
-              x = y /* 7 */
-
-           fi
-        fi
-        /* 7 */
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([
-        (0, 2), (0, 3), (0, 4), (0, 1),
-        (2, 1), (3, 1), (5, 1),
-        (1, 2), (1, 3), (1, 4), (1, 1),
-        (4, 5),
-        # false; if ...
-        (0, 6),
-        (6, 7), (6, 8), (8, 7)])
-    dump(g, node_label=None)
-    assert iso.is_isomorphic(g, h)
-
-
-def invalid_label_pg_test():
-    s = '''
-    proctype p(){
-        do
-        :: S0: x = 1;
-        od
-    }
-    '''
-    tree = parser.parse(s)
-    with assert_raises(Exception):
-        tree[0].to_pg()
-
-
-def goto_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        x = 1;
-        goto S0;
-        x = 2;
-        S0: x = 3;
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (1, 2)])
-    assert nx.is_isomorphic(g, h)
-
-
-def double_goto_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        x = 1;
-        goto S0;
-        x = 2;
-        S0: goto S1;
-        x = 3;
-        S1: skip
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (1, 2)])
-    assert nx.is_isomorphic(g, h)
-
-
-def goto_inside_if_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        if
-        :: true; goto S0; x = 1;
-        :: x = 3; false
-        fi;
-        S0: skip
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 2), (2, 1), (1, 3)])
-    assert nx.is_isomorphic(g, h)
-
-
-def goto_loop_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        S0: if
-        :: true; goto S0; x = 1;
-        :: x = 3;
-        fi;
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (0, 0)])
-    assert nx.is_isomorphic(g, h)
-
-
-def goto_self_loop_pg_test():
-    s = '''
-    proctype p(){
-        S0: goto S1;
-        S1: goto S0
-    }
-    '''
-    tree = parser.parse(s)
-    with assert_raises(AssertionError):
-        tree[0].to_pg()
-
-
-def break_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        do
-        :: true; x = 1;
-        :: break; x = 3;
-        od
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 1), (1, 0), (0, 2)])
-    assert nx.is_isomorphic(g, h)
-
-
-def nested_break_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        /* 0 */
-        do
-        :: true; /* 2 */
-           x == 1; /* 0 */
-
-        :: do
-           :: x == 2;
-              break /* 0 */
-
-           :: false; /* 4 */
-              x == 3 /* 5 */
-
-           od
-           /* 5 */
-
-        :: break; /* 1 */
-           x == 4;
-
-        od
-        /* 1 */
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([
-        (0, 1),
-        (0, 2), (2, 0),
-        (0, 0),
-        (0, 4), (4, 5),
-        (5, 4), (5, 0)])
-    assert nx.is_isomorphic(g, h)
-
-
-def atomic_pg_test():
-    s = '''
-    proctype p(){
-        bit x;
-        x = 1;
-        atomic { x = 2; }
-        x = 3;
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_node(0, context=None)
-    h.add_node(1, context=None)
-    h.add_node(2, context='atomic')
-    h.add_node(3, context=None)
-    h.add_edges_from([(0, 1), (1, 2), (2, 3)])
-    nm = lambda x, y: x['context'] == y['context']
-    gm = iso.GraphMatcher(g, h, node_match=nm)
-    assert gm.is_isomorphic()
-
-
-def do_atomic_dissapears_pg_test():
-    s = '''
-    proctype p(){
-        bit x, y;
-        /* 0 */
-        do
-        :: true; /* 3 */
-           atomic { x = 2; goto S0; /* 1 */ y = 1}
-        :: x == 1; /* 4 */ y == 2; /* 0 */
-        od;
-        x = 3;
-        /* 1 */
-        S0: skip
-        /* 2 */
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([
-        (0, 3), (3, 1), (1, 2),
-        (0, 4), (4, 0)])
-    for u in h:
-        h.add_node(u, context=None)
-    nm = lambda x, y: x['context'] == y['context']
-    gm = iso.GraphMatcher(g, h, node_match=nm)
-    assert gm.is_isomorphic()
-
-
-def do_atomic_pg_test():
-    s = '''
-    proctype p(){
-        bit x, y;
-        /* 0 */
-        do
-        :: true; /* 1 */
-           atomic { x = 2; /* 2 */
-           y = 1; goto S0; } /* 3 */
-        :: x == 1; /* 4 */ y == 2; /* 0 */
-        od;
-        x = 3;
-        /* 3 */
-        S0: skip
-        /* 5 */
-    }
-    '''
-    tree = parser.parse(s)
-    g = tree[0].to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([
-        (0, 1), (1, 2), (2, 3),
-        (3, 5), (0, 4), (4, 0)])
-    for u in h:
-        h.add_node(u, context=None)
-    h.add_node(2, context='atomic')
-    nm = lambda x, y: x['context'] == y['context']
-    gm = iso.GraphMatcher(g, h, node_match=nm)
-    assert gm.is_isomorphic()
-
-
-def ltl_block_test():
-    s = '''
-    bit x, y, c;
-
-    proctype p(){
-        if
-        :: x;
-        fi
-    }
-
-    ltl { (x == 1) && []<>(y != 2) && <>[](c == 1) && (x U y) }
-    '''
-    tree = parser.parse(s)
-    assert len(tree) == 3, repr(tree)
-    decl, p, ltl = tree
-    assert isinstance(p, ast.Proctype)
-    assert isinstance(ltl, ast.LTL)
-    s = str(ltl.formula)
-    assert s == (
-        '((((x == 1) && ([] (<> (y != 2)))) && '
-        '(<> ([] (c == 1)))) && (x U y))'), s
-
-
-def test_else():
-    s = '''
-    proctype p(){
-        byte x;
-        do
-        :: x == 0
-        :: x == 1
-        :: else
-        od
-    }
-    '''
-    (proc,) = parser.parse(s)
-    g = proc.to_pg()
-    dump(g)
-    for u, v, d in g.edges(data=True):
-        c = d['stmt']
-        if isinstance(c, ast.Else):
-            print(c.other_guards)
-
-
-def test_nested_else():
-    s = '''
-    proctype p(){
-        byte x;
-        do
-        ::
-            if
-            :: false
-            :: else
-            fi
-        od
-    }
-    '''
-    (proc,) = parser.parse(s)
-    g = proc.to_pg()
-    dump(g)
-    h = nx.MultiDiGraph()
-    h.add_edges_from([(0, 0), (0, 0)])
-    for u in h:
-        h.add_node(u, context=None)
-    nm = lambda x, y: x['context'] == y['context']
-    gm = iso.GraphMatcher(g, h, node_match=nm)
-    assert gm.is_isomorphic()
-
-
-def test_double_else():
-    s = '''
-    proctype foo(){
-        bit x;
-        do
-        ::
-            if
-            :: x
-            :: else
-            fi
-        :: else
-        od
-    }
-    '''
-    # syntactic else = Promela language definition
-    (proc,) = parser.parse(s)
-    with assert_raises(AssertionError):
-        proc.to_pg()
-    # different from Promela language definition
-    g = proc.to_pg(syntactic_else=True)
-    active_else = 0
-    off_else = 0
-    for u, v, d in g.edges(data=True):
-        stmt = d['stmt']
-        if isinstance(stmt, ast.Else):
-            other = stmt.other_guards
-            if other is None:
-                off_else += 1
-            else:
-                active_else += 1
-                assert len(other) == 1, other
-                (other_stmt,) = other
-                s = str(other_stmt)
-                assert s == 'x', s
-    assert active_else == 1, active_else
-    assert off_else == 1, off_else
-
-
-def test_pg_node_order():
-    s = '''
-    proctype foo(){
-        bit x;
-        if
-        ::
-            do
-            :: x > 2; x = 1
-            :: else; break
-            od;
-            x = 1
-        :: x = 2
-        fi
-    }
-    '''
-    (proc,) = parser.parse(s)
-    g = proc.to_pg()
-    dump(g)
-    # Final indexing depends on the
-    # aux goto nodes created and the contraction order.
-    # The latter depend on the intermediate indexing,
-    # which is fixed syntactically
-    # (see `generate_unique_node`).
-    edges = {(0, 1), (0, 3), (0, 4), (2, 3),
-             (2, 4), (3, 1), (4, 2)}
-    assert set(g) == set(range(5)), g.nodes()
-    assert set(g.edges()) == edges, g.edges()
-
-
-def test_labels():
-    s = '''
-    active proctype foo(){
-        progress:
-        do
-        :: true
-        od
-    }
-    '''
-    (proc,) = parser.parse(s)
-    g = proc.to_pg()
-    for u, d in g.nodes(data=True):
-        print(d.get('label'))
-
-
-def test_remote_ref():
-    s = '''
-    proctype foo(){
-        bar @ critical
-    }
-    '''
-    (proc,) = parser.parse(s)
-    g = proc.to_pg()
-    (e,) = g.edges(data=True)
-    u, v, d = e
-    s = d['stmt']
-    assert isinstance(s, ast.Expression), s
-    ref = s.expr
-    assert isinstance(ref, ast.RemoteRef), ref
-    assert ref.proctype == 'bar', ref.proctype
-    assert ref.label == 'critical', ref.label
-    assert ref.pid is None, ref.pid
-
-
-def dump(g, fname='g.pdf', node_label='context'):
-    if logger.getEffectiveLevel() >= logging.DEBUG:
-        return
-    # map nodes to integers
-    ast.dump_graph(
-        g, fname, node_label=node_label, edge_label='stmt')
-
-
-if __name__ == '__main__':
-    test_labels()
-- 
2.37.1 (Apple Git-137.1)




More information about the devel mailing list