[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