1 /* q2c - parser generator for PSPP procedures.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
36 #define EXIT_SUCCESS 0
40 #define EXIT_FAILURE 1
44 #include "misc/strerror.c"
47 #include "debug-print.h"
49 /* Max length of an input line. */
50 #define MAX_LINE_LEN 1024
52 /* Max token length. */
53 #define MAX_TOK_LEN 1024
58 /* Have the input and output files been opened yet? */
61 /* Input, output files. */
64 /* Input, output file names. */
67 /* Input, output file line number. */
70 /* Input line buffer, current position. */
76 T_STRING = 256, /* String literal. */
77 T_ID = 257 /* Identifier. */
80 /* Current token: either one of the above, or a single character. */
83 /* Token string value. */
86 /* Utility functions. */
90 /* Close all open files and delete the output file, on failure. */
99 if (remove (ofn) == -1)
100 fprintf (stderr, "%s: %s: remove: %s\n", pgmname, ofn, strerror (errno));
103 void hcf (void) NO_RETURN;
105 /* Terminate unsuccessfully. */
113 int fail (const char *, ...) PRINTF_FORMAT (1, 2);
114 int error (const char *, ...) PRINTF_FORMAT (1, 2);
116 /* Output an error message and terminate unsuccessfully. */
118 fail (const char *format, ...)
122 va_start (args, format);
123 fprintf (stderr, "%s: ", pgmname);
124 vfprintf (stderr, format, args);
125 fprintf (stderr, "\n");
131 /* Output a context-dependent error message and terminate
134 error (const char *format,...)
138 va_start (args, format);
139 fprintf (stderr, "%s:%d: (column %d) ", ifn, ln, (int) (cp - buf));
140 vfprintf (stderr, format, args);
141 fprintf (stderr, "\n");
147 #define VME "virtual memory exhausted"
149 /* Allocate a block of SIZE bytes and return a pointer to its
152 xmalloc (size_t size)
161 fail ("xmalloc(%lu): %s", (unsigned long) size, VME);
166 /* Make a dynamically allocated copy of string S and return a pointer
167 to the first character. */
169 xstrdup (const char *s)
175 size = strlen (s) + 1;
179 fail ("xstrdup(%lu): %s", (unsigned long) strlen (s), VME);
185 /* Returns a pointer to one of 8 static buffers. The buffers are used
190 static char b[8][256];
199 /* Copies a string to a static buffer, converting it to lowercase in
200 the process, and returns a pointer to the static buffer. */
202 st_lower (const char *s)
206 p = cp = get_buffer ();
208 *cp++ = tolower ((unsigned char) (*s++));
214 /* Copies a string to a static buffer, converting it to uppercase in
215 the process, and returns a pointer to the static buffer. */
217 st_upper (const char *s)
221 p = cp = get_buffer ();
223 *cp++ = toupper ((unsigned char) (*s++));
229 /* Returns the address of the first non-whitespace character in S, or
230 the address of the null terminator if none. */
232 skip_ws (const char *s)
234 while (isspace ((unsigned char) *s))
239 /* Read one line from the input file into buf. Lines having special
240 formats are handled specially. */
245 if (0 == fgets (buf, MAX_LINE_LEN, in))
248 fail ("%s: fgets: %s", ifn, strerror (errno));
252 cp = strchr (buf, '\n');
260 /* Symbol table manager. */
262 /* Symbol table entry. */
263 typedef struct symbol symbol;
266 symbol *next; /* Next symbol in symbol table. */
267 char *name; /* Symbol name. */
268 int unique; /* 1=Name must be unique in this file. */
269 int ln; /* Line number of definition. */
270 int value; /* Symbol value. */
276 /* Add a symbol to the symbol table having name NAME, uniqueness
277 UNIQUE, and value VALUE. If a symbol having the same name is found
278 in the symbol table, its sequence number is returned and the symbol
279 table is not modified. Otherwise, the symbol is added and the next
280 available sequence number is returned. */
282 add_symbol (const char *name, int unique, int value)
287 sym = xmalloc (sizeof (symbol));
288 sym->name = xstrdup (name);
289 sym->unique = unique;
302 if (!strcmp (iter->name, name))
306 fprintf (stderr, "%s:%d: `%s' is already defined above\n", ifn,
308 fprintf (stderr, "%s:%d: location of previous definition\n", ifn,
325 /* Finds the symbol having given sequence number X within the symbol
326 table, and returns the associated symbol structure. */
333 while (x > 1 && iter)
343 /* Writes a printable representation of the current token to
351 printf ("TR_STRING\t\"%s\"\n", tokstr);
354 printf ("STRING\t\"%s\"\n", tokstr);
357 printf ("ID\t%s\n", tokstr);
360 printf ("PUNCT\t%c\n", token);
363 #endif /* DEBUGGING */
365 /* Reads a token from the input file. */
369 /* Skip whitespace and check for end of file. */
377 fail ("%s: Unexpected end of file.", ifn);
385 while (*cp != '"' && *cp)
391 error ("Unterminated string literal.");
399 error ("Unterminated string literal.");
402 else if (*cp == '_' || isalnum ((unsigned char) *cp))
406 while (*cp == '_' || isalnum ((unsigned char) *cp))
407 *dest++ = toupper ((unsigned char) (*cp++));
420 /* Force the current token to be an identifier token. */
425 error ("Identifier expected.");
428 /* Force the current token to be a string token. */
432 if (token != T_STRING)
433 error ("String expected.");
436 /* Checks whether the current token is the identifier S; if so, skips
437 the token and returns 1; otherwise, returns 0. */
439 match_id (const char *s)
441 if (token == T_ID && !strcmp (tokstr, s))
449 /* Checks whether the current token is T. If so, skips the token and
450 returns 1; otherwise, returns 0. */
462 /* Force the current token to be T, and skip it. */
467 error ("`%c' expected.", t);
473 /* Some specifiers have associated values. */
476 VAL_NONE, /* No value. */
477 VAL_INT, /* Integer value. */
478 VAL_DBL /* Floating point value. */
481 /* For those specifiers with values, the syntax of those values. */
484 VT_PLAIN, /* Unadorned value. */
485 VT_PAREN /* Value must be enclosed in parentheses. */
488 /* Forward definition. */
489 typedef struct specifier specifier;
491 /* A single setting. */
492 typedef struct setting setting;
495 specifier *parent; /* Owning specifier. */
496 setting *next; /* Next in the chain. */
497 char *specname; /* Name of the setting. */
498 int con; /* Sequence number. */
501 int valtype; /* One of VT_*. */
502 int value; /* One of VAL_*. */
503 int optvalue; /* 1=value is optional, 0=value is required. */
504 char *valname; /* Variable name for the value. */
505 char *restriction; /* !=NULL: expression specifying valid values. */
508 /* A single specifier. */
511 specifier *next; /* Next in the chain. */
512 char *varname; /* Variable name. */
513 setting *s; /* Associated settings. */
515 setting *def; /* Default setting. */
516 setting *omit_kw; /* Setting for which the keyword can be omitted. */
518 int index; /* Next array index. */
521 /* Subcommand types. */
524 SBC_PLAIN, /* The usual case. */
525 SBC_VARLIST, /* Variable list. */
526 SBC_INT, /* Integer value. */
527 SBC_PINT, /* Integer inside parentheses. */
528 SBC_DBL, /* Floating point value. */
529 SBC_INT_LIST, /* List of integers (?). */
530 SBC_DBL_LIST, /* List of floating points (?). */
531 SBC_CUSTOM, /* Custom. */
532 SBC_ARRAY, /* Array of boolean values. */
533 SBC_STRING, /* String value. */
534 SBC_VAR /* Single variable name. */
538 /* A single subcommand. */
539 typedef struct subcommand subcommand;
542 subcommand *next; /* Next in the chain. */
543 char *name; /* Subcommand name. */
544 subcommand_type type; /* One of SBC_*. */
545 int once; /* 1=Subcommand may appear only once. */
546 int narray; /* Index of next array element. */
547 const char *prefix; /* Prefix for variable and constant names. */
548 specifier *spec; /* Array of specifiers. */
550 /* SBC_STRING and SBC_INT only. */
551 char *restriction; /* Expression restricting string length. */
552 char *message; /* Error message. */
553 int translatable; /* Error message is translatable */
556 typedef struct aux_subcommand aux_subcommand;
557 struct aux_subcommand
559 aux_subcommand *next; /* Next in the chain. */
560 char *name; /* Subcommand name. */
561 char *value; /* Subcommand value */
564 static aux_subcommand *aux_subcommands ;
566 /* Name of the command; i.e., DESCRIPTIVES. */
569 /* Short prefix for the command; i.e., `dsc_'. */
572 /* List of subcommands. */
573 subcommand *subcommands;
575 /* Default subcommand if any, or NULL. */
580 void parse_subcommands (void);
582 /* Parse an entire specification. */
586 /* Get the command name and prefix. */
587 if (token != T_STRING && token != T_ID)
588 error ("Command name expected.");
589 cmdname = xstrdup (tokstr);
593 prefix = xstrdup (tokstr);
598 /* Read all the subcommands. */
601 parse_subcommands ();
604 /* Parses a single setting into S, given subcommand information SBC
605 and specifier information SPEC. */
607 parse_setting (setting *s, specifier *spec)
611 if (match_token ('*'))
614 error ("Cannot have two settings with omittable keywords.");
619 if (match_token ('!'))
622 error ("Cannot have two default settings.");
628 s->specname = xstrdup (tokstr);
629 s->con = add_symbol (s->specname, 0, 0);
634 /* Parse setting value info if necessary. */
635 if (token != '/' && token != ';' && token != '.' && token != ',')
639 s->valtype = VT_PAREN;
643 s->valtype = VT_PLAIN;
645 s->optvalue = match_token ('*');
649 else if (match_id ("D"))
652 error ("`n' or `d' expected.");
657 s->valname = xstrdup (tokstr);
664 s->restriction = xstrdup (tokstr);
668 s->restriction = NULL;
670 if (s->valtype == VT_PAREN)
675 /* Parse a single specifier into SPEC, given subcommand information
678 parse_specifier (specifier *spec, subcommand *sbc)
683 spec->omit_kw = NULL;
684 spec->varname = NULL;
688 spec->varname = xstrdup (st_lower (tokstr));
692 /* Handle array elements. */
695 spec->index = sbc->narray;
696 if (sbc->type == SBC_ARRAY)
708 if ( sbc->type == SBC_ARRAY && token == T_ID )
710 spec->varname = xstrdup (st_lower (tokstr));
711 spec->index = sbc->narray;
717 /* Parse all the settings. */
719 setting **s = &spec->s;
723 *s = xmalloc (sizeof (setting));
724 parse_setting (*s, spec);
725 if (token == ',' || token == ';' || token == '.')
734 /* Parse a list of specifiers for subcommand SBC. */
736 parse_specifiers (subcommand *sbc)
738 specifier **spec = &sbc->spec;
740 if (token == ';' || token == '.')
748 *spec = xmalloc (sizeof (specifier));
749 parse_specifier (*spec, sbc);
750 if (token == ';' || token == '.')
753 spec = &(*spec)->next;
755 (*spec)->next = NULL;
758 /* Parse a subcommand into SBC. */
760 parse_subcommand (subcommand *sbc)
762 if (match_token ('*'))
765 error ("Multiple default subcommands.");
769 sbc->once = match_token ('+');
772 sbc->name = xstrdup (tokstr);
776 sbc->type = SBC_PLAIN;
778 sbc->translatable = 0;
780 if (match_token ('['))
783 sbc->prefix = xstrdup (st_lower (tokstr));
789 sbc->type = SBC_ARRAY;
790 parse_specifiers (sbc);
795 if (match_token ('('))
798 sbc->prefix = xstrdup (st_lower (tokstr));
808 if (match_id ("VAR"))
810 if (match_id ("VARLIST"))
812 if (match_token ('('))
815 sbc->message = xstrdup (tokstr);
820 else sbc->message = NULL;
822 sbc->type = SBC_VARLIST;
824 else if (match_id ("INTEGER"))
826 sbc->type = match_id ("LIST") ? SBC_INT_LIST : SBC_INT;
827 if ( token == T_STRING)
829 sbc->restriction = xstrdup (tokstr);
831 if ( match_id("N_") )
837 sbc->translatable = 1;
843 sbc->message = xstrdup (tokstr);
846 sbc->restriction = NULL;
848 else if (match_id ("PINT"))
849 sbc->type = SBC_PINT;
850 else if (match_id ("DOUBLE"))
852 if ( match_id ("LIST") )
853 sbc->type = SBC_DBL_LIST;
857 else if (match_id ("STRING"))
859 sbc->type = SBC_STRING;
860 if (token == T_STRING)
862 sbc->restriction = xstrdup (tokstr);
865 sbc->message = xstrdup (tokstr);
869 sbc->restriction = NULL;
871 else if (match_id ("CUSTOM"))
872 sbc->type = SBC_CUSTOM;
874 parse_specifiers (sbc);
878 /* Parse all the subcommands. */
880 parse_subcommands (void)
882 subcommand **sbc = &subcommands;
886 *sbc = xmalloc (sizeof (subcommand));
889 parse_subcommand (*sbc);
901 #define BASE_INDENT 2 /* Starting indent. */
902 #define INC_INDENT 2 /* Indent increment. */
904 /* Increment the indent. */
905 #define indent() indent += INC_INDENT
906 #define outdent() indent -= INC_INDENT
908 /* Size of the indent from the left margin. */
911 void dump (int, const char *, ...) PRINTF_FORMAT (2, 3);
913 /* Write line FORMAT to the output file, formatted as with printf,
914 indented `indent' characters from the left margin. If INDENTION is
915 greater than 0, indents BASE_INDENT * INDENTION characters after
916 writing the line; if INDENTION is less than 0, dedents BASE_INDENT
917 * INDENTION characters _before_ writing the line. */
919 dump (int indention, const char *format, ...)
925 indent += BASE_INDENT * indention;
928 va_start (args, format);
929 for (i = 0; i < indent; i++)
931 vfprintf (out, format, args);
936 indent += BASE_INDENT * indention;
939 /* Write the structure members for specifier SPEC to the output file.
940 SBC is the including subcommand. */
942 dump_specifier_vars (const specifier *spec, const subcommand *sbc)
945 dump (0, "long %s%s;", sbc->prefix, spec->varname);
950 for (s = spec->s; s; s = s->next)
952 if (s->value != VAL_NONE)
954 const char *typename;
956 assert (s->value == VAL_INT || s->value == VAL_DBL);
957 typename = s->value == VAL_INT ? "long" : "double";
959 dump (0, "%s %s%s;", typename, sbc->prefix, st_lower (s->valname));
965 /* Returns 1 if string T is a PSPP keyword, 0 otherwise. */
967 is_keyword (const char *t)
969 static const char *kw[] =
971 "AND", "OR", "NOT", "EQ", "GE", "GT", "LE", "LT",
972 "NE", "ALL", "BY", "TO", "WITH", 0,
976 for (cp = kw; *cp; cp++)
977 if (!strcmp (t, *cp))
982 /* Transforms a string NAME into a valid C identifier: makes
983 everything lowercase and maps nonalphabetic characters to
984 underscores. Returns a pointer to a static buffer. */
986 make_identifier (const char *name)
988 char *p = get_buffer ();
991 for (cp = p; *name; name++)
992 if (isalpha ((unsigned char) *name))
993 *cp++ = tolower ((unsigned char) (*name));
1001 /* Writes the struct and enum declarations for the parser. */
1003 dump_declarations (void)
1007 /* Write out enums for all the identifiers in the symbol table. */
1013 /* Note the squirmings necessary to make sure that the last enum
1014 is not followed by a comma, as mandated by ANSI C89. */
1015 for (sym = symtab, f = k = 0; sym; sym = sym->next)
1016 if (!sym->unique && !is_keyword (sym->name))
1020 dump (0, "/* Settings for subcommand specifiers. */");
1027 buf = xmalloc (1024);
1032 sprintf (buf, "%s%s,", st_upper (prefix), sym->name);
1036 sprintf (buf, "%s%s = 1000,", st_upper (prefix), sym->name);
1041 buf[strlen (buf) - 1] = 0;
1052 /* Write out some type definitions */
1054 dump (0, "#define MAXLISTS 10");
1058 /* For every array subcommand, write out the associated enumerated
1063 for (sbc = subcommands; sbc; sbc = sbc->next)
1064 if (sbc->type == SBC_ARRAY && sbc->narray)
1066 dump (0, "/* Array indices for %s subcommand. */", sbc->name);
1074 for (spec = sbc->spec; spec; spec = spec->next)
1075 dump (0, "%s%s%s = %d,",
1076 st_upper (prefix), st_upper (sbc->prefix),
1077 st_upper (spec->varname), spec->index);
1079 dump (0, "%s%scount", st_upper (prefix), st_upper (sbc->prefix));
1087 /* Write out structure declaration. */
1091 dump (0, "/* %s structure. */", cmdname);
1092 dump (1, "struct cmd_%s", make_identifier (cmdname));
1094 for (sbc = subcommands; sbc; sbc = sbc->next)
1098 if (sbc != subcommands)
1101 dump (0, "/* %s subcommand. */", sbc->name);
1102 dump (0, "int sbc_%s;", st_lower (sbc->name));
1111 for (spec = sbc->spec; spec; spec = spec->next)
1115 if (sbc->type == SBC_PLAIN)
1116 dump (0, "long int %s%s;", st_lower (sbc->prefix),
1120 dump (0, "int a_%s[%s%scount];",
1121 st_lower (sbc->name),
1123 st_upper (sbc->prefix)
1130 dump_specifier_vars (spec, sbc);
1136 dump (0, "int %sn_%s;", st_lower (sbc->prefix),
1137 st_lower (sbc->name));
1138 dump (0, "struct variable **%sv_%s;", st_lower (sbc->prefix),
1139 st_lower (sbc->name));
1143 dump (0, "struct variable *%sv_%s;", st_lower (sbc->prefix),
1144 st_lower (sbc->name));
1148 dump (0, "char *s_%s;", st_lower (sbc->name));
1153 dump (0, "long n_%s[MAXLISTS];", st_lower (sbc->name));
1157 dump (0, "double n_%s[MAXLISTS];", st_lower (sbc->name));
1161 dump (0, "subc_list_double dl_%s[MAXLISTS];",
1162 st_lower(sbc->name));
1166 dump (0, "subc_list_int il_%s[MAXLISTS];",
1167 st_lower(sbc->name));
1180 /* Write out prototypes for custom_*() functions as necessary. */
1185 for (sbc = subcommands; sbc; sbc = sbc->next)
1186 if (sbc->type == SBC_CUSTOM)
1191 dump (0, "/* Prototype for custom subcommands of %s. */",
1194 dump (0, "static int %scustom_%s (struct cmd_%s *);",
1195 st_lower (prefix), st_lower (sbc->name),
1196 make_identifier (cmdname));
1203 /* Prototypes for parsing and freeing functions. */
1205 dump (0, "/* Command parsing functions. */");
1206 dump (0, "static int parse_%s (struct cmd_%s *);",
1207 make_identifier (cmdname), make_identifier (cmdname));
1208 dump (0, "static void free_%s (struct cmd_%s *);",
1209 make_identifier (cmdname), make_identifier (cmdname));
1214 /* Writes out code to initialize all the variables that need
1215 initialization for particular specifier SPEC inside subcommand SBC. */
1217 dump_specifier_init (const specifier *spec, const subcommand *sbc)
1225 st_upper (prefix), find_symbol (spec->def->con)->name);
1228 dump (0, "p->%s%s = %s;", sbc->prefix, spec->varname, s);
1234 for (s = spec->s; s; s = s->next)
1236 if (s->value != VAL_NONE)
1240 assert (s->value == VAL_INT || s->value == VAL_DBL);
1241 init = s->value == VAL_INT ? "NOT_LONG" : "SYSMIS";
1243 dump (0, "p->%s%s = %s;", sbc->prefix, st_lower (s->valname), init);
1249 /* Write code to initialize all variables. */
1251 dump_vars_init (int persistent)
1253 /* Loop through all the subcommands. */
1257 for (sbc = subcommands; sbc; sbc = sbc->next)
1261 dump (0, "p->sbc_%s = 0;", st_lower (sbc->name));
1272 dump (1, "for (i = 0; i < MAXLISTS; ++i)");
1273 dump (0, "subc_list_double_create(&p->dl_%s[i]) ;",
1274 st_lower (sbc->name)
1282 dump (1, "for (i = 0; i < MAXLISTS; ++i)");
1283 dump (0, "p->n_%s[i] = SYSMIS;", st_lower (sbc->name));
1296 for (spec = sbc->spec; spec; spec = spec->next)
1297 if (spec->s == NULL)
1299 if (sbc->type == SBC_PLAIN)
1300 dump (0, "p->%s%s = 0;", sbc->prefix, spec->varname);
1303 dump (0, "memset (p->a_%s, 0, sizeof p->a_%s);",
1304 st_lower (sbc->name), st_lower (sbc->name));
1309 dump_specifier_init (spec, sbc);
1314 dump (0, "p->%sn_%s = 0;",
1315 st_lower (sbc->prefix), st_lower (sbc->name));
1316 dump (0, "p->%sv_%s = NULL;",
1317 st_lower (sbc->prefix), st_lower (sbc->name));
1321 dump (0, "p->%sv_%s = NULL;",
1322 st_lower (sbc->prefix), st_lower (sbc->name));
1326 dump (0, "p->s_%s = NULL;", st_lower (sbc->name));
1333 dump (1, "for (i = 0; i < MAXLISTS; ++i)");
1334 dump (0, "p->n_%s[i] = NOT_LONG;", st_lower (sbc->name));
1346 /* Return a pointer to a static buffer containing an expression that
1347 will match token T. */
1349 make_match (const char *t)
1359 sprintf (s, "lex_match (T_%s)", t);
1360 else if (!strcmp (t, "ON") || !strcmp (t, "YES"))
1361 strcpy (s, "(lex_match_id (\"ON\") || lex_match_id (\"YES\") "
1362 "|| lex_match_id (\"TRUE\"))");
1363 else if (!strcmp (t, "OFF") || !strcmp (t, "NO"))
1364 strcpy (s, "(lex_match_id (\"OFF\") || lex_match_id (\"NO\") "
1365 "|| lex_match_id (\"FALSE\"))");
1366 else if (isdigit ((unsigned char) t[0]))
1367 sprintf (s, "lex_match_int (%s)", t);
1369 sprintf (s, "lex_match_id (\"%s\")", t);
1374 /* Write out the parsing code for specifier SPEC within subcommand
1377 dump_specifier_parse (const specifier *spec, const subcommand *sbc)
1381 if (spec->omit_kw && spec->omit_kw->next)
1382 error ("Omittable setting is not last setting in `%s' specifier.",
1384 if (spec->omit_kw && spec->omit_kw->parent->next)
1385 error ("Default specifier is not in last specifier in `%s' "
1386 "subcommand.", sbc->name);
1388 for (s = spec->s; s; s = s->next)
1390 int first = spec == sbc->spec && s == spec->s;
1392 /* Match the setting's keyword. */
1393 if (spec->omit_kw == s)
1400 dump (1, "%s;", make_match (s->specname));
1403 dump (1, "%sif (%s)", first ? "" : "else ",
1404 make_match (s->specname));
1407 /* Handle values. */
1408 if (s->value == VAL_NONE)
1409 dump (0, "p->%s%s = %s%s;", sbc->prefix, spec->varname,
1410 st_upper (prefix), find_symbol (s->con)->name);
1413 if (spec->omit_kw != s)
1418 dump (0, "p->%s%s = %s%s;", sbc->prefix, spec->varname,
1419 st_upper (prefix), find_symbol (s->con)->name);
1421 if ( sbc->type == SBC_ARRAY )
1422 dump (0, "p->a_%s[%s%s%s] = 1;",
1423 st_lower (sbc->name),
1424 st_upper (prefix), st_upper (sbc->prefix),
1425 st_upper (spec->varname));
1429 if (s->valtype == VT_PAREN)
1433 dump (1, "if (lex_match ('('))");
1438 dump (1, "if (!lex_match ('('))");
1440 dump (0, "msg (SE, _(\"`(' expected after %s "
1441 "specifier of %s subcommand.\"));",
1442 s->specname, sbc->name);
1443 dump (0, "goto lossage;");
1449 if (s->value == VAL_INT)
1451 dump (1, "if (!lex_integer_p ())");
1453 dump (0, "msg (SE, _(\"%s specifier of %s subcommand "
1454 "requires an integer argument.\"));",
1455 s->specname, sbc->name);
1456 dump (0, "goto lossage;");
1458 dump (-1, "p->%s%s = lex_integer ();",
1459 sbc->prefix, st_lower (s->valname));
1463 dump (1, "if (token != T_NUM)");
1465 dump (0, "msg (SE, _(\"Number expected after %s "
1466 "specifier of %s subcommand.\"));",
1467 s->specname, sbc->name);
1468 dump (0, "goto lossage;");
1470 dump (-1, "p->%s%s = tokval;", sbc->prefix,
1471 st_lower (s->valname));
1478 str = xmalloc (MAX_TOK_LEN);
1479 str2 = xmalloc (MAX_TOK_LEN);
1480 sprintf (str2, "p->%s%s", sbc->prefix, st_lower (s->valname));
1481 sprintf (str, s->restriction, str2, str2, str2, str2,
1482 str2, str2, str2, str2);
1483 dump (1, "if (!(%s))", str);
1489 dump (0, "msg (SE, _(\"Bad argument for %s "
1490 "specifier of %s subcommand.\"));",
1491 s->specname, sbc->name);
1492 dump (0, "goto lossage;");
1497 dump (0, "lex_get ();");
1499 if (s->valtype == VT_PAREN)
1501 dump (1, "if (!lex_match (')'))");
1503 dump (0, "msg (SE, _(\"`)' expected after argument for "
1504 "%s specifier of %s.\"));",
1505 s->specname, sbc->name);
1506 dump (0, "goto lossage;");
1516 if (s != spec->omit_kw)
1520 if (s == spec->omit_kw)
1529 /* Write out the code to parse subcommand SBC. */
1531 dump_subcommand (const subcommand *sbc)
1533 if (sbc->type == SBC_PLAIN || sbc->type == SBC_ARRAY)
1537 dump (1, "while (token != '/' && token != '.')");
1543 for (count = 0, spec = sbc->spec; spec; spec = spec->next)
1546 dump_specifier_parse (spec, sbc);
1550 dump (1, "%sif (%s)", spec != sbc->spec ? "else " : "",
1551 make_match (st_upper (spec->varname)));
1552 if (sbc->type == SBC_PLAIN)
1553 dump (0, "p->%s%s = 1;", st_lower (sbc->prefix),
1556 dump (0, "p->a_%s[%s%s%s] = 1;",
1557 st_lower (sbc->name),
1558 st_upper (prefix), st_upper (sbc->prefix),
1559 st_upper (spec->varname));
1569 /* This code first finds the last specifier in sbc. Then it
1570 finds the last setting within that last specifier. Either
1571 or both might be NULL. */
1584 if (spec && (!spec->s || !spec->omit_kw))
1588 dump (0, "lex_error (NULL);");
1589 dump (0, "goto lossage;");
1595 dump (0, "lex_match (',');");
1599 else if (sbc->type == SBC_VARLIST)
1601 dump (1, "if (!parse_variables (default_dict, &p->%sv_%s, &p->%sn_%s, "
1603 st_lower (sbc->prefix), st_lower (sbc->name),
1604 st_lower (sbc->prefix), st_lower (sbc->name),
1605 sbc->message ? " |" : "",
1606 sbc->message ? sbc->message : "");
1607 dump (0, "goto lossage;");
1610 else if (sbc->type == SBC_VAR)
1612 dump (0, "p->%sv_%s = parse_variable ();",
1613 st_lower (sbc->prefix), st_lower (sbc->name));
1614 dump (1, "if (!p->%sv_%s)",
1615 st_lower (sbc->prefix), st_lower (sbc->name));
1616 dump (0, "goto lossage;");
1619 else if (sbc->type == SBC_STRING)
1621 if (sbc->restriction)
1626 dump (1, "if (!lex_force_string ())");
1627 dump (0, "return 0;");
1629 if (sbc->restriction)
1631 dump (0, "x = ds_length (&tokstr);");
1632 dump (1, "if (!(%s))", sbc->restriction);
1634 dump (0, "msg (SE, _(\"String for %s must be %s.\"));",
1635 sbc->name, sbc->message);
1636 dump (0, "goto lossage;");
1640 dump (0, "free(p->s_%s);", st_lower(sbc->name) );
1641 dump (0, "p->s_%s = xstrdup (ds_c_str (&tokstr));",
1642 st_lower (sbc->name));
1643 dump (0, "lex_get ();");
1644 if (sbc->restriction)
1647 else if (sbc->type == SBC_DBL)
1649 dump (1, "if (!lex_force_num ())");
1650 dump (0, "goto lossage;");
1651 dump (-1, "p->n_%s[p->sbc_%s - 1] = lex_double ();",
1652 st_lower (sbc->name), st_lower (sbc->name) );
1653 dump (0, "lex_get();");
1655 else if (sbc->type == SBC_INT)
1659 dump (1, "if (!lex_force_int ())");
1660 dump (0, "goto lossage;");
1661 dump (-1, "x = lex_integer ();");
1662 dump (0, "lex_get();");
1663 if (sbc->restriction)
1666 dump (1, "if (!(%s))", sbc->restriction);
1668 sprintf(buf,sbc->message,sbc->name);
1669 if ( sbc->translatable )
1670 dump (0, "msg (SE, gettext(\"%s\"));",buf);
1672 dump (0, "msg (SE, \"%s\");",buf);
1673 dump (0, "goto lossage;");
1676 dump (0, "p->n_%s[p->sbc_%s - 1] = x;", st_lower (sbc->name), st_lower(sbc->name) );
1679 else if (sbc->type == SBC_PINT)
1681 dump (0, "lex_match ('(');");
1682 dump (1, "if (!lex_force_int ())");
1683 dump (0, "goto lossage;");
1684 dump (-1, "p->n_%s = lex_integer ();", st_lower (sbc->name));
1685 dump (0, "lex_match (')');");
1687 else if (sbc->type == SBC_DBL_LIST)
1689 dump (0, "if ( p->sbc_%s > MAXLISTS)",st_lower(sbc->name));
1691 dump (0, "msg (SE, \"No more than %%d %s subcommands allowed\",MAXLISTS);",st_lower(sbc->name));
1692 dump (0, "goto lossage;");
1695 dump (1, "while (token != '/' && token != '.')");
1697 dump (0, "lex_match(',');");
1698 dump (0, "if (!lex_force_num ())");
1700 dump (0, "goto lossage;");
1703 dump (0, "subc_list_double_push(&p->dl_%s[p->sbc_%s-1],lex_double ());",
1704 st_lower (sbc->name),st_lower (sbc->name)
1707 dump (0, "lex_get();");
1711 else if (sbc->type == SBC_CUSTOM)
1713 dump (1, "switch (%scustom_%s (p))",
1714 st_lower (prefix), st_lower (sbc->name));
1716 dump (1, "case 0:");
1717 dump (0, "goto lossage;");
1718 dump (-1, "case 1:");
1721 dump (-1, "case 2:");
1723 dump (0, "lex_error (NULL);");
1724 dump (0, "goto lossage;");
1725 dump (-1, "default:");
1727 dump (0, "assert (0);");
1733 /* Write out entire parser. */
1735 dump_parser (int persistent)
1741 dump (0, "static int");
1742 dump (0, "parse_%s (struct cmd_%s *p)", make_identifier (cmdname),
1743 make_identifier (cmdname));
1746 dump_vars_init (persistent);
1748 dump (1, "for (;;)");
1752 if (def && (def->type == SBC_VARLIST))
1754 if (def->type == SBC_VARLIST)
1755 dump (1, "if (token == T_ID "
1756 "&& dict_lookup_var (default_dict, tokid) != NULL "
1757 "&& lex_look_ahead () != '=')");
1760 dump (0, "if ((token == T_ID "
1761 "&& dict_lookup_var (default_dict, tokid) "
1762 "&& lex_look_ahead () != '=')");
1763 dump (1, " || token == T_ALL)");
1766 dump (0, "p->sbc_%s++;", st_lower (def->name));
1767 dump (1, "if (!parse_variables (default_dict, &p->%sv_%s, &p->%sn_%s, "
1769 st_lower (def->prefix), st_lower (def->name),
1770 st_lower (def->prefix), st_lower (def->name));
1771 dump (0, "goto lossage;");
1776 else if (def && def->type == SBC_CUSTOM)
1778 dump (1, "switch (%scustom_%s (p))",
1779 st_lower (prefix), st_lower (def->name));
1781 dump (1, "case 0:");
1782 dump (0, "goto lossage;");
1783 dump (-1, "case 1:");
1785 dump (0, "p->sbc_%s++;", st_lower (def->name));
1786 dump (0, "continue;");
1787 dump (-1, "case 2:");
1790 dump (-1, "default:");
1792 dump (0, "assert (0);");
1800 for (sbc = subcommands; sbc; sbc = sbc->next)
1802 dump (1, "%sif (%s)", f ? "else " : "", make_match (sbc->name));
1806 dump (0, "lex_match ('=');");
1807 dump (0, "p->sbc_%s++;", st_lower (sbc->name));
1810 dump (1, "if (p->sbc_%s > 1)", st_lower (sbc->name));
1812 dump (0, "msg (SE, _(\"%s subcommand may be given only once.\"));",
1814 dump (0, "goto lossage;");
1818 dump_subcommand (sbc);
1823 /* Now deal with the /ALGORITHM subcommand implicit to all commands */
1824 dump(1,"else if ( get_syntax() != COMPATIBLE && lex_match_id(\"ALGORITHM\"))");
1827 dump (0, "lex_match ('=');");
1829 dump(1,"if (lex_match_id(\"COMPATIBLE\"))");
1830 dump(0,"set_cmd_algorithm(COMPATIBLE);");
1832 dump(1,"else if (lex_match_id(\"ENHANCED\"))");
1833 dump(0,"set_cmd_algorithm(ENHANCED);");
1839 dump (1, "if (!lex_match ('/'))");
1844 dump (1, "if (token != '.')");
1846 dump (0, "lex_error (_(\"expecting end of command\"));");
1847 dump (0, "goto lossage;");
1850 dump (-1, "return 1;");
1852 dump (-1, "lossage:");
1854 dump (0, "free_%s (p);", make_identifier (cmdname));
1855 dump (0, "return 0;");
1861 /* Write out the code to parse aux subcommand SBC. */
1863 dump_aux_subcommand (const subcommand *sbc)
1865 if (sbc->type == SBC_PLAIN )
1869 for (spec = sbc->spec; spec; spec = spec->next)
1872 sprintf(buf,"p->%s%s",st_lower(sbc->prefix),spec->varname);
1874 dump (0, "msg(MM,\"%s is %%s\",",sbc->name);
1875 dump (0, "(%s < 1000)?\"not set\":settings[%s - 1000]", buf, buf);
1880 else if (sbc->type == SBC_STRING)
1882 dump (0, "msg(MM,\"%s is \\\"%%s\\\"\",p->s_%s);", sbc->name,st_lower(sbc->name) );
1884 else if (sbc->type == SBC_INT)
1888 dump (1, "for (i = 0; i < MAXLISTS; ++i)");
1889 dump (0, "msg(MM,\"%s is %%ld\",p->n_%s[i]);", sbc->name,st_lower(sbc->name) );
1893 else if (sbc->type == SBC_CUSTOM)
1895 dump (0, "aux_%scustom_%s(p);",st_lower(prefix),make_identifier(sbc->name));
1903 /* Write out auxilliary parser. */
1905 dump_aux_parser (void)
1909 aux_subcommand *asbc;
1911 /* Write out English strings for all the identifiers in the symbol table. */
1917 /* Note the squirmings necessary to make sure that the last string
1918 is not followed by a comma (is it necessary to do that ?? ) */
1919 for (sym = symtab, f = k = 0; sym; sym = sym->next)
1920 if (!sym->unique && !is_keyword (sym->name))
1924 dump (0, "/* Strings for subcommand specifiers. */");
1925 dump (1, "static const char *settings[]=");
1931 buf = xmalloc (1024);
1935 sprintf (buf, "\"%s\",",sym->name);
1939 buf[strlen (buf) - 1] = 0;
1953 dump (0, "static int");
1954 dump (0, "aux_parse_%s (struct cmd_%s *p)", make_identifier (cmdname),
1955 make_identifier (cmdname));
1958 dump (1, "for (;;)");
1962 for (sbc = subcommands; sbc; sbc = sbc->next)
1964 dump (1, "%sif (%s)", f ? "else " : "", make_match (sbc->name));
1968 dump_aux_subcommand (sbc);
1974 for (asbc = aux_subcommands ; asbc ; asbc = asbc->next)
1976 dump (1, "%sif (%s)", f ? "else " : "", make_match (asbc->name));
1979 dump(0,"aux_%s();",make_identifier(asbc->value));
1984 dump (1, "if (!lex_match ('/'))");
1989 dump (1, "if (token != '.')");
1991 dump (0, "lex_error (_(\"expecting end of command\"));");
1992 dump (0, "goto lossage;");
1995 dump (-1, "return 1;");
1997 dump (-1, "lossage:");
1999 dump (0, "free_%s (p);", make_identifier (cmdname));
2000 dump (0, "return 0;");
2001 dump (-1, "} /* aux_parse_%s (struct cmd_%s *p) */",
2002 make_identifier (cmdname), make_identifier (cmdname));
2007 /* Write the output file header. */
2016 curtime = time (NULL);
2017 loctime = localtime (&curtime);
2018 timep = asctime (loctime);
2019 timep[strlen (timep) - 1] = 0;
2020 dump (0, "/* %s\t\t-*- mode: c; buffer-read-only: t -*-", ofn);
2022 dump (0, " Generated by q2c from %s on %s.", ifn, timep);
2023 dump (0, " Do not modify!");
2027 /* Write out commands to free variable state. */
2029 dump_free (int persistent)
2039 for (sbc = subcommands; sbc; sbc = sbc->next)
2041 if (sbc->type == SBC_STRING)
2043 if (sbc->type == SBC_DBL_LIST)
2049 dump (0, "static void");
2050 dump (0, "free_%s (struct cmd_%s *p%s)", make_identifier (cmdname),
2051 make_identifier (cmdname), used ? "" : " UNUSED");
2057 for (sbc = subcommands; sbc; sbc = sbc->next)
2062 dump (0, "free (p->s_%s);", st_lower (sbc->name));
2066 dump (1, "for(i = 0; i < MAXLISTS ; ++i)");
2067 dump (0, "subc_list_double_destroy(&p->dl_%s[i]);", st_lower (sbc->name));
2082 /* Returns the name of a directive found on the current input line, if
2083 any, or a null pointer if none found. */
2085 recognize_directive (void)
2087 static char directive[16];
2091 if (strncmp (sp, "/*", 2))
2093 sp = skip_ws (sp + 2);
2098 ep = strchr (sp, ')');
2104 memcpy (directive, sp, ep - sp);
2105 directive[ep - sp] = '\0';
2109 static void aux_parse (void);
2112 main (int argc, char *argv[])
2116 fail ("Syntax: q2c input.q output.c");
2119 in = fopen (ifn, "r");
2121 fail ("%s: open: %s.", ifn, strerror (errno));
2124 out = fopen (ofn, "w");
2126 fail ("%s: open: %s.", ofn, strerror (errno));
2129 buf = xmalloc (MAX_LINE_LEN);
2130 tokstr = xmalloc (MAX_TOK_LEN);
2136 dump (0, "#line %d \"%s\"", ln + 1, ifn);
2139 const char *directive = recognize_directive ();
2140 if (directive == NULL)
2142 dump (0, "%s", buf);
2146 dump (0, "#line %d \"%s\"", oln + 1, ofn);
2147 if (!strcmp (directive, "specification"))
2149 /* Skip leading slash-star line. */
2155 /* Skip trailing star-slash line. */
2158 else if (!strcmp (directive, "headers"))
2162 dump (0, "#include <stdlib.h>");
2163 dump (0, "#include \"alloc.h\"");
2164 dump (0, "#include \"error.h\"");
2165 dump (0, "#include \"lexer.h\"");
2166 dump (0, "#include \"settings.h\"");
2167 dump (0, "#include \"str.h\"");
2168 dump (0, "#include \"subclist.h\"");
2169 dump (0, "#include \"var.h\"");
2173 else if (!strcmp (directive, "declarations"))
2174 dump_declarations ();
2175 else if (!strcmp (directive, "functions"))
2180 else if (!strcmp (directive, "_functions"))
2185 else if (!strcmp (directive, "aux_functions"))
2191 error ("unknown directive `%s'", directive);
2193 dump (0, "#line %d \"%s\"", ln + 1, ifn);
2198 return EXIT_SUCCESS;
2201 /* Parse an entire auxilliary specification. */
2205 aux_subcommand *sbc;
2206 aux_subcommand *prevsbc = 0 ;
2212 sbc = xmalloc(sizeof(aux_subcommand));
2213 sbc->next = prevsbc;
2214 sbc->name = xstrdup (tokstr);
2217 sbc->value = xstrdup (tokstr);
2225 /* Skip trailing star-slash line. */
2227 aux_subcommands = sbc;