[430] | 1 | /* |
---|
| 2 | o---------------------------------------------------------------------o |
---|
| 3 | | |
---|
| 4 | | Numdiff |
---|
| 5 | | |
---|
| 6 | | Copyright (c) 2012+ laurent.deniau@cern.ch |
---|
| 7 | | Gnu General Public License |
---|
| 8 | | |
---|
| 9 | o---------------------------------------------------------------------o |
---|
| 10 | |
---|
| 11 | Purpose: |
---|
| 12 | create constraints content |
---|
| 13 | print, scan constraints from file |
---|
| 14 | |
---|
| 15 | o---------------------------------------------------------------------o |
---|
| 16 | */ |
---|
| 17 | |
---|
| 18 | #include <assert.h> |
---|
| 19 | #include <string.h> |
---|
| 20 | #include <ctype.h> |
---|
| 21 | #include "constraint.h" |
---|
| 22 | #include "utils.h" |
---|
| 23 | #include "error.h" |
---|
| 24 | #include "args.h" |
---|
| 25 | |
---|
| 26 | #define T struct constraint |
---|
| 27 | |
---|
| 28 | // ----- constants |
---|
| 29 | |
---|
| 30 | const char * const eps_cmd_cstr[] = { |
---|
| 31 | [eps_invalid] = "invalid", |
---|
| 32 | [eps_dig] = "dig", |
---|
| 33 | [eps_rel] = "rel", |
---|
| 34 | [eps_rel|eps_dig] = "rel&dig", |
---|
| 35 | [eps_abs] = "abs", |
---|
| 36 | [eps_abs|eps_dig] = "abs&dig", |
---|
| 37 | [eps_abs|eps_rel] = "abs&rel", |
---|
| 38 | [eps_abs|eps_rel|eps_dig] = "abs&rel&dig", |
---|
| 39 | [eps_equ] = "equ", |
---|
| 40 | [eps_ign] = "ign", |
---|
| 41 | [eps_omit] = "omit", |
---|
| 42 | [eps_skip] = "skip", |
---|
| 43 | [eps_goto] = "goto", |
---|
| 44 | }; |
---|
| 45 | |
---|
| 46 | // ----- private |
---|
| 47 | |
---|
| 48 | static void |
---|
| 49 | printSlc(const struct slice *s, FILE *out) |
---|
| 50 | { |
---|
| 51 | if (slice_first(s) <= 1 && slice_isInfinite(s) && slice_isDense(s)) { |
---|
| 52 | putc('*', out); |
---|
| 53 | return; |
---|
| 54 | } |
---|
| 55 | |
---|
| 56 | fprintf(out, "%u", slice_first(s)); |
---|
| 57 | |
---|
| 58 | if (slice_isUnit(s)) return; |
---|
| 59 | |
---|
| 60 | if (slice_isInfinite(s)) { |
---|
| 61 | putc('-', out); |
---|
| 62 | putc('$', out); |
---|
| 63 | } else |
---|
| 64 | fprintf(out, "-%u", slice_last(s)); |
---|
| 65 | |
---|
| 66 | if (slice_stride(s) != 1) |
---|
| 67 | fprintf(out, "/%u", slice_stride(s)); |
---|
| 68 | } |
---|
| 69 | |
---|
| 70 | static int |
---|
| 71 | readSlcOrRng(struct slice *s, FILE *in) |
---|
| 72 | { |
---|
| 73 | int c, r = 1; |
---|
| 74 | uint first=0, last=0, stride=1; |
---|
| 75 | |
---|
| 76 | // skip spaces |
---|
| 77 | while((c = getc(in)) != EOF && isblank(c)) ; |
---|
| 78 | if (c == EOF) return EOF; |
---|
| 79 | |
---|
| 80 | // ('*'|num) |
---|
| 81 | if (c == '*') { last = UINT_MAX; goto finish; } |
---|
| 82 | else { |
---|
| 83 | ungetc(c, in); |
---|
| 84 | if (fscanf(in, "%u", &first) != 1) return EOF; |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | // (':'|'-')? |
---|
| 88 | c = getc(in); |
---|
| 89 | if (c == ':') r = 0; // slice |
---|
| 90 | else if (c == '-') ; // range |
---|
| 91 | else { ungetc(c, in); last = first; goto finish; } |
---|
| 92 | |
---|
| 93 | // ('$'|num) |
---|
| 94 | c = getc(in); |
---|
| 95 | if (c == '$') last = UINT_MAX; |
---|
| 96 | else { |
---|
| 97 | ungetc(c, in); |
---|
| 98 | if (fscanf(in, "%u", &last) != 1) return EOF; |
---|
| 99 | } |
---|
| 100 | |
---|
| 101 | // ('/'num)? |
---|
| 102 | c = getc(in); |
---|
| 103 | if (c != '/') { ungetc(c, in); stride = 1; goto finish; } |
---|
| 104 | else |
---|
| 105 | if (fscanf(in, "%u", &stride) != 1) return EOF; |
---|
| 106 | |
---|
| 107 | finish: |
---|
| 108 | if (r) |
---|
| 109 | *s = slice_initLastStride(first, last, stride); |
---|
| 110 | else |
---|
| 111 | *s = slice_initSizeStride(first, last, stride); |
---|
| 112 | |
---|
| 113 | trace("<-readSlcOrRng %u%c%u/%u", first, r ? '-' : ':', last, stride); |
---|
| 114 | |
---|
| 115 | return 0; |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | static int |
---|
| 119 | readEps(struct eps *e, FILE *in, int row) |
---|
| 120 | { |
---|
| 121 | int c = 0, n = 0, cmd = eps_invalid; |
---|
| 122 | char str[16]; |
---|
| 123 | |
---|
| 124 | while (1) { |
---|
| 125 | // parse next constraint |
---|
| 126 | *str = 0; |
---|
| 127 | n = fscanf(in, "%*[ \t]%10[^= \t\n\r!#]", str); |
---|
| 128 | if (n == EOF || *str == 0) break; |
---|
| 129 | |
---|
| 130 | if (strcmp(str, "skip") == 0) { |
---|
| 131 | cmd |= eps_skip; trace("[%d] skip", row); |
---|
| 132 | } |
---|
| 133 | else if (strcmp(str, "ign") == 0) { |
---|
| 134 | cmd |= eps_ign; trace("[%d] ign", row); |
---|
| 135 | } |
---|
| 136 | else if (strcmp(str, "equ") == 0) { |
---|
| 137 | cmd |= eps_equ; trace("[%d] equ", row); |
---|
| 138 | } |
---|
| 139 | else if (strcmp(str, "either") == 0) { |
---|
| 140 | e->either = 1; trace("[%d] either", row); |
---|
| 141 | } |
---|
| 142 | else if (strcmp(str, "dig") == 0 && (n = fscanf(in, "=%lf", &e->dig)) == 1) { |
---|
| 143 | cmd |= eps_dig; trace("[%d] dig=%g", row, e->dig); |
---|
| 144 | ensure(e->dig > 1.0, "invalid digital error (%s.cfg:%d)", option.indexed_filename, row); |
---|
| 145 | } |
---|
| 146 | else if (strcmp(str, "rel") == 0 && (n = fscanf(in, "=%lf", &e->rel)) == 1) { |
---|
| 147 | cmd |= eps_rel; trace("[%d] rel=%g", row, e->rel); |
---|
| 148 | ensure(e->rel > 0.0 && (option.largerr || e->rel < 1.0), "invalid relative constraint (%s.cfg:%d)", option.indexed_filename, row); |
---|
| 149 | } |
---|
| 150 | else if (strcmp(str, "abs") == 0 && (n = fscanf(in, "=%lf", &e->abs)) == 1) { |
---|
| 151 | cmd |= eps_abs; trace("[%d] abs=%g", row, e->abs); |
---|
| 152 | ensure(e->abs > 0.0 && (option.largerr || e->abs < 1.0), "invalid absolute constraint (%s.cfg:%d)", option.indexed_filename, row); |
---|
| 153 | } |
---|
| 154 | else if (strcmp(str, "omit") == 0 && (n = fscanf(in, "='%48[^']'", e->tag)) == 1) { |
---|
| 155 | cmd |= eps_omit | eps_equ; e->tag[sizeof e->tag-1] = 0; |
---|
| 156 | trace("[%d] omit='%s'", row, e->tag); |
---|
| 157 | ensure(*e->tag, "invalid empty tag (%s.cfg:%d)", option.indexed_filename, row); |
---|
| 158 | } |
---|
| 159 | else if (strcmp(str, "goto") == 0 && (n = fscanf(in, "='%48[^']'", e->tag)) == 1) { |
---|
| 160 | cmd |= eps_goto; e->tag[sizeof e->tag-1] = 0; |
---|
| 161 | trace("[%d] goto='%s'", row, e->tag); |
---|
| 162 | ensure(*e->tag, "invalid empty tag (%s.cfg:%d)", option.indexed_filename, row); |
---|
| 163 | } |
---|
| 164 | else { |
---|
| 165 | trace("[%d] invalid '%s'", row, str); |
---|
| 166 | cmd = eps_invalid; |
---|
| 167 | break; |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | // next char |
---|
| 171 | ungetc((c = getc(in)), in); |
---|
| 172 | if (c == EOF || (isspace(c) && !isblank(c)) || c == '#' || c == '!') break; |
---|
| 173 | } |
---|
| 174 | |
---|
| 175 | e->cmd = (enum eps_cmd)cmd; // because of icc spurious warnings |
---|
| 176 | |
---|
| 177 | trace("<-readEps cmd = %d, str = '%s', c = '%c'", cmd, str, c); |
---|
| 178 | |
---|
| 179 | return cmd == eps_invalid || n == EOF ? EOF : 0; |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | // ----- interface |
---|
| 183 | |
---|
| 184 | void |
---|
| 185 | constraint_print(const T* cst, FILE *out) |
---|
| 186 | { |
---|
| 187 | if (!out) out = stdout; |
---|
| 188 | if (!cst) { fprintf(out, "(null)"); return; } |
---|
| 189 | |
---|
| 190 | printSlc(&cst->row, out); |
---|
| 191 | putc(' ', out); |
---|
| 192 | printSlc(&cst->col, out); |
---|
| 193 | putc(' ', out); |
---|
| 194 | |
---|
| 195 | if (cst->eps.either) fprintf(out, "either "); |
---|
| 196 | if (cst->eps.cmd & eps_dig) fprintf(out, "dig=%g ", cst->eps.dig); |
---|
| 197 | if (cst->eps.cmd & eps_rel) fprintf(out, "rel=%g ", cst->eps.rel); |
---|
| 198 | if (cst->eps.cmd & eps_abs) fprintf(out, "abs=%g ", cst->eps.abs); |
---|
| 199 | if (cst->eps.cmd & eps_equ) fprintf(out, "equ "); |
---|
| 200 | if (cst->eps.cmd & eps_ign) fprintf(out, "ign "); |
---|
| 201 | if (cst->eps.cmd & eps_omit) fprintf(out, "omit='%s' ", cst->eps.tag); |
---|
| 202 | if (cst->eps.cmd & eps_skip) fprintf(out, "skip "); |
---|
| 203 | if (cst->eps.cmd & eps_goto) fprintf(out, "goto='%s' ", cst->eps.tag); |
---|
| 204 | } |
---|
| 205 | |
---|
| 206 | void |
---|
| 207 | constraint_scan(T* cst, FILE *in, int *row) |
---|
| 208 | { |
---|
| 209 | int c; |
---|
| 210 | assert(cst && row); |
---|
| 211 | |
---|
| 212 | *cst = (T){ .eps.cmd = (enum eps_cmd)eps_invalid }; // because of icc spurious warnings |
---|
| 213 | |
---|
| 214 | if (!in) in = stdin; |
---|
| 215 | |
---|
| 216 | retry: |
---|
| 217 | |
---|
| 218 | while((c = getc(in)) != EOF && isblank(c)) ; |
---|
| 219 | |
---|
| 220 | // end of file |
---|
| 221 | if (c == EOF) return; |
---|
| 222 | |
---|
| 223 | ungetc(c, in); |
---|
| 224 | |
---|
| 225 | // comment or empty line |
---|
| 226 | if (c == '\n' || c == '\r' || c == '#' || c == '!') { |
---|
| 227 | if (skipLine(in, 0) == '\n') ++*row; |
---|
| 228 | goto retry; |
---|
| 229 | } |
---|
| 230 | |
---|
| 231 | cst->line = *row; |
---|
| 232 | ensure(readSlcOrRng(&cst->row, in ) != EOF, "invalid row range (%s.cfg:%d)" , option.indexed_filename, *row); |
---|
| 233 | ensure(readSlcOrRng(&cst->col, in ) != EOF, "invalid column range (%s.cfg:%d)", option.indexed_filename, *row); |
---|
| 234 | ensure(readEps (&cst->eps, in, *row) != EOF, "invalid constraint or command (%s.cfg:%d)", option.indexed_filename, *row); |
---|
| 235 | if (skipLine(in, 0) == '\n') ++*row; |
---|
| 236 | } |
---|
| 237 | |
---|