| |
| /* Parser generator main program */ |
| |
| /* This expects a filename containing the grammar as argv[1] (UNIX) |
| or asks the console for such a file name (THINK C). |
| It writes its output on two files in the current directory: |
| - "graminit.c" gets the grammar as a bunch of initialized data |
| - "graminit.h" gets the grammar's non-terminals as #defines. |
| Error messages and status info during the generation process are |
| written to stdout, or sometimes to stderr. */ |
| |
| /* XXX TO DO: |
| - check for duplicate definitions of names (instead of fatal err) |
| */ |
| |
| #define PGEN |
| |
| #include "Python.h" |
| #include "pgenheaders.h" |
| #include "grammar.h" |
| #include "node.h" |
| #include "parsetok.h" |
| #include "pgen.h" |
| |
| int Py_DebugFlag; |
| int Py_VerboseFlag; |
| int Py_IgnoreEnvironmentFlag; |
| |
| /* Forward */ |
| grammar *getgrammar(char *filename); |
| |
| void |
| Py_Exit(int sts) |
| { |
| exit(sts); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| grammar *g; |
| FILE *fp; |
| char *filename, *graminit_h, *graminit_c; |
| |
| if (argc != 4) { |
| fprintf(stderr, |
| "usage: %s grammar graminit.h graminit.c\n", argv[0]); |
| Py_Exit(2); |
| } |
| filename = argv[1]; |
| graminit_h = argv[2]; |
| graminit_c = argv[3]; |
| g = getgrammar(filename); |
| fp = fopen(graminit_c, "w"); |
| if (fp == NULL) { |
| perror(graminit_c); |
| Py_Exit(1); |
| } |
| if (Py_DebugFlag) |
| printf("Writing %s ...\n", graminit_c); |
| printgrammar(g, fp); |
| fclose(fp); |
| fp = fopen(graminit_h, "w"); |
| if (fp == NULL) { |
| perror(graminit_h); |
| Py_Exit(1); |
| } |
| if (Py_DebugFlag) |
| printf("Writing %s ...\n", graminit_h); |
| printnonterminals(g, fp); |
| fclose(fp); |
| Py_Exit(0); |
| return 0; /* Make gcc -Wall happy */ |
| } |
| |
| grammar * |
| getgrammar(char *filename) |
| { |
| FILE *fp; |
| node *n; |
| grammar *g0, *g; |
| perrdetail err; |
| |
| fp = fopen(filename, "r"); |
| if (fp == NULL) { |
| perror(filename); |
| Py_Exit(1); |
| } |
| g0 = meta_grammar(); |
| n = PyParser_ParseFile(fp, filename, g0, g0->g_start, |
| (char *)NULL, (char *)NULL, &err); |
| fclose(fp); |
| if (n == NULL) { |
| fprintf(stderr, "Parsing error %d, line %d.\n", |
| err.error, err.lineno); |
| if (err.text != NULL) { |
| size_t i; |
| fprintf(stderr, "%s", err.text); |
| i = strlen(err.text); |
| if (i == 0 || err.text[i-1] != '\n') |
| fprintf(stderr, "\n"); |
| for (i = 0; i < err.offset; i++) { |
| if (err.text[i] == '\t') |
| putc('\t', stderr); |
| else |
| putc(' ', stderr); |
| } |
| fprintf(stderr, "^\n"); |
| PyObject_FREE(err.text); |
| } |
| Py_Exit(1); |
| } |
| g = pgen(n); |
| if (g == NULL) { |
| printf("Bad grammar.\n"); |
| Py_Exit(1); |
| } |
| return g; |
| } |
| |
| /* Can't happen in pgen */ |
| PyObject* |
| PyErr_Occurred() |
| { |
| return 0; |
| } |
| |
| void |
| Py_FatalError(const char *msg) |
| { |
| fprintf(stderr, "pgen: FATAL ERROR: %s\n", msg); |
| Py_Exit(1); |
| } |
| |
| /* No-nonsense my_readline() for tokenizer.c */ |
| |
| char * |
| PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) |
| { |
| size_t n = 1000; |
| char *p = (char *)PyMem_MALLOC(n); |
| char *q; |
| if (p == NULL) |
| return NULL; |
| fprintf(stderr, "%s", prompt); |
| q = fgets(p, n, sys_stdin); |
| if (q == NULL) { |
| *p = '\0'; |
| return p; |
| } |
| n = strlen(p); |
| if (n > 0 && p[n-1] != '\n') |
| p[n-1] = '\n'; |
| return (char *)PyMem_REALLOC(p, n+1); |
| } |
| |
| /* No-nonsense fgets */ |
| char * |
| Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) |
| { |
| return fgets(buf, n, stream); |
| } |
| |
| |
| #include <stdarg.h> |
| |
| void |
| PySys_WriteStderr(const char *format, ...) |
| { |
| va_list va; |
| |
| va_start(va, format); |
| vfprintf(stderr, format, va); |
| va_end(va); |
| } |