2023-03-26 19:15:17 +08:00

452 lines
8.9 KiB
C

#include "error.h"
#include "options.h"
#include "print.h"
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef NOPTIONS
static const opt table[] = {
#define OPTION(N,V,L,H,D) \
{ #N, (int)(V), D },
OPTIONS
#undef OPTION
};
#else
static const opt table[] = {
#define OPTION(N,V,L,H,D) \
{ #N, (int)(V), (int)(L), (int)(H), D },
OPTIONS
#undef OPTION
};
#endif
#define size_table (sizeof table / sizeof * table)
const opt *kissat_options_begin = table;
const opt *kissat_options_end = table + size_table;
static void
check_table_sorted (void)
{
#ifndef NDEBUG
const opt *p = 0;
for (all_options (o))
if (p && strcmp (p->name, o->name) >= 0)
kissat_fatal ("option '%s' before option '%s'", p->name, o->name);
else
p = o;
#endif
}
const opt *
kissat_options_has (const char *name)
{
size_t l = 0, m, r = size_table;
int tmp;
const opt *o;
assert (l < r);
while (l + 1 < r)
{
m = l + (r - l) / 2;
tmp = strcmp (name, (o = table + m)->name);
if (tmp < 0)
r = m;
else if (tmp > 0)
l = m;
else
return o;
}
o = table + l;
tmp = strcmp (o->name, name);
return tmp ? 0 : o;
}
bool
kissat_parse_option_value (const char *val_str, int *res_ptr)
{
if (!strcmp (val_str, "true"))
{
*res_ptr = 1;
return true;
}
if (!strcmp (val_str, "false"))
{
*res_ptr = 0;
return true;
}
int sign = 1;
const char *p = val_str;
char ch = *p++;
if (ch == '-')
{
sign = -1;
ch = *p++;
}
if (!isdigit (ch)) // at least one digit
return false;
const unsigned max = -(unsigned) INT_MIN;
unsigned res = ch - '0';
while (isdigit ((ch = *p++)))
{
if (max / 10 < res)
return false;
res *= 10;
const unsigned digit = ch - '0';
if (max - digit < res)
return false;
res += digit;
if (!res)
return false; // invalid '00'
}
if (ch == 'e') // parse '13e5' etc.
{
if (!isdigit ((ch = *p++))) // at least one digit
return false;
if (res)
{
if (*p) // exactly one digit
return false;
const unsigned digit = ch - '0';
for (unsigned i = 0; i < digit; i++)
{
if (max / 10 < res)
return false;
res *= 10;
}
}
else // parse '0^123123123' etc.
{
while (isdigit (ch = *p++)) // arbitrary many digits
;
if (ch)
return false;
}
}
else if (ch == '^') // parse '2^11' etc.
{
const unsigned base = res;
if (!isdigit ((ch = *p++))) // at least one digit
return false;
unsigned exp = ch - '0';
if (base < 2) // parse '0^123123123' etc.
{
while (isdigit (ch = *p++)) // arbitrary many digits
;
if (ch)
return false;
}
else if (isdigit (ch = *p++)) // parse '2^30' etc.
{
if (*p) // at most two digits
return false;
exp *= 10;
const unsigned digit = ch - '0';
exp += digit;
if (!exp) // '2^00' invalid
return false;
}
else if (ch)
return false;
if (exp)
for (unsigned i = 1; i < exp; i++)
{
if (max / base < res)
return false;
res *= base;
}
else if (base)
res = 1; // parse '3^0'
else
return false; // '0^0' invalid
}
else if (ch)
return false;
assert (res <= max);
if (sign > 0 && res == max)
return false;
res *= sign;
*res_ptr = res;
return true;
}
const char *
kissat_parse_option_name (const char *arg, const char *name)
{
if (arg[0] != '-' || arg[1] != '-')
return 0;
const char *p = arg + 2, *q = name;
while (*p && *p == *q)
p++, q++;
if (*q)
return 0;
if (*p != '=')
return 0;
return p + 1;
}
#ifdef NOPTIONS
void
kissat_init_options (void)
{
check_table_sorted ();
}
int
kissat_options_get (const char *name)
{
const opt *o = kissat_options_has (name);
return o ? o->value : 0;
}
#else
#include "format.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
static void
kissat_printf_usage (const char *option, const char *fmt, ...)
{
va_list ap;
printf (" %-26s ", option);
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
fputc ('\n', stdout);
}
static void
check_ranges (void)
{
#define OPTION(N,V,L,H,D) \
do { \
if ((int)(L) > (int)(H)) \
kissat_fatal ("minimum '%d' of option '%s' above maximum '%d'", \
(int)(L), #N, (int)(H)); \
if ((int)(V) < (int)(L)) \
kissat_fatal ("default value '%d' of option '%s' below minimum '%d'", \
(int)(V), #N, (int)(L)); \
if ((int)(V) > (int)(H)) \
kissat_fatal ("default value '%d' of option '%s' above maximum '%d'", \
(int)(V), #N, (int)(H)); \
} while (0);
OPTIONS
#undef OPTION
}
static void
check_name_length (void)
{
#ifndef NDEBUG
#define OPTION(N,V,L,H,D) \
if (strlen (#N) + 1 > kissat_options_max_name_buffer_size) \
kissat_fatal ("option '%s' name length %zu " \
"exceeds maximum name buffer size %zu", \
#N, strlen (#N), kissat_options_max_name_buffer_size);
OPTIONS
#undef OPTION
#endif
}
int
kissat_options_get (const options * options, const char *name)
{
const int *p = kissat_options_ref (options, kissat_options_has (name));
return p ? *p : 0;
}
int
kissat_options_set_opt (options * options, const opt * o, int value)
{
assert (kissat_options_begin <= o);
assert (o < kissat_options_end);
int *p = (int *) options + (o - table);
int res = *p;
if (value == res)
return res;
if (value < o->low)
value = o->low;
if (value > o->high)
value = o->high;
*p = value;
return res;
}
int
kissat_options_set (options * options, const char *name, int value)
{
const opt *o = kissat_options_has (name);
if (!o)
return 0;
return kissat_options_set_opt (options, o, value);
}
void
kissat_init_options (options * options)
{
check_ranges ();
check_name_length ();
check_table_sorted ();
#define OPTION(N,V,L,H,D) \
assert ((L) <= (V)); \
assert ((V) <= (H)); \
options->N = (V);
OPTIONS
#undef OPTION
}
#define FORMAT_OPTION_LIMIT(V) \
(((V) == INT_MIN || (V) == INT_MAX) ? \
"." : kissat_format_value (&format, false, (V)))
void
kissat_options_usage (void)
{
check_ranges ();
check_name_length ();
check_table_sorted ();
format format;
memset (&format, 0, sizeof format);
#define OPTION(N,V,L,H,D) \
do { \
const bool b = ((L) == 0 && (H) == 1); \
char buffer[96]; \
if (b) \
sprintf (buffer, "--%s=<bool>", #N); \
else \
{ \
const char * low_str = FORMAT_OPTION_LIMIT ((L)); \
const char * high_str = FORMAT_OPTION_LIMIT ((H)); \
sprintf (buffer, "--%s=%s..%s", #N, low_str, high_str); \
} \
const char * val_str = kissat_format_value (&format, b, (V)); \
kissat_printf_usage (buffer, "%s [%s]", D, val_str); \
} while (0);
OPTIONS
#undef OPTION
}
bool
kissat_options_parse_arg (const char *arg, char *buffer, int *val_ptr)
{
if (arg[0] != '-' || arg[1] != '-')
return false;
const char *name = arg + 2, *p = name;
int ch;
while ((ch = *p) && ch != '=')
p++;
if (ch)
{
assert (ch == '=');
const size_t len = p - name;
if (len >= kissat_options_max_name_buffer_size)
return false;
memcpy (buffer, name, len);
buffer[len] = 0;
const opt *o = kissat_options_has (buffer);
if (!o)
return false;
int value;
if (!kissat_parse_option_value (p + 1, &value))
return false;
if (value < o->low || value > o->high)
return false;
*val_ptr = value;
}
else
{
int value = 0;
if (arg[2] == 'n' && arg[3] == 'o' && arg[4] == '-')
{
name += 3;
const opt *o = kissat_options_has (name);
if (!o || o->low > (value = 0))
return false;
}
else
{
const opt *o = kissat_options_has (name);
if (!o || o->high < (value = 1))
return false;
}
assert (strlen (name) < kissat_options_max_name_buffer_size);
strcpy (buffer, name);
*val_ptr = value;
}
return true;
}
static bool
ignore_embedded_option_for_fuzzing (const char *name)
{
#ifndef NEMBEDDED
if (!strcmp (name, "embedded"))
return true;
#endif
#ifndef QUIET
if (!strcmp (name, "quiet"))
return true;
#endif
(void) name;
return false;
}
void
kissat_print_embedded_option_list (void)
{
#define OPTION(N,V,L,H,D) \
if (!ignore_embedded_option_for_fuzzing (#N)) \
printf ("c --%s=%d\n", #N, (int) (V));
OPTIONS
#undef OPTION
}
static bool
ignore_range_option_for_fuzzing (const char *name)
{
#ifdef LOGGING
if (!strcmp (name, "log"))
return true;
#endif
#ifndef NEMBEDDED
if (!strcmp (name, "embedded"))
return true;
#endif
#ifndef QUIET
if (!strcmp (name, "quiet"))
return true;
#endif
if (!strcmp (name, "reduce"))
return true;
if (!strcmp (name, "reluctant"))
return true;
if (!strcmp (name, "rephase"))
return true;
if (!strcmp (name, "restart"))
return true;
return false;
}
void
kissat_print_option_range_list (void)
{
#define OPTION(N,V,L,H,D) \
if (!ignore_range_option_for_fuzzing (#N)) \
printf ("%s %d %d %d\n", #N, (int)(L), (int) (V), (int)(H));
OPTIONS
#undef OPTION
}
#endif