407 lines
9.1 KiB
C
407 lines
9.1 KiB
C
#include "internal.h"
|
|
#include "parse.h"
|
|
#include "print.h"
|
|
#include "profile.h"
|
|
#include "resize.h"
|
|
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
|
|
static int
|
|
next (file * file, uint64_t * lineno_ptr)
|
|
{
|
|
int ch = kissat_getc (file);
|
|
if (ch == '\n')
|
|
*lineno_ptr += 1;
|
|
return ch;
|
|
}
|
|
|
|
#define NEXT() \
|
|
next (file, lineno_ptr)
|
|
|
|
static const char *
|
|
nonl (int ch, const char *str, uint64_t * lineno_ptr)
|
|
{
|
|
if (ch == '\n')
|
|
{
|
|
assert (*lineno_ptr > 1);
|
|
*lineno_ptr -= 1;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
#define TRY_RELAXED_PARSING "(try '--relaxed' parsing)"
|
|
|
|
static const char *
|
|
parse_dimacs (kissat * solver, strictness strict,
|
|
file * file, uint64_t * lineno_ptr, int *max_var_ptr)
|
|
{
|
|
*lineno_ptr = 1;
|
|
bool first = true;
|
|
int ch;
|
|
for (;;)
|
|
{
|
|
ch = NEXT ();
|
|
if (ch == 'p')
|
|
break;
|
|
else if (ch == EOF)
|
|
{
|
|
if (first)
|
|
return "empty file";
|
|
else
|
|
return "end-of-file before header";
|
|
}
|
|
first = false;
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
if (strict == PEDANTIC_PARSING)
|
|
return nonl (ch, "unexpected empty line", lineno_ptr);
|
|
}
|
|
else if (ch == '\n')
|
|
{
|
|
if (strict == PEDANTIC_PARSING)
|
|
return nonl (ch, "unexpected empty line", lineno_ptr);
|
|
}
|
|
else if (ch == 'c')
|
|
{
|
|
START:
|
|
ch = NEXT ();
|
|
if (ch == '\n')
|
|
continue;
|
|
else if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
continue;
|
|
}
|
|
else if (ch == EOF)
|
|
return "end-of-file in header comment";
|
|
else if (ch == ' ' || ch == '\t')
|
|
goto START;
|
|
#if !defined(NOPTIONS) && !defined(NEMBEDDED)
|
|
else if (ch == '-' && GET_OPTION (embedded))
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '-')
|
|
goto COMPLETE;
|
|
|
|
char name[32];
|
|
unsigned pos = 0;
|
|
while ('a' <= (ch = NEXT ()) && ch <= 'z')
|
|
{
|
|
assert (pos < sizeof name);
|
|
name[pos++] = ch;
|
|
if (pos == sizeof name)
|
|
goto COMPLETE;
|
|
}
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
}
|
|
if (ch == '\n')
|
|
continue;
|
|
if (ch != '=')
|
|
goto COMPLETE;
|
|
assert (pos < sizeof name);
|
|
name[pos++] = 0;
|
|
|
|
pos = 0;
|
|
ch = NEXT ();
|
|
int sign;
|
|
if (ch == '-')
|
|
{
|
|
ch = NEXT ();
|
|
sign = -1;
|
|
}
|
|
else
|
|
sign = 1;
|
|
if (!isdigit (ch))
|
|
goto COMPLETE;
|
|
int arg = ch - '0';
|
|
while (isdigit (ch = NEXT ()))
|
|
{
|
|
if (INT_MAX / 10 < arg)
|
|
goto COMPLETE;
|
|
arg *= 10;
|
|
int digit = ch - '0';
|
|
if (INT_MAX - digit < arg)
|
|
goto COMPLETE;
|
|
arg += digit;
|
|
}
|
|
while (ch == ' ' || ch == '\t')
|
|
ch = NEXT ();
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
}
|
|
if (ch != '\n')
|
|
goto COMPLETE;
|
|
arg *= sign;
|
|
const opt *opt = kissat_options_has (name);
|
|
if (opt)
|
|
{
|
|
(void) kissat_options_set_opt (&solver->options, opt, arg);
|
|
kissat_verbose (solver,
|
|
"parsed embedded option '--%s=%d'",
|
|
name, arg);
|
|
}
|
|
else
|
|
kissat_warning (solver,
|
|
"invalid embedded option '--%s=%d'",
|
|
name, arg);
|
|
continue;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
while ((ch = NEXT ()) != '\n')
|
|
#if !defined(NOPTIONS) && !defined(NEMBEDDED)
|
|
COMPLETE:
|
|
#endif
|
|
if (ch == EOF)
|
|
return "end-of-file in header comment";
|
|
else if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return "expected 'c' or 'p' at start of line";
|
|
}
|
|
assert (ch == 'p');
|
|
ch = NEXT ();
|
|
if (ch != ' ')
|
|
return nonl (ch, "expected space after 'p'", lineno_ptr);
|
|
ch = NEXT ();
|
|
if (strict != PEDANTIC_PARSING)
|
|
{
|
|
while (ch == ' ' || ch == '\t')
|
|
ch = NEXT ();
|
|
}
|
|
if (ch != 'c')
|
|
return nonl (ch, "expected 'c' after 'p '", lineno_ptr);
|
|
ch = NEXT ();
|
|
if (ch != 'n')
|
|
return nonl (ch, "expected 'n' after 'p c'", lineno_ptr);
|
|
ch = NEXT ();
|
|
if (ch != 'f')
|
|
return nonl (ch, "expected 'n' after 'p cn'", lineno_ptr);
|
|
ch = NEXT ();
|
|
if (ch != ' ')
|
|
return nonl (ch, "expected space after 'p cnf'", lineno_ptr);
|
|
ch = NEXT ();
|
|
if (strict != PEDANTIC_PARSING)
|
|
{
|
|
while (ch == ' ' || ch == '\t')
|
|
ch = NEXT ();
|
|
}
|
|
if (!isdigit (ch))
|
|
return nonl (ch, "expected digit after 'p cnf '", lineno_ptr);
|
|
int variables = ch - '0';
|
|
while (isdigit (ch = NEXT ()))
|
|
{
|
|
if (EXTERNAL_MAX_VAR / 10 < variables)
|
|
return "maximum variable too large";
|
|
variables *= 10;
|
|
const int digit = ch - '0';
|
|
if (EXTERNAL_MAX_VAR - digit < variables)
|
|
return "maximum variable too large";
|
|
variables += digit;
|
|
}
|
|
if (ch == EOF)
|
|
return "unexpected end-of-file while parsing maximum variable";
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
}
|
|
if (ch == '\n')
|
|
return nonl (ch, "unexpected new-line after maximum variable",
|
|
lineno_ptr);
|
|
if (ch != ' ')
|
|
return "expected space after maximum variable";
|
|
ch = NEXT ();
|
|
if (strict != PEDANTIC_PARSING)
|
|
{
|
|
while (ch == ' ' || ch == '\t')
|
|
ch = NEXT ();
|
|
}
|
|
if (!isdigit (ch))
|
|
return "expected number of clauses after maximum variable";
|
|
uint64_t clauses = ch - '0';
|
|
while (isdigit (ch = NEXT ()))
|
|
{
|
|
if (UINT64_MAX / 10 < clauses)
|
|
return "number of clauses too large";
|
|
clauses *= 10;
|
|
const int digit = ch - '0';
|
|
if (UINT64_MAX - digit < clauses)
|
|
return "number of clauses too large";
|
|
clauses += digit;
|
|
}
|
|
if (ch == EOF)
|
|
return "unexpected end-of-file while parsing number of clauses";
|
|
if (strict != PEDANTIC_PARSING)
|
|
{
|
|
while (ch == ' ' || ch == '\t')
|
|
ch = NEXT ();
|
|
}
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
}
|
|
if (ch == EOF)
|
|
return "unexpected end-of-file after parsing number of clauses";
|
|
if (ch != '\n')
|
|
return "expected new-line after parsing number of clauses";
|
|
// kissat_message (solver,
|
|
// "parsed 'p cnf %d %" PRIu64 "' header", variables, clauses);
|
|
*max_var_ptr = variables;
|
|
kissat_reserve (solver, variables);
|
|
uint64_t parsed = 0;
|
|
int lit = 0;
|
|
for (;;)
|
|
{
|
|
ch = NEXT ();
|
|
if (ch == ' ')
|
|
continue;
|
|
if (ch == '\t')
|
|
continue;
|
|
if (ch == '\n')
|
|
continue;
|
|
if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
continue;
|
|
}
|
|
if (ch == 'c')
|
|
{
|
|
while ((ch = NEXT ()) != '\n')
|
|
if (ch == EOF)
|
|
{
|
|
if (strict != PEDANTIC_PARSING)
|
|
break;
|
|
return "unexpected end-of-file in comment after header";
|
|
}
|
|
if (ch == EOF)
|
|
break;
|
|
continue;
|
|
}
|
|
if (ch == EOF)
|
|
break;
|
|
int sign;
|
|
if (ch == '-')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch == EOF)
|
|
return "unexpected end-of-file after '-'";
|
|
if (ch == '\n')
|
|
return nonl (ch, "unexpected new-line after '-'", lineno_ptr);
|
|
if (!isdigit (ch))
|
|
return "expected digit after '-'";
|
|
if (ch == '0')
|
|
return "expected non-zero digit after '-'";
|
|
sign = -1;
|
|
}
|
|
else if (!isdigit (ch))
|
|
return "expected digit or '-'";
|
|
else
|
|
sign = 1;
|
|
assert (isdigit (ch));
|
|
int idx = ch - '0';
|
|
while (isdigit (ch = NEXT ()))
|
|
{
|
|
if (EXTERNAL_MAX_VAR / 10 < idx)
|
|
return "variable index too large";
|
|
idx *= 10;
|
|
const int digit = ch - '0';
|
|
if (EXTERNAL_MAX_VAR - digit < idx)
|
|
return "variable index too large";
|
|
idx += digit;
|
|
}
|
|
if (ch == EOF)
|
|
{
|
|
if (strict == PEDANTIC_PARSING)
|
|
{
|
|
if (idx)
|
|
return "unexpected end-of-file after literal";
|
|
else
|
|
return "unexpected end-of-file after trailing zero";
|
|
}
|
|
}
|
|
else if (ch == '\r')
|
|
{
|
|
ch = NEXT ();
|
|
if (ch != '\n')
|
|
return "expected new-line after carriage-return";
|
|
}
|
|
else if (ch == 'c')
|
|
{
|
|
while ((ch = NEXT ()) != '\n')
|
|
if (ch == EOF)
|
|
{
|
|
if (strict != PEDANTIC_PARSING)
|
|
break;
|
|
return "unexpected end-of-file in comment after literal";
|
|
}
|
|
}
|
|
else if (ch != ' ' && ch != '\t' && ch != '\n')
|
|
return "expected white space after literal";
|
|
if (strict != RELAXED_PARSING && idx > variables)
|
|
return nonl (ch, "maximum variable index exceeded "
|
|
TRY_RELAXED_PARSING, lineno_ptr);
|
|
if (idx)
|
|
{
|
|
assert (sign == 1 || sign == -1);
|
|
assert (idx != INT_MIN);
|
|
lit = sign * idx;
|
|
}
|
|
else
|
|
{
|
|
if (strict != RELAXED_PARSING && parsed == clauses)
|
|
return "too many clauses " TRY_RELAXED_PARSING;
|
|
parsed++;
|
|
lit = 0;
|
|
}
|
|
kissat_add (solver, lit);
|
|
}
|
|
if (lit)
|
|
return "trailing zero missing";
|
|
if (strict != RELAXED_PARSING && parsed < clauses)
|
|
{
|
|
if (parsed + 1 == clauses)
|
|
return "one clause missing " TRY_RELAXED_PARSING;
|
|
return "more than one clause missing " TRY_RELAXED_PARSING;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
kissat_parse_dimacs (kissat * solver,
|
|
strictness strict,
|
|
file * file, uint64_t * lineno_ptr, int *max_var_ptr)
|
|
{
|
|
const char *res;
|
|
START (parse);
|
|
res = parse_dimacs (solver, strict, file, lineno_ptr, max_var_ptr);
|
|
STOP (parse);
|
|
return res;
|
|
}
|