// -*- C++ -*- /* Copyright (C) 1989-1992, 2000-2003, 2005, 2007-2009, 2013 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) This file is part of groff. groff is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. groff is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "eqn.h" #include "eqn_tab.h" #include "stringclass.h" #include "ptable.h" // declarations to avoid friend name injection problems int get_char(); int peek_char(); int get_location(char **, int *); struct definition { char is_macro; char is_simple; union { int tok; char *contents; }; definition(); ~definition(); }; definition::definition() : is_macro(1), is_simple(0) { contents = 0; } definition::~definition() { if (is_macro) a_delete contents; } declare_ptable(definition) implement_ptable(definition) PTABLE(definition) macro_table; static struct { const char *name; int token; } token_table[] = { { "over", OVER }, { "smallover", SMALLOVER }, { "sqrt", SQRT }, { "sub", SUB }, { "sup", SUP }, { "lpile", LPILE }, { "rpile", RPILE }, { "cpile", CPILE }, { "pile", PILE }, { "left", LEFT }, { "right", RIGHT }, { "to", TO }, { "from", FROM }, { "size", SIZE }, { "font", FONT }, { "roman", ROMAN }, { "bold", BOLD }, { "italic", ITALIC }, { "fat", FAT }, { "bar", BAR }, { "under", UNDER }, { "accent", ACCENT }, { "uaccent", UACCENT }, { "above", ABOVE }, { "fwd", FWD }, { "back", BACK }, { "down", DOWN }, { "up", UP }, { "matrix", MATRIX }, { "col", COL }, { "lcol", LCOL }, { "rcol", RCOL }, { "ccol", CCOL }, { "mark", MARK }, { "lineup", LINEUP }, { "space", SPACE }, { "gfont", GFONT }, { "gsize", GSIZE }, { "define", DEFINE }, { "sdefine", SDEFINE }, { "ndefine", NDEFINE }, { "tdefine", TDEFINE }, { "undef", UNDEF }, { "ifdef", IFDEF }, { "include", INCLUDE }, { "copy", INCLUDE }, { "delim", DELIM }, { "chartype", CHARTYPE }, { "type", TYPE }, { "vcenter", VCENTER }, { "set", SET }, { "opprime", PRIME }, { "grfont", GRFONT }, { "gbfont", GBFONT }, { "split", SPLIT }, { "nosplit", NOSPLIT }, { "special", SPECIAL }, }; struct builtin_def { const char *name; const char *def; }; static struct builtin_def common_defs[] = { { "ALPHA", "\\(*A" }, { "BETA", "\\(*B" }, { "CHI", "\\(*X" }, { "DELTA", "\\(*D" }, { "EPSILON", "\\(*E" }, { "ETA", "\\(*Y" }, { "GAMMA", "\\(*G" }, { "IOTA", "\\(*I" }, { "KAPPA", "\\(*K" }, { "LAMBDA", "\\(*L" }, { "MU", "\\(*M" }, { "NU", "\\(*N" }, { "OMEGA", "\\(*W" }, { "OMICRON", "\\(*O" }, { "PHI", "\\(*F" }, { "PI", "\\(*P" }, { "PSI", "\\(*Q" }, { "RHO", "\\(*R" }, { "SIGMA", "\\(*S" }, { "TAU", "\\(*T" }, { "THETA", "\\(*H" }, { "UPSILON", "\\(*U" }, { "XI", "\\(*C" }, { "ZETA", "\\(*Z" }, { "Alpha", "\\(*A" }, { "Beta", "\\(*B" }, { "Chi", "\\(*X" }, { "Delta", "\\(*D" }, { "Epsilon", "\\(*E" }, { "Eta", "\\(*Y" }, { "Gamma", "\\(*G" }, { "Iota", "\\(*I" }, { "Kappa", "\\(*K" }, { "Lambda", "\\(*L" }, { "Mu", "\\(*M" }, { "Nu", "\\(*N" }, { "Omega", "\\(*W" }, { "Omicron", "\\(*O" }, { "Phi", "\\(*F" }, { "Pi", "\\(*P" }, { "Psi", "\\(*Q" }, { "Rho", "\\(*R" }, { "Sigma", "\\(*S" }, { "Tau", "\\(*T" }, { "Theta", "\\(*H" }, { "Upsilon", "\\(*U" }, { "Xi", "\\(*C" }, { "Zeta", "\\(*Z" }, { "alpha", "\\(*a" }, { "beta", "\\(*b" }, { "chi", "\\(*x" }, { "delta", "\\(*d" }, { "epsilon", "\\(*e" }, { "eta", "\\(*y" }, { "gamma", "\\(*g" }, { "iota", "\\(*i" }, { "kappa", "\\(*k" }, { "lambda", "\\(*l" }, { "mu", "\\(*m" }, { "nu", "\\(*n" }, { "omega", "\\(*w" }, { "omicron", "\\(*o" }, { "phi", "\\(*f" }, { "pi", "\\(*p" }, { "psi", "\\(*q" }, { "rho", "\\(*r" }, { "sigma", "\\(*s" }, { "tau", "\\(*t" }, { "theta", "\\(*h" }, { "upsilon", "\\(*u" }, { "xi", "\\(*c" }, { "zeta", "\\(*z" }, { "max", "{type \"operator\" roman \"max\"}" }, { "min", "{type \"operator\" roman \"min\"}" }, { "lim", "{type \"operator\" roman \"lim\"}" }, { "sin", "{type \"operator\" roman \"sin\"}" }, { "cos", "{type \"operator\" roman \"cos\"}" }, { "tan", "{type \"operator\" roman \"tan\"}" }, { "sinh", "{type \"operator\" roman \"sinh\"}" }, { "cosh", "{type \"operator\" roman \"cosh\"}" }, { "tanh", "{type \"operator\" roman \"tanh\"}" }, { "arc", "{type \"operator\" roman \"arc\"}" }, { "log", "{type \"operator\" roman \"log\"}" }, { "ln", "{type \"operator\" roman \"ln\"}" }, { "exp", "{type \"operator\" roman \"exp\"}" }, { "Re", "{type \"operator\" roman \"Re\"}" }, { "Im", "{type \"operator\" roman \"Im\"}" }, { "det", "{type \"operator\" roman \"det\"}" }, { "and", "{roman \"and\"}" }, { "if", "{roman \"if\"}" }, { "for", "{roman \"for\"}" }, { "times", "type \"binary\" \\(mu" }, { "ldots", "type \"inner\" { . . . }" }, { "inf", "\\(if" }, { "partial", "\\(pd" }, { "nothing", "\"\"" }, { "half", "{1 smallover 2}" }, { "hat_def", "roman \"^\"" }, { "hat", "accent { hat_def }" }, { "tilde_def", "\"~\"" }, { "tilde", "accent { tilde_def }" }, { "==", "type \"relation\" \\(==" }, { "!=", "type \"relation\" \\(!=" }, { "+-", "type \"binary\" \\(+-" }, { "->", "type \"relation\" \\(->" }, { "<-", "type \"relation\" \\(<-" }, { "<<", "type \"relation\" \\(<<" }, { ">>", "type \"relation\" \\(>>" }, { "prime", "'" }, { "approx", "type \"relation\" \"\\(~=\"" }, { "grad", "\\(gr" }, { "del", "\\(gr" }, { "cdot", "type \"binary\" \\(md" }, { "cdots", "type \"inner\" { \\(md \\(md \\(md }" }, { "dollar", "$" }, }; /* composite definitions that require troff size and motion operators */ static struct builtin_def troff_defs[] = { { "sum", "{type \"operator\" vcenter size +5 \\(*S}" }, { "prod", "{type \"operator\" vcenter size +5 \\(*P}" }, { "int", "{type \"operator\" vcenter size +8 \\(is}" }, { "union", "{type \"operator\" vcenter size +5 \\(cu}" }, { "inter", "{type \"operator\" vcenter size +5 \\(ca}" }, { "dot_def", "up 52 back 15 \".\"" }, { "dot", "accent { dot_def }" }, { "dotdot_def", "up 52 back 25 \"..\"" }, { "dotdot", "accent { dotdot_def }" }, { "utilde_def", "down 75 \"~\"" }, { "utilde", "uaccent { utilde_def }" }, { "vec_def", "up 52 size -5 \\(->" }, { "vec", "accent { vec_def }" }, { "dyad_def", "up 52 size -5 { \\(<> }" }, { "dyad", "accent { dyad_def }" }, { "...", "type \"inner\" vcenter { . . . }" }, }; /* equivalent definitions for MathML mode */ static struct builtin_def mathml_defs[] = { { "sum", "{type \"operator\" size big \\(*S}" }, { "prod", "{type \"operator\" size big \\(*P}" }, { "int", "{type \"operator\" size big \\(is}" }, { "union", "{type \"operator\" size big \\(cu}" }, { "inter", "{type \"operator\" size big \\(ca}" }, { "dot", "accent { \".\" }" }, { "dotdot", "accent { \"..\" }" }, { "utilde", "uaccent { \"~\" }" }, { "vec", "accent { \\(-> }" }, { "dyad", "accent { \\(<> }" }, { "...", "type \"inner\" { . . . }" }, }; void init_table(const char *device) { unsigned int i; for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) { definition *def = new definition[1]; def->is_macro = 0; def->tok = token_table[i].token; macro_table.define(token_table[i].name, def); } for (i = 0; i < sizeof(common_defs)/sizeof(common_defs[0]); i++) { definition *def = new definition[1]; def->is_macro = 1; def->contents = strsave(common_defs[i].def); def->is_simple = 1; macro_table.define(common_defs[i].name, def); } if (output_format == troff) { for (i = 0; i < sizeof(troff_defs)/sizeof(troff_defs[0]); i++) { definition *def = new definition[1]; def->is_macro = 1; def->contents = strsave(troff_defs[i].def); def->is_simple = 1; macro_table.define(troff_defs[i].name, def); } } else if (output_format == mathml) { for (i = 0; i < sizeof(mathml_defs)/sizeof(mathml_defs[0]); i++) { definition *def = new definition[1]; def->is_macro = 1; def->contents = strsave(mathml_defs[i].def); def->is_simple = 1; macro_table.define(mathml_defs[i].name, def); } } definition *def = new definition[1]; def->is_macro = 1; def->contents = strsave("1"); macro_table.define(device, def); } class input { input *next; public: input(input *p); virtual ~input(); virtual int get() = 0; virtual int peek() = 0; virtual int get_location(char **, int *); friend int get_char(); friend int peek_char(); friend int get_location(char **, int *); friend void init_lex(const char *str, const char *filename, int lineno); }; class file_input : public input { FILE *fp; char *filename; int lineno; string line; const char *ptr; int read_line(); public: file_input(FILE *, const char *, input *); ~file_input(); int get(); int peek(); int get_location(char **, int *); }; class macro_input : public input { char *s; char *p; public: macro_input(const char *, input *); ~macro_input(); int get(); int peek(); }; class top_input : public macro_input { char *filename; int lineno; public: top_input(const char *, const char *, int, input *); ~top_input(); int get(); int get_location(char **, int *); }; class argument_macro_input: public input { char *s; char *p; char *ap; int argc; char *argv[9]; public: argument_macro_input(const char *, int, char **, input *); ~argument_macro_input(); int get(); int peek(); }; input::input(input *x) : next(x) { } input::~input() { } int input::get_location(char **, int *) { return 0; } file_input::file_input(FILE *f, const char *fn, input *p) : input(p), lineno(0), ptr("") { fp = f; filename = strsave(fn); } file_input::~file_input() { a_delete filename; fclose(fp); } int file_input::read_line() { for (;;) { line.clear(); lineno++; for (;;) { int c = getc(fp); if (c == EOF) break; else if (invalid_input_char(c)) lex_error("invalid input character code %1", c); else { line += char(c); if (c == '\n') break; } } if (line.length() == 0) return 0; if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E' && (line[2] == 'Q' || line[2] == 'N') && (line.length() == 3 || line[3] == ' ' || line[3] == '\n' || compatible_flag))) { line += '\0'; ptr = line.contents(); return 1; } } } int file_input::get() { if (*ptr != '\0' || read_line()) return *ptr++ & 0377; else return EOF; } int file_input::peek() { if (*ptr != '\0' || read_line()) return *ptr; else return EOF; } int file_input::get_location(char **fnp, int *lnp) { *fnp = filename; *lnp = lineno; return 1; } macro_input::macro_input(const char *str, input *x) : input(x) { p = s = strsave(str); } macro_input::~macro_input() { a_delete s; } int macro_input::get() { if (p == 0 || *p == '\0') return EOF; else return *p++ & 0377; } int macro_input::peek() { if (p == 0 || *p == '\0') return EOF; else return *p & 0377; } top_input::top_input(const char *str, const char *fn, int ln, input *x) : macro_input(str, x), lineno(ln) { filename = strsave(fn); } top_input::~top_input() { a_delete filename; } int top_input::get() { int c = macro_input::get(); if (c == '\n') lineno++; return c; } int top_input::get_location(char **fnp, int *lnp) { *fnp = filename; *lnp = lineno; return 1; } // Character representing $1. Must be invalid input character. #define ARG1 14 argument_macro_input::argument_macro_input(const char *body, int ac, char **av, input *x) : input(x), ap(0), argc(ac) { int i; for (i = 0; i < argc; i++) argv[i] = av[i]; p = s = strsave(body); int j = 0; for (i = 0; s[i] != '\0'; i++) if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') { if (s[i+1] != '0') s[j++] = ARG1 + s[++i] - '1'; } else s[j++] = s[i]; s[j] = '\0'; } argument_macro_input::~argument_macro_input() { for (int i = 0; i < argc; i++) a_delete argv[i]; a_delete s; } int argument_macro_input::get() { if (ap) { if (*ap != '\0') return *ap++ & 0377; ap = 0; } if (p == 0) return EOF; while (*p >= ARG1 && *p <= ARG1 + 8) { int i = *p++ - ARG1; if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { ap = argv[i]; return *ap++ & 0377; } } if (*p == '\0') return EOF; return *p++ & 0377; } int argument_macro_input::peek() { if (ap) { if (*ap != '\0') return *ap & 0377; ap = 0; } if (p == 0) return EOF; while (*p >= ARG1 && *p <= ARG1 + 8) { int i = *p++ - ARG1; if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { ap = argv[i]; return *ap & 0377; } } if (*p == '\0') return EOF; return *p & 0377; } static input *current_input = 0; /* we insert a newline between input from different levels */ int get_char() { if (current_input == 0) return EOF; else { int c = current_input->get(); if (c != EOF) return c; else { input *tem = current_input; current_input = current_input->next; delete tem; return '\n'; } } } int peek_char() { if (current_input == 0) return EOF; else { int c = current_input->peek(); if (c != EOF) return c; else return '\n'; } } int get_location(char **fnp, int *lnp) { for (input *p = current_input; p; p = p->next) if (p->get_location(fnp, lnp)) return 1; return 0; } string token_buffer; const int NCONTEXT = 4; string context_ring[NCONTEXT]; int context_index = 0; void flush_context() { for (int i = 0; i < NCONTEXT; i++) context_ring[i] = ""; context_index = 0; } void show_context() { int i = context_index; fputs(" context is\n\t", stderr); for (;;) { int j = (i + 1) % NCONTEXT; if (j == context_index) { fputs(">>> ", stderr); put_string(context_ring[i], stderr); fputs(" <<<", stderr); break; } else if (context_ring[i].length() > 0) { put_string(context_ring[i], stderr); putc(' ', stderr); } i = j; } putc('\n', stderr); } void add_context(const string &s) { context_ring[context_index] = s; context_index = (context_index + 1) % NCONTEXT; } void add_context(char c) { context_ring[context_index] = c; context_index = (context_index + 1) % NCONTEXT; } void add_quoted_context(const string &s) { string &r = context_ring[context_index]; r = '"'; for (int i = 0; i < s.length(); i++) if (s[i] == '"') r += "\\\""; else r += s[i]; r += '"'; context_index = (context_index + 1) % NCONTEXT; } void init_lex(const char *str, const char *filename, int lineno) { while (current_input != 0) { input *tem = current_input; current_input = current_input->next; delete tem; } current_input = new top_input(str, filename, lineno, 0); flush_context(); } void get_delimited_text() { char *filename; int lineno; int got_location = get_location(&filename, &lineno); int start = get_char(); while (start == ' ' || start == '\t' || start == '\n') start = get_char(); token_buffer.clear(); if (start == EOF) { if (got_location) error_with_file_and_line(filename, lineno, "end of input while defining macro"); else error("end of input while defining macro"); return; } for (;;) { int c = get_char(); if (c == EOF) { if (got_location) error_with_file_and_line(filename, lineno, "end of input while defining macro"); else error("end of input while defining macro"); add_context(start + token_buffer); return; } if (c == start) break; token_buffer += char(c); } add_context(start + token_buffer + start); } void interpolate_macro_with_args(const char *body) { char *argv[9]; int argc = 0; int i; for (i = 0; i < 9; i++) argv[i] = 0; int level = 0; int c; do { token_buffer.clear(); for (;;) { c = get_char(); if (c == EOF) { lex_error("end of input while scanning macro arguments"); break; } if (level == 0 && (c == ',' || c == ')')) { if (token_buffer.length() > 0) { token_buffer += '\0'; argv[argc] = strsave(token_buffer.contents()); } // for `foo()', argc = 0 if (argc > 0 || c != ')' || i > 0) argc++; break; } token_buffer += char(c); if (c == '(') level++; else if (c == ')') level--; } } while (c != ')' && c != EOF); current_input = new argument_macro_input(body, argc, argv, current_input); } /* If lookup flag is non-zero the token will be looked up to see if it is macro. If it's 1, it will looked up to see if it's a token. */ int get_token(int lookup_flag = 0) { for (;;) { int c = get_char(); while (c == ' ' || c == '\n') c = get_char(); switch (c) { case EOF: { add_context("end of input"); } return 0; case '"': { int quoted = 0; token_buffer.clear(); for (;;) { c = get_char(); if (c == EOF) { lex_error("missing \""); break; } else if (c == '\n') { lex_error("newline before end of quoted text"); break; } else if (c == '"') { if (!quoted) break; token_buffer[token_buffer.length() - 1] = '"'; quoted = 0; } else { token_buffer += c; quoted = quoted ? 0 : c == '\\'; } } } add_quoted_context(token_buffer); return QUOTED_TEXT; case '{': case '}': case '^': case '~': case '\t': add_context(c); return c; default: { int break_flag = 0; int quoted = 0; token_buffer.clear(); if (c == '\\') quoted = 1; else token_buffer += c; int done = 0; while (!done) { c = peek_char(); if (!quoted && lookup_flag != 0 && c == '(') { token_buffer += '\0'; definition *def = macro_table.lookup(token_buffer.contents()); if (def && def->is_macro && !def->is_simple) { (void)get_char(); // skip initial '(' interpolate_macro_with_args(def->contents); break_flag = 1; break; } token_buffer.set_length(token_buffer.length() - 1); } if (quoted) { quoted = 0; switch (c) { case EOF: lex_error("`\\' ignored at end of equation"); done = 1; break; case '\n': lex_error("`\\' ignored because followed by newline"); done = 1; break; case '\t': lex_error("`\\' ignored because followed by tab"); done = 1; break; case '"': (void)get_char(); token_buffer += '"'; break; default: (void)get_char(); token_buffer += '\\'; token_buffer += c; break; } } else { switch (c) { case EOF: case '{': case '}': case '^': case '~': case '"': case ' ': case '\t': case '\n': done = 1; break; case '\\': (void)get_char(); quoted = 1; break; default: (void)get_char(); token_buffer += char(c); break; } } } if (break_flag || token_buffer.length() == 0) break; if (lookup_flag != 0) { token_buffer += '\0'; definition *def = macro_table.lookup(token_buffer.contents()); token_buffer.set_length(token_buffer.length() - 1); if (def) { if (def->is_macro) { current_input = new macro_input(def->contents, current_input); break; } else if (lookup_flag == 1) { add_context(token_buffer); return def->tok; } } } add_context(token_buffer); return TEXT; } } } } void do_include() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad filename for include"); return; } token_buffer += '\0'; const char *filename = token_buffer.contents(); errno = 0; FILE *fp = fopen(filename, "r"); if (fp == 0) { lex_error("can't open included file `%1'", filename); return; } current_input = new file_input(fp, filename, current_input); } void ignore_definition() { int t = get_token(); if (t != TEXT) { lex_error("bad definition"); return; } get_delimited_text(); } void do_definition(int is_simple) { int t = get_token(); if (t != TEXT) { lex_error("bad definition"); return; } token_buffer += '\0'; const char *name = token_buffer.contents(); definition *def = macro_table.lookup(name); if (def == 0) { def = new definition[1]; macro_table.define(name, def); } else if (def->is_macro) { a_delete def->contents; } get_delimited_text(); token_buffer += '\0'; def->is_macro = 1; def->contents = strsave(token_buffer.contents()); def->is_simple = is_simple; } void do_undef() { int t = get_token(); if (t != TEXT) { lex_error("bad undef command"); return; } token_buffer += '\0'; macro_table.define(token_buffer.contents(), 0); } void do_gsize() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad argument to gsize command"); return; } token_buffer += '\0'; if (!set_gsize(token_buffer.contents())) lex_error("invalid size `%1'", token_buffer.contents()); } void do_gfont() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad argument to gfont command"); return; } token_buffer += '\0'; set_gfont(token_buffer.contents()); } void do_grfont() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad argument to grfont command"); return; } token_buffer += '\0'; set_grfont(token_buffer.contents()); } void do_gbfont() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad argument to gbfont command"); return; } token_buffer += '\0'; set_gbfont(token_buffer.contents()); } void do_space() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad argument to space command"); return; } token_buffer += '\0'; char *ptr; long n = strtol(token_buffer.contents(), &ptr, 10); if (n == 0 && ptr == token_buffer.contents()) lex_error("bad argument `%1' to space command", token_buffer.contents()); else set_space(int(n)); } void do_ifdef() { int t = get_token(); if (t != TEXT) { lex_error("bad ifdef"); return; } token_buffer += '\0'; definition *def = macro_table.lookup(token_buffer.contents()); int result = def && def->is_macro && !def->is_simple; get_delimited_text(); if (result) { token_buffer += '\0'; current_input = new macro_input(token_buffer.contents(), current_input); } } char start_delim_saved = '\0'; char end_delim_saved = '\0'; void do_delim() { int c = get_char(); while (c == ' ' || c == '\n') c = get_char(); int d; if (c == EOF || (d = get_char()) == EOF) lex_error("end of file while reading argument to `delim'"); else { if (c == 'o' && d == 'f' && peek_char() == 'f') { (void)get_char(); if (start_delim) start_delim_saved = start_delim; if (end_delim) end_delim_saved = end_delim; start_delim = end_delim = '\0'; } else if (c == 'o' && d == 'n' && !compatible_flag) { if (start_delim_saved) start_delim = start_delim_saved; if (end_delim_saved) end_delim = end_delim_saved; } else { start_delim = c; end_delim = d; } } } void do_chartype() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad chartype"); return; } token_buffer += '\0'; string type = token_buffer; t = get_token(); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad chartype"); return; } token_buffer += '\0'; set_char_type(type.contents(), strsave(token_buffer.contents())); } void do_set() { int t = get_token(2); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad set"); return; } token_buffer += '\0'; string param = token_buffer; t = get_token(); if (t != TEXT && t != QUOTED_TEXT) { lex_error("bad set"); return; } token_buffer += '\0'; int n; if (sscanf(&token_buffer[0], "%d", &n) != 1) { lex_error("bad number `%1'", token_buffer.contents()); return; } set_param(param.contents(), n); } int yylex() { for (;;) { int tk = get_token(1); switch(tk) { case UNDEF: do_undef(); break; case SDEFINE: do_definition(1); break; case DEFINE: do_definition(0); break; case TDEFINE: if (!nroff) do_definition(0); else ignore_definition(); break; case NDEFINE: if (nroff) do_definition(0); else ignore_definition(); break; case GSIZE: do_gsize(); break; case GFONT: do_gfont(); break; case GRFONT: do_grfont(); break; case GBFONT: do_gbfont(); break; case SPACE: do_space(); break; case INCLUDE: do_include(); break; case IFDEF: do_ifdef(); break; case DELIM: do_delim(); break; case CHARTYPE: do_chartype(); break; case SET: do_set(); break; case QUOTED_TEXT: case TEXT: token_buffer += '\0'; yylval.str = strsave(token_buffer.contents()); // fall through default: return tk; } } } void lex_error(const char *message, const errarg &arg1, const errarg &arg2, const errarg &arg3) { char *filename; int lineno; if (!get_location(&filename, &lineno)) error(message, arg1, arg2, arg3); else error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); } void yyerror(const char *s) { char *filename; int lineno; if (!get_location(&filename, &lineno)) error(s); else error_with_file_and_line(filename, lineno, s); show_context(); }