#include "btor2parser.h"
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "util/btor2stack.h"
struct Btor2Parser
{
char *error;
Btor2Line **table, *new_line;
Btor2Sort **stable;
int64_t sztable, ntable, szstable, nstable, szbuf, nbuf, lineno;
int32_t saved;
char *buf;
FILE *file;
};
static void *
btor2parser_malloc (size_t size)
{
assert (size);
void *res = malloc (size);
if (!res)
{
fprintf (stderr, "[btor2parser] memory allocation failed\n");
abort ();
}
return res;
}
static void *
btor2parser_realloc (void *ptr, size_t size)
{
assert (size);
void *res = realloc (ptr, size);
if (!res)
{
fprintf (stderr, "[btor2parser] memory reallocation failed\n");
abort ();
}
return res;
}
static char *
btor2parser_strdup (const char *str)
{
assert (str);
char *res = btor2parser_malloc (strlen (str) + 1);
strcpy (res, str);
return res;
}
Btor2Parser *
btor2parser_new ()
{
Btor2Parser *res = btor2parser_malloc (sizeof *res);
memset (res, 0, sizeof *res);
return res;
}
static void
reset_bfr (Btor2Parser *bfr)
{
int32_t i;
assert (bfr);
if (bfr->error)
{
free (bfr->error);
bfr->error = 0;
}
if (bfr->table)
{
for (i = 0; i < bfr->ntable; i++)
{
Btor2Line *l = bfr->table[i];
if (!l) continue;
if (l->symbol) free (l->symbol);
if (l->constant) free (l->constant);
free (l->args);
free (l);
}
free (bfr->table);
bfr->table = 0;
bfr->ntable = bfr->sztable = 0;
}
if (bfr->buf)
{
free (bfr->buf);
bfr->buf = 0;
bfr->nbuf = bfr->szbuf = 0;
}
}
void
btor2parser_delete (Btor2Parser *bfr)
{
reset_bfr (bfr);
free (bfr);
}
static int32_t
getc_bfr (Btor2Parser *bfr)
{
int32_t ch;
if ((ch = bfr->saved) == EOF)
ch = getc (bfr->file);
else
bfr->saved = EOF;
if (ch == '\n') bfr->lineno++;
return ch;
}
static void
ungetc_bfr (Btor2Parser *bfr, int32_t ch)
{
assert (bfr->saved == EOF);
if (ch == EOF) return;
bfr->saved = ch;
if (ch == '\n')
{
assert (bfr->lineno > 1);
bfr->lineno--;
}
}
static int32_t
perr_bfr (Btor2Parser *bfr, const char *fmt, ...)
{
assert (!bfr->error);
char buf[1024];
va_list ap;
va_start (ap, fmt);
vsnprintf (buf, 1023, fmt, ap);
va_end (ap);
buf[1023] = '\0';
bfr->error = btor2parser_malloc (strlen (buf) + 28);
sprintf (bfr->error, "line %" PRId64 ": %s", bfr->lineno, buf);
return 0;
}
static void
pushc_bfr (Btor2Parser *bfr, int32_t ch)
{
if (bfr->nbuf >= bfr->szbuf)
{
bfr->szbuf = bfr->szbuf ? 2 * bfr->szbuf : 1;
bfr->buf = btor2parser_realloc (bfr->buf, bfr->szbuf * sizeof *bfr->buf);
}
bfr->buf[bfr->nbuf++] = ch;
}
static void
pusht_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (bfr->ntable >= bfr->sztable)
{
bfr->sztable = bfr->sztable ? 2 * bfr->sztable : 1;
bfr->table =
btor2parser_realloc (bfr->table, bfr->sztable * sizeof *bfr->table);
}
bfr->table[bfr->ntable++] = l;
}
static int32_t
parse_id_bfr (Btor2Parser *bfr, int64_t *res)
{
int64_t id;
int32_t ch;
ch = getc_bfr (bfr);
if (ch == '0') return perr_bfr (bfr, "id should start with non-zero digit");
if (!isdigit (ch)) return perr_bfr (bfr, "id should start with digit");
id = ch - '0';
while (isdigit (ch = getc_bfr (bfr)))
{
id = 10 * id + (ch - '0');
if (id >= BTOR2_FORMAT_MAXID) return perr_bfr (bfr, "id exceeds maximum");
}
ungetc_bfr (bfr, ch);
*res = id;
return 1;
}
static int32_t
parse_signed_id_bfr (Btor2Parser *bfr, int64_t *res)
{
int32_t ch, sign;
ch = getc_bfr (bfr);
if (ch == '-')
sign = -1;
else
{
ungetc_bfr (bfr, ch);
sign = 1;
}
if (!parse_id_bfr (bfr, res)) return 0;
if (sign < 0) *res = -*res;
return 1;
}
static int32_t
parse_pos_number_bfr (Btor2Parser *bfr, uint32_t *res)
{
int64_t num;
int32_t ch;
ch = getc_bfr (bfr);
if (!isdigit (ch))
{
if (isprint (ch))
return perr_bfr (bfr, "expected number but got '%c'", ch);
else if (ch == '\n')
return perr_bfr (bfr, "expected number but got new line");
else
return perr_bfr (
bfr, "expected number but got character code 0x%02x", ch);
}
num = ch - '0';
ch = getc_bfr (bfr);
if (num == 0 && isdigit (ch))
return perr_bfr (bfr, "number should start with non-zero digit");
while (isdigit (ch))
{
num = 10 * num + (ch - '0');
if (num >= BTOR2_FORMAT_MAXBITWIDTH)
return perr_bfr (bfr,
"number exceeds maximum bit width of %" PRId64,
BTOR2_FORMAT_MAXBITWIDTH);
ch = getc_bfr (bfr);
}
ungetc_bfr (bfr, ch);
*res = num;
return 1;
}
static Btor2Line *
id2line_bfr (Btor2Parser *bfr, int64_t id)
{
int64_t absid = labs (id);
if (!absid || absid >= bfr->ntable) return 0;
return bfr->table[absid];
}
static int32_t
skip_comment (Btor2Parser *bfr)
{
int32_t ch;
while ((ch = getc_bfr (bfr)) != '\n')
{
if (ch == EOF) return perr_bfr (bfr, "unexpected end-of-file in comment");
}
return 1;
}
static int32_t
cmp_sort_ids (Btor2Parser *bfr, int64_t sort_id1, int64_t sort_id2)
{
(void) bfr;
Btor2Line *s1, *s2;
s1 = id2line_bfr (bfr, sort_id1);
s2 = id2line_bfr (bfr, sort_id2);
if (s1->sort.tag != s2->sort.tag) return 1;
if (s1->sort.tag == BTOR2_TAG_SORT_bitvec)
return s1->sort.bitvec.width - s2->sort.bitvec.width;
assert (s1->sort.tag == BTOR2_TAG_SORT_array);
if (cmp_sort_ids (bfr, s1->sort.array.index, s2->sort.array.index)) return 1;
return cmp_sort_ids (bfr, s1->sort.array.element, s2->sort.array.element);
}
static int32_t
cmp_sorts (Btor2Parser *bfr, Btor2Line *l1, Btor2Line *l2)
{
return cmp_sort_ids (bfr, l1->sort.id, l2->sort.id);
}
static int32_t
parse_sort_id_bfr (Btor2Parser *bfr, Btor2Sort *res)
{
int64_t sort_id;
Btor2Line *s;
if (!parse_id_bfr (bfr, &sort_id)) return 0;
if (sort_id >= bfr->ntable || id2line_bfr (bfr, sort_id) == 0)
return perr_bfr (bfr, "undefined sort id");
s = id2line_bfr (bfr, sort_id);
if (s->tag != BTOR2_TAG_sort)
return perr_bfr (bfr, "id after tag is not a sort id");
*res = s->sort;
return 1;
}
static const char *
parse_tag (Btor2Parser *bfr)
{
int32_t ch;
bfr->nbuf = 0;
while ('a' <= (ch = getc_bfr (bfr)) && ch <= 'z') pushc_bfr (bfr, ch);
if (!bfr->nbuf)
{
perr_bfr (bfr, "expected tag");
return 0;
}
if (ch != ' ')
{
perr_bfr (bfr, "expected space after tag");
return 0;
}
pushc_bfr (bfr, 0);
return bfr->buf;
}
static int32_t
parse_symbol_bfr (Btor2Parser *bfr)
{
int32_t ch;
bfr->nbuf = 0;
while ((ch = getc_bfr (bfr)) != '\n')
{
if (ch == EOF)
return perr_bfr (bfr, "unexpected end-of-file in symbol");
else if (ch == ' ' || ch == '\t')
{
ch = getc_bfr (bfr);
if (ch == ';')
{
if (!skip_comment (bfr)) return 0;
break;
}
else
return perr_bfr (bfr, "unexpected white-space in symbol");
}
pushc_bfr (bfr, ch);
}
if (!bfr->nbuf)
{
assert (bfr->lineno > 1);
bfr->lineno--;
return perr_bfr (bfr, "empty symbol");
}
pushc_bfr (bfr, 0);
return 1;
}
static int32_t
parse_opt_symbol_bfr (Btor2Parser *bfr, Btor2Line *l)
{
int32_t ch;
if ((ch = getc_bfr (bfr)) == ' ')
{
ch = getc_bfr (bfr);
if (ch == ';')
{
if (!skip_comment (bfr)) return 0;
}
else
{
ungetc_bfr (bfr, ch);
if (!parse_symbol_bfr (bfr)) return 0;
l->symbol = btor2parser_strdup (bfr->buf);
}
}
else if (ch != '\n')
return perr_bfr (
bfr, "expected new-line at the end of the line got '%c'", ch);
return 1;
}
static Btor2Line *
new_line_bfr (Btor2Parser *bfr,
int64_t id,
int64_t lineno,
const char *name,
Btor2Tag tag)
{
Btor2Line *res;
assert (0 < id);
assert (bfr->ntable <= id);
res = btor2parser_malloc (sizeof *res);
memset (res, 0, sizeof (*res));
res->id = id;
res->lineno = lineno;
res->tag = tag;
res->name = name;
res->args = btor2parser_malloc (sizeof (int64_t) * 3);
memset (res->args, 0, sizeof (int64_t) * 3);
while (bfr->ntable < id) pusht_bfr (bfr, 0);
assert (bfr->ntable == id);
return res;
}
static int32_t
check_sort_bitvec (Btor2Parser *bfr, Btor2Line *l, Btor2Line *args[])
{
uint32_t i;
if (l->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr, "expected bitvec sort for %s", l->name);
for (i = 0; i < l->nargs; i++)
{
if (args[i]->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr,
"expected bitvec sort for %s argument of %s",
i == 0 ? "first" : (i == 1 ? "second" : "third"),
l->name);
}
return 1;
}
static int32_t
is_constant_bfr (Btor2Parser *bfr, int64_t id)
{
Btor2Line *l = id2line_bfr (bfr, id);
assert (l);
Btor2Tag tag = l->tag;
return tag == BTOR2_TAG_const || tag == BTOR2_TAG_constd
|| tag == BTOR2_TAG_consth || tag == BTOR2_TAG_one
|| tag == BTOR2_TAG_ones || tag == BTOR2_TAG_zero;
}
static int32_t
check_sorts_bfr (Btor2Parser *bfr, Btor2Line *l)
{
Btor2Line *arg;
Btor2Line *args[3];
uint32_t i;
if (l->tag != BTOR2_TAG_justice)
{
for (i = 0; i < l->nargs; i++)
{
args[i] = id2line_bfr (bfr, l->args[i]);
if (args[i]->sort.tag == BTOR2_TAG_SORT_array && l->args[i] < 0)
{
return perr_bfr (bfr, "negated arrays not allowed");
}
}
}
switch (l->tag)
{
case BTOR2_TAG_dec:
case BTOR2_TAG_inc:
case BTOR2_TAG_neg:
case BTOR2_TAG_not:
assert (l->nargs == 1);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.bitvec.width != args[0]->sort.bitvec.width)
return perr_bfr (bfr,
"expected bitvec of size %u for "
"argument but got %u",
l->sort.bitvec.width,
args[0]->sort.bitvec.width);
break;
case BTOR2_TAG_redand:
case BTOR2_TAG_redor:
case BTOR2_TAG_redxor:
assert (l->nargs == 1);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for "
"%s but got %u",
l->name,
l->sort.bitvec.width);
break;
case BTOR2_TAG_add:
case BTOR2_TAG_and:
case BTOR2_TAG_mul:
case BTOR2_TAG_nand:
case BTOR2_TAG_nor:
case BTOR2_TAG_or:
case BTOR2_TAG_rol:
case BTOR2_TAG_ror:
case BTOR2_TAG_sdiv:
case BTOR2_TAG_sll:
case BTOR2_TAG_smod:
case BTOR2_TAG_sra:
case BTOR2_TAG_srem:
case BTOR2_TAG_srl:
case BTOR2_TAG_sub:
case BTOR2_TAG_udiv:
case BTOR2_TAG_urem:
case BTOR2_TAG_xnor:
case BTOR2_TAG_xor:
assert (l->nargs == 2);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.bitvec.width != args[0]->sort.bitvec.width)
return perr_bfr (bfr,
"expected bitvec of size %u for "
"first argument but got %u",
l->sort.bitvec.width,
args[0]->sort.bitvec.width);
if (l->sort.bitvec.width != args[1]->sort.bitvec.width)
return perr_bfr (bfr,
"expected bitvec of size %u for "
"second argument but got %u",
l->sort.bitvec.width,
args[1]->sort.bitvec.width);
break;
case BTOR2_TAG_init:
if (l->sort.tag == BTOR2_TAG_SORT_array)
{
if (cmp_sorts (bfr, l, args[0]))
return perr_bfr (bfr, "sort of first argument does not match");
if (args[1]->sort.tag == BTOR2_TAG_SORT_bitvec)
{
if (!is_constant_bfr (bfr, args[1]->id))
return perr_bfr (bfr,
"expected bit-vector constant as second argument");
if (cmp_sort_ids (bfr, l->sort.array.element, args[1]->sort.id))
return perr_bfr (bfr,
"sort of init value does not match element "
"sort of state '%" PRId64 "'",
args[0]->id);
}
break;
}
case BTOR2_TAG_next:
assert (l->nargs == 2);
if (cmp_sorts (bfr, l, args[0]))
return perr_bfr (bfr, "sort of first argument does not match");
if (cmp_sorts (bfr, l, args[1]))
return perr_bfr (bfr, "sort of second argument does not match");
break;
case BTOR2_TAG_uaddo:
case BTOR2_TAG_ugt:
case BTOR2_TAG_ugte:
case BTOR2_TAG_ult:
case BTOR2_TAG_ulte:
case BTOR2_TAG_umulo:
case BTOR2_TAG_usubo:
case BTOR2_TAG_saddo:
case BTOR2_TAG_sdivo:
case BTOR2_TAG_sgt:
case BTOR2_TAG_sgte:
case BTOR2_TAG_slt:
case BTOR2_TAG_slte:
case BTOR2_TAG_smulo:
case BTOR2_TAG_ssubo:
assert (l->nargs == 2);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for "
"%s but got %u",
l->name,
l->sort.bitvec.width);
if (args[0]->sort.bitvec.width != args[1]->sort.bitvec.width)
return perr_bfr (bfr,
"sorts of first and second "
"argument do not match");
break;
case BTOR2_TAG_eq:
case BTOR2_TAG_neq:
assert (l->nargs == 2);
if (l->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for %s but got %u",
l->name,
l->sort.bitvec.width);
if (cmp_sorts (bfr, args[0], args[1]))
return perr_bfr (bfr,
"sorts of first and second "
"argument do not match");
break;
case BTOR2_TAG_concat:
assert (l->nargs == 2);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.bitvec.width
!= args[0]->sort.bitvec.width + args[1]->sort.bitvec.width)
return perr_bfr (
bfr,
"expected bitvec of size %u for %s but got %u",
l->sort.bitvec.width,
l->name,
args[0]->sort.bitvec.width + args[1]->sort.bitvec.width);
break;
case BTOR2_TAG_slice:
assert (l->nargs == 1);
if (!check_sort_bitvec (bfr, l, args)) return 0;
uint32_t upper = (uint32_t) l->args[1];
uint32_t lower = (uint32_t) l->args[2];
if (l->sort.bitvec.width != upper - lower + 1)
return perr_bfr (bfr,
"expected bitvec of size %u for %s but got %u",
l->sort.bitvec.width,
l->name,
upper - lower + 1);
break;
case BTOR2_TAG_iff:
case BTOR2_TAG_implies:
assert (l->nargs == 2);
if (!check_sort_bitvec (bfr, l, args)) return 0;
if (l->sort.tag != BTOR2_TAG_SORT_bitvec || l->sort.bitvec.width != 1)
return perr_bfr (bfr, "expected bitvec of size 1");
if (cmp_sorts (bfr, args[0], l))
return perr_bfr (bfr,
"expected bitvec of size 1 for "
"first argument");
if (cmp_sorts (bfr, args[1], l))
return perr_bfr (bfr,
"expected bitvec of size 1 for "
"second argument");
break;
case BTOR2_TAG_bad:
case BTOR2_TAG_constraint:
case BTOR2_TAG_fair:
assert (l->nargs == 1);
if (args[0]->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr, "expected bitvec of size 1 for %s", l->name);
if (args[0]->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for %s "
"but got %u",
l->name,
args[0]->sort.bitvec.width);
break;
case BTOR2_TAG_output:
assert (l->nargs == 1);
if (args[0]->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr, "expected bitvec sort for %s", l->name);
break;
case BTOR2_TAG_ite:
assert (l->nargs == 3);
if (args[0]->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr,
"expected bitvec of size 1 for ",
"first argument of %s",
l->name);
if (args[0]->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for "
"first argument of %s but got %u",
l->name,
args[0]->sort.bitvec.width);
if (cmp_sorts (bfr, args[1], args[2]))
return perr_bfr (bfr,
"sorts of second and third "
"argument do not match");
break;
case BTOR2_TAG_justice:
for (i = 0; i < l->nargs; i++)
{
arg = id2line_bfr (bfr, l->args[i]);
if (arg->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr, "expected bitvec of size 1 for argument %u", i);
if (arg->sort.bitvec.width != 1)
return perr_bfr (bfr,
"expected bitvec of size 1 for argument %u "
"but got %u",
i,
arg->sort.bitvec.width);
}
break;
case BTOR2_TAG_sext:
case BTOR2_TAG_uext:
assert (l->nargs == 1);
if (!check_sort_bitvec (bfr, l, args)) return 0;
uint32_t ext = args[0]->sort.bitvec.width + (uint32_t) l->args[1];
if (l->sort.bitvec.width != ext)
return perr_bfr (bfr,
"expected bitvec of size %u for %s but got %u",
l->sort.bitvec.width,
l->name,
ext);
break;
case BTOR2_TAG_read:
assert (l->nargs == 2);
if (args[0]->sort.tag != BTOR2_TAG_SORT_array)
return perr_bfr (
bfr, "expected array sort for first argument of %s", l->name);
if (cmp_sort_ids (bfr, l->sort.id, args[0]->sort.array.element))
return perr_bfr (bfr,
"sort of %s does not match element sort of first "
"argument",
l->name);
if (cmp_sort_ids (bfr, args[1]->sort.id, args[0]->sort.array.index))
return perr_bfr (bfr,
"sort of second argument does not match index "
"sort of first argument");
break;
case BTOR2_TAG_write:
assert (l->nargs == 3);
if (l->sort.tag != BTOR2_TAG_SORT_array)
return perr_bfr (bfr, "expected array sort for %s", l->name);
if (args[0]->sort.tag != BTOR2_TAG_SORT_array)
return perr_bfr (
bfr, "expected array sort for first argument of %s", l->name);
if (cmp_sort_ids (bfr, args[1]->sort.id, args[0]->sort.array.index))
return perr_bfr (bfr,
"sort of second argument does not match index "
"sort of first argument",
l->name);
if (cmp_sort_ids (bfr, args[2]->sort.id, args[0]->sort.array.element))
return perr_bfr (bfr,
"sort of third argument does not match element "
"sort of first argument",
l->name);
break;
case BTOR2_TAG_const:
case BTOR2_TAG_constd:
case BTOR2_TAG_consth:
case BTOR2_TAG_input:
case BTOR2_TAG_one:
case BTOR2_TAG_ones:
case BTOR2_TAG_sort:
case BTOR2_TAG_state:
case BTOR2_TAG_zero: break;
default:
assert (0);
return perr_bfr (bfr, "invalid tag '%s' for sort checking", l->tag);
}
return 1;
}
static int64_t
parse_arg_bfr (Btor2Parser *bfr)
{
Btor2Line *l;
int64_t res, absres;
if (!parse_signed_id_bfr (bfr, &res)) return 0;
absres = labs (res);
if (absres >= bfr->ntable)
return perr_bfr (bfr, "argument id too large (undefined)");
l = bfr->table[absres];
if (!l) return perr_bfr (bfr, "undefined argument id");
if (l->tag == BTOR2_TAG_sort || l->tag == BTOR2_TAG_init
|| l->tag == BTOR2_TAG_next || l->tag == BTOR2_TAG_bad
|| l->tag == BTOR2_TAG_constraint || l->tag == BTOR2_TAG_fair
|| l->tag == BTOR2_TAG_justice)
{
return perr_bfr (bfr, "'%s' cannot be used as argument", l->name);
}
if (!l->sort.id) return perr_bfr (bfr, "declaration used as argument");
return res;
}
static int32_t
parse_sort_bfr (Btor2Parser *bfr, Btor2Line *l)
{
const char *tag;
Btor2Sort tmp, s;
tag = parse_tag (bfr);
if (!tag) return 0;
if (!strcmp (tag, "bitvec"))
{
tmp.tag = BTOR2_TAG_SORT_bitvec;
tmp.name = "bitvec";
if (!parse_pos_number_bfr (bfr, &tmp.bitvec.width)) return 0;
if (tmp.bitvec.width == 0)
return perr_bfr (bfr, "bit width must be greater than 0");
}
else if (!strcmp (tag, "array"))
{
tmp.tag = BTOR2_TAG_SORT_array;
tmp.name = "array";
s.id = 0;
if (!parse_sort_id_bfr (bfr, &s)) return 0;
if (getc_bfr (bfr) != ' ') return perr_bfr (bfr, "expected space");
tmp.array.index = s.id;
if (!parse_sort_id_bfr (bfr, &s)) return 0;
tmp.array.element = s.id;
}
else
{
return perr_bfr (bfr, "invalid sort tag");
}
tmp.id = l->id;
l->sort = tmp;
return 1;
}
static int32_t
parse_args (Btor2Parser *bfr, Btor2Line *l, uint32_t nargs)
{
uint32_t i = 0;
if (getc_bfr (bfr) != ' ')
return perr_bfr (bfr, "expected space after sort id");
while (i < nargs)
{
if (!(l->args[i] = parse_arg_bfr (bfr))) return 0;
if (i < nargs - 1 && getc_bfr (bfr) != ' ')
return perr_bfr (bfr, "expected space after argument (argument missing)");
i++;
}
l->nargs = nargs;
return 1;
}
static int32_t
parse_unary_op_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 1)) return 0;
return 1;
}
static int32_t
parse_ext_bfr (Btor2Parser *bfr, Btor2Line *l)
{
uint32_t ext;
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 1)) return 0;
if (getc_bfr (bfr) != ' ')
return perr_bfr (bfr, "expected space after first argument");
if (!parse_pos_number_bfr (bfr, &ext)) return 0;
l->args[1] = ext;
return 1;
}
static int32_t
parse_slice_bfr (Btor2Parser *bfr, Btor2Line *l)
{
uint32_t lower;
if (!parse_ext_bfr (bfr, l)) return 0;
if (getc_bfr (bfr) != ' ')
return perr_bfr (bfr, "expected space after second argument");
if (!parse_pos_number_bfr (bfr, &lower)) return 0;
l->args[2] = lower;
if (lower > l->args[1])
return perr_bfr (bfr, "lower has to be less than or equal to upper");
return 1;
}
static int32_t
parse_binary_op_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 2)) return 0;
return 1;
}
static int32_t
parse_ternary_op_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 3)) return 0;
return 1;
}
static int32_t
check_consth (const char *consth, uint32_t width)
{
char c;
size_t i, len, req_width;
len = strlen (consth);
req_width = len * 4;
for (i = 0; i < len; i++)
{
c = consth[i];
assert (isxdigit (c));
if (c >= 'A' && c <= 'F') c = tolower (c);
if (c == '0')
{
req_width -= 4;
continue;
}
c = (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 0xa;
assert (c > 0 && c <= 15);
if (c >> 1 == 0)
req_width -= 3;
else if (c >> 2 == 0)
req_width -= 2;
else if (c >> 3 == 0)
req_width -= 1;
break;
}
if (req_width <= width) return 1;
return 0;
}
#ifndef NDEBUG
static int32_t
is_bin_str (const char *c)
{
const char *p;
char ch;
assert (c != NULL);
for (p = c; (ch = *p); p++)
if (ch != '0' && ch != '1') return 0;
return 1;
}
#endif
static const char *digit2const_table[10] = {
"",
"1",
"10",
"11",
"100",
"101",
"110",
"111",
"1000",
"1001",
};
static const char *
digit2const (char ch)
{
assert ('0' <= ch);
assert (ch <= '9');
return digit2const_table[ch - '0'];
}
static const char *
strip_zeroes (const char *a)
{
assert (a);
while (*a == '0') a++;
return a;
}
static char *
mult_unbounded_bin_str (const char *a, const char *b)
{
assert (a);
assert (b);
assert (is_bin_str (a));
assert (is_bin_str (b));
char *res, *r, c, x, y, s, m;
uint32_t alen, blen, rlen, i;
const char *p;
a = strip_zeroes (a);
if (!*a) return btor2parser_strdup ("");
if (a[0] == '1' && !a[1]) return btor2parser_strdup (b);
b = strip_zeroes (b);
if (!*b) return btor2parser_strdup ("");
if (b[0] == '1' && !b[1]) return btor2parser_strdup (a);
alen = strlen (a);
blen = strlen (b);
rlen = alen + blen;
res = btor2parser_malloc (rlen + 1);
res[rlen] = 0;
for (r = res; r < res + blen; r++) *r = '0';
for (p = a; p < a + alen; p++) *r++ = *p;
assert (r == res + rlen);
for (i = 0; i < alen; i++)
{
m = res[rlen - 1];
c = '0';
if (m == '1')
{
p = b + blen;
r = res + blen;
while (res < r && b < p)
{
assert (b < p);
x = *--p;
y = *--r;
s = x ^ y ^ c;
c = (x & y) | (x & c) | (y & c);
*r = s;
}
}
memmove (res + 1, res, rlen - 1);
res[0] = c;
}
return res;
}
static char *
add_unbounded_bin_str (const char *a, const char *b)
{
assert (a);
assert (b);
assert (is_bin_str (a));
assert (is_bin_str (b));
char *res, *r, c, x, y, s, *tmp;
uint32_t alen, blen, rlen;
const char *p, *q;
a = strip_zeroes (a);
b = strip_zeroes (b);
if (!*a) return btor2parser_strdup (b);
if (!*b) return btor2parser_strdup (a);
alen = strlen (a);
blen = strlen (b);
rlen = (alen < blen) ? blen : alen;
rlen++;
res = btor2parser_malloc (rlen + 1);
p = a + alen;
q = b + blen;
c = '0';
r = res + rlen;
*r = 0;
while (res < r)
{
x = (a < p) ? *--p : '0';
y = (b < q) ? *--q : '0';
s = x ^ y ^ c;
c = (x & y) | (x & c) | (y & c);
*--r = s;
}
p = strip_zeroes (res);
if ((p != res))
{
tmp = btor2parser_strdup (p);
if (!tmp)
{
free (res);
return 0;
}
free (res);
res = tmp;
}
return res;
}
static char *
dec_to_bin_str (const char *str, uint32_t len)
{
assert (str);
const char *end, *p;
char *res, *tmp;
res = btor2parser_strdup ("");
if (!res) return 0;
end = str + len;
for (p = str; p < end; p++)
{
tmp = mult_unbounded_bin_str (res, "1010");
if (!tmp)
{
free (res);
return 0;
}
free (res);
res = tmp;
tmp = add_unbounded_bin_str (res, digit2const (*p));
if (!tmp)
{
free (res);
return 0;
}
free (res);
res = tmp;
}
assert (strip_zeroes (res) == res);
if (strlen (res)) return res;
free (res);
return btor2parser_strdup ("0");
}
int32_t
check_constd (const char *str, uint32_t width)
{
assert (str);
assert (width);
int32_t is_neg, is_min_val = 0, res;
char *bits;
size_t size_bits, len;
is_neg = (str[0] == '-');
len = is_neg ? strlen (str) - 1 : strlen (str);
bits = dec_to_bin_str (is_neg ? str + 1 : str, len);
size_bits = strlen (bits);
if (is_neg)
{
is_min_val = (bits[0] == '1');
for (size_t i = 1; is_min_val && i < size_bits; i++)
is_min_val = (bits[i] == '0');
}
res = ((is_neg && !is_min_val) || size_bits <= width)
&& (!is_neg || is_min_val || size_bits + 1 <= width);
free (bits);
return res;
}
static int32_t
parse_constant_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (l->sort.tag != BTOR2_TAG_SORT_bitvec)
return perr_bfr (bfr, "expected bitvec sort for %s", l->name);
if (l->tag == BTOR2_TAG_one || l->tag == BTOR2_TAG_ones
|| l->tag == BTOR2_TAG_zero)
{
return 1;
}
int32_t ch = getc_bfr (bfr);
if (ch != ' ') return perr_bfr (bfr, "expected space after sort id");
bfr->nbuf = 0;
if (l->tag == BTOR2_TAG_const)
{
while ('0' == (ch = getc_bfr (bfr)) || ch == '1') pushc_bfr (bfr, ch);
}
else if (l->tag == BTOR2_TAG_constd)
{
if ((ch = getc_bfr (bfr)) && (ch == '-' || isdigit (ch)))
pushc_bfr (bfr, ch);
while (isdigit ((ch = getc_bfr (bfr)))) pushc_bfr (bfr, ch);
}
else if (l->tag == BTOR2_TAG_consth)
{
while (isxdigit ((ch = getc_bfr (bfr)))) pushc_bfr (bfr, ch);
}
if (!bfr->nbuf)
{
perr_bfr (bfr, "expected number");
return 0;
}
if (ch != '\n' && ch != ' ')
{
perr_bfr (bfr, "invalid character '%c' in constant definition", ch);
return 0;
}
ungetc_bfr (bfr, ch);
pushc_bfr (bfr, 0);
if (l->tag == BTOR2_TAG_const && strlen (bfr->buf) != l->sort.bitvec.width)
{
return perr_bfr (bfr,
"constant '%s' does not match bit-vector sort size %u",
bfr->buf,
l->sort.bitvec.width);
}
else if (l->tag == BTOR2_TAG_constd
&& !check_constd (bfr->buf, l->sort.bitvec.width))
{
return perr_bfr (bfr,
"constant '%s' does not match bit-vector sort size %u",
bfr->buf,
l->sort.bitvec.width);
}
else if (l->tag == BTOR2_TAG_consth
&& !check_consth (bfr->buf, l->sort.bitvec.width))
{
return perr_bfr (bfr,
"constant '%s' does not fit into bit-vector of size %u",
bfr->buf,
l->sort.bitvec.width);
}
l->constant = btor2parser_strdup (bfr->buf);
return 1;
}
static int32_t
parse_input_bfr (Btor2Parser *bfr, Btor2Line *l)
{
return parse_sort_id_bfr (bfr, &l->sort);
}
BTOR2_DECLARE_STACK (Btor2Long, int64_t);
static int32_t
check_state_init (Btor2Parser *bfr, int64_t state_id, int64_t init_id)
{
assert (state_id > init_id);
(void) state_id;
int32_t res = 1;
int64_t id;
uint32_t i;
Btor2Line *line;
Btor2LongStack stack;
char *cache;
line = id2line_bfr (bfr, init_id);
size_t size = (labs (init_id) + 1) * sizeof (char);
cache = btor2parser_malloc (size);
memset (cache, 0, size);
BTOR2_INIT_STACK (stack);
BTOR2_PUSH_STACK (stack, init_id);
do
{
id = BTOR2_POP_STACK (stack);
assert (id);
if (id < 0) id = -id;
assert (id != state_id);
if (cache[id]) continue;
cache[id] = 1;
line = id2line_bfr (bfr, id);
if (line->tag == BTOR2_TAG_input)
{
res = perr_bfr (bfr,
"inputs are not allowed in initialization expressions, "
"use a state instead of input %" PRId64 ".",
id);
break;
}
for (i = 0; i < line->nargs; i++) BTOR2_PUSH_STACK (stack, line->args[i]);
} while (!BTOR2_EMPTY_STACK (stack));
free (cache);
BTOR2_RELEASE_STACK (stack);
return res;
}
static int32_t
parse_init_bfr (Btor2Parser *bfr, Btor2Line *l)
{
Btor2Line *state;
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 2)) return 0;
if (l->args[0] < 0) return perr_bfr (bfr, "invalid negated first argument");
state = id2line_bfr (bfr, l->args[0]);
if (state->tag != BTOR2_TAG_state)
return perr_bfr (bfr, "expected state as first argument");
if (l->args[0] < labs (l->args[1]))
return perr_bfr (bfr, "state id must be greater than id of second operand");
if (!check_state_init (bfr, l->args[0], l->args[1])) return 0;
if (state->init)
return perr_bfr (bfr, "state %" PRId64 " initialized twice", state->id);
state->init = l->args[1];
return 1;
}
static int32_t
parse_next_bfr (Btor2Parser *bfr, Btor2Line *l)
{
Btor2Line *state;
if (!parse_sort_id_bfr (bfr, &l->sort)) return 0;
if (!parse_args (bfr, l, 2)) return 0;
if (l->args[0] < 0) return perr_bfr (bfr, "invalid negated first argument");
state = id2line_bfr (bfr, l->args[0]);
if (state->tag != BTOR2_TAG_state)
return perr_bfr (bfr, "expected state as first argument");
if (state->next)
return perr_bfr (bfr, "next for state %" PRId64 " set twice", state->id);
state->next = l->args[1];
return 1;
}
static int32_t
parse_constraint_bfr (Btor2Parser *bfr, Btor2Line *l)
{
if (!(l->args[0] = parse_arg_bfr (bfr))) return 0;
Btor2Line *arg = id2line_bfr (bfr, l->args[0]);
if (arg->tag == BTOR2_TAG_sort)
return perr_bfr (bfr, "unexpected sort id after tag");
l->nargs = 1;
return 1;
}
static int32_t
parse_justice_bfr (Btor2Parser *bfr, Btor2Line *l)
{
uint32_t nargs;
if (!parse_pos_number_bfr (bfr, &nargs)) return 0;
l->args = btor2parser_realloc (l->args, sizeof (int64_t) * nargs);
l->nargs = nargs;
if (!parse_args (bfr, l, nargs)) return 0;
return 1;
}
#define PARSE(NAME, GENERIC) \
do \
{ \
if (!strcmp (tag, #NAME)) \
{ \
Btor2Line *LINE = \
new_line_bfr (bfr, id, lineno, #NAME, BTOR2_TAG_##NAME); \
if (parse_##GENERIC##_bfr (bfr, LINE)) \
{ \
pusht_bfr (bfr, LINE); \
assert (bfr->table[id] == LINE); \
if (!check_sorts_bfr (bfr, LINE) || !parse_opt_symbol_bfr (bfr, LINE)) \
{ \
return 0; \
} \
return 1; \
} \
else \
{ \
return 0; \
} \
} \
} while (0)
static int32_t
readl_bfr (Btor2Parser *bfr)
{
const char *tag;
int64_t lineno;
int64_t id;
int32_t ch;
START:
while ((ch = getc_bfr (bfr)) == ' ')
;
if (ch == EOF) return 0;
if (ch == '\n') goto START;
if (ch == '\t')
return perr_bfr (bfr, "unexpected tab character at start of line");
if (ch == ';')
{
if (!skip_comment (bfr)) return 0;
goto START;
}
ungetc_bfr (bfr, ch);
if (!parse_id_bfr (bfr, &id)) return 0;
if (getc_bfr (bfr) != ' ') return perr_bfr (bfr, "expected space after id");
if (id < bfr->ntable)
{
if (id2line_bfr (bfr, id) != 0) return perr_bfr (bfr, "id already defined");
return perr_bfr (bfr, "id out-of-order");
}
lineno = bfr->lineno;
tag = parse_tag (bfr);
if (!tag) return 0;
switch (tag[0])
{
case 'a':
PARSE (add, binary_op);
PARSE (and, binary_op);
break;
case 'b': PARSE (bad, constraint); break;
case 'c':
PARSE (constraint, constraint);
PARSE (concat, binary_op);
PARSE (const, constant);
PARSE (constd, constant);
PARSE (consth, constant);
break;
case 'd': PARSE (dec, unary_op); break;
case 'e': PARSE (eq, binary_op); break;
case 'f': PARSE (fair, constraint); break;
case 'i':
PARSE (implies, binary_op);
PARSE (iff, binary_op);
PARSE (inc, unary_op);
PARSE (init, init);
PARSE (input, input);
PARSE (ite, ternary_op);
break;
case 'j': PARSE (justice, justice); break;
case 'm': PARSE (mul, binary_op); break;
case 'n':
PARSE (nand, binary_op);
PARSE (neq, binary_op);
PARSE (neg, unary_op);
PARSE (next, next);
PARSE (nor, binary_op);
PARSE (not, unary_op);
break;
case 'o':
PARSE (one, constant);
PARSE (ones, constant);
PARSE (or, binary_op);
PARSE (output, constraint);
break;
case 'r':
PARSE (read, binary_op);
PARSE (redand, unary_op);
PARSE (redor, unary_op);
PARSE (redxor, unary_op);
PARSE (rol, binary_op);
PARSE (ror, binary_op);
break;
case 's':
PARSE (saddo, binary_op);
PARSE (sext, ext);
PARSE (sgt, binary_op);
PARSE (sgte, binary_op);
PARSE (sdiv, binary_op);
PARSE (sdivo, binary_op);
PARSE (slice, slice);
PARSE (sll, binary_op);
PARSE (slt, binary_op);
PARSE (slte, binary_op);
PARSE (sort, sort);
PARSE (smod, binary_op);
PARSE (smulo, binary_op);
PARSE (ssubo, binary_op);
PARSE (sra, binary_op);
PARSE (srl, binary_op);
PARSE (srem, binary_op);
PARSE (state, input);
PARSE (sub, binary_op);
break;
case 'u':
PARSE (uaddo, binary_op);
PARSE (udiv, binary_op);
PARSE (uext, ext);
PARSE (ugt, binary_op);
PARSE (ugte, binary_op);
PARSE (ult, binary_op);
PARSE (ulte, binary_op);
PARSE (umulo, binary_op);
PARSE (urem, binary_op);
PARSE (usubo, binary_op);
break;
case 'w': PARSE (write, ternary_op); break;
case 'x':
PARSE (xnor, binary_op);
PARSE (xor, binary_op);
break;
case 'z': PARSE (zero, constant); break;
}
return perr_bfr (bfr, "invalid tag '%s'", tag);
}
int32_t
btor2parser_read_lines (Btor2Parser *bfr, FILE *file)
{
reset_bfr (bfr);
bfr->lineno = 1;
bfr->saved = EOF;
bfr->file = file;
while (readl_bfr (bfr))
;
return !bfr->error;
}
const char *
btor2parser_error (Btor2Parser *bfr)
{
return bfr->error;
}
static int64_t
find_non_zero_line_bfr (Btor2Parser *bfr, int64_t start)
{
int64_t res;
for (res = start; res < bfr->ntable; res++)
if (bfr->table[res]) return res;
return 0;
}
Btor2LineIterator
btor2parser_iter_init (Btor2Parser *bfr)
{
Btor2LineIterator res;
res.reader = bfr;
if (bfr->error)
res.next = 0;
else
res.next = find_non_zero_line_bfr (bfr, 1);
return res;
}
Btor2Line *
btor2parser_iter_next (Btor2LineIterator *it)
{
Btor2Line *res;
if (!it->next) return 0;
assert (0 < it->next);
assert (it->next < it->reader->ntable);
res = it->reader->table[it->next];
it->next = find_non_zero_line_bfr (it->reader, it->next + 1);
return res;
}
Btor2Line *
btor2parser_get_line_by_id (Btor2Parser *bfr, int64_t id)
{
return id2line_bfr (bfr, id);
}
int64_t
btor2parser_max_id (Btor2Parser *bfr)
{
return bfr->ntable - 1;
}