mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-04 05:11:22 +00:00
Improve sva2smt parser
Signed-off-by: Claire Xenia Wolf <claire@clairexen.net>
This commit is contained in:
parent
e42fddefec
commit
7233e6f8f5
1 changed files with 116 additions and 100 deletions
|
@ -16,135 +16,147 @@
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
Op = namedtuple("Op", ["op", "args", "tail"], defaults=[list, ""])
|
||||||
|
|
||||||
|
def pp(v, indent=""):
|
||||||
|
if isinstance(v, Op):
|
||||||
|
lit = True
|
||||||
|
if isinstance(v.args, list):
|
||||||
|
for a in v.args:
|
||||||
|
if isinstance(a, str): continue
|
||||||
|
if isinstance(a, int): continue
|
||||||
|
lit = False
|
||||||
|
if lit:
|
||||||
|
print(indent + v.op + " " + str(v.args))
|
||||||
|
else:
|
||||||
|
print(indent + v.op + ":")
|
||||||
|
pp(v.args, indent=indent+" ")
|
||||||
|
elif isinstance(v, list):
|
||||||
|
for a in v:
|
||||||
|
pp(a, indent=indent)
|
||||||
|
elif isinstance(v, str):
|
||||||
|
print(indent + '"' + v + '"')
|
||||||
|
else:
|
||||||
|
print(indent + "=" + str(v))
|
||||||
|
|
||||||
class SvaParserError(Exception):
|
class SvaParserError(Exception):
|
||||||
def __init__(self, text, expected):
|
def __init__(self, text, expected):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.expected = expected
|
self.expected = expected
|
||||||
|
|
||||||
def parse_str(text, s):
|
def parse_str(text, s):
|
||||||
t1 = text.lstrip()
|
text = text.lstrip()
|
||||||
if not t1.startswith(s):
|
if not text.startswith(s):
|
||||||
raise SvaParserError(t1, s)
|
raise SvaParserError(text, s)
|
||||||
return ("str", s), t1[len(s):]
|
return Op("str", [s], text[len(s):])
|
||||||
|
|
||||||
def parse_binop(text, ops, arg1_parser, arg2_parser=None):
|
def parse_binop(text, ops, arg1_parser, arg2_parser=None):
|
||||||
if arg2_parser is None:
|
if arg2_parser is None:
|
||||||
arg2_parser = arg1_parser
|
arg2_parser = arg1_parser
|
||||||
|
|
||||||
t1 = text.lstrip()
|
text = text.lstrip()
|
||||||
s1, t1 = arg1_parser(t1)
|
p = arg1_parser(text)
|
||||||
|
|
||||||
for op, label, chain in ops:
|
for op, cmd in ops:
|
||||||
try:
|
try:
|
||||||
_, t2 = parse_str(t1, op)
|
q = parse_str(p.tail, op)
|
||||||
s2, t2 = arg2_parser(t2)
|
q = arg2_parser(q.tail)
|
||||||
if chain and s2[0] == label:
|
return Op(cmd, [p, q], q.tail)
|
||||||
return (label, s1) + s2[1:], t2
|
|
||||||
return (label, s1, s2), t2
|
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return s1, t1
|
return p
|
||||||
|
|
||||||
|
def parse_int(text, label, base, digits):
|
||||||
|
text = text.lstrip()
|
||||||
|
tail = text.lstrip(digits)
|
||||||
|
|
||||||
|
if len(text) == len(tail) or text.startswith("_"):
|
||||||
|
raise SvaParserError(text, label)
|
||||||
|
|
||||||
|
text = text[:-len(tail)].replace("_", "")
|
||||||
|
return Op("int", int(text, base), tail)
|
||||||
|
|
||||||
def parse_dec(text):
|
def parse_dec(text):
|
||||||
t1 = text.lstrip()
|
return parse_int(text, "dec", 10, "0123456789_")
|
||||||
t2 = t1.lstrip("0123456789_")
|
|
||||||
|
|
||||||
if len(t1) == len(t2) or t1.startswith("_"):
|
|
||||||
raise SvaParserError(t1, "dec")
|
|
||||||
|
|
||||||
n = t1[:-len(t2)].replace("_", "")
|
|
||||||
return int(n, 10), t2
|
|
||||||
|
|
||||||
def parse_hex(text):
|
def parse_hex(text):
|
||||||
t1 = text.lstrip()
|
return parse_int(text, "hex", 16, "0123456789abcdefABCDEF_")
|
||||||
t2 = t1.lstrip("0123456789abcdefABCDEF_")
|
|
||||||
|
|
||||||
if len(t1) == len(t2) or t1.startswith("_"):
|
|
||||||
raise SvaParserError(t1, "hex")
|
|
||||||
|
|
||||||
n = t1[:-len(t2)].replace("_", "")
|
|
||||||
return int(n, 16), t2
|
|
||||||
|
|
||||||
def parse_bin(text):
|
def parse_bin(text):
|
||||||
t1 = text.lstrip()
|
return parse_int(text, "bin", 2, "01_")
|
||||||
t2 = t1.lstrip("01_")
|
|
||||||
|
|
||||||
if len(t1) == len(t2) or t1.startswith("_"):
|
|
||||||
raise SvaParserError(t1, "bin")
|
|
||||||
|
|
||||||
n = t1[:-len(t2)].replace("_", "")
|
|
||||||
return int(n, 2), t2
|
|
||||||
|
|
||||||
def parse_name(text):
|
def parse_name(text):
|
||||||
t1 = text.lstrip()
|
text = text.lstrip()
|
||||||
|
|
||||||
t2 = t1
|
tail = text
|
||||||
if len(t2) and t2[0].isalpha():
|
if len(tail) and tail[0].isalpha():
|
||||||
t2 = t2[1:]
|
tail = tail[1:]
|
||||||
while len(t2) and t2[0].isalnum():
|
while len(tail) and tail[0].isalnum():
|
||||||
t2 = t2[1:]
|
tail = tail[1:]
|
||||||
|
|
||||||
if len(t1) == len(t2):
|
if len(text) == len(tail):
|
||||||
raise SvaParserError(t1, "name")
|
raise SvaParserError(text, "name")
|
||||||
|
|
||||||
return t1[:-len(t2)], t2
|
return Op("name", text[:-len(tail)], tail)
|
||||||
|
|
||||||
# const: dec
|
# const: dec
|
||||||
# const: dec 'h hex
|
# const: dec 'h hex
|
||||||
# const: dec 'b bin
|
# const: dec 'b bin
|
||||||
def parse_const(text):
|
def parse_const(text):
|
||||||
s1, t1 = parse_dec(text)
|
p = parse_dec(text)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, t2 = parse_str(t1, "'h")
|
q = parse_str(p.tail, "'h")
|
||||||
s2, t2 = parse_hex(t2)
|
q = parse_hex(q.tail)
|
||||||
return ("const", s1, s2), t2
|
return Op("const", [p.args, q.args], q.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, t2 = parse_str(t1, "'b")
|
q = parse_str(p.tail, "'b")
|
||||||
s2, t2 = parse_bin(t2)
|
q = parse_bin(q.tail)
|
||||||
return ("const", s1, s2), t2
|
return Op("const", [p.args, q.args], q.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return ("const", 32, s1), t1
|
return Op("const", [32, p.args], p.tail)
|
||||||
|
|
||||||
# signal: name
|
# signal: name
|
||||||
# signal: name [ idx ]
|
# signal: name [ idx ]
|
||||||
# signal: name [ msb : lsb ]
|
# signal: name [ msb : lsb ]
|
||||||
# signal: name [ lsb +: width ]
|
# signal: name [ lsb +: width ]
|
||||||
def parse_signal(text):
|
def parse_signal(text):
|
||||||
s1, t1 = parse_name(text)
|
p = parse_name(text)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, t2 = parse_str(t1, "[")
|
q = parse_str(p.tail, "[")
|
||||||
s2, t2 = parse_dec(t2)
|
q = parse_dec(q.tail)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, t3 = parse_str(t2, ":")
|
r = parse_str(q.tail, ":")
|
||||||
s3, t3 = parse_dec(t3)
|
r = parse_dec(r.tail)
|
||||||
_, t3 = parse_str(t3, "]")
|
s = parse_str(r.tail, "]")
|
||||||
return ("signal", s1, s3, s2-s3+1), t3
|
return Op("signal", [p.args, r.args, q.args-r.args+1], s.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, t3 = parse_str(t2, "+:")
|
r = parse_str(q.tail, "+:")
|
||||||
s3, t3 = parse_dec(t3)
|
r = parse_dec(r.tail)
|
||||||
_, t3 = parse_str(t3, "]")
|
s = parse_str(r.tail, "]")
|
||||||
return ("signal", s1, s2, s3), t3
|
return Op("signal", [p.args, q.args, r.args], s.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
_, t2 = parse_str(t2, "]")
|
r = parse_str(q.tail, "]")
|
||||||
return ("signal", s1, s2), t2
|
return Op("signal", [p.args, q.args], r.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return ("signal", s1), t1
|
return Op("signal", [p.args], p.tail)
|
||||||
|
|
||||||
# atom: ! atom
|
# atom: ! atom
|
||||||
# atom: const
|
# atom: const
|
||||||
|
@ -152,9 +164,9 @@ def parse_signal(text):
|
||||||
# atom: ( expr )
|
# atom: ( expr )
|
||||||
def parse_atom(text):
|
def parse_atom(text):
|
||||||
try:
|
try:
|
||||||
_, t = parse_str(text, "!")
|
p = parse_str(text, "!")
|
||||||
s, t = parse_atom(t)
|
p = parse_atom(p.tail)
|
||||||
return ("not", s), t
|
return Op("not", [p], p.tail)
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -168,25 +180,25 @@ def parse_atom(text):
|
||||||
except SvaParserError:
|
except SvaParserError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
_, t = parse_str(text, "(")
|
p = parse_str(text, "(")
|
||||||
s, t = parse_expr(t)
|
p = parse_expr(p.tail)
|
||||||
_, t = parse_str(t, ")")
|
q = parse_str(p.tail, ")")
|
||||||
return s, t
|
return p._replace(tail=q.tail)
|
||||||
|
|
||||||
# prod: atom
|
# prod: atom
|
||||||
# prod: atom & prod
|
# prod: atom & prod
|
||||||
def parse_prod(text):
|
def parse_prod(text):
|
||||||
return parse_binop(text, [("&", "and", True)], parse_atom, parse_prod)
|
return parse_binop(text, [("&", "and")], parse_atom, parse_prod)
|
||||||
|
|
||||||
# sum: prod
|
# sum: prod
|
||||||
# sum: prod | sum
|
# sum: prod | sum
|
||||||
def parse_sum(text):
|
def parse_sum(text):
|
||||||
return parse_binop(text, [("|", "or", True)], parse_prod, parse_sum)
|
return parse_binop(text, [("|", "or")], parse_prod, parse_sum)
|
||||||
|
|
||||||
# term: sum
|
# term: sum
|
||||||
# term: sum ^ term
|
# term: sum ^ term
|
||||||
def parse_term(text):
|
def parse_term(text):
|
||||||
return parse_binop(text, [("^", "xor", True)], parse_sum, parse_term)
|
return parse_binop(text, [("^", "xor")], parse_sum, parse_term)
|
||||||
|
|
||||||
# expr: term
|
# expr: term
|
||||||
# expr: term < term
|
# expr: term < term
|
||||||
|
@ -197,37 +209,35 @@ def parse_term(text):
|
||||||
# expr: term != term
|
# expr: term != term
|
||||||
def parse_expr(text):
|
def parse_expr(text):
|
||||||
return parse_binop(text, [
|
return parse_binop(text, [
|
||||||
("<", "lt", False),
|
("<", "lt"),
|
||||||
(">", "gt", False),
|
(">", "gt"),
|
||||||
("<=", "le", False),
|
("<=", "le"),
|
||||||
(">=", "ge", False),
|
(">=", "ge"),
|
||||||
("==", "eq", False),
|
("==", "eq"),
|
||||||
("!=", "ne", False)
|
("!=", "ne")
|
||||||
], parse_term)
|
], parse_term)
|
||||||
|
|
||||||
# conj: expr
|
# conj: expr
|
||||||
# conj: expr "and" conj
|
# conj: expr "and" conj
|
||||||
def parse_conj(text):
|
def parse_conj(text):
|
||||||
return parse_binop(text, [("and", "conj", True)], parse_expr, parse_conj)
|
return parse_binop(text, [("and", "conj")], parse_expr, parse_conj)
|
||||||
|
|
||||||
# prop:
|
# prop:
|
||||||
# expr "|->" conj ";"
|
# expr "|->" conj ";"
|
||||||
# expr ";"
|
# expr ";"
|
||||||
def parse_prop(text):
|
def parse_prop(text):
|
||||||
s, t = parse_binop(text, [("|->", "impl", False)], parse_expr, parse_conj)
|
p = parse_binop(text, [("|->", "impl")], parse_expr, parse_conj)
|
||||||
_, t = parse_str(t, ";")
|
q = parse_str(p.tail, ";")
|
||||||
return s, t
|
return p._replace(tail=q.tail)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import fileinput, pprint
|
|
||||||
pp = pprint.PrettyPrinter(indent=4)
|
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
|
import fileinput
|
||||||
with fileinput.input() as f:
|
with fileinput.input() as f:
|
||||||
sva_text = "".join(f).rstrip()
|
sva_text = "".join(f).rstrip()
|
||||||
else:
|
else:
|
||||||
sva_text = """
|
sva_text = """
|
||||||
1 & S[1] |-> S[3:2] and 8'h a_B | S[2+:2] ^ 3 'b 101 == S;
|
1 & S[1] |-> S[3:2] and 8'h a_B | S[2+:2] | 1 ^ 3 'b 101 == S;
|
||||||
|
|
||||||
a
|
a
|
||||||
|-> b
|
|-> b
|
||||||
|
@ -241,13 +251,19 @@ x |-> c > d;
|
||||||
print("-------------")
|
print("-------------")
|
||||||
|
|
||||||
props = []
|
props = []
|
||||||
while len(sva_text):
|
try:
|
||||||
s, sva_text = parse_prop(sva_text)
|
while len(sva_text):
|
||||||
props.append(s)
|
props.append(parse_prop(sva_text))
|
||||||
|
sva_text = props[-1].tail
|
||||||
|
except SvaParserError as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
if len(props) == 1:
|
||||||
|
props = props[0]
|
||||||
|
else:
|
||||||
|
props = Op("conj", props, sva_text)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("--- S-Expr ---")
|
print("--- Property ---")
|
||||||
for s in props:
|
pp(props)
|
||||||
pp.pprint(s)
|
print("----------------")
|
||||||
print("--------------")
|
|
||||||
print()
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue