diff options
Diffstat (limited to 'src/preproc/eqn')
26 files changed, 11813 insertions, 0 deletions
diff --git a/src/preproc/eqn/TODO b/src/preproc/eqn/TODO new file mode 100644 index 0000000..2a22183 --- /dev/null +++ b/src/preproc/eqn/TODO @@ -0,0 +1,49 @@ +Use the same size increases for sum prod int as eqn does. + +Perhaps chartype should be renamed. + +TeX makes {sub,super}script on a single character with an accent +into an accent onto the (character with the script). Should we do this? + +Implement mark and lineups within scripts, matrices and piles, and accents. +(Why would this be useful?) + +Perhaps push hmotions down through lists to avoid upsetting spacing +adjustments. + +Possibly generate .lf commands during compute_metrics phase. + +Consider whether there should be extra space at the side of piles. + +Provide scriptstyle displaystyle etc. + +Provide a nicer matrix syntax, e.g., +matrix ccc { +a then b then c above +e then f then g above +h then i then k +} + +Perhaps generate syntax error messages using the style of gpic. + +Wide accents. + +More use of \Z. + +Extensible square roots. + +Vphantom + +Smash. + +Provide a variant of vec that extends over the length of the accentee. + +Support vertical arrow delimiters. + +Make the following work: +.EQ +delim @@ +.EN +.EQ @<-@ +some equation +.EN diff --git a/src/preproc/eqn/box.cpp b/src/preproc/eqn/box.cpp new file mode 100644 index 0000000..5f6343e --- /dev/null +++ b/src/preproc/eqn/box.cpp @@ -0,0 +1,651 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include <assert.h> + +#include "eqn.h" +#include "pbox.h" + +const char *current_roman_font; + +char *gfont = 0; +char *grfont = 0; +char *gbfont = 0; +int gsize = 0; + +int script_size_reduction = -1; // negative means reduce by a percentage + +int positive_space = -1; +int negative_space = -1; + +int minimum_size = 5; + +int fat_offset = 4; +int body_height = 85; +int body_depth = 35; + +int over_hang = 0; +int accent_width = 31; +int delimiter_factor = 900; +int delimiter_shortfall = 50; + +int null_delimiter_space = 12; +int script_space = 5; +int thin_space = 17; +int medium_space = 22; +int thick_space = 28; + +int num1 = 70; +int num2 = 40; +// we don't use num3, because we don't have \atop +int denom1 = 70; +int denom2 = 36; +int axis_height = 26; // in 100ths of an em +int sup1 = 42; +int sup2 = 37; +int sup3 = 28; +int default_rule_thickness = 4; +int sub1 = 20; +int sub2 = 23; +int sup_drop = 38; +int sub_drop = 5; +int x_height = 45; +int big_op_spacing1 = 11; +int big_op_spacing2 = 17; +int big_op_spacing3 = 20; +int big_op_spacing4 = 60; +int big_op_spacing5 = 10; + +// These are for piles and matrices. + +int baseline_sep = 140; // = num1 + denom1 +int shift_down = 26; // = axis_height +int column_sep = 100; // = em space +int matrix_side_sep = 17; // = thin space + +int nroff = 0; // should we grok ndefine or tdefine? + +struct S { + const char *name; + int *ptr; +} param_table[] = { + { "fat_offset", &fat_offset }, + { "over_hang", &over_hang }, + { "accent_width", &accent_width }, + { "delimiter_factor", &delimiter_factor }, + { "delimiter_shortfall", &delimiter_shortfall }, + { "null_delimiter_space", &null_delimiter_space }, + { "script_space", &script_space }, + { "thin_space", &thin_space }, + { "medium_space", &medium_space }, + { "thick_space", &thick_space }, + { "num1", &num1 }, + { "num2", &num2 }, + { "denom1", &denom1 }, + { "denom2", &denom2 }, + { "axis_height", &axis_height }, + { "sup1", ¹ }, + { "sup2", ² }, + { "sup3", ³ }, + { "default_rule_thickness", &default_rule_thickness }, + { "sub1", &sub1 }, + { "sub2", &sub2 }, + { "sup_drop", &sup_drop }, + { "sub_drop", &sub_drop }, + { "x_height", &x_height }, + { "big_op_spacing1", &big_op_spacing1 }, + { "big_op_spacing2", &big_op_spacing2 }, + { "big_op_spacing3", &big_op_spacing3 }, + { "big_op_spacing4", &big_op_spacing4 }, + { "big_op_spacing5", &big_op_spacing5 }, + { "minimum_size", &minimum_size }, + { "baseline_sep", &baseline_sep }, + { "shift_down", &shift_down }, + { "column_sep", &column_sep }, + { "matrix_side_sep", &matrix_side_sep }, + { "draw_lines", &draw_flag }, + { "body_height", &body_height }, + { "body_depth", &body_depth }, + { "nroff", &nroff }, + { 0, 0 } +}; + +void set_param(const char *name, int value) +{ + for (int i = 0; param_table[i].name != 0; i++) + if (strcmp(param_table[i].name, name) == 0) { + *param_table[i].ptr = value; + return; + } + error("unrecognised parameter '%1'", name); +} + +int script_style(int style) +{ + return style > SCRIPT_STYLE ? style - 2 : style; +} + +int cramped_style(int style) +{ + return (style & 1) ? style - 1 : style; +} + +void set_space(int n) +{ + if (n < 0) + negative_space = -n; + else + positive_space = n; +} + +// Return 0 if the specified size is bad. +// The caller is responsible for giving the error message. + +int set_gsize(const char *s) +{ + const char *p = (*s == '+' || *s == '-') ? s + 1 : s; + char *end; + long n = strtol(p, &end, 10); + if (n <= 0 || *end != '\0' || n > INT_MAX) + return 0; + if (p > s) { + if (!gsize) + gsize = 10; + if (*s == '+') { + if (gsize > INT_MAX - n) + return 0; + gsize += int(n); + } + else { + if (gsize - n <= 0) + return 0; + gsize -= int(n); + } + } + else + gsize = int(n); + return 1; +} + +void set_script_reduction(int n) +{ + script_size_reduction = n; +} + +const char *get_gfont() +{ + return gfont ? gfont : "I"; +} + +const char *get_grfont() +{ + return grfont ? grfont : "R"; +} + +const char *get_gbfont() +{ + return gbfont ? gbfont : "B"; +} + +void set_gfont(const char *s) +{ + delete[] gfont; + gfont = strsave(s); +} + +void set_grfont(const char *s) +{ + delete[] grfont; + grfont = strsave(s); +} + +void set_gbfont(const char *s) +{ + delete[] gbfont; + gbfont = strsave(s); +} + +// this must be precisely 2 characters in length +#define COMPATIBLE_REG "0C" + +void start_string() +{ + if (output_format == troff) { + printf(".nr " COMPATIBLE_REG " \\n(.C\n"); + printf(".cp 0\n"); + printf(".ds " LINE_STRING "\n"); + } +} + +void output_string() +{ + if (output_format == troff) + printf("\\*(" LINE_STRING "\n"); + else if (output_format == mathml && !xhtml) + putchar('\n'); +} + +void restore_compatibility() +{ + if (output_format == troff) + printf(".cp \\n(" COMPATIBLE_REG "\n"); +} + +void do_text(const char *s) +{ + if (output_format == troff) { + printf(".eo\n"); + printf(".as " LINE_STRING " \"%s\n", s); + printf(".ec\n"); + } + else if (output_format == mathml) { + fputs(s, stdout); + if (xhtml && strlen(s) > 0) + printf("\n"); + } +} + +void set_minimum_size(int n) +{ + minimum_size = n; +} + +void set_script_size() +{ + if (minimum_size < 0) + minimum_size = 0; + if (script_size_reduction >= 0) + printf(".ps \\n[.s]-%d>?%d\n", script_size_reduction, minimum_size); + else + printf(".ps (u;\\n[.ps]*7+5/10>?%dz)\n", minimum_size); +} + +int box::next_uid = 0; + +box::box() : spacing_type(ORDINARY_TYPE), uid(next_uid++) +{ +} + +box::~box() +{ +} + +void box::top_level() +{ + box *b = this; + if (output_format == troff) { + // debug_print(); + // putc('\n', stderr); + printf(".nr " SAVED_FONT_REG " \\n[.f]\n"); + printf(".ft\n"); + printf(".nr " SAVED_PREV_FONT_REG " \\n[.f]\n"); + printf(".ft %s\n", get_gfont()); + printf(".nr " SAVED_SIZE_REG " \\n[.ps]\n"); + if (gsize > 0) { + char buf[INT_DIGITS + 1]; + sprintf(buf, "%d", gsize); + b = new size_box(strsave(buf), b); + } + current_roman_font = get_grfont(); + // This catches tabs used within \Z (which aren't allowed). + b->check_tabs(0); + int r = b->compute_metrics(DISPLAY_STYLE); + printf(".ft \\n[" SAVED_PREV_FONT_REG "]\n"); + printf(".ft \\n[" SAVED_FONT_REG "]\n"); + printf(".nr " MARK_OR_LINEUP_FLAG_REG " %d\n", r); + if (r == FOUND_MARK) { + printf(".nr " SAVED_MARK_REG " \\n[" MARK_REG "]\n"); + printf(".nr " MARK_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", b->uid); + } + else if (r == FOUND_LINEUP) + printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" + SAVED_MARK_REG "]u-\\n[" MARK_REG "]u'\n"); + else + assert(r == FOUND_NOTHING); + // If we use \R directly, the space will prevent it working in a + // macro argument; so we hide it in a string instead. + printf(".ds " SAVE_FONT_STRING " " + "\\R'" SAVED_INLINE_FONT_REG " \\En[.f]'" + "\\fP" + "\\R'" SAVED_INLINE_PREV_FONT_REG " \\En[.f]'" + "\\R'" SAVED_INLINE_SIZE_REG " \\En[.ps]'" + "\\s0" + "\\R'" SAVED_INLINE_PREV_SIZE_REG " \\En[.ps]'" + "\n" + ".ds " RESTORE_FONT_STRING " " + "\\f[\\En[" SAVED_INLINE_PREV_FONT_REG "]]" + "\\f[\\En[" SAVED_INLINE_FONT_REG "]]" + "\\s'\\En[" SAVED_INLINE_PREV_SIZE_REG "]u'" + "\\s'\\En[" SAVED_INLINE_SIZE_REG "]u'" + "\n"); + printf(".as1 " LINE_STRING " \\&\\E*[" SAVE_FONT_STRING "]"); + printf("\\f[%s]", get_gfont()); + printf("\\s'\\En[" SAVED_SIZE_REG "]u'"); + current_roman_font = get_grfont(); + b->output(); + printf("\\E*[" RESTORE_FONT_STRING "]\n"); + if (r == FOUND_LINEUP) + printf(".if r" SAVED_MARK_REG " .as1 " LINE_STRING " \\h'\\n[" + MARK_WIDTH_REG "]u-\\n[" SAVED_MARK_REG "]u-(\\n[" + WIDTH_FORMAT "]u-\\n[" MARK_REG "]u)'\n", + b->uid); + b->extra_space(); + if (!inline_flag) + printf(".ne \\n[" HEIGHT_FORMAT "]u-%dM>?0+(\\n[" + DEPTH_FORMAT "]u-%dM>?0)\n", + b->uid, body_height, b->uid, body_depth); + } + else if (output_format == mathml) { + if (xhtml) + printf(".MATHML "); + printf("<math>"); + b->output(); + printf("</math>"); + } + delete b; + next_uid = 0; +} + +// gpic defines this register so as to make geqn not produce '\x's +#define EQN_NO_EXTRA_SPACE_REG "0x" + +void box::extra_space() +{ + printf(".if !r" EQN_NO_EXTRA_SPACE_REG " " + ".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); + if (positive_space >= 0 || negative_space >= 0) { + if (positive_space > 0) + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".as1 " LINE_STRING " \\x'-%dM'\n", positive_space); + if (negative_space > 0) + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".as1 " LINE_STRING " \\x'%dM'\n", negative_space); + positive_space = negative_space = -1; + } + else { + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".if \\n[" HEIGHT_FORMAT "]>%dM .as1 " LINE_STRING + " \\x'-(\\n[" HEIGHT_FORMAT + "]u-%dM)'\n", + uid, body_height, uid, body_height); + printf(".if !\\n[" EQN_NO_EXTRA_SPACE_REG "] " + ".if \\n[" DEPTH_FORMAT "]>%dM .as1 " LINE_STRING + " \\x'\\n[" DEPTH_FORMAT + "]u-%dM'\n", + uid, body_depth, uid, body_depth); + } +} + +int box::compute_metrics(int) +{ + printf(".nr " WIDTH_FORMAT " 0\n", uid); + printf(".nr " HEIGHT_FORMAT " 0\n", uid); + printf(".nr " DEPTH_FORMAT " 0\n", uid); + return FOUND_NOTHING; +} + +void box::compute_subscript_kern() +{ + printf(".nr " SUB_KERN_FORMAT " 0\n", uid); +} + +void box::compute_skew() +{ + printf(".nr " SKEW_FORMAT " 0\n", uid); +} + +void box::output() +{ +} + +void box::check_tabs(int) +{ +} + +int box::is_char() +{ + return 0; +} + +int box::left_is_italic() +{ + return 0; +} + +int box::right_is_italic() +{ + return 0; +} + +void box::hint(unsigned) +{ +} + +void box::handle_char_type(int, int) +{ +} + + +box_list::box_list(box *pp) +{ + p = new box*[10]; + for (int i = 0; i < 10; i++) + p[i] = 0; + maxlen = 10; + len = 1; + p[0] = pp; +} + +void box_list::append(box *pp) +{ + if (len + 1 > maxlen) { + box **oldp = p; + maxlen *= 2; + p = new box*[maxlen]; + memcpy(p, oldp, sizeof(box*)*len); + delete[] oldp; + } + p[len++] = pp; +} + +box_list::~box_list() +{ + for (int i = 0; i < len; i++) + delete p[i]; + delete[] p; +} + +void box_list::list_check_tabs(int level) +{ + for (int i = 0; i < len; i++) + p[i]->check_tabs(level); +} + + +pointer_box::pointer_box(box *pp) : p(pp) +{ + spacing_type = p->spacing_type; +} + +pointer_box::~pointer_box() +{ + delete p; +} + +int pointer_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void pointer_box::compute_subscript_kern() +{ + p->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", uid, p->uid); +} + +void pointer_box::compute_skew() +{ + p->compute_skew(); + printf(".nr " SKEW_FORMAT " 0\\n[" SKEW_FORMAT "]\n", + uid, p->uid); +} + +void pointer_box::check_tabs(int level) +{ + p->check_tabs(level); +} + +int simple_box::compute_metrics(int) +{ + printf(".nr " WIDTH_FORMAT " 0\\w" DELIMITER_CHAR, uid); + output(); + printf(DELIMITER_CHAR "\n"); + printf(".nr " HEIGHT_FORMAT " 0>?\\n[rst]\n", uid); + printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?0\n", uid); + printf(".nr " SUB_KERN_FORMAT " 0-\\n[ssc]>?0\n", uid); + printf(".nr " SKEW_FORMAT " 0\\n[skw]\n", uid); + return FOUND_NOTHING; +} + +void simple_box::compute_subscript_kern() +{ + // do nothing, we already computed it in do_metrics +} + +void simple_box::compute_skew() +{ + // do nothing, we already computed it in do_metrics +} + +int box::is_simple() +{ + return 0; +} + +int simple_box::is_simple() +{ + return 1; +} + +quoted_text_box::quoted_text_box(char *s) : text(s) +{ +} + +quoted_text_box::~quoted_text_box() +{ + free(text); +} + +void quoted_text_box::output() +{ + if (text) { + if (output_format == troff) + fputs(text, stdout); + else if (output_format == mathml) { + fputs("<mtext>", stdout); + fputs(text, stdout); + fputs("</mtext>", stdout); + } + } +} + +tab_box::tab_box() : disabled(0) +{ +} + +// We treat a tab_box as having width 0 for width computations. + +void tab_box::output() +{ + if (!disabled) + printf("\\t"); +} + +void tab_box::check_tabs(int level) +{ + if (level > 0) { + error("tabs allowed only at outermost level"); + disabled = 1; + } +} + +space_box::space_box() +{ + spacing_type = SUPPRESS_TYPE; +} + +void space_box::output() +{ + if (output_format == troff) + printf("\\h'%dM'", thick_space); + else if (output_format == mathml) + //    doesn't display right under Firefox 1.5. + printf("<mtext> </mtext>"); +} + +half_space_box::half_space_box() +{ + spacing_type = SUPPRESS_TYPE; +} + +void half_space_box::output() +{ + if (output_format == troff) + printf("\\h'%dM'", thin_space); + else if (output_format == mathml) + printf("<mtext> </mtext>"); +} + +void box_list::list_debug_print(const char *sep) +{ + p[0]->debug_print(); + for (int i = 1; i < len; i++) { + fprintf(stderr, "%s", sep); + p[i]->debug_print(); + } +} + +void quoted_text_box::debug_print() +{ + fprintf(stderr, "\"%s\"", (text ? text : "")); +} + +void half_space_box::debug_print() +{ + fprintf(stderr, "^"); +} + +void space_box::debug_print() +{ + fprintf(stderr, "~"); +} + +void tab_box::debug_print() +{ + fprintf(stderr, "<tab>"); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/box.h b/src/preproc/eqn/box.h new file mode 100644 index 0000000..981b464 --- /dev/null +++ b/src/preproc/eqn/box.h @@ -0,0 +1,278 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +class list_box; + +class box { +private: + static int next_uid; +public: + int spacing_type; + const int uid; + box(); + virtual void debug_print() = 0; + virtual ~box(); + void top_level(); + virtual int compute_metrics(int); + virtual void compute_subscript_kern(); + virtual void compute_skew(); + virtual void output(); + void extra_space(); + virtual list_box *to_list_box(); + virtual int is_simple(); + virtual int is_char(); + virtual int left_is_italic(); + virtual int right_is_italic(); + virtual void handle_char_type(int, int); + enum { FOUND_NOTHING = 0, FOUND_MARK = 1, FOUND_LINEUP = 2 }; + void set_spacing_type(char *type); + virtual void hint(unsigned); + virtual void check_tabs(int); +}; + +class box_list { +private: + int maxlen; +public: + box **p; + int len; + + box_list(box *); + ~box_list(); + void append(box *); + void list_check_tabs(int); + void list_debug_print(const char *sep); + friend class list_box; +}; + +// declarations to avoid friend name injection problems +box *make_script_box(box *, box *, box *); +box *make_mark_box(box *); +box *make_lineup_box(box *); + +class list_box : public box { + int is_script; + box_list list; + int sty; +public: + list_box(box *); + void debug_print(); + int compute_metrics(int); + void compute_subscript_kern(); + void output(); + void check_tabs(int); + void append(box *); + list_box *to_list_box(); + void handle_char_type(int, int); + void compute_sublist_width(int n); + friend box *make_script_box(box *, box *, box *); + friend box *make_mark_box(box *); + friend box *make_lineup_box(box *); +}; + +enum alignment { LEFT_ALIGN, RIGHT_ALIGN, CENTER_ALIGN }; + +class column : public box_list { + alignment align; + int space; +public: + column(box *); + void set_alignment(alignment); + void set_space(int); + void debug_print(const char *); + + friend class matrix_box; + friend class pile_box; +}; + +class pile_box : public box { + column col; +public: + pile_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); + void set_alignment(alignment a) { col.set_alignment(a); } + void set_space(int n) { col.set_space(n); } + void append(box *p) { col.append(p); } +}; + +class matrix_box : public box { +private: + int len; + int maxlen; + column **p; +public: + matrix_box(column *); + ~matrix_box(); + void append(column *); + int compute_metrics(int); + void output(); + void check_tabs(int); + void debug_print(); +}; + +class pointer_box : public box { +protected: + box *p; +public: + pointer_box(box *); + ~pointer_box(); + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void debug_print() = 0; + void check_tabs(int); +}; + +class vcenter_box : public pointer_box { +public: + vcenter_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class simple_box : public box { +public: + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void output() = 0; + void debug_print() = 0; + int is_simple(); +}; + +class quoted_text_box : public simple_box { + char *text; +public: + quoted_text_box(char *); + ~quoted_text_box(); + void debug_print(); + void output(); +}; + +class half_space_box : public simple_box { +public: + half_space_box(); + void output(); + void debug_print(); +}; + +class space_box : public simple_box { +public: + space_box(); + void output(); + void debug_print(); +}; + +class tab_box : public box { + int disabled; +public: + tab_box(); + void output(); + void debug_print(); + void check_tabs(int); +}; + +class size_box : public pointer_box { +private: + char *size; +public: + size_box(char *, box *); + ~size_box(); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class font_box : public pointer_box { +private: + char *f; +public: + font_box(char *, box *); + ~font_box(); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class fat_box : public pointer_box { +public: + fat_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class vmotion_box : public pointer_box { +private: + int n; // up is >= 0 +public: + vmotion_box(int, box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +class hmotion_box : public pointer_box { + int n; +public: + hmotion_box(int, box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +box *split_text(char *); +box *make_delim_box(char *, box *, char *); +box *make_sqrt_box(box *); +box *make_prime_box(box *); +box *make_over_box(box *, box *); +box *make_small_over_box(box *, box *); +box *make_limit_box(box *, box *, box *); +box *make_accent_box(box *, box *); +box *make_uaccent_box(box *, box *); +box *make_overline_box(box *); +box *make_underline_box(box *); +box *make_special_box(char *, box *); + +void set_space(int); +int set_gsize(const char *); +void set_gfont(const char *); +void set_grfont(const char *); +void set_gbfont(const char *); +const char *get_gfont(); +const char *get_grfont(); +const char *get_gbfont(); +void start_string(); +void output_string(); +void do_text(const char *); +void restore_compatibility(); +void set_script_reduction(int n); +void set_minimum_size(int n); +void set_param(const char *name, int value); + +void set_char_type(const char *type, char *ch); + +void init_char_table(); +void init_extensible(); +void define_extensible(const char *name, const char *ext, const char *top = 0, + const char *mid = 0, const char *bot = 0); diff --git a/src/preproc/eqn/delim.cpp b/src/preproc/eqn/delim.cpp new file mode 100644 index 0000000..01557c5 --- /dev/null +++ b/src/preproc/eqn/delim.cpp @@ -0,0 +1,418 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include <assert.h> + +#include "eqn.h" +#include "pbox.h" + +enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 }; + +// Small must be none-zero and must exist in each device. +// Small will be put in the roman font, others are assumed to be +// on the special font (so no font change will be necessary.) + +struct delimiter { + const char *name; + int flags; + const char *small; + const char *chain_format; + const char *ext; + const char *top; + const char *mid; + const char *bot; +} delim_table[] = { + { + "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]", + "\\[parenleftex]", + "\\[parenlefttp]", + 0, + "\\[parenleftbt]", + }, + { + ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]", + "\\[parenrightex]", + "\\[parenrighttp]", + 0, + "\\[parenrightbt]", + }, + { + "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]", + "\\[bracketleftex]", + "\\[bracketlefttp]", + 0, + "\\[bracketleftbt]", + }, + { + "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]", + "\\[bracketrightex]", + "\\[bracketrighttp]", + 0, + "\\[bracketrightbt]", + }, + { + "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]", + "\\[braceleftex]", + "\\[bracelefttp]", + "\\[braceleftmid]", + "\\[braceleftbt]", + }, + { + "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]", + "\\[bracerightex]", + "\\[bracerighttp]", + "\\[bracerightmid]", + "\\[bracerightbt]", + }, + { + "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", + "\\[barex]", + 0, + 0, + 0, + }, + { + "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]", + "\\[bracketleftex]", + 0, + 0, + "\\[bracketleftbt]", + }, + { + "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]", + "\\[bracketrightex]", + 0, + 0, + "\\[bracketrightbt]", + }, + { + "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]", + "\\[bracketleftex]", + "\\[bracketlefttp]", + 0, + 0, + }, + { + "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]", + "\\[bracketrightex]", + "\\[bracketrighttp]", + 0, + 0, + }, + { + "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]", + "\\[bardblex]", + 0, + 0, + 0, + }, + { + "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]", + 0, + 0, + 0, + 0, + }, + { + ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]", + 0, + 0, + 0, + 0, + }, + { + "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]", + "\\[arrowvertex]", + "\\[arrowverttp]", + 0, + 0, + }, + { + "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]", + "\\[arrowvertex]", + 0, + 0, + "\\[arrowvertbt]", + }, + { + "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]", + "\\[arrowvertex]", + "\\[arrowverttp]", + 0, + "\\[arrowvertbt]", + }, +}; + +const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0])); + +class delim_box : public box { +private: + char *left; + char *right; + box *p; +public: + delim_box(char *, box *, char *); + ~delim_box(); + int compute_metrics(int); + void output(); + void check_tabs(int); + void debug_print(); +}; + +box *make_delim_box(char *l, box *pp, char *r) +{ + if (l != 0 && *l == '\0') { + delete[] l; + l = 0; + } + if (r != 0 && *r == '\0') { + delete[] r; + r = 0; + } + return new delim_box(l, pp, r); +} + +delim_box::delim_box(char *l, box *pp, char *r) +: left(l), right(r), p(pp) +{ +} + +delim_box::~delim_box() +{ + delete[] left; + delete[] right; + delete p; +} + +static void build_extensible(const char *ext, const char *top, const char *mid, + const char *bot) +{ + assert(ext != 0); + printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + ext); + printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n"); + if (top) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + top); + printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n"); + } + if (mid) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + mid); + printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n"); + } + if (bot) { + printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]" + ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n", + bot); + printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n"); + printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n"); + } + printf(".nr " TOTAL_HEIGHT_REG " 0"); + if (top) + printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]"); + if (bot) + printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]"); + if (mid) + printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]"); + printf("\n"); + // determine how many extensible characters we need + printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]"); + if (mid) + printf("/2"); + printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n[" + EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n"); + + printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n[" + EXT_DEPTH_REG "]*\\n[" TEMP_REG "]"); + if (mid) + printf("*2"); + printf(")\n"); + printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n", + axis_height); + if (top) + printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" TOP_DEPTH_REG "]u'\n", + top); + + // this macro appends $2 copies of $3 to string $1 + printf(".de " REPEAT_APPEND_STRING_MACRO "\n" + ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n" + "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n" + ".\\}\n" + "..\n"); + + printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] " + "\\v'\\n[" EXT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" EXT_DEPTH_REG "]u'\n", + ext); + + if (mid) { + printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" MID_DEPTH_REG "]u'\n", + mid); + printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING + " \\n[" TEMP_REG "] " + "\\v'\\n[" EXT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" EXT_DEPTH_REG "]u'\n", + ext); + } + if (bot) + printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'" + "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR + "\\v'\\n[" BOT_DEPTH_REG "]u'\n", + bot); + printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n"); +} + +static void define_extensible_string(char *delim, int uid, + left_or_right_t left_or_right) +{ + printf(".ds " DELIM_STRING "\n"); + delimiter *d = delim_table; + int delim_len = strlen(delim); + int i; + for (i = 0; i < DELIM_TABLE_SIZE; i++, d++) + if (strncmp(delim, d->name, delim_len) == 0 + && (left_or_right & d->flags) != 0) + break; + if (i >= DELIM_TABLE_SIZE) { + error("there is no '%1' delimiter", delim); + printf(".nr " DELIM_WIDTH_REG " 0\n"); + return; + } + + printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n" + ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n" + ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n" + ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " + "\\{", + current_roman_font, d->small, axis_height, + current_roman_font, d->small); + + char buf[256]; +// The format string in the sprintf below comes from a struct +// initializer above, and is not subject to external influence. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]"); +#pragma GCC diagnostic pop + printf(".nr " INDEX_REG " 0\n" + ".de " TEMP_MACRO "\n" + ".ie c%s \\{\\\n" + ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n" + ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR + "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n" + ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n" + ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] " + "\\{.nr " INDEX_REG " +1\n" + "." TEMP_MACRO "\n" + ".\\}\\}\n" + ".el .nr " INDEX_REG " 0-1\n" + "..\n" + "." TEMP_MACRO "\n", + buf, buf, axis_height, buf); + if (d->ext) { + printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext); + build_extensible(d->ext, d->top, d->mid, d->bot); + printf(".\\}\\}\n"); + } + printf(".\\}\n"); + printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n"); + printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n", + uid, uid, axis_height); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n", + uid, uid, axis_height); +} + +int delim_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM" + ">?(\\n[" DEPTH_FORMAT "]+%dM)\n", + p->uid, axis_height, p->uid, axis_height); + printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500" + ">?(\\n[" DELTA_REG "]*2-%dM)\n", + delimiter_factor, delimiter_shortfall); + if (left) { + define_extensible_string(left, uid, LEFT_DELIM); + printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n", + uid); + if (r) + printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n"); + } + if (right) { + define_extensible_string(right, uid, RIGHT_DELIM); + printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n", + uid); + } + return r; +} + +void delim_box::output() +{ + if (output_format == troff) { + if (left) + printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid); + p->output(); + if (right) + printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid); + } + else if (output_format == mathml) { + printf("<mrow><mo>%s</mo>", left); + p->output(); + printf("<mo>%s</mo></mrow>", right); + } +} + +void delim_box::check_tabs(int level) +{ + p->check_tabs(level); +} + +void delim_box::debug_print() +{ + fprintf(stderr, "left \"%s\" { ", left ? left : ""); + p->debug_print(); + fprintf(stderr, " }"); + if (right) + fprintf(stderr, " right \"%s\"", right); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/eqn.1.man b/src/preproc/eqn/eqn.1.man new file mode 100644 index 0000000..9c3d43c --- /dev/null +++ b/src/preproc/eqn/eqn.1.man @@ -0,0 +1,2240 @@ +'\" et +.TH @g@eqn @MAN1EXT@ "@MDATE@" "groff @VERSION@" +.SH Name +@g@eqn \- format mathematics (equations) for +.I groff +or MathML +. +. +.\" ==================================================================== +.\" Legal Terms +.\" ==================================================================== +.\" +.\" Copyright (C) 1989-2023 Free Software Foundation, Inc. +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Permission is granted to copy and distribute translations of this +.\" manual into another language, under the above conditions for +.\" modified versions, except that this permission notice may be +.\" included in translations approved by the Free Software Foundation +.\" instead of in the original English. +. +. +.\" Save and disable compatibility mode (for, e.g., Solaris 10/11). +.do nr *groff_eqn_1_man_C \n[.cp] +.cp 0 +. +.\" Define fallback for groff 1.23's MR macro if the system lacks it. +.nr do-fallback 0 +.if !\n(.f .nr do-fallback 1 \" mandoc +.if \n(.g .if !d MR .nr do-fallback 1 \" older groff +.if !\n(.g .nr do-fallback 1 \" non-groff *roff +.if \n[do-fallback] \{\ +. de MR +. ie \\n(.$=1 \ +. I \%\\$1 +. el \ +. IR \%\\$1 (\\$2)\\$3 +. . +.\} +.rr do-fallback +. +. +.ie \n(.V<\n(.v \ +. ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X +.el \ +. ds tx TeX +. +. +.\" ==================================================================== +.SH Synopsis +.\" ==================================================================== +. +.SY @g@eqn +.RB [ \-CNrR ] +.RB [ \- d +.IR xy ] +.RB [ \-f +.IR F ] +.RB [ \-m +.IR n ] +.RB [ \-M +.IR dir ] +.RB [ \-p +.IR n ] +.RB [ \-s +.IR n ] +.RB [ \-T +.IR dev ] +.RI [ file\~ .\|.\|.] +.YS +. +. +.SY @g@eqn +.B \-\-help +.YS +. +. +.SY @g@eqn +.B \-v +. +.SY @g@eqn +.B \-\-version +.YS +. +. +.\" ==================================================================== +.SH Description +.\" ==================================================================== +. +The GNU implementation of +.I eqn \" GNU +is part of the +.MR groff @MAN7EXT@ +document formatting system. +. +.I @g@eqn +is a +.MR @g@troff @MAN1EXT@ +preprocessor that translates expressions in its own language, +embedded in +.MR roff @MAN7EXT@ +input files, +into mathematical notation typeset by +.MR @g@troff @MAN1EXT@ . +. +It copies each +.IR file 's +contents to the standard output stream, +translating each +.I equation +between lines starting with +.B .EQ +and +.BR .EN , +or within a pair of user-specified delimiters. +. +Normally, +.I @g@eqn +is not executed directly by the user, +but invoked by specifying the +.B \-e +option to +.MR groff @MAN1EXT@ . +. +While GNU +.IR eqn 's \" GNU +input syntax is highly compatible with AT&T +.IR eqn , \" AT&T +the output +.I @g@eqn +produces cannot be processed by AT&T +.IR troff ; \" AT&T +GNU +.I troff \" GNU +(or a +.I troff \" generic +implementing relevant GNU extensions) +must be used. +. +If no +.I file +operands are given on the command line, +or if +.I file +is +.RB \[lq] \- \[rq], +.I @g@eqn +reads the standard input stream. +. +. +.P +Unless the +.B \-R +option is used, +.I @g@eqn +searches for the file +.I eqnrc +in the directories given with the +.B \-M +option first, +then in +.if !'@COMPATIBILITY_WRAPPERS@'no' .IR @SYSTEMMACRODIR@ , +.IR @LOCALMACRODIR@ , +and finally in the standard macro directory +.IR @MACRODIR@ . +. +If it exists and is readable, +.I @g@eqn +processes it before any input files. +. +. +.P +This man page primarily discusses the differences between GNU +.I eqn \" GNU +and AT&T +.IR eqn .\" AT&T +. +Most of the new features of the GNU +.I eqn \" GNU +input language are based on \*[tx]. +. +There are some references to the differences between \*[tx] and GNU +.I eqn \" GNU +below; +these may safely be ignored if you do not know \*[tx]. +. +. +.P +Three points are worth special note. \" good, bad, and different +. +. +.IP \[bu] 2n +GNU +.I eqn \" GNU +emits Presentation MathML output when invoked with the +.RB \[lq] "\-T\~MathML" \[rq] +option. +. +. +.IP \[bu] +GNU +.I eqn \" GNU +does not support terminal devices well, +though it may suffice for simple inputs. +. +. +.IP \[bu] +GNU +.I eqn +sets the input token +.RB \[lq] .\|.\|.\& \[rq] +as an ellipsis on the text baseline, +not the three centered dots of AT&T +.IR eqn . \" AT&T +. +Set an ellipsis on the math axis with the GNU extension macro +.BR cdots . +. +. +.\" ==================================================================== +.SS "Anatomy of an equation" +.\" ==================================================================== +. +.I eqn +input consists of tokens. +. +Consider a form of Newton's second law of motion. +. +The input +. +. +.P +.RS +.EX +\&.EQ +F = +m a +\&.EN +.EE +.RE +. +. +.P +becomes +.EQ +F = +m a. +.EN +. +Each of +.BR F , +.BR = , +.BR m , +and +.B a +is a token. +. +. +Spaces and newlines are interchangeable; +they separate tokens but do not break lines or produce space in +the output. +. +. +.P +The following input characters not only separate tokens, +but manage their grouping and spacing as well. +. +. +.TP +.B "{ }" +Braces perform grouping. +. +Whereas +.RB \[lq] "e sup a b" \[rq] +expresses +.ie n .RI \[lq]( e "\~to the\~" a )\~times\~ b \[rq], +.el \{\ +.EQ +e sup a b , +.EN +.\} +.RB \[lq] "e sup { a b }" \[rq] +means +.ie n .RI \[lq] e "\~to the\~(" a \~times\~ b )\[rq]. +.el \{\ +.EQ +e sup { a b } . +.EN +.\} +. +When immediately preceded by a +.RB \[lq] left \[rq] +or +.RB \[lq] right \[rq] +primitive, +a brace loses its special meaning. +. +. +.TP +.B "\[ha] \[ti] +are the +.I "half space" +and +.I "full space," +respectively. +. +Use them to tune the appearance of the output. +. +. +.P +Tab and leader characters separate tokens as well as advancing the +drawing position to the next tab stop, +but are seldom used in +.I eqn +input. +. +When they occur, +they must appear at the outermost lexical scope. +. +This roughly means that they can't appear within braces that are +necessary to disambiguate the input; +.I @g@eqn +will diagnose an error in this event. +. +(See subsection \[lq]Macros\[rq] below for additional token separation +rules.) +. +. +.P +Other tokens are primitives, +macros, +an argument to either of the foregoing, +or components of an equation. +. +. +.br +.ne 4v +.P +.I Primitives +are fundamental keywords of the +.I eqn +language. +. +They can configure an aspect of the preprocessor's state, +as when setting a \[lq]global\[rq] font selection or type size +.RB ( gfont +and +.BR gsize ), +or declaring or deleting macros +.RB \%(\[lq] define \[rq] +and +.BR undef ); +these are termed +.I commands. +. +Other primitives perform formatting operations on the tokens after them +(as with +.BR fat , +.BR over , +.BR sqrt , +or +.BR up ). +. +. +.P +Equation +.I components +include mathematical variables, +constants, +numeric literals, +and operators. +. +.I @g@eqn +remaps some input character sequences to +.I groff +special character escape sequences for economy in equation entry and to +ensure that glyphs from an unstyled font are used; +see +.MR groff_char @MAN7EXT@ . +. +. +.P +.RS +.TS +tab(@); +Lf(CR) Lf(CR) Lw(1i) Lf(CR) Lf(CR). ++@\[rs][pl]@\&@\[aq]@\[rs][fm] +-@\[rs][mi]@\&@<=@\[rs][<=] +\&=@\[rs][eq]@\&@>=@\[rs][>=] +.TE +.RE +. +. +.P +.I Macros +permit primitives, +components, +and other macros to be collected and referred to by a single token. +. +Predefined macros make convenient the preparation of +.I eqn +input in a form resembling its spoken expression; +for example, +consider +.BR cos , +.BR hat , +.BR inf , +and +.BR lim . +. +. +.\" ==================================================================== +.SS "Spacing and typeface" +.\" ==================================================================== +. +GNU +.I eqn +imputes types to the components of an equation, +adjusting the spacing between them accordingly. +. +Recognized types are as follows; +most affect spacing only, +whereas the +.RB \%\[lq] letter \[rq] +subtype of +.RB \%\[lq] ordinary \[rq] +also assigns a style. +. +. +.RS 2n \" we need quite a bit of horizontal space for this table +.P +.TS +Lf(CR) Lx +Af(CR) Lx +Af(CR) Lx +Lf(CR) Lx. +ordinary T{ +character such as \[lq]1\[rq], +\[lq]a\[rq], +or +\[lq]!\&\[rq] +T} +letter character to be italicized by default +digit \f[I]n/a\f[] +operator T{ +large operator such as +.ds Su \[lq]\s+5\[*S]\s0\[rq] +.if \n(.g .if !c\[*S] .ds Su the summation operator +\*[Su] +.rm Su +T} +binary binary operator such as \[lq]\[pl]\[rq] +relation relational operator such as \[lq]=\[rq] +opening opening bracket such as \[lq](\[rq] +closing closing bracket such as \[lq])\[rq] +punctuation punctuation character such as \[lq],\[rq] +inner sub-formula contained within brackets +suppress component to which automatic spacing is not applied +.TE +.RE +. +. +.P +Two primitives apply types to equation components. +. +. +.TP +.BI type\~ "t e" +Apply +.RI type\~ t +to +.RI expression\~ e . +. +. +.TP +.BI chartype\~ "t text" +Assign each character in (unquoted) +.I text +.RI type\~ t , +persistently. +. +. +.P +.I @g@eqn \" GNU +sets up spacings and styles as if by the following commands. +. +.P +.RS +.TS +tab(@); +Lf(CR)1 Lf(CR). +chartype \[dq]letter\[dq]@abcdefghiklmnopqrstuvwxyz +chartype \[dq]letter\[dq]@ABCDEFGHIKLMNOPQRSTUVWXYZ +chartype \[dq]letter\[dq]@\[rs][*a]\[rs][*b]\[rs][*g]\[rs][*d]\[rs][*e]\ +\[rs][*z] +chartype \[dq]letter\[dq]@\[rs][*y]\[rs][*h]\[rs][*i]\[rs][*k]\[rs][*l]\ +\[rs][*m] +chartype \[dq]letter\[dq]@\[rs][*n]\[rs][*c]\[rs][*o]\[rs][*p]\[rs][*r]\ +\[rs][*s] +chartype \[dq]letter\[dq]@\[rs][*t]\[rs][*u]\[rs][*f]\[rs][*x]\[rs][*q]\ +\[rs][*w] +chartype \[dq]binary\[dq]@*\[rs][pl]\[rs][mi] +chartype \[dq]relation\[dq]@<>\[rs][eq]\[rs][<=]\[rs][>=] +chartype \[dq]opening\[dq]@{([ +chartype \[dq]closing\[dq]@})] +chartype \[dq]punctuation\[dq]@,;:. +chartype \[dq]suppress\[dq]@\[ha]\[ti] +.TE +.RE +. +. +.P +.I @g@eqn +assigns all other ordinary and special +.I roff +characters, +including numerals 0\[en]9, +the +.RB \%\[lq] ordinary \[rq] +type. +. +(The +.RB \[lq] digit \[rq] +type is not used, +but is available for customization.) +.\" XXX: How would you actually customize it, though? There doesn't +.\" seem to be a means of replacing the font associated with a type. +.\" Is the "digit" type just cruft? +. +In keeping with common practice in mathematical typesetting, +lowercase, +but not uppercase, +Greek letters are assigned the +.RB \%\[lq] letter \[rq] +type to style them in italics. +. +The macros for producing ellipses, +.RB \[lq] .\|.\|. \[rq], +.BR cdots , +and +.BR ldots , +use the +.RB \%\[lq] inner \[rq] +type. +. +. +.\" ==================================================================== +.SS Primitives +.\" ==================================================================== +. +.I @g@eqn +supports without alteration the AT&T +.I eqn \" AT&T +primitives +.BR above , +.BR back , +.BR bar , +.BR bold , +.BR \%define , +.BR down , +.BR fat , +.BR font , +.BR from , +.BR fwd , +.BR gfont , +.BR gsize , +.BR italic , +.BR left , +.BR lineup , +.BR mark , +.BR \%matrix , +.BR \%ndefine , +.BR over , +.BR right , +.BR roman , +.BR size , +.BR sqrt , +.BR sub , +.BR sup , +.BR \%tdefine , +.BR to , +.BR \%under , +and +.BR up . +. +. +.\" ==================================================================== +.SS "New primitives" +.\" ==================================================================== +. +The GNU extension primitives +.RB \[lq] type \[rq] +and +.B \%chartype +are discussed in subsection \[lq]Spacing and typeface\[rq] above; +.RB \[lq] set \[rq] +in subsection \[lq]Customization\[rq] below; +and +.B grfont +and +.B gbfont +in subsection \[lq]Fonts\[rq] below. +. +In the following synopses, +.I X +can be any character not appearing in the parameter thus bracketed. +. +. +.TP +.IB e1 \~accent\~ e2 +Set +.I e2 +as an accent over +.IR e1 . +. +.I e2 +is assumed to be at the appropriate height for a lowercase letter +without an ascender; +.I @g@ eqn +vertically shifts it depending on +.IR e1 's +height. +. +For example, +.B hat +is defined as follows. +. +. +.RS +.IP +.EX +accent { "\[ha]" } +.EE +.RE +. +. +.IP +.BR dotdot , +.BR dot , +.BR tilde , +.BR vec , +and +.B dyad +are also defined using the +.B \%accent +primitive. +. +. +.TP +.BI big\~ e +Enlarge the expression +.IR e ; +semantics like those of CSS \[lq]large\[rq] are intended. +. +In +.I @g@troff +output, +the type size is increased by\~5 scaled points. +. +MathML output emits the following. +. +. +.RS +.IP +.EX +<mstyle \%mathsize=\[aq]big\[aq]> +.EE +.RE +. +. +.TP +.BI copy\~ file +.TQ +.BI include\~ file +Interpolate the contents of +.IR file , +omitting lines +beginning with +.B .EQ +or +.BR .EN . +. +If a relative path name, +.I file +is sought relative to the current working directory. +. +. +.TP +.BI ifdef\~ "name X anything X" +If +.I name +is defined as a primitive or macro, +interpret +.IR anything . +. +. +.TP +.BI nosplit\~ text +As +.RI \[dq] text \[dq], +but since +.I text +is not quoted it is subject to macro expansion; +it is not split up and the spacing between characters not adjusted per +subsection \[lq]Spacing and typeface\[rq] above. +. +. +.TP +.IB e\~ opprime +As +.BR prime , +but set the prime symbol as an operator +.RI on\~ e . +. +In the input +.RB \[lq] "A opprime sub 1" \[rq], +the\~\[lq]1\[rq] is tucked under the prime as a subscript to +the\~\[lq]A\[rq] +(as is conventional in mathematical typesetting), +whereas when +.B prime +is used, +the\~\[lq]1\[rq] is a subscript to the prime character. +. +The precedence of +.B \%opprime +is the same as that of +.B bar +and +.RB \%\[lq] under \[rq], +and higher than that of other primitives except +.B \%accent +and +.BR uaccent . +. +In unquoted text, +a neutral apostrophe +.RB ( \[aq] ) +that is not the first character on the input line is treated like +.BR \%opprime . +. +. +.TP +.BI sdefine\~ "name X anything X" +As +.RB \%\[lq] define \[rq], +but +.I name +is not recognized as a macro if called with arguments. +. +. +.TP +.IB e1 \~smallover\~ e2 +As +.BR over , +but reduces the type size of +.I e1 +and +.IR e2 , +and puts less vertical space between +.I e1 +and +.I e2 +and the fraction bar. +. +The +.B over +primitive corresponds to the \*[tx] +.B \[rs]over +primitive in displayed equation styles; +.B smallover +corresponds to +.B \[rs]over +in non-display (\[lq]inline\[rq]) styles. +. +. +.br +.ne 5v +.TP +.BI space\~ n +Set extra vertical spacing around the equation, +replacing the default values, +where +.IR n \~is +an integer in hundredths of an em. +. +If positive, +.IR n \~increases +vertical spacing before the equation; +if negative, +it does so after the equation. +. +This primitive provides an interface to +.IR groff 's +.B \[rs]x +escape sequence, +but with the opposite sign convention. +. +It has no effect if the equation is part of a +.MR @g@pic @MAN1EXT@ +picture. +. +. +.TP +.BI special\~ "troff-macro e" +Construct an object by calling +.I troff-macro +.RI on\~ e . +. +The +.I troff \" generic +string +.B 0s +contains the +.I eqn \" generic +output +.RI for\~ e , +and the registers +.BR 0w , +.BR 0h , +.BR 0d , +.BR 0skern , +and +.B 0skew +the width, +height, +depth, +subscript kern, +and skew +.RI of\~ e , +respectively. +. +(The +.I subscript kern +of an object indicates how much a subscript on that object should be +\[lq]tucked in\[rq], +or placed to the left relative to a non-subscripted glyph of the same +size. +. +The +.I skew +of an object is how far to the right of the center of the object an +accent over it should be placed.) +. +The macro must modify +.B 0s +so that it outputs the desired result, +returns the drawing position to the text baseline at the beginning of +.IR e , +and updates the foregoing registers to correspond to the new dimensions +of the result. +. +. +.IP +Suppose you want a construct that \[lq]cancels\[rq] an expression by +drawing a diagonal line through it. +. +. +.br +.ne 11v +.RS +.IP +.EX +\&.de Ca +\&. ds 0s \[rs] +\[rs]Z\[aq]\[rs]\[rs]*(0s\[aq]\[rs] +\[rs]v\[aq]\[rs]\[rs]n(0du\[aq]\[rs] +\[rs]D\[aq]l \[rs]\[rs]n(0wu \-\[rs]\[rs]n(0hu\-\[rs]\ +\[rs]n(0du\[aq]\[rs] +\[rs]v\[aq]\[rs]\[rs]n(0hu\[aq] +\&.. +\&.EQ +special Ca "x \[rs][mi] 3 \[rs][pl] x" \[ti] 3 +\&.EN +.EE +.RE +. +. +.IP +We use the +.B \[rs][mi] +and +.B \[rs][pl] +special characters instead of + and \- +because they are part of the argument to a +.I @g@troff +macro, +so +.I @g@eqn +does not transform them to mathematical glyphs for us. +. +Here's a more complicated construct that draws a box around an +expression; +the bottom of the box rests on the text baseline. +. +We define the +.I eqn \" generic +macro +.B box +to wrap the call of the +.I @g@troff +macro +.BR Bx . +. +. +.br +.ne 17v +.RS +.IP +.EX +\&.de Bx +\&.ds 0s \[rs] +\[rs]Z\[aq]\[rs]\[rs]h\[aq]1n\[aq]\[rs]\[rs]*[0s]\[aq]\[rs] +\[rs]v\[aq]\[rs]\[rs]n(0du+1n\[aq]\[rs] +\[rs]D\[aq]l \[rs]\[rs]n(0wu+2n 0\[aq]\[rs] +\[rs]D\[aq]l 0 \-\[rs]\[rs]n(0hu\-\[rs]\[rs]n(0du\-2n\[aq]\[rs] +\[rs]D\[aq]l \-\[rs]\[rs]n(0wu\-2n 0\[aq]\[rs] +\[rs]D\[aq]l 0 \[rs]\[rs]n(0hu+\[rs]\[rs]n(0du+2n\[aq]\[rs] +\[rs]h\[aq]\[rs]\[rs]n(0wu+2n\[aq] +\&.nr 0w +2n +\&.nr 0d +1n +\&.nr 0h +1n +\&.. +\&.EQ +define box \[aq] special Bx $1 \[aq] +box(foo) \[ti] "bar" +\&.EN +.EE +.RE +. +. +.TP +.BI "split \[dq]" text \[dq] +As +.IR text , +but since +.I text +is quoted, +it is not subject to macro expansion; +it is split up and the spacing between characters adjusted per +subsection \[lq]Spacing and typeface\[rq] above. +. +. +.TP +.IB e1 \~uaccent\~ e2 +Set +.I e2 +as an accent under +.IR e1 . +. +.I e2 +is assumed to be at the appropriate height for a letter without a +descender; +.I @g@ eqn +vertically shifts it depending on whether +.I e1 +has a descender. +. +.B utilde +is predefined using +.B uaccent +as a tilde accent below the baseline. +. +. +.TP +.BI undef\~ name +Remove definition of macro or primitive +.IR name , +making it undefined. +. +. +.TP +.BI vcenter\~ e +Vertically center +.I e +about the +.IR "math axis" , +a horizontal line upon which fraction bars and characters such as +\[lq]\[pl]\[rq] and \[lq]\[mi]\[rq] are aligned. +. +MathML already behaves this way, +so +.I @g@eqn +ignores this primitive when producing that output format. +. +The built-in +.B sum +macro is defined as if by the following. +. +.RS +.IP +.EX +define sum ! { type "operator" vcenter size +5 \[rs](*S } ! +.EE +.RE +. +. +.br +.ne 8v +.\" ==================================================================== +.SS "Extended primitives" +.\" ==================================================================== +. +GNU +.I eqn \" GNU +extends the syntax of some AT&T +.I eqn \" AT&T +primitives, +introducing one deliberate incompatibility. +. +. +.TP +.B "delim on" +.I @g@eqn +recognizes an +.RB \[lq] on \[rq] +argument to the +.B \%delim +primitive specially, +restoring any delimiters previously disabled with +.RB \%\[lq] "delim off" \[rq]. +. +If delimiters haven't been specified, +neither command has effect. +. +Few +.I eqn \" generic +documents are expected to use \[lq]o\[rq] and \[lq]n\[rq] as left and +right delimiters, +respectively. +. +If yours does, +consider swapping them, +or select others. +. +. +.TP +.BI col\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI ccol\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI lcol\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI rcol\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI pile\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI cpile\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI lpile\~ n\~\c +.BR {\~ .\|.\|.\& \~} +.TQ +.BI rpile\~ n\~\c +.BR {\~ .\|.\|.\& \~} +The integer +.RI value\~ n +(in hundredths of an em) +increases the vertical spacing between rows, +using +.IR groff 's +.B \[rs]x +escape sequence +(the value has no effect in MathML mode). +. +Negative values are accepted but have no effect. +. +If more than one +.I n +occurs in a matrix or pile, +the largest is used. +. +. +.\" ==================================================================== +.SS Customization +.\" ==================================================================== +. +When +.I @g@eqn +generates +.I @g@troff +input, +the appearance of equations is controlled by a large number of +parameters. +. +They have no effect when generating MathML, +which delegates typesetting to a MathML rendering engine. +. +Configure these parameters with the +.B set +primitive. +. +. +.TP +.BI set\~ "p n" +assigns +.RI parameter\~ p +the integer +.RI value\~ n ; +.IR n \~is +interpreted in units of hundredths of an em unless otherwise stated. +. +For example, +. +. +.RS +.IP +.EX +set x_height 45 +.EE +.RE +. +. +.IP +says that +.I @g@eqn +should assume that the font's x-height is 0.45\~ems. +. +. +.RS +.P +Available parameters are as follows; +defaults are shown in parentheses. +. +We intend these descriptions to be expository rather than rigorous. +. +. +.TP 17n +.B minimum_size +sets a floor for the type size +(in scaled points) +at which equations are set +.RB ( 5 ). +. +. +.TP +.B fat_offset +The +.B fat +primitive emboldens an equation by overprinting two copies of the +equation horizontally offset by this amount +.RB ( 4 ). +. +In MathML mode, +components to which +.B \%fat_offset +applies instead use the following. +. +.RS +.RS +.EX +<mstyle mathvariant=\[aq]double\-struck\[aq]> +.EE +.RE +.RE +. +. +.TP +.B over_hang +A fraction bar is longer by twice this amount than +the maximum of the widths of the numerator and denominator; +in other words, +it overhangs the numerator and denominator by at least this amount +.RB ( 0 ). +. +. +.TP +.B accent_width +When +.B bar +or +.B \%under +is applied to a single character, +the line is this long +.RB ( 31 ). +. +Normally, +.B bar +or +.B \%under +produces a line whose length is the width of the object to which it +applies; +in the case of a single character, +this tends to produce a line that looks too long. +. +. +.TP +.B delimiter_factor +Extensible delimiters produced with the +.B left +and +.B right +primitives have a combined height and depth of at least this many +thousandths of twice the maximum amount by which the sub-equation that +the delimiters enclose extends away from the axis +.RB ( 900 ). +. +. +.TP +.B delimiter_shortfall +Extensible delimiters produced with the +.B left +and +.B right +primitives have a combined height and depth not less than the +difference of twice the maximum amount by which the sub-equation that +the delimiters enclose extends away from the axis and this amount +.RB ( 50 ). +. +. +.TP +.B null_delimiter_space +This much horizontal space is inserted on each side of a fraction +.RB ( 12 ). +. +. +.TP +.B script_space +The width of subscripts and superscripts is increased by this amount +.RB ( 5 ). +. +. +.TP +.B thin_space +This amount of space is automatically inserted after punctuation +characters. +. +It also configures the width of the space produced by the +.B \[ha] +token +.RB ( 17 ). +. +. +.TP +.B medium_space +This amount of space is automatically inserted on either side of +binary operators +.RB ( 22 ). +. +. +.TP +.B thick_space +This amount of space is automatically inserted on either side of +relations. +. +It also configures the width of the space produced by the +.B \[ti] +token +.RB ( 28 ). +. +. +.TP +.B x_height +The height of lowercase letters without ascenders such as \[lq]x\[rq] +.RB ( 45 ). +. +. +.TP +.B axis_height +The height above the baseline of the center of characters such as +\[lq]\[pl]\[rq] and \[lq]\[mi]\[rq] +.RB ( 26 ). +. +It is important that this value is correct for the font +you are using. +. +. +.TP +.B default_rule_thickness +This should be set to the thickness of the +.B \[rs][ru] +character, +or the thickness of horizontal lines produced with the +.B \[rs]D +escape sequence +.RB ( 4 ). +. +. +.TP +.B num1 +The +.B over +primitive shifts up the numerator by at least this amount +.RB ( 70 ). +. +. +.TP +.B num2 +The +.B smallover +primitive shifts up the numerator by at least this amount +.RB ( 36 ). +. +. +.TP +.B denom1 +The +.B over +primitive shifts down the denominator by at least this amount +.RB ( 70 ). +. +. +.TP +.B denom2 +The +.B smallover +primitive shifts down the denominator by at least this amount +.RB ( 36 ). +. +. +.TP +.B sup1 +Normally superscripts are shifted up by at least this amount +.RB ( 42 ). +. +. +.TP +.B sup2 +Superscripts within superscripts or upper limits +or numerators of +.B smallover +fractions are shifted up by at least this amount +.RB ( 37 ). +. +Conventionally, +this is less than +.BR sup1 . +. +. +.TP +.B sup3 +Superscripts within denominators or square roots +or subscripts or lower limits are shifted up by at least +this amount +.RB ( 28 ). +. +Conventionally, +this is less than +.BR sup2 . +. +. +.TP +.B sub1 +Subscripts are normally shifted down by at least this amount +.RB ( 20 ). +. +. +.TP +.B sub2 +When there is both a subscript and a superscript, +the subscript is shifted down by at least this amount +.RB ( 23 ). +. +. +.TP +.B sup_drop +The baseline of a superscript is no more than this much below the top of +the object on which the superscript is set +.RB ( 38 ). +. +. +.TP +.B sub_drop +The baseline of a subscript is at least this much below the bottom of +the object on which the subscript is set +.RB ( 5 ). +. +. +.TP +.B big_op_spacing1 +The baseline of an upper limit is at least this much above the top of +the object on which the limit is set +.RB ( 11 ). +. +. +.TP +.B big_op_spacing2 +The baseline of a lower limit is at least this much below the bottom +of the object on which the limit is set +.RB ( 17 ). +. +. +.TP +.B big_op_spacing3 +The bottom of an upper limit is at least this much above the top of +the object on which the limit is set +.RB ( 20 ). +. +. +.TP +.B big_op_spacing4 +The top of a lower limit is at least this much below the bottom of the +object on which the limit is set +.RB ( 60 ). +. +. +.TP +.B big_op_spacing5 +This much vertical space is added above and below limits +.RB ( 10 ). +. +. +.TP +.B baseline_sep +The baselines of the rows in a pile or matrix are normally this far +apart +.RB ( 140 ). +. +Usually equal to the sum of +.B num1 +and +.BR denom1 . +. +. +.TP +.B shift_down +The midpoint between the top baseline and the bottom baseline in a +matrix or pile is shifted down by this much from the axis +.RB ( 26 ). +. +Usually equal to +.BR axis_height . +. +. +.TP +.B column_sep +This much space is added between columns in a matrix +.RB ( 100 ). +. +. +.TP +.B matrix_side_sep +This much space is added at each side of a matrix +.RB ( 17 ). +. +. +.br +.ne 4v +.TP +.B draw_lines +If non-zero, +.I @g@eqn +draws lines using the +.I troff \" generic +.B \[rs]D +escape sequence, +rather than the +.B \[rs]l +escape sequence and the +.B \[rs][ru] +special character. +. +The +.I eqnrc +file sets the default: +.BR 1 \~on +.BR ps , +.BR html , +and the X11 devices, +.RB otherwise\~ 0 . +. +. +.TP +.B body_height +is the presumed height of an equation above the text baseline; +.I @g@eqn +adds any excess as extra pre-vertical line spacing with +.IR troff 's\" generic +.B \[rs]x +escape sequence +.RB ( 85 ). +. +. +.TP +.B body_depth +is the presumed depth of an equation below the text baseline; +.I @g@eqn +adds any excess as extra post-vertical line spacing with +.IR troff 's\" generic +.B \[rs]x +escape sequence +.RB ( 35 ). +. +. +.TP +.B nroff +If non-zero, +then +.B \%ndefine +behaves like +.B \%define +and +.B \%tdefine +is ignored, +otherwise +.B \%tdefine +behaves like +.B \%define +and +.B \%ndefine +is ignored. +. +The +.I eqnrc +file sets the default: +.BR 1 \~on +.BR ascii , +.BR latin1 , +.BR utf8 , +and +.B cp1047 +devices, +.RB otherwise\~ 0 . +.RE +. +. +.\" ==================================================================== +.SS Macros +.\" ==================================================================== +. +In GNU +.IR eqn , \" GNU +macros can take arguments. +. +A word defined by any of the +.BR \%define , +.BR \%ndefine , +or +.B \%tdefine +primitives followed immediately by a left parenthesis is treated as a +.I "parameterized macro call:" +subsequent tokens up to a matching right parenthesis are treated as +comma-separated arguments. +. +In this context only, +commas and parentheses also serve as token separators. +. +A macro argument is not terminated by a comma inside parentheses nested +within it. +. +In a macro definition, +.BI $ n\c +, +where +.I n +is between 1 and\~9 inclusive, +is replaced by the +.IR n th +argument; +if there are fewer than +.IR n \~arguments, +it is replaced by nothing. +. +. +.\" ==================================================================== +.SS "Predefined macros" +.\" ==================================================================== +. +GNU +.I eqn \" GNU +supports the predefined macros offered by AT&T +.IR eqn : \" AT&T +.BR and , +.BR \%approx , +.BR arc , +.BR cos , +.BR cosh , +.BR del , +.BR det , +.BR dot , +.BR \%dotdot , +.BR dyad , +.BR exp , +.BR for , +.BR grad , +.BR half , +.BR hat , +.BR if , +.BR \%inter , +.BR Im , +.BR inf , +.BR int , +.BR lim , +.BR ln , +.BR log , +.BR max , +.BR min , +.BR \%nothing , +.BR \%partial , +.BR prime , +.BR prod , +.BR Re , +.BR sin , +.BR sinh , +.BR sum , +.BR tan , +.BR tanh , +.BR tilde , +.BR times , +.BR union , +.BR vec , +.BR == , +.BR != , +.BR += , +.BR \-> , +.BR <\- , +.BR << , +.BR >> , +and +.RB \[lq] .\|.\|. \[rq]. +. +The lowercase classical Greek letters are available as +.BR \%alpha , +.BR beta , +.BR chi , +.BR delta , +.BR \%epsilon , +.BR eta , +.BR gamma , +.BR iota , +.BR kappa , +.BR lambda , +.BR mu , +.BR nu , +.BR omega , +.BR \%omicron , +.BR phi , +.BR pi , +.BR psi , +.BR rho , +.BR sigma , +.BR tau , +.BR theta , +.BR \%upsilon , +.BR xi , +and +.BR zeta . +. +Spell them with an initial capital letter +.RB \%( Alpha ) +or in full capitals +.RB \%( ALPHA ) +to obtain uppercase forms. +. +. +.P +GNU +.I eqn \" GNU +further defines the macros +.BR cdot , +.BR cdots , +and +.B utilde +(all discussed above), +.BR \%dollar , +which sets a dollar sign, +and +.BR ldots , +which sets an ellipsis on the text baseline. +. +. +.\" ==================================================================== +.SS Fonts +.\" ==================================================================== +. +.I @g@eqn +uses up to three typefaces to set an equation: +italic (oblique), +roman (upright), +and bold. +. +Assign each a +.I groff +typeface with the primitives +.BR gfont , +.BR \%grfont , +and +.B \%gbfont. +. +The defaults are the styles +.BR I , +.BR R , +and +.B B +(applied to the current font family). +. +The +.B \%chartype +primitive +(see above) +sets a character's type, +which determines the face used to set it. +. +The +.RB \%\[lq] letter \[rq] +type is set in italics; +others are set in roman. +. +Use the +.B bold +primitive to select an (upright) bold style. +. +. +.TP +.BI gbfont\~ f +.RI Select\~ f +as the bold font. +. +This is a GNU extension. +. +. +.TP +.BI gfont\~ f +.RI Select\~ f +as the italic font. +. +. +.TP +.BI grfont\~ f +.RI Select\~ f +as the roman font. +. +This is a GNU extension. +. +. +.br +.ne 4v +.\" ==================================================================== +.SH Options +.\" ==================================================================== +. +.B \-\-help +displays a usage message, +while +.B \-v +and +.B \-\-version +show version information; +all exit afterward. +. +. +.TP +.B \-C +Recognize +.B .EQ +and +.B .EN +even when followed by a character other than space or newline. +. +. +.TP +.BI \-d\~ xy +Specify delimiters +.I x +for left +.RI and\~ y +for right ends +of equations not bracketed by +.BR .EQ / .EN . +. +.I x +and +.I y +need not be distinct. +. +Any +.RB \%\[lq] delim +.IR xy \[rq] +statements in the source file override this option. +. +. +.TP +.BI \-f\~ F +is equivalent to +.RB \[lq] gfont +.IR F \[rq]. +. +. +.TP +.BI \-m\~ n +is equivalent to +.RB \[lq] "set \%minimum_size" +.IR n \[rq]. +. +. +.TP +.BI \-M\~ dir +Search +.I dir +for +.I eqnrc +before those listed in section \[lq]Description\[rq] above. +. +. +.TP +.B \-N +Prohibit newlines within delimiters. +. +This option allows +.I @g@eqn +to recover better from missing closing delimiters. +. +. +.TP +.BI \-p\~ n +Set sub- and superscripts +.IR n \~points +smaller than the surrounding text. +. +This option is deprecated. +. +.I @g@eqn +normally sets sub- and superscripts at 70% of the type size of the +surrounding text. +. +. +.TP +.B \-r +Reduce the type size of subscripts at most once relative to the base +type size for the equation. +. +. +.TP +.B \-R +Don't load +.IR eqnrc . +. +. +.TP +.BI \-s\~ n +is equivalent to +.RB \[lq] gsize +.IR n \[rq]. +. +This option is deprecated. +. +. +.TP +.BI \-T\~ dev +Prepare output for the device +.IR dev . +. +In most cases, +the effect of this is to define a macro +.I dev +with a value +.RB of\~ 1 ; +.I eqnrc +uses this to provide definitions appropriate for the device. +. +However, +if the specified driver is \[lq]MathML\[rq], +the output is MathML markup rather than +.I @g@troff +input, +and +.I eqnrc +is not loaded at all. +. +The default output device is +.BR @DEVICE@ . +. +. +.\" ==================================================================== +.SH Files +.\" ==================================================================== +. +.TP +.I @MACRODIR@/\:\%eqnrc +Initialization file. +. +. +.\" ==================================================================== +.SH "MathML mode limitations" +.\" ==================================================================== +. +MathML is designed on the assumption that it cannot know the exact +physical characteristics of the media and devices on which it will +be rendered. +. +It does not support control of motions and sizes to the same +degree +.I @g@troff +does. +. +. +.IP \[bu] 2n +.I @g@eqn +customization parameters have no effect on generated MathML. +. +. +.IP \[bu] +The +.BR \%special , +.BR up , +.BR down , +.BR fwd , +and +.B back +primitives cannot be implemented, +and yield a MathML \%\[lq]<merror>\[rq] message instead. +. +. +.IP \[bu] +The +.B vcenter +primitive is silently ignored, +as centering on the math axis is the MathML default. +. +. +.IP \[bu] +Characters that +.I @g@eqn +sets extra large in +.I troff \" mode +mode\[em]notably the integral sign\[em]may appear too small and need to +have their \[lq]<mstyle>\[rq] wrappers adjusted by hand. +. +. +.P +As in its +.I troff \" mode +mode, +.I @g@eqn +in MathML mode leaves the +.B .EQ +and +.B .EN +tokens in place, +but emits nothing corresponding to +.B \%delim +delimiters. +. +They can, +however, +be recognized as character sequences that begin with \[lq]<math>\[rq], +end with \[lq]</math>\[rq], +and do not cross line boundaries. +. +. +.\" ==================================================================== +.SH Caveats +.\" ==================================================================== +. +Tokens must be double-quoted in +.I eqn \" generic +input if they are not to be recognized as names of macros or primitives, +or if they are to be interpreted by +.IR troff . \" generic +. +In particular, +short ones, +like +.RB \[lq] pi \[rq] +and +.RB \[lq] PI \[rq], +can collide with +.I troff \" generic +identifiers. +. +For instance, +the +.I eqn \" generic +command +.RB \%\[lq]\^ "gfont PI" \^\[rq] +does not select +.IR groff 's +Palatino italic font for the global italic face; +you must use +.RB \%\[lq]\^ "gfont \[dq]PI\[dq]" \^\[rq] +instead. +. +. +.P +Delimited equations are set at the type size current at the beginning of +the input line, +not necessarily that immediately preceding the opening delimiter. +. +. +.P +Unlike \*[tx], +.I eqn \" generic +does not inherently distinguish displayed and inline equation styles; +see the +.B smallover +primitive above. +. +However, +macro packages frequently define +.B EQ +and +.B EN +macros such that the equation within is displayed. +. +These macros may accept arguments permitting the equation to be labeled +or captioned; +see the package's documentation. +. +. +.\" ==================================================================== +.SH Bugs +.\" ==================================================================== +. +.I eqn \" generic +abuses terminology\[em]its +\[lq]equations\[rq] +can be inequalities, +bare expressions, +or unintelligible gibberish. +. +But there's no changing it now. +. +. +.P +In +.I nroff \" mode +mode, +lowercase Greek letters are rendered in roman instead of italic style. +. +. +.P +In MathML mode, +the +.B mark +and +.B lineup +features don't work. +. +These could, +in theory, +be implemented with \%\[lq]<maligngroup>\[rq] elements. +. +. +.P +In MathML mode, +each digit of a numeric literal gets a separate \[lq]<mn>\:</mn>\[rq] +pair, +and decimal points are tagged with \[lq]<mo>\:</mo>\[rq]. +. +This is allowed by the specification, +but inefficient. +. +. +.\" ==================================================================== +.SH Examples +.\" ==================================================================== +. +We first illustrate +.I @g@eqn +usage with a trigonometric identity. +. +. +.RS +.P +.EX +\&.EQ +sin ( alpha + beta ) = sin alpha cos beta + cos alpha sin beta +\&.EN +.EE +.if t \{\ +. +. +.P +.RS +.EQ +sin ( alpha + beta ) = sin alpha cos beta + cos alpha sin beta +.EN +.RE +.\} +.RE +. +. +.P +It can be convenient to set up delimiters if mathematical content will +appear frequently in running text. +. +. +.RS +.P +.EX +\&.EQ +delim $$ +\&.EN +. +Having cached a table of logarithms, +the property $ln ( x y ) = ln x + ln y$ sped calculations. +.EE +.if t \{\ +. +. +.P +.RS +.EQ +delim $$ +.EN +. +Having cached a table of logarithms, +the property $ln ( x y ) = ln x + ln y$ sped calculations. +. +.\" We _must_ shut delimiters back off when serially processing man +.\" pages, or subsequent documents cannot safely use those characters. +.EQ +delim off +.EN +.RE +.\} +.RE +. +. +.P +The quadratic formula illustrates use of fractions and radicals, +and affords an opportunity to use the full space token +.BR \[ti] . +. +. +.RS +.P +.EX +\&.EQ +x = { \- b \[ti] \[rs][+\-] \[ti] sqrt { b sup 2 \- 4 a c } } \ +over { 2 a } +\&.EN +.EE +.if t \{\ +. +. +.P +.RS +.EQ +x = { - b ~ \[+-] ~ sqrt { b sup 2 - 4 a c } } over { 2 a } +.EN +.RE +.\} +.RE +. +. +.P +Alternatively, +we could define the plus-minus sign as a binary operator. +. +Automatic spacing puts 0.06\~em less space on either side of the +plus-minus than \[ti] does, +this being the difference between the widths of the +.B medium_space +parameter used by binary operators and that of the full space. +. +Independently, +we can define a macro \[lq]frac\[rq] for setting fractions. +. +. +.RS +.P +.EX +\&.EQ +chartype "binary" \[rs][+\-] +define frac ! { $1 } over { $2 } ! +x = frac(\- b \[rs][+\-] sqrt { b sup 2 \- 4 a c }, 2 a) +\&.EN +.EE +.if t \{\ +. +. +.P +.RS +.EQ +chartype "binary" \[+-] +define frac ! { $1 } over { $2 } ! +x = frac(- b \[+-] sqrt { b sup 2 - 4 a c }, 2 a) +.EN +.RE +.\} +.RE +. +. +.\" ==================================================================== +.SH "See also" +.\" ==================================================================== +. +\[lq]Typesetting Mathematics\[em]User's Guide\[rq] +(2nd edition), +by Brian W.\& Kernighan +and Lorinda L.\& Cherry, +1978, +AT&T Bell Laboratories Computing Science Technical Report No.\& 17. +. +. +.P +.IR The\~\*[tx]book , +by Donald E.\& Knuth, +1984, +Addison-Wesley Professional. +. +Appendix\~G +discusses many of the parameters from section \[lq]Customization\[rq] +above in greater detail. +. +. +.P +.MR groff_char @MAN7EXT@ , +particularly subsections \[lq]Logical symbols\[rq], +\[lq]Mathematical symbols\[rq], +and \[lq]Greek glyphs\[rq], +documents a variety of special character escape sequences useful in +mathematical typesetting. +. +. +.P +.MR groff @MAN1EXT@ , +.MR @g@troff @MAN1EXT@ , +.MR @g@pic @MAN1EXT@ , +.MR groff_font @MAN5EXT@ +. +. +.\" Clean up. +.rm tx +. +.\" Restore compatibility mode (for, e.g., Solaris 10/11). +.cp \n[*groff_eqn_1_man_C] +.do rr *groff_eqn_1_man_C +. +. +.\" Local Variables: +.\" fill-column: 72 +.\" mode: nroff +.\" tab-width: 12 +.\" End: +.\" vim: set filetype=groff tabstop=12 textwidth=72: diff --git a/src/preproc/eqn/eqn.am b/src/preproc/eqn/eqn.am new file mode 100644 index 0000000..e32bfb5 --- /dev/null +++ b/src/preproc/eqn/eqn.am @@ -0,0 +1,79 @@ +# Copyright (C) 2014-2020 Free Software Foundation, Inc. +# +# 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 <http://www.gnu.org/licenses/>. + +prefixexecbin_PROGRAMS += eqn +prefixexecbin_SCRIPTS += neqn +eqn_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I $(top_srcdir)/src/preproc/eqn \ + -I $(top_builddir)/src/preproc/eqn +eqn_LDADD = $(LIBM) libgroff.a lib/libgnu.a +eqn_SOURCES = \ + src/preproc/eqn/main.cpp \ + src/preproc/eqn/lex.cpp \ + src/preproc/eqn/box.cpp \ + src/preproc/eqn/limit.cpp \ + src/preproc/eqn/list.cpp \ + src/preproc/eqn/over.cpp \ + src/preproc/eqn/text.cpp \ + src/preproc/eqn/script.cpp \ + src/preproc/eqn/mark.cpp \ + src/preproc/eqn/other.cpp \ + src/preproc/eqn/delim.cpp \ + src/preproc/eqn/sqrt.cpp \ + src/preproc/eqn/pile.cpp \ + src/preproc/eqn/special.cpp \ + src/preproc/eqn/eqn.ypp \ + src/preproc/eqn/box.h \ + src/preproc/eqn/pbox.h \ + src/preproc/eqn/eqn.h + +PREFIXMAN1 += src/preproc/eqn/eqn.1 src/preproc/eqn/neqn.1 +EXTRA_DIST += \ + src/preproc/eqn/TODO \ + src/preproc/eqn/eqn.1.man \ + src/preproc/eqn/neqn.1.man \ + src/preproc/eqn/neqn.sh + +# Since eqn_CPPFLAGS was set, all .o files have an 'eqn-' prefix. +src/preproc/eqn/eqn-lex.$(OBJEXT): src/preproc/eqn/eqn.hpp + +MAINTAINERCLEANFILES += \ + src/preproc/eqn/eqn.hpp \ + src/preproc/eqn/eqn.cpp \ + src/preproc/eqn/eqn.output + +neqn: $(top_srcdir)/src/preproc/eqn/neqn.sh $(SH_DEPS_SED_SCRIPT) + $(AM_V_GEN)sed -e 's/[@]g[@]/$(g)/g' \ + -f $(SH_DEPS_SED_SCRIPT) \ + -e $(SH_SCRIPT_SED_CMD) \ + $(top_srcdir)/src/preproc/eqn/neqn.sh \ + >$@.tmp \ + && chmod +x $@.tmp \ + && mv $@.tmp $@ + +eqn_TESTS = \ + src/preproc/eqn/tests/diagnostics-report-correct-line-numbers.sh +TESTS += $(eqn_TESTS) +EXTRA_DIST += $(eqn_TESTS) + + +# Local Variables: +# fill-column: 72 +# mode: makefile-automake +# End: +# vim: set autoindent filetype=automake textwidth=72: diff --git a/src/preproc/eqn/eqn.cpp b/src/preproc/eqn/eqn.cpp new file mode 100644 index 0000000..6756b9e --- /dev/null +++ b/src/preproc/eqn/eqn.cpp @@ -0,0 +1,2112 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program 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. + + This program 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 <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 18 "../src/preproc/eqn/eqn.ypp" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "lib.h" +#include "box.h" +extern int non_empty_flag; +int yylex(); +void yyerror(const char *); + +#line 87 "src/preproc/eqn/eqn.cpp" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED +# define YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + OVER = 258, /* OVER */ + SMALLOVER = 259, /* SMALLOVER */ + SQRT = 260, /* SQRT */ + SUB = 261, /* SUB */ + SUP = 262, /* SUP */ + LPILE = 263, /* LPILE */ + RPILE = 264, /* RPILE */ + CPILE = 265, /* CPILE */ + PILE = 266, /* PILE */ + LEFT = 267, /* LEFT */ + RIGHT = 268, /* RIGHT */ + TO = 269, /* TO */ + FROM = 270, /* FROM */ + SIZE = 271, /* SIZE */ + FONT = 272, /* FONT */ + ROMAN = 273, /* ROMAN */ + BOLD = 274, /* BOLD */ + ITALIC = 275, /* ITALIC */ + FAT = 276, /* FAT */ + ACCENT = 277, /* ACCENT */ + BAR = 278, /* BAR */ + UNDER = 279, /* UNDER */ + ABOVE = 280, /* ABOVE */ + TEXT = 281, /* TEXT */ + QUOTED_TEXT = 282, /* QUOTED_TEXT */ + FWD = 283, /* FWD */ + BACK = 284, /* BACK */ + DOWN = 285, /* DOWN */ + UP = 286, /* UP */ + MATRIX = 287, /* MATRIX */ + COL = 288, /* COL */ + LCOL = 289, /* LCOL */ + RCOL = 290, /* RCOL */ + CCOL = 291, /* CCOL */ + MARK = 292, /* MARK */ + LINEUP = 293, /* LINEUP */ + TYPE = 294, /* TYPE */ + VCENTER = 295, /* VCENTER */ + PRIME = 296, /* PRIME */ + SPLIT = 297, /* SPLIT */ + NOSPLIT = 298, /* NOSPLIT */ + UACCENT = 299, /* UACCENT */ + SPECIAL = 300, /* SPECIAL */ + SPACE = 301, /* SPACE */ + GFONT = 302, /* GFONT */ + GSIZE = 303, /* GSIZE */ + DEFINE = 304, /* DEFINE */ + NDEFINE = 305, /* NDEFINE */ + TDEFINE = 306, /* TDEFINE */ + SDEFINE = 307, /* SDEFINE */ + UNDEF = 308, /* UNDEF */ + IFDEF = 309, /* IFDEF */ + INCLUDE = 310, /* INCLUDE */ + DELIM = 311, /* DELIM */ + CHARTYPE = 312, /* CHARTYPE */ + SET = 313, /* SET */ + GRFONT = 314, /* GRFONT */ + GBFONT = 315 /* GBFONT */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define OVER 258 +#define SMALLOVER 259 +#define SQRT 260 +#define SUB 261 +#define SUP 262 +#define LPILE 263 +#define RPILE 264 +#define CPILE 265 +#define PILE 266 +#define LEFT 267 +#define RIGHT 268 +#define TO 269 +#define FROM 270 +#define SIZE 271 +#define FONT 272 +#define ROMAN 273 +#define BOLD 274 +#define ITALIC 275 +#define FAT 276 +#define ACCENT 277 +#define BAR 278 +#define UNDER 279 +#define ABOVE 280 +#define TEXT 281 +#define QUOTED_TEXT 282 +#define FWD 283 +#define BACK 284 +#define DOWN 285 +#define UP 286 +#define MATRIX 287 +#define COL 288 +#define LCOL 289 +#define RCOL 290 +#define CCOL 291 +#define MARK 292 +#define LINEUP 293 +#define TYPE 294 +#define VCENTER 295 +#define PRIME 296 +#define SPLIT 297 +#define NOSPLIT 298 +#define UACCENT 299 +#define SPECIAL 300 +#define SPACE 301 +#define GFONT 302 +#define GSIZE 303 +#define DEFINE 304 +#define NDEFINE 305 +#define TDEFINE 306 +#define SDEFINE 307 +#define UNDEF 308 +#define IFDEF 309 +#define INCLUDE 310 +#define DELIM 311 +#define CHARTYPE 312 +#define SET 313 +#define GRFONT 314 +#define GBFONT 315 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 34 "../src/preproc/eqn/eqn.ypp" + + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; + +#line 269 "src/preproc/eqn/eqn.cpp" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED */ +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_OVER = 3, /* OVER */ + YYSYMBOL_SMALLOVER = 4, /* SMALLOVER */ + YYSYMBOL_SQRT = 5, /* SQRT */ + YYSYMBOL_SUB = 6, /* SUB */ + YYSYMBOL_SUP = 7, /* SUP */ + YYSYMBOL_LPILE = 8, /* LPILE */ + YYSYMBOL_RPILE = 9, /* RPILE */ + YYSYMBOL_CPILE = 10, /* CPILE */ + YYSYMBOL_PILE = 11, /* PILE */ + YYSYMBOL_LEFT = 12, /* LEFT */ + YYSYMBOL_RIGHT = 13, /* RIGHT */ + YYSYMBOL_TO = 14, /* TO */ + YYSYMBOL_FROM = 15, /* FROM */ + YYSYMBOL_SIZE = 16, /* SIZE */ + YYSYMBOL_FONT = 17, /* FONT */ + YYSYMBOL_ROMAN = 18, /* ROMAN */ + YYSYMBOL_BOLD = 19, /* BOLD */ + YYSYMBOL_ITALIC = 20, /* ITALIC */ + YYSYMBOL_FAT = 21, /* FAT */ + YYSYMBOL_ACCENT = 22, /* ACCENT */ + YYSYMBOL_BAR = 23, /* BAR */ + YYSYMBOL_UNDER = 24, /* UNDER */ + YYSYMBOL_ABOVE = 25, /* ABOVE */ + YYSYMBOL_TEXT = 26, /* TEXT */ + YYSYMBOL_QUOTED_TEXT = 27, /* QUOTED_TEXT */ + YYSYMBOL_FWD = 28, /* FWD */ + YYSYMBOL_BACK = 29, /* BACK */ + YYSYMBOL_DOWN = 30, /* DOWN */ + YYSYMBOL_UP = 31, /* UP */ + YYSYMBOL_MATRIX = 32, /* MATRIX */ + YYSYMBOL_COL = 33, /* COL */ + YYSYMBOL_LCOL = 34, /* LCOL */ + YYSYMBOL_RCOL = 35, /* RCOL */ + YYSYMBOL_CCOL = 36, /* CCOL */ + YYSYMBOL_MARK = 37, /* MARK */ + YYSYMBOL_LINEUP = 38, /* LINEUP */ + YYSYMBOL_TYPE = 39, /* TYPE */ + YYSYMBOL_VCENTER = 40, /* VCENTER */ + YYSYMBOL_PRIME = 41, /* PRIME */ + YYSYMBOL_SPLIT = 42, /* SPLIT */ + YYSYMBOL_NOSPLIT = 43, /* NOSPLIT */ + YYSYMBOL_UACCENT = 44, /* UACCENT */ + YYSYMBOL_SPECIAL = 45, /* SPECIAL */ + YYSYMBOL_SPACE = 46, /* SPACE */ + YYSYMBOL_GFONT = 47, /* GFONT */ + YYSYMBOL_GSIZE = 48, /* GSIZE */ + YYSYMBOL_DEFINE = 49, /* DEFINE */ + YYSYMBOL_NDEFINE = 50, /* NDEFINE */ + YYSYMBOL_TDEFINE = 51, /* TDEFINE */ + YYSYMBOL_SDEFINE = 52, /* SDEFINE */ + YYSYMBOL_UNDEF = 53, /* UNDEF */ + YYSYMBOL_IFDEF = 54, /* IFDEF */ + YYSYMBOL_INCLUDE = 55, /* INCLUDE */ + YYSYMBOL_DELIM = 56, /* DELIM */ + YYSYMBOL_CHARTYPE = 57, /* CHARTYPE */ + YYSYMBOL_SET = 58, /* SET */ + YYSYMBOL_GRFONT = 59, /* GRFONT */ + YYSYMBOL_GBFONT = 60, /* GBFONT */ + YYSYMBOL_61_ = 61, /* '^' */ + YYSYMBOL_62_ = 62, /* '~' */ + YYSYMBOL_63_t_ = 63, /* '\t' */ + YYSYMBOL_64_ = 64, /* '{' */ + YYSYMBOL_65_ = 65, /* '}' */ + YYSYMBOL_YYACCEPT = 66, /* $accept */ + YYSYMBOL_top = 67, /* top */ + YYSYMBOL_equation = 68, /* equation */ + YYSYMBOL_mark = 69, /* mark */ + YYSYMBOL_from_to = 70, /* from_to */ + YYSYMBOL_sqrt_over = 71, /* sqrt_over */ + YYSYMBOL_script = 72, /* script */ + YYSYMBOL_nonsup = 73, /* nonsup */ + YYSYMBOL_simple = 74, /* simple */ + YYSYMBOL_number = 75, /* number */ + YYSYMBOL_pile_element_list = 76, /* pile_element_list */ + YYSYMBOL_pile_arg = 77, /* pile_arg */ + YYSYMBOL_column_list = 78, /* column_list */ + YYSYMBOL_column_element_list = 79, /* column_element_list */ + YYSYMBOL_column_arg = 80, /* column_arg */ + YYSYMBOL_column = 81, /* column */ + YYSYMBOL_text = 82, /* text */ + YYSYMBOL_delim = 83 /* delim */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 72 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 379 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 66 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 18 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 75 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 142 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 315 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 63, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 61, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 64, 2, 65, 62, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 125, 125, 127, 132, 134, 145, 147, 149, 154, + 156, 158, 160, 162, 167, 169, 171, 173, 178, 180, + 185, 187, 189, 194, 196, 198, 200, 202, 204, 206, + 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, + 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, + 248, 250, 252, 254, 256, 258, 263, 273, 275, 280, + 282, 287, 289, 294, 296, 301, 303, 308, 310, 312, + 314, 318, 320, 325, 327, 329 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "OVER", "SMALLOVER", + "SQRT", "SUB", "SUP", "LPILE", "RPILE", "CPILE", "PILE", "LEFT", "RIGHT", + "TO", "FROM", "SIZE", "FONT", "ROMAN", "BOLD", "ITALIC", "FAT", "ACCENT", + "BAR", "UNDER", "ABOVE", "TEXT", "QUOTED_TEXT", "FWD", "BACK", "DOWN", + "UP", "MATRIX", "COL", "LCOL", "RCOL", "CCOL", "MARK", "LINEUP", "TYPE", + "VCENTER", "PRIME", "SPLIT", "NOSPLIT", "UACCENT", "SPECIAL", "SPACE", + "GFONT", "GSIZE", "DEFINE", "NDEFINE", "TDEFINE", "SDEFINE", "UNDEF", + "IFDEF", "INCLUDE", "DELIM", "CHARTYPE", "SET", "GRFONT", "GBFONT", + "'^'", "'~'", "'\\t'", "'{'", "'}'", "$accept", "top", "equation", + "mark", "from_to", "sqrt_over", "script", "nonsup", "simple", "number", + "pile_element_list", "pile_arg", "column_list", "column_element_list", + "column_arg", "column", "text", "delim", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-76) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 230, 269, 6, 6, 6, 6, 2, 14, 14, 308, + 308, 308, 308, -76, -76, 14, 14, 14, 14, -50, + 230, 230, 14, 308, 4, 23, 14, -76, -76, -76, + 230, 24, 230, -76, -76, 70, -76, -76, 20, -76, + -76, -76, 230, -44, -76, -76, -76, -76, -76, -76, + -76, -76, 230, 308, 308, 57, 57, 57, 57, 308, + 308, 308, 308, 3, -76, -76, 308, 57, -76, -76, + 308, 130, -76, -76, 269, 269, 269, 269, 308, 308, + 308, -76, -76, -76, 308, 230, -12, 230, 191, 57, + 57, 57, 57, 57, 57, 8, 8, 8, 8, 12, + -76, 57, 57, -76, -76, -76, -76, 79, -76, 335, + -76, -76, -76, 230, -76, -6, 2, 230, 28, -76, + -76, -76, -76, -76, -76, 269, 269, 308, 230, -76, + -76, 230, -3, 230, -76, -76, -76, 230, -76, -2, + 230, -76 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, + 0, 0, 3, 4, 6, 9, 14, 18, 20, 15, + 71, 72, 0, 0, 32, 56, 33, 34, 31, 74, + 75, 73, 0, 0, 0, 43, 44, 45, 46, 0, + 0, 0, 0, 0, 7, 8, 0, 54, 25, 26, + 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, + 0, 38, 39, 40, 0, 57, 0, 0, 37, 48, + 47, 49, 50, 52, 51, 0, 0, 0, 0, 0, + 61, 53, 55, 30, 16, 17, 10, 11, 21, 20, + 19, 41, 42, 0, 59, 0, 0, 0, 0, 67, + 68, 69, 70, 35, 62, 0, 0, 0, 58, 60, + 36, 63, 0, 0, 12, 13, 22, 0, 65, 0, + 64, 66 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -76, -76, 0, -17, -75, 1, -67, -13, 46, -7, + 9, 13, -76, -47, 22, -4, -1, -29 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_uint8 yydefgoto[] = +{ + 0, 31, 85, 33, 34, 35, 36, 37, 38, 43, + 86, 44, 99, 132, 119, 100, 45, 52 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint8 yytable[] = +{ + 32, 106, 39, 64, 65, 51, 53, 54, 59, 60, + 61, 62, 110, 113, 63, 73, 46, 47, 48, 113, + 87, 66, 137, 137, 72, 70, 78, 79, 40, 41, + 71, 68, 40, 41, 40, 41, 95, 96, 97, 98, + 40, 41, 80, 81, 82, 95, 96, 97, 98, 69, + 134, 135, 88, 114, 73, 55, 56, 57, 58, 129, + 136, 83, 138, 141, 84, 108, 49, 50, 73, 67, + 42, 73, 117, 74, 75, 104, 105, 123, 107, 80, + 81, 82, 74, 75, 76, 77, 139, 130, 118, 118, + 118, 118, 133, 125, 126, 124, 115, 0, 83, 89, + 90, 84, 0, 0, 0, 91, 92, 93, 94, 0, + 0, 73, 101, 128, 73, 51, 102, 131, 120, 121, + 122, 0, 0, 73, 109, 0, 111, 0, 0, 0, + 112, 0, 0, 131, 0, 1, 0, 140, 2, 3, + 4, 5, 6, 0, 0, 0, 7, 8, 9, 10, + 11, 12, 0, 0, 0, 0, 13, 14, 15, 16, + 17, 18, 19, 0, 0, 0, 0, 20, 21, 22, + 23, 0, 24, 25, 0, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 27, 28, 29, 30, 103, 1, 0, 0, 2, + 3, 4, 5, 6, 116, 0, 0, 7, 8, 9, + 10, 11, 12, 0, 0, 0, 0, 13, 14, 15, + 16, 17, 18, 19, 0, 0, 0, 0, 20, 21, + 22, 23, 0, 24, 25, 1, 26, 0, 2, 3, + 4, 5, 6, 0, 0, 0, 7, 8, 9, 10, + 11, 12, 27, 28, 29, 30, 13, 14, 15, 16, + 17, 18, 19, 0, 0, 0, 0, 20, 21, 22, + 23, 0, 24, 25, 1, 26, 0, 2, 3, 4, + 5, 6, 0, 0, 0, 7, 8, 9, 10, 11, + 12, 27, 28, 29, 30, 13, 14, 15, 16, 17, + 18, 19, 0, 0, 0, 0, 0, 0, 22, 23, + 0, 24, 25, 0, 26, 0, 2, 3, 4, 5, + 6, 0, 0, 0, 7, 8, 9, 10, 11, 12, + 27, 28, 29, 30, 13, 14, 15, 16, 17, 18, + 19, 78, 127, 0, 0, 0, 0, 22, 23, 0, + 24, 25, 0, 26, 0, 0, 0, 80, 81, 82, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, + 28, 29, 30, 0, 0, 0, 83, 0, 0, 84 +}; + +static const yytype_int16 yycheck[] = +{ + 0, 76, 1, 20, 21, 6, 7, 8, 15, 16, + 17, 18, 79, 25, 64, 32, 3, 4, 5, 25, + 64, 22, 25, 25, 0, 26, 6, 7, 26, 27, + 30, 27, 26, 27, 26, 27, 33, 34, 35, 36, + 26, 27, 22, 23, 24, 33, 34, 35, 36, 26, + 125, 126, 52, 65, 71, 9, 10, 11, 12, 65, + 127, 41, 65, 65, 44, 78, 64, 65, 85, 23, + 64, 88, 64, 3, 4, 74, 75, 65, 77, 22, + 23, 24, 3, 4, 14, 15, 133, 116, 95, 96, + 97, 98, 64, 14, 15, 99, 87, -1, 41, 53, + 54, 44, -1, -1, -1, 59, 60, 61, 62, -1, + -1, 128, 66, 113, 131, 116, 70, 117, 96, 97, + 98, -1, -1, 140, 78, -1, 80, -1, -1, -1, + 84, -1, -1, 133, -1, 5, -1, 137, 8, 9, + 10, 11, 12, -1, -1, -1, 16, 17, 18, 19, + 20, 21, -1, -1, -1, -1, 26, 27, 28, 29, + 30, 31, 32, -1, -1, -1, -1, 37, 38, 39, + 40, -1, 42, 43, -1, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 61, 62, 63, 64, 65, 5, -1, -1, 8, + 9, 10, 11, 12, 13, -1, -1, 16, 17, 18, + 19, 20, 21, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, -1, -1, -1, -1, 37, 38, + 39, 40, -1, 42, 43, 5, 45, -1, 8, 9, + 10, 11, 12, -1, -1, -1, 16, 17, 18, 19, + 20, 21, 61, 62, 63, 64, 26, 27, 28, 29, + 30, 31, 32, -1, -1, -1, -1, 37, 38, 39, + 40, -1, 42, 43, 5, 45, -1, 8, 9, 10, + 11, 12, -1, -1, -1, 16, 17, 18, 19, 20, + 21, 61, 62, 63, 64, 26, 27, 28, 29, 30, + 31, 32, -1, -1, -1, -1, -1, -1, 39, 40, + -1, 42, 43, -1, 45, -1, 8, 9, 10, 11, + 12, -1, -1, -1, 16, 17, 18, 19, 20, 21, + 61, 62, 63, 64, 26, 27, 28, 29, 30, 31, + 32, 6, 7, -1, -1, -1, -1, 39, 40, -1, + 42, 43, -1, 45, -1, -1, -1, 22, 23, 24, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, + 62, 63, 64, -1, -1, -1, 41, -1, -1, 44 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 5, 8, 9, 10, 11, 12, 16, 17, 18, + 19, 20, 21, 26, 27, 28, 29, 30, 31, 32, + 37, 38, 39, 40, 42, 43, 45, 61, 62, 63, + 64, 67, 68, 69, 70, 71, 72, 73, 74, 71, + 26, 27, 64, 75, 77, 82, 77, 77, 77, 64, + 65, 82, 83, 82, 82, 74, 74, 74, 74, 75, + 75, 75, 75, 64, 69, 69, 82, 74, 27, 26, + 82, 68, 0, 69, 3, 4, 14, 15, 6, 7, + 22, 23, 24, 41, 44, 68, 76, 64, 68, 74, + 74, 74, 74, 74, 74, 33, 34, 35, 36, 78, + 81, 74, 74, 65, 71, 71, 70, 71, 73, 74, + 72, 74, 74, 25, 65, 76, 13, 64, 75, 80, + 80, 80, 80, 65, 81, 14, 15, 7, 68, 65, + 83, 68, 79, 64, 70, 70, 72, 25, 65, 79, + 68, 65 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 66, 67, 67, 68, 68, 69, 69, 69, 70, + 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, + 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 75, 76, 76, 77, + 77, 78, 78, 79, 79, 80, 80, 81, 81, 81, + 81, 82, 82, 83, 83, 83 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 1, 1, 2, 1, 2, 2, 1, + 3, 3, 5, 5, 1, 2, 3, 3, 1, 3, + 1, 3, 5, 1, 1, 2, 2, 1, 1, 1, + 3, 2, 2, 2, 2, 4, 5, 3, 2, 2, + 2, 3, 3, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 2, 3, 1, 1, 3, 3, + 4, 1, 2, 1, 3, 3, 4, 2, 2, 2, + 2, 1, 1, 1, 1, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: /* top: equation */ +#line 128 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].b)->top_level(); non_empty_flag = 1; } +#line 1472 "src/preproc/eqn/eqn.cpp" + break; + + case 4: /* equation: mark */ +#line 133 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1478 "src/preproc/eqn/eqn.cpp" + break; + + case 5: /* equation: equation mark */ +#line 135 "../src/preproc/eqn/eqn.ypp" + { + list_box *lb = (yyvsp[-1].b)->to_list_box(); + if (!lb) + lb = new list_box((yyvsp[-1].b)); + lb->append((yyvsp[0].b)); + (yyval.b) = lb; + } +#line 1490 "src/preproc/eqn/eqn.cpp" + break; + + case 6: /* mark: from_to */ +#line 146 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1496 "src/preproc/eqn/eqn.cpp" + break; + + case 7: /* mark: MARK mark */ +#line 148 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_mark_box((yyvsp[0].b)); } +#line 1502 "src/preproc/eqn/eqn.cpp" + break; + + case 8: /* mark: LINEUP mark */ +#line 150 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_lineup_box((yyvsp[0].b)); } +#line 1508 "src/preproc/eqn/eqn.cpp" + break; + + case 9: /* from_to: sqrt_over */ +#line 155 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1514 "src/preproc/eqn/eqn.cpp" + break; + + case 10: /* from_to: sqrt_over TO from_to */ +#line 157 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_limit_box((yyvsp[-2].b), 0, (yyvsp[0].b)); } +#line 1520 "src/preproc/eqn/eqn.cpp" + break; + + case 11: /* from_to: sqrt_over FROM sqrt_over */ +#line 159 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_limit_box((yyvsp[-2].b), (yyvsp[0].b), 0); } +#line 1526 "src/preproc/eqn/eqn.cpp" + break; + + case 12: /* from_to: sqrt_over FROM sqrt_over TO from_to */ +#line 161 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_limit_box((yyvsp[-4].b), (yyvsp[-2].b), (yyvsp[0].b)); } +#line 1532 "src/preproc/eqn/eqn.cpp" + break; + + case 13: /* from_to: sqrt_over FROM sqrt_over FROM from_to */ +#line 163 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_limit_box((yyvsp[-4].b), make_limit_box((yyvsp[-2].b), (yyvsp[0].b), 0), 0); } +#line 1538 "src/preproc/eqn/eqn.cpp" + break; + + case 14: /* sqrt_over: script */ +#line 168 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1544 "src/preproc/eqn/eqn.cpp" + break; + + case 15: /* sqrt_over: SQRT sqrt_over */ +#line 170 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_sqrt_box((yyvsp[0].b)); } +#line 1550 "src/preproc/eqn/eqn.cpp" + break; + + case 16: /* sqrt_over: sqrt_over OVER sqrt_over */ +#line 172 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_over_box((yyvsp[-2].b), (yyvsp[0].b)); } +#line 1556 "src/preproc/eqn/eqn.cpp" + break; + + case 17: /* sqrt_over: sqrt_over SMALLOVER sqrt_over */ +#line 174 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_small_over_box((yyvsp[-2].b), (yyvsp[0].b)); } +#line 1562 "src/preproc/eqn/eqn.cpp" + break; + + case 18: /* script: nonsup */ +#line 179 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1568 "src/preproc/eqn/eqn.cpp" + break; + + case 19: /* script: simple SUP script */ +#line 181 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_script_box((yyvsp[-2].b), 0, (yyvsp[0].b)); } +#line 1574 "src/preproc/eqn/eqn.cpp" + break; + + case 20: /* nonsup: simple */ +#line 186 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[0].b); } +#line 1580 "src/preproc/eqn/eqn.cpp" + break; + + case 21: /* nonsup: simple SUB nonsup */ +#line 188 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_script_box((yyvsp[-2].b), (yyvsp[0].b), 0); } +#line 1586 "src/preproc/eqn/eqn.cpp" + break; + + case 22: /* nonsup: simple SUB simple SUP script */ +#line 190 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_script_box((yyvsp[-4].b), (yyvsp[-2].b), (yyvsp[0].b)); } +#line 1592 "src/preproc/eqn/eqn.cpp" + break; + + case 23: /* simple: TEXT */ +#line 195 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = split_text((yyvsp[0].str)); } +#line 1598 "src/preproc/eqn/eqn.cpp" + break; + + case 24: /* simple: QUOTED_TEXT */ +#line 197 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new quoted_text_box((yyvsp[0].str)); } +#line 1604 "src/preproc/eqn/eqn.cpp" + break; + + case 25: /* simple: SPLIT QUOTED_TEXT */ +#line 199 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = split_text((yyvsp[0].str)); } +#line 1610 "src/preproc/eqn/eqn.cpp" + break; + + case 26: /* simple: NOSPLIT TEXT */ +#line 201 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new quoted_text_box((yyvsp[0].str)); } +#line 1616 "src/preproc/eqn/eqn.cpp" + break; + + case 27: /* simple: '^' */ +#line 203 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new half_space_box; } +#line 1622 "src/preproc/eqn/eqn.cpp" + break; + + case 28: /* simple: '~' */ +#line 205 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new space_box; } +#line 1628 "src/preproc/eqn/eqn.cpp" + break; + + case 29: /* simple: '\t' */ +#line 207 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new tab_box; } +#line 1634 "src/preproc/eqn/eqn.cpp" + break; + + case 30: /* simple: '{' equation '}' */ +#line 209 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[-1].b); } +#line 1640 "src/preproc/eqn/eqn.cpp" + break; + + case 31: /* simple: PILE pile_arg */ +#line 211 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].pb)->set_alignment(CENTER_ALIGN); (yyval.b) = (yyvsp[0].pb); } +#line 1646 "src/preproc/eqn/eqn.cpp" + break; + + case 32: /* simple: LPILE pile_arg */ +#line 213 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].pb)->set_alignment(LEFT_ALIGN); (yyval.b) = (yyvsp[0].pb); } +#line 1652 "src/preproc/eqn/eqn.cpp" + break; + + case 33: /* simple: RPILE pile_arg */ +#line 215 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].pb)->set_alignment(RIGHT_ALIGN); (yyval.b) = (yyvsp[0].pb); } +#line 1658 "src/preproc/eqn/eqn.cpp" + break; + + case 34: /* simple: CPILE pile_arg */ +#line 217 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].pb)->set_alignment(CENTER_ALIGN); (yyval.b) = (yyvsp[0].pb); } +#line 1664 "src/preproc/eqn/eqn.cpp" + break; + + case 35: /* simple: MATRIX '{' column_list '}' */ +#line 219 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = (yyvsp[-1].mb); } +#line 1670 "src/preproc/eqn/eqn.cpp" + break; + + case 36: /* simple: LEFT delim equation RIGHT delim */ +#line 221 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_delim_box((yyvsp[-3].str), (yyvsp[-2].b), (yyvsp[0].str)); } +#line 1676 "src/preproc/eqn/eqn.cpp" + break; + + case 37: /* simple: LEFT delim equation */ +#line 223 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_delim_box((yyvsp[-1].str), (yyvsp[0].b), 0); } +#line 1682 "src/preproc/eqn/eqn.cpp" + break; + + case 38: /* simple: simple BAR */ +#line 225 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_overline_box((yyvsp[-1].b)); } +#line 1688 "src/preproc/eqn/eqn.cpp" + break; + + case 39: /* simple: simple UNDER */ +#line 227 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_underline_box((yyvsp[-1].b)); } +#line 1694 "src/preproc/eqn/eqn.cpp" + break; + + case 40: /* simple: simple PRIME */ +#line 229 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_prime_box((yyvsp[-1].b)); } +#line 1700 "src/preproc/eqn/eqn.cpp" + break; + + case 41: /* simple: simple ACCENT simple */ +#line 231 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_accent_box((yyvsp[-2].b), (yyvsp[0].b)); } +#line 1706 "src/preproc/eqn/eqn.cpp" + break; + + case 42: /* simple: simple UACCENT simple */ +#line 233 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_uaccent_box((yyvsp[-2].b), (yyvsp[0].b)); } +#line 1712 "src/preproc/eqn/eqn.cpp" + break; + + case 43: /* simple: ROMAN simple */ +#line 235 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new font_box(strsave(get_grfont()), (yyvsp[0].b)); } +#line 1718 "src/preproc/eqn/eqn.cpp" + break; + + case 44: /* simple: BOLD simple */ +#line 237 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new font_box(strsave(get_gbfont()), (yyvsp[0].b)); } +#line 1724 "src/preproc/eqn/eqn.cpp" + break; + + case 45: /* simple: ITALIC simple */ +#line 239 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new font_box(strsave(get_gfont()), (yyvsp[0].b)); } +#line 1730 "src/preproc/eqn/eqn.cpp" + break; + + case 46: /* simple: FAT simple */ +#line 241 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new fat_box((yyvsp[0].b)); } +#line 1736 "src/preproc/eqn/eqn.cpp" + break; + + case 47: /* simple: FONT text simple */ +#line 243 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new font_box((yyvsp[-1].str), (yyvsp[0].b)); } +#line 1742 "src/preproc/eqn/eqn.cpp" + break; + + case 48: /* simple: SIZE text simple */ +#line 245 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new size_box((yyvsp[-1].str), (yyvsp[0].b)); } +#line 1748 "src/preproc/eqn/eqn.cpp" + break; + + case 49: /* simple: FWD number simple */ +#line 247 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new hmotion_box((yyvsp[-1].n), (yyvsp[0].b)); } +#line 1754 "src/preproc/eqn/eqn.cpp" + break; + + case 50: /* simple: BACK number simple */ +#line 249 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new hmotion_box(-(yyvsp[-1].n), (yyvsp[0].b)); } +#line 1760 "src/preproc/eqn/eqn.cpp" + break; + + case 51: /* simple: UP number simple */ +#line 251 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new vmotion_box((yyvsp[-1].n), (yyvsp[0].b)); } +#line 1766 "src/preproc/eqn/eqn.cpp" + break; + + case 52: /* simple: DOWN number simple */ +#line 253 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new vmotion_box(-(yyvsp[-1].n), (yyvsp[0].b)); } +#line 1772 "src/preproc/eqn/eqn.cpp" + break; + + case 53: /* simple: TYPE text simple */ +#line 255 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].b)->set_spacing_type((yyvsp[-1].str)); (yyval.b) = (yyvsp[0].b); } +#line 1778 "src/preproc/eqn/eqn.cpp" + break; + + case 54: /* simple: VCENTER simple */ +#line 257 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = new vcenter_box((yyvsp[0].b)); } +#line 1784 "src/preproc/eqn/eqn.cpp" + break; + + case 55: /* simple: SPECIAL text simple */ +#line 259 "../src/preproc/eqn/eqn.ypp" + { (yyval.b) = make_special_box((yyvsp[-1].str), (yyvsp[0].b)); } +#line 1790 "src/preproc/eqn/eqn.cpp" + break; + + case 56: /* number: text */ +#line 264 "../src/preproc/eqn/eqn.ypp" + { + int n; + if (sscanf((yyvsp[0].str), "%d", &n) == 1) + (yyval.n) = n; + delete[] (yyvsp[0].str); + } +#line 1801 "src/preproc/eqn/eqn.cpp" + break; + + case 57: /* pile_element_list: equation */ +#line 274 "../src/preproc/eqn/eqn.ypp" + { (yyval.pb) = new pile_box((yyvsp[0].b)); } +#line 1807 "src/preproc/eqn/eqn.cpp" + break; + + case 58: /* pile_element_list: pile_element_list ABOVE equation */ +#line 276 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[-2].pb)->append((yyvsp[0].b)); (yyval.pb) = (yyvsp[-2].pb); } +#line 1813 "src/preproc/eqn/eqn.cpp" + break; + + case 59: /* pile_arg: '{' pile_element_list '}' */ +#line 281 "../src/preproc/eqn/eqn.ypp" + { (yyval.pb) = (yyvsp[-1].pb); } +#line 1819 "src/preproc/eqn/eqn.cpp" + break; + + case 60: /* pile_arg: number '{' pile_element_list '}' */ +#line 283 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[-1].pb)->set_space((yyvsp[-3].n)); (yyval.pb) = (yyvsp[-1].pb); } +#line 1825 "src/preproc/eqn/eqn.cpp" + break; + + case 61: /* column_list: column */ +#line 288 "../src/preproc/eqn/eqn.ypp" + { (yyval.mb) = new matrix_box((yyvsp[0].col)); } +#line 1831 "src/preproc/eqn/eqn.cpp" + break; + + case 62: /* column_list: column_list column */ +#line 290 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[-1].mb)->append((yyvsp[0].col)); (yyval.mb) = (yyvsp[-1].mb); } +#line 1837 "src/preproc/eqn/eqn.cpp" + break; + + case 63: /* column_element_list: equation */ +#line 295 "../src/preproc/eqn/eqn.ypp" + { (yyval.col) = new column((yyvsp[0].b)); } +#line 1843 "src/preproc/eqn/eqn.cpp" + break; + + case 64: /* column_element_list: column_element_list ABOVE equation */ +#line 297 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[-2].col)->append((yyvsp[0].b)); (yyval.col) = (yyvsp[-2].col); } +#line 1849 "src/preproc/eqn/eqn.cpp" + break; + + case 65: /* column_arg: '{' column_element_list '}' */ +#line 302 "../src/preproc/eqn/eqn.ypp" + { (yyval.col) = (yyvsp[-1].col); } +#line 1855 "src/preproc/eqn/eqn.cpp" + break; + + case 66: /* column_arg: number '{' column_element_list '}' */ +#line 304 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[-1].col)->set_space((yyvsp[-3].n)); (yyval.col) = (yyvsp[-1].col); } +#line 1861 "src/preproc/eqn/eqn.cpp" + break; + + case 67: /* column: COL column_arg */ +#line 309 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].col)->set_alignment(CENTER_ALIGN); (yyval.col) = (yyvsp[0].col); } +#line 1867 "src/preproc/eqn/eqn.cpp" + break; + + case 68: /* column: LCOL column_arg */ +#line 311 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].col)->set_alignment(LEFT_ALIGN); (yyval.col) = (yyvsp[0].col); } +#line 1873 "src/preproc/eqn/eqn.cpp" + break; + + case 69: /* column: RCOL column_arg */ +#line 313 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].col)->set_alignment(RIGHT_ALIGN); (yyval.col) = (yyvsp[0].col); } +#line 1879 "src/preproc/eqn/eqn.cpp" + break; + + case 70: /* column: CCOL column_arg */ +#line 315 "../src/preproc/eqn/eqn.ypp" + { (yyvsp[0].col)->set_alignment(CENTER_ALIGN); (yyval.col) = (yyvsp[0].col); } +#line 1885 "src/preproc/eqn/eqn.cpp" + break; + + case 71: /* text: TEXT */ +#line 319 "../src/preproc/eqn/eqn.ypp" + { (yyval.str) = (yyvsp[0].str); } +#line 1891 "src/preproc/eqn/eqn.cpp" + break; + + case 72: /* text: QUOTED_TEXT */ +#line 321 "../src/preproc/eqn/eqn.ypp" + { (yyval.str) = (yyvsp[0].str); } +#line 1897 "src/preproc/eqn/eqn.cpp" + break; + + case 73: /* delim: text */ +#line 326 "../src/preproc/eqn/eqn.ypp" + { (yyval.str) = (yyvsp[0].str); } +#line 1903 "src/preproc/eqn/eqn.cpp" + break; + + case 74: /* delim: '{' */ +#line 328 "../src/preproc/eqn/eqn.ypp" + { (yyval.str) = strsave("{"); } +#line 1909 "src/preproc/eqn/eqn.cpp" + break; + + case 75: /* delim: '}' */ +#line 330 "../src/preproc/eqn/eqn.ypp" + { (yyval.str) = strsave("}"); } +#line 1915 "src/preproc/eqn/eqn.cpp" + break; + + +#line 1919 "src/preproc/eqn/eqn.cpp" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 333 "../src/preproc/eqn/eqn.ypp" + diff --git a/src/preproc/eqn/eqn.h b/src/preproc/eqn/eqn.h new file mode 100644 index 0000000..a4143cb --- /dev/null +++ b/src/preproc/eqn/eqn.h @@ -0,0 +1,57 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "lib.h" + +#include <stdlib.h> +#include <errno.h> +#include "cset.h" +#include "errarg.h" +#include "error.h" + +#include "box.h" + +typedef enum {troff, mathml} eqnmode_t; + +extern char start_delim; +extern char end_delim; +extern int non_empty_flag; +extern int inline_flag; +extern int draw_flag; +extern int one_size_reduction_flag; +extern int compatible_flag; +extern int nroff; +extern eqnmode_t output_format; +extern int xhtml; + +void init_lex(const char *str, const char *filename, int lineno); +void lex_error(const char *message, + const errarg &arg1 = empty_errarg, + const errarg &arg2 = empty_errarg, + const errarg &arg3 = empty_errarg); + +void init_table(const char *device); + +// prefix for all registers, strings, macros +#define PREFIX "0" + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/eqn.hpp b/src/preproc/eqn/eqn.hpp new file mode 100644 index 0000000..de02d7c --- /dev/null +++ b/src/preproc/eqn/eqn.hpp @@ -0,0 +1,210 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program 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. + + This program 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 <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED +# define YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + OVER = 258, /* OVER */ + SMALLOVER = 259, /* SMALLOVER */ + SQRT = 260, /* SQRT */ + SUB = 261, /* SUB */ + SUP = 262, /* SUP */ + LPILE = 263, /* LPILE */ + RPILE = 264, /* RPILE */ + CPILE = 265, /* CPILE */ + PILE = 266, /* PILE */ + LEFT = 267, /* LEFT */ + RIGHT = 268, /* RIGHT */ + TO = 269, /* TO */ + FROM = 270, /* FROM */ + SIZE = 271, /* SIZE */ + FONT = 272, /* FONT */ + ROMAN = 273, /* ROMAN */ + BOLD = 274, /* BOLD */ + ITALIC = 275, /* ITALIC */ + FAT = 276, /* FAT */ + ACCENT = 277, /* ACCENT */ + BAR = 278, /* BAR */ + UNDER = 279, /* UNDER */ + ABOVE = 280, /* ABOVE */ + TEXT = 281, /* TEXT */ + QUOTED_TEXT = 282, /* QUOTED_TEXT */ + FWD = 283, /* FWD */ + BACK = 284, /* BACK */ + DOWN = 285, /* DOWN */ + UP = 286, /* UP */ + MATRIX = 287, /* MATRIX */ + COL = 288, /* COL */ + LCOL = 289, /* LCOL */ + RCOL = 290, /* RCOL */ + CCOL = 291, /* CCOL */ + MARK = 292, /* MARK */ + LINEUP = 293, /* LINEUP */ + TYPE = 294, /* TYPE */ + VCENTER = 295, /* VCENTER */ + PRIME = 296, /* PRIME */ + SPLIT = 297, /* SPLIT */ + NOSPLIT = 298, /* NOSPLIT */ + UACCENT = 299, /* UACCENT */ + SPECIAL = 300, /* SPECIAL */ + SPACE = 301, /* SPACE */ + GFONT = 302, /* GFONT */ + GSIZE = 303, /* GSIZE */ + DEFINE = 304, /* DEFINE */ + NDEFINE = 305, /* NDEFINE */ + TDEFINE = 306, /* TDEFINE */ + SDEFINE = 307, /* SDEFINE */ + UNDEF = 308, /* UNDEF */ + IFDEF = 309, /* IFDEF */ + INCLUDE = 310, /* INCLUDE */ + DELIM = 311, /* DELIM */ + CHARTYPE = 312, /* CHARTYPE */ + SET = 313, /* SET */ + GRFONT = 314, /* GRFONT */ + GBFONT = 315 /* GBFONT */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define OVER 258 +#define SMALLOVER 259 +#define SQRT 260 +#define SUB 261 +#define SUP 262 +#define LPILE 263 +#define RPILE 264 +#define CPILE 265 +#define PILE 266 +#define LEFT 267 +#define RIGHT 268 +#define TO 269 +#define FROM 270 +#define SIZE 271 +#define FONT 272 +#define ROMAN 273 +#define BOLD 274 +#define ITALIC 275 +#define FAT 276 +#define ACCENT 277 +#define BAR 278 +#define UNDER 279 +#define ABOVE 280 +#define TEXT 281 +#define QUOTED_TEXT 282 +#define FWD 283 +#define BACK 284 +#define DOWN 285 +#define UP 286 +#define MATRIX 287 +#define COL 288 +#define LCOL 289 +#define RCOL 290 +#define CCOL 291 +#define MARK 292 +#define LINEUP 293 +#define TYPE 294 +#define VCENTER 295 +#define PRIME 296 +#define SPLIT 297 +#define NOSPLIT 298 +#define UACCENT 299 +#define SPECIAL 300 +#define SPACE 301 +#define GFONT 302 +#define GSIZE 303 +#define DEFINE 304 +#define NDEFINE 305 +#define TDEFINE 306 +#define SDEFINE 307 +#define UNDEF 308 +#define IFDEF 309 +#define INCLUDE 310 +#define DELIM 311 +#define CHARTYPE 312 +#define SET 313 +#define GRFONT 314 +#define GBFONT 315 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 34 "../src/preproc/eqn/eqn.ypp" + + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; + +#line 196 "src/preproc/eqn/eqn.hpp" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_SRC_PREPROC_EQN_EQN_HPP_INCLUDED */ diff --git a/src/preproc/eqn/eqn.ypp b/src/preproc/eqn/eqn.ypp new file mode 100644 index 0000000..a22ad59 --- /dev/null +++ b/src/preproc/eqn/eqn.ypp @@ -0,0 +1,333 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ +%{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "lib.h" +#include "box.h" +extern int non_empty_flag; +int yylex(); +void yyerror(const char *); +%} + +%union { + char *str; + box *b; + pile_box *pb; + matrix_box *mb; + int n; + column *col; +} + +%token OVER +%token SMALLOVER +%token SQRT +%token SUB +%token SUP +%token LPILE +%token RPILE +%token CPILE +%token PILE +%token LEFT +%token RIGHT +%token TO +%token FROM +%token SIZE +%token FONT +%token ROMAN +%token BOLD +%token ITALIC +%token FAT +%token ACCENT +%token BAR +%token UNDER +%token ABOVE +%token <str> TEXT +%token <str> QUOTED_TEXT +%token FWD +%token BACK +%token DOWN +%token UP +%token MATRIX +%token COL +%token LCOL +%token RCOL +%token CCOL +%token MARK +%token LINEUP +%token TYPE +%token VCENTER +%token PRIME +%token SPLIT +%token NOSPLIT +%token UACCENT +%token SPECIAL + +/* these are handled in the lexer */ +%token SPACE +%token GFONT +%token GSIZE +%token DEFINE +%token NDEFINE +%token TDEFINE +%token SDEFINE +%token UNDEF +%token IFDEF +%token INCLUDE +%token DELIM +%token CHARTYPE +%token SET +%token GRFONT +%token GBFONT + +/* The original eqn manual says that 'left' is right associative. It's lying. +Consider 'left ( ~ left ( ~ right ) right )'. */ + +%right LEFT +%left RIGHT +%right LPILE RPILE CPILE PILE TEXT QUOTED_TEXT MATRIX MARK LINEUP '^' '~' '\t' '{' SPLIT NOSPLIT +%right FROM TO +%left SQRT OVER SMALLOVER +%right SUB SUP +%right ROMAN BOLD ITALIC FAT FONT SIZE FWD BACK DOWN UP TYPE VCENTER SPECIAL +%right BAR UNDER PRIME +%left ACCENT UACCENT + +%type <b> mark from_to sqrt_over script simple equation nonsup +%type <n> number +%type <str> text delim +%type <pb> pile_element_list pile_arg +%type <mb> column_list +%type <col> column column_arg column_element_list + +%% +top: + /* empty */ + | equation + { $1->top_level(); non_empty_flag = 1; } + ; + +equation: + mark + { $$ = $1; } + | equation mark + { + list_box *lb = $1->to_list_box(); + if (!lb) + lb = new list_box($1); + lb->append($2); + $$ = lb; + } + ; + +mark: + from_to + { $$ = $1; } + | MARK mark + { $$ = make_mark_box($2); } + | LINEUP mark + { $$ = make_lineup_box($2); } + ; + +from_to: + sqrt_over %prec FROM + { $$ = $1; } + | sqrt_over TO from_to + { $$ = make_limit_box($1, 0, $3); } + | sqrt_over FROM sqrt_over + { $$ = make_limit_box($1, $3, 0); } + | sqrt_over FROM sqrt_over TO from_to + { $$ = make_limit_box($1, $3, $5); } + | sqrt_over FROM sqrt_over FROM from_to + { $$ = make_limit_box($1, make_limit_box($3, $5, 0), 0); } + ; + +sqrt_over: + script + { $$ = $1; } + | SQRT sqrt_over + { $$ = make_sqrt_box($2); } + | sqrt_over OVER sqrt_over + { $$ = make_over_box($1, $3); } + | sqrt_over SMALLOVER sqrt_over + { $$ = make_small_over_box($1, $3); } + ; + +script: + nonsup + { $$ = $1; } + | simple SUP script + { $$ = make_script_box($1, 0, $3); } + ; + +nonsup: + simple %prec SUP + { $$ = $1; } + | simple SUB nonsup + { $$ = make_script_box($1, $3, 0); } + | simple SUB simple SUP script + { $$ = make_script_box($1, $3, $5); } + ; + +simple: + TEXT + { $$ = split_text($1); } + | QUOTED_TEXT + { $$ = new quoted_text_box($1); } + | SPLIT QUOTED_TEXT + { $$ = split_text($2); } + | NOSPLIT TEXT + { $$ = new quoted_text_box($2); } + | '^' + { $$ = new half_space_box; } + | '~' + { $$ = new space_box; } + | '\t' + { $$ = new tab_box; } + | '{' equation '}' + { $$ = $2; } + | PILE pile_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | LPILE pile_arg + { $2->set_alignment(LEFT_ALIGN); $$ = $2; } + | RPILE pile_arg + { $2->set_alignment(RIGHT_ALIGN); $$ = $2; } + | CPILE pile_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | MATRIX '{' column_list '}' + { $$ = $3; } + | LEFT delim equation RIGHT delim + { $$ = make_delim_box($2, $3, $5); } + | LEFT delim equation + { $$ = make_delim_box($2, $3, 0); } + | simple BAR + { $$ = make_overline_box($1); } + | simple UNDER + { $$ = make_underline_box($1); } + | simple PRIME + { $$ = make_prime_box($1); } + | simple ACCENT simple + { $$ = make_accent_box($1, $3); } + | simple UACCENT simple + { $$ = make_uaccent_box($1, $3); } + | ROMAN simple + { $$ = new font_box(strsave(get_grfont()), $2); } + | BOLD simple + { $$ = new font_box(strsave(get_gbfont()), $2); } + | ITALIC simple + { $$ = new font_box(strsave(get_gfont()), $2); } + | FAT simple + { $$ = new fat_box($2); } + | FONT text simple + { $$ = new font_box($2, $3); } + | SIZE text simple + { $$ = new size_box($2, $3); } + | FWD number simple + { $$ = new hmotion_box($2, $3); } + | BACK number simple + { $$ = new hmotion_box(-$2, $3); } + | UP number simple + { $$ = new vmotion_box($2, $3); } + | DOWN number simple + { $$ = new vmotion_box(-$2, $3); } + | TYPE text simple + { $3->set_spacing_type($2); $$ = $3; } + | VCENTER simple + { $$ = new vcenter_box($2); } + | SPECIAL text simple + { $$ = make_special_box($2, $3); } + ; + +number: + text + { + int n; + if (sscanf($1, "%d", &n) == 1) + $$ = n; + delete[] $1; + } + ; + +pile_element_list: + equation + { $$ = new pile_box($1); } + | pile_element_list ABOVE equation + { $1->append($3); $$ = $1; } + ; + +pile_arg: + '{' pile_element_list '}' + { $$ = $2; } + | number '{' pile_element_list '}' + { $3->set_space($1); $$ = $3; } + ; + +column_list: + column + { $$ = new matrix_box($1); } + | column_list column + { $1->append($2); $$ = $1; } + ; + +column_element_list: + equation + { $$ = new column($1); } + | column_element_list ABOVE equation + { $1->append($3); $$ = $1; } + ; + +column_arg: + '{' column_element_list '}' + { $$ = $2; } + | number '{' column_element_list '}' + { $3->set_space($1); $$ = $3; } + ; + +column: + COL column_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + | LCOL column_arg + { $2->set_alignment(LEFT_ALIGN); $$ = $2; } + | RCOL column_arg + { $2->set_alignment(RIGHT_ALIGN); $$ = $2; } + | CCOL column_arg + { $2->set_alignment(CENTER_ALIGN); $$ = $2; } + ; + +text: TEXT + { $$ = $1; } + | QUOTED_TEXT + { $$ = $1; } + ; + +delim: + text + { $$ = $1; } + | '{' + { $$ = strsave("{"); } + | '}' + { $$ = strsave("}"); } + ; + +%% diff --git a/src/preproc/eqn/lex.cpp b/src/preproc/eqn/lex.cpp new file mode 100644 index 0000000..e38a486 --- /dev/null +++ b/src/preproc/eqn/lex.cpp @@ -0,0 +1,1236 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "eqn.hpp" +#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) + free(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\" { . . . }" }, +}; + +/* 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() +{ + if (fclose(fp) < 0) + fatal("unable to close '%1': %2", filename, strerror(errno)); + delete[] filename; +} + +int file_input::read_line() +{ + for (;;) { + line.clear(); + lineno++; + for (;;) { + int c = getc(fp); + if (c == '\r') { + c = getc(fp); + if (c != '\n') + lex_error("invalid input character code %1", '\r'); + } + if (c == EOF) + break; + else if (is_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() +{ + free(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() +{ + free(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++) + delete[] argv[i]; + 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, *last_seen_filename; + int lineno; + int got_location = get_location(&filename, &lineno); + // `filename` gets invalidated if we iterate off the end of the file. + last_seen_filename = strdup(filename); + int start = get_char(); + while (start == ' ' || start == '\t' || start == '\n') + start = get_char(); + token_buffer.clear(); + if (start == EOF) { + current_lineno = 0; + if (got_location) + error_with_file_and_line(last_seen_filename, lineno, + "end of input while defining macro"); + else + error("end of input while defining macro"); + free(last_seen_filename); + return; + } + for (;;) { + int c = get_char(); + if (c == EOF) { + current_lineno = 0; + if (got_location) + error_with_file_and_line(last_seen_filename, lineno, + "end of input while defining macro"); + else + error("end of input while defining macro"); + add_context(start + token_buffer); + free(last_seen_filename); + return; + } + if (c == start) + break; + token_buffer += char(c); + } + add_context(start + token_buffer + start); + free(last_seen_filename); +} + +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) { + free(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(); + start_delim_saved = start_delim; + end_delim_saved = end_delim; + start_delim = end_delim = '\0'; + } + else if (c == 'o' && d == 'n') { + start_delim = start_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(); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/limit.cpp b/src/preproc/eqn/limit.cpp new file mode 100644 index 0000000..bf64a04 --- /dev/null +++ b/src/preproc/eqn/limit.cpp @@ -0,0 +1,217 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + +class limit_box : public box { +private: + box *p; + box *from; + box *to; +public: + limit_box(box *, box *, box *); + ~limit_box(); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_limit_box(box *pp, box *qq, box *rr) +{ + return new limit_box(pp, qq, rr); +} + +limit_box::limit_box(box *pp, box *qq, box *rr) +: p(pp), from(qq), to(rr) +{ + spacing_type = p->spacing_type; +} + +limit_box::~limit_box() +{ + delete p; + delete from; + delete to; +} + +int limit_box::compute_metrics(int style) +{ + printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid); + if (!(style <= SCRIPT_STYLE && one_size_reduction_flag)) + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid); + int res = 0; + int mark_uid = -1; + if (from != 0) { + res = from->compute_metrics(cramped_style(script_style(style))); + if (res) + mark_uid = from->uid; + } + if (to != 0) { + int r = to->compute_metrics(script_style(style)); + if (res && r) + error("multiple marks and lineups"); + else { + mark_uid = to->uid; + res = r; + } + } + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + int r = p->compute_metrics(style); + p->compute_subscript_kern(); + if (res && r) + error("multiple marks and lineups"); + else { + mark_uid = p->uid; + res = r; + } + printf(".nr " LEFT_WIDTH_FORMAT " " + "0\\n[" WIDTH_FORMAT "]", + uid, p->uid); + if (from != 0) + printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, from->uid); + if (to != 0) + printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, to->uid); + printf("/2\n"); + printf(".nr " WIDTH_FORMAT " " + "0\\n[" WIDTH_FORMAT "]", + uid, p->uid); + if (from != 0) + printf(">?(-\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, from->uid); + if (to != 0) + printf(">?(\\n[" SUB_KERN_FORMAT "]+\\n[" WIDTH_FORMAT "])", + p->uid, to->uid); + printf("/2+\\n[" LEFT_WIDTH_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid); + if (to != 0) + printf(">?\\n[" WIDTH_FORMAT "]", to->uid); + if (from != 0) + printf(">?\\n[" WIDTH_FORMAT "]", from->uid); + printf("\n"); + if (res) + printf(".nr " MARK_REG " +(\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2))\n", + uid, mark_uid); + if (to != 0) { + printf(".nr " SUP_RAISE_FORMAT " %dM+\\n[" DEPTH_FORMAT + "]>?%dM+\\n[" HEIGHT_FORMAT "]\n", + uid, big_op_spacing1, to->uid, big_op_spacing3, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]+%dM\n", + uid, uid, to->uid, big_op_spacing5); + } + else + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + if (from != 0) { + printf(".nr " SUB_LOWER_FORMAT " %dM+\\n[" HEIGHT_FORMAT + "]>?%dM+\\n[" DEPTH_FORMAT "]\n", + uid, big_op_spacing2, from->uid, big_op_spacing4, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n[" + DEPTH_FORMAT "]+%dM\n", + uid, uid, from->uid, big_op_spacing5); + } + else + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return res; +} + +void limit_box::output() +{ + if (output_format == troff) { + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + if (to != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "+(-\\n[" WIDTH_FORMAT "]u+\\n[" SUB_KERN_FORMAT "]u/2u)'", + uid, to->uid, p->uid); + to->output(); + printf(DELIMITER_CHAR); + } + if (from != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "+(-\\n[" SUB_KERN_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid, from->uid); + from->output(); + printf(DELIMITER_CHAR); + } + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u" + "-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); + } + else if (output_format == mathml) { + if (from != 0 && to != 0) { + printf("<munderover>"); + p->output(); + from->output(); + to->output(); + printf("</munderover>"); + } + else if (from != 0) { + printf("<munder>"); + p->output(); + from->output(); + printf("</munder>"); + } + else if (to != 0) { + printf("<mover>"); + p->output(); + to->output(); + printf("</mover>"); + } + } +} + +void limit_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " }"); + if (from) { + fprintf(stderr, " from { "); + from->debug_print(); + fprintf(stderr, " }"); + } + if (to) { + fprintf(stderr, " to { "); + to->debug_print(); + fprintf(stderr, " }"); + } +} + +void limit_box::check_tabs(int level) +{ + if (to) + to->check_tabs(level + 1); + if (from) + from->check_tabs(level + 1); + p->check_tabs(level + 1); +} diff --git a/src/preproc/eqn/list.cpp b/src/preproc/eqn/list.cpp new file mode 100644 index 0000000..52644c5 --- /dev/null +++ b/src/preproc/eqn/list.cpp @@ -0,0 +1,241 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + +list_box *box::to_list_box() +{ + return 0; +} + +list_box *list_box::to_list_box() +{ + return this; +} + +void list_box::append(box *pp) +{ + list_box *q = pp->to_list_box(); + if (q == 0) + list.append(pp); + else { + for (int i = 0; i < q->list.len; i++) { + list.append(q->list.p[i]); + q->list.p[i] = 0; + } + q->list.len = 0; + delete q; + } +} + +list_box::list_box(box *pp) : list(pp), sty(-1) +{ + list_box *q = pp->to_list_box(); + if (q != 0) { + // flatten it + list.p[0] = q->list.p[0]; + for (int i = 1; i < q->list.len; i++) { + list.append(q->list.p[i]); + q->list.p[i] = 0; + } + q->list.len = 0; + delete q; + } +} + +static int compute_spacing(int is_script, int left, int right) +{ + if (left == SUPPRESS_TYPE || right == SUPPRESS_TYPE) + return 0; + if (left == PUNCTUATION_TYPE) + return is_script ? 0 : thin_space; + if (left == OPENING_TYPE || right == CLOSING_TYPE) + return 0; + if (right == BINARY_TYPE || left == BINARY_TYPE) + return is_script ? 0 : medium_space; + if (right == RELATION_TYPE) { + if (left == RELATION_TYPE) + return 0; + else + return is_script ? 0 : thick_space; + } + if (left == RELATION_TYPE) + return is_script ? 0 : thick_space; + if (right == OPERATOR_TYPE) + return thin_space; + if (left == INNER_TYPE || right == INNER_TYPE) + return is_script ? 0 : thin_space; + if (left == OPERATOR_TYPE && right == ORDINARY_TYPE) + return thin_space; + return 0; +} + +int list_box::compute_metrics(int style) +{ + sty = style; + int i; + for (i = 0; i < list.len; i++) { + int t = list.p[i]->spacing_type; + // 5 + if (t == BINARY_TYPE) { + int prevt; + if (i == 0 + || (prevt = list.p[i-1]->spacing_type) == BINARY_TYPE + || prevt == OPERATOR_TYPE + || prevt == RELATION_TYPE + || prevt == OPENING_TYPE + || prevt == SUPPRESS_TYPE + || prevt == PUNCTUATION_TYPE) + list.p[i]->spacing_type = ORDINARY_TYPE; + } + // 7 + else if ((t == RELATION_TYPE || t == CLOSING_TYPE + || t == PUNCTUATION_TYPE) + && i > 0 && list.p[i-1]->spacing_type == BINARY_TYPE) + list.p[i-1]->spacing_type = ORDINARY_TYPE; + } + for (i = 0; i < list.len; i++) { + unsigned flags = 0; + if (i - 1 >= 0 && list.p[i - 1]->right_is_italic()) + flags |= HINT_PREV_IS_ITALIC; + if (i + 1 < list.len && list.p[i + 1]->left_is_italic()) + flags |= HINT_NEXT_IS_ITALIC; + if (flags) + list.p[i]->hint(flags); + } + is_script = (style <= SCRIPT_STYLE); + int total_spacing = 0; + for (i = 1; i < list.len; i++) + total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + int res = 0; + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) { + int r = list.p[i]->compute_metrics(style); + if (r) { + if (res) + error("multiple marks and lineups"); + else { + compute_sublist_width(i); + printf(".nr " MARK_REG " +\\n[" TEMP_REG"]\n"); + res = r; + } + } + } + printf(".nr " WIDTH_FORMAT " %dM", uid, total_spacing); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid); + printf("\n"); + printf(".nr " HEIGHT_FORMAT " 0", uid); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf(">?\\n[" HEIGHT_FORMAT "]", list.p[i]->uid); + printf("\n"); + printf(".nr " DEPTH_FORMAT " 0", uid); + for (i = 0; i < list.len; i++) + if (!list.p[i]->is_simple()) + printf(">?\\n[" DEPTH_FORMAT "]", list.p[i]->uid); + printf("\n"); + int have_simple = 0; + for (i = 0; i < list.len && !have_simple; i++) + have_simple = list.p[i]->is_simple(); + if (have_simple) { + printf(".nr " WIDTH_FORMAT " +\\w" DELIMITER_CHAR, uid); + for (i = 0; i < list.len; i++) + if (list.p[i]->is_simple()) + list.p[i]->output(); + printf(DELIMITER_CHAR "\n"); + printf(".nr " HEIGHT_FORMAT " \\n[rst]>?\\n[" HEIGHT_FORMAT "]\n", + uid, uid); + printf(".nr " DEPTH_FORMAT " 0-\\n[rsb]>?\\n[" DEPTH_FORMAT "]\n", + uid, uid); + } + return res; +} + +void list_box::compute_sublist_width(int n) +{ + int total_spacing = 0; + int i; + for (i = 1; i < n + 1 && i < list.len; i++) + total_spacing += compute_spacing(is_script, list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + printf(".nr " TEMP_REG " %dM", total_spacing); + for (i = 0; i < n; i++) + if (!list.p[i]->is_simple()) + printf("+\\n[" WIDTH_FORMAT "]", list.p[i]->uid); + int have_simple = 0; + for (i = 0; i < n && !have_simple; i++) + have_simple = list.p[i]->is_simple(); + if (have_simple) { + printf("+\\w" DELIMITER_CHAR); + for (i = 0; i < n; i++) + if (list.p[i]->is_simple()) + list.p[i]->output(); + printf(DELIMITER_CHAR); + } + printf("\n"); +} + +void list_box::compute_subscript_kern() +{ + // We can only call compute_subscript_kern if we have called + // compute_metrics first. + if (list.p[list.len-1]->is_simple()) + list.p[list.len-1]->compute_metrics(sty); + list.p[list.len-1]->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " \\n[" SUB_KERN_FORMAT "]\n", + uid, list.p[list.len-1]->uid); +} + +void list_box::output() +{ + if (output_format == mathml) + printf("<mrow>"); + for (int i = 0; i < list.len; i++) { + if (output_format == troff && i > 0) { + int n = compute_spacing(is_script, + list.p[i-1]->spacing_type, + list.p[i]->spacing_type); + if (n > 0) + printf("\\h'%dM'", n); + } + list.p[i]->output(); + } + if (output_format == mathml) + printf("</mrow>"); +} + +void list_box::handle_char_type(int st, int ft) +{ + for (int i = 0; i < list.len; i++) + list.p[i]->handle_char_type(st, ft); +} + +void list_box::debug_print() +{ + list.list_debug_print(" "); +} + +void list_box::check_tabs(int level) +{ + list.list_check_tabs(level); +} diff --git a/src/preproc/eqn/main.cpp b/src/preproc/eqn/main.cpp new file mode 100644 index 0000000..81d5184 --- /dev/null +++ b/src/preproc/eqn/main.cpp @@ -0,0 +1,485 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "stringclass.h" +#include "device.h" +#include "searchpath.h" +#include "macropath.h" +#include "htmlhint.h" +#include "pbox.h" +#include "ctype.h" +#include "lf.h" + +#define STARTUP_FILE "eqnrc" + +extern int yyparse(); +extern "C" const char *Version_string; + +static char *delim_search (char *, int); +static int inline_equation (FILE *, string &, string &); + +char start_delim = '\0'; +char end_delim = '\0'; +int non_empty_flag; +int inline_flag; +int draw_flag = 0; +int one_size_reduction_flag = 0; +int compatible_flag = 0; +int no_newline_in_delim_flag = 0; +int html = 0; +int xhtml = 0; +eqnmode_t output_format; + +static const char *input_char_description(int c) +{ + switch (c) { + case '\001': + return "a leader character"; + case '\n': + return "a newline character"; + case '\b': + return "a backspace character"; + case '\t': + return "a tab character"; + case ' ': + return "a space character"; + case '\177': + return "a delete character"; + } + size_t bufsz = sizeof "character code " + INT_DIGITS + 1; + // repeat expression; no VLAs in ISO C++ + static char buf[sizeof "character code " + INT_DIGITS + 1]; + (void) memset(buf, 0, bufsz); + if (csprint(c)) { + buf[0] = '\''; + buf[1] = c; + buf[2] = '\''; + return buf; + } + (void) sprintf(buf, "character code %d", c); + return buf; +} + +static bool read_line(FILE *fp, string *p) +{ + p->clear(); + int c = -1; + while ((c = getc(fp)) != EOF) { + if (!is_invalid_input_char(c)) + *p += char(c); + else + error("invalid input (%1)", input_char_description(c)); + if (c == '\n') + break; + } + return (p->length() > 0); +} + +void do_file(FILE *fp, const char *filename) +{ + string linebuf; + string str; + string fn(filename); + fn += '\0'; + normalize_for_lf(fn); + current_filename = fn.contents(); + if (output_format == troff) + printf(".lf 1 %s\n", current_filename); + current_lineno = 1; + while (read_line(fp, &linebuf)) { + if (linebuf.length() >= 4 + && linebuf[0] == '.' && linebuf[1] == 'l' && linebuf[2] == 'f' + && (linebuf[3] == ' ' || linebuf[3] == '\n' || compatible_flag)) + { + put_string(linebuf, stdout); + linebuf += '\0'; + // In GNU roff, `lf` assigns the number of the _next_ line. + if (interpret_lf_args(linebuf.contents() + 3)) + current_lineno--; + } + else if (linebuf.length() >= 4 + && linebuf[0] == '.' + && linebuf[1] == 'E' + && linebuf[2] == 'Q' + && (linebuf[3] == ' ' || linebuf[3] == '\n' + || compatible_flag)) { + put_string(linebuf, stdout); + int start_lineno = current_lineno + 1; + str.clear(); + for (;;) { + if (!read_line(fp, &linebuf)) { + current_lineno = 0; // suppress report of line number + fatal("end of file before .EN"); + } + if (linebuf.length() >= 3 + && linebuf[0] == '.' + && linebuf[1] == 'E') { + if (linebuf[2] == 'N' + && (linebuf.length() == 3 || linebuf[3] == ' ' + || linebuf[3] == '\n' || compatible_flag)) + break; + else if (linebuf[2] == 'Q' && linebuf.length() > 3 + && (linebuf[3] == ' ' || linebuf[3] == '\n' + || compatible_flag)) { + current_lineno++; // We just read another line. + fatal("equations cannot be nested (.EQ within .EQ)"); + } + } + str += linebuf; + } + str += '\0'; + start_string(); + init_lex(str.contents(), current_filename, start_lineno); + non_empty_flag = 0; + inline_flag = 0; + yyparse(); + restore_compatibility(); + if (non_empty_flag) { + if (output_format == mathml) + putchar('\n'); + else { + current_lineno++; + printf(".lf %d\n", current_lineno); + output_string(); + } + } + if (output_format == troff) { + current_lineno++; + printf(".lf %d\n", current_lineno); + } + put_string(linebuf, stdout); + } + else if (start_delim != '\0' && linebuf.search(start_delim) >= 0 + && inline_equation(fp, linebuf, str)) + ; + else + put_string(linebuf, stdout); + current_lineno++; + } + current_filename = 0; + current_lineno = 0; +} + +// Handle an inline equation. Return 1 if it was an inline equation, +// otherwise. +static int inline_equation(FILE *fp, string &linebuf, string &str) +{ + linebuf += '\0'; + char *ptr = &linebuf[0]; + char *start = delim_search(ptr, start_delim); + if (!start) { + // It wasn't a delimiter after all. + linebuf.set_length(linebuf.length() - 1); // strip the '\0' + return 0; + } + start_string(); + inline_flag = 1; + for (;;) { + if (no_newline_in_delim_flag && strchr(start + 1, end_delim) == 0) { + error("unterminated inline equation; started with %1," + " expecting %2", input_char_description(start_delim), + input_char_description(end_delim)); + char *nl = strchr(start + 1, '\n'); + if (nl != 0) + *nl = '\0'; + do_text(ptr); + break; + } + int start_lineno = current_lineno; + *start = '\0'; + do_text(ptr); + ptr = start + 1; + str.clear(); + for (;;) { + char *end = strchr(ptr, end_delim); + if (end != 0) { + *end = '\0'; + str += ptr; + ptr = end + 1; + break; + } + str += ptr; + if (!read_line(fp, &linebuf)) + fatal("unterminated inline equation; started with %1," + " expecting %2", input_char_description(start_delim), + input_char_description(end_delim)); + linebuf += '\0'; + ptr = &linebuf[0]; + } + str += '\0'; + if (output_format == troff && html) { + printf(".as1 %s ", LINE_STRING); + html_begin_suppress(); + printf("\n"); + } + init_lex(str.contents(), current_filename, start_lineno); + yyparse(); + if (output_format == troff && html) { + printf(".as1 %s ", LINE_STRING); + html_end_suppress(); + printf("\n"); + } + if (output_format == mathml) + printf("\n"); + if (xhtml) { + /* skip leading spaces */ + while ((*ptr != '\0') && (*ptr == ' ')) + ptr++; + } + start = delim_search(ptr, start_delim); + if (start == 0) { + char *nl = strchr(ptr, '\n'); + if (nl != 0) + *nl = '\0'; + do_text(ptr); + break; + } + } + restore_compatibility(); + if (output_format == troff) + printf(".lf %d\n", current_lineno); + output_string(); + if (output_format == troff) + printf(".lf %d\n", current_lineno + 1); + return 1; +} + +/* Search for delim. Skip over number register and string names etc. */ + +static char *delim_search(char *ptr, int delim) +{ + while (*ptr) { + if (*ptr == delim) + return ptr; + if (*ptr++ == '\\') { + switch (*ptr) { + case 'n': + case '*': + case 'f': + case 'g': + case 'k': + switch (*++ptr) { + case '\0': + case '\\': + break; + case '(': + if (*++ptr != '\\' && *ptr != '\0' + && *++ptr != '\\' && *ptr != '\0') + ptr++; + break; + case '[': + while (*++ptr != '\0') + if (*ptr == ']') { + ptr++; + break; + } + break; + default: + ptr++; + break; + } + break; + case '\\': + case '\0': + break; + default: + ptr++; + break; + } + } + } + return 0; +} + +void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [-CNrR] [-d xy] [-f font] [-m n] [-M dir] [-p n] [-s n]" + " [-T name] [file ...]\n" + "usage: %s {-v | --version}\n" + "usage: %s --help\n", + program_name, program_name, program_name); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int opt; + int load_startup_file = 1; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((opt = getopt_long(argc, argv, "CNrRd:f:m:M:p:s:T:v", + long_options, NULL)) + != EOF) + switch (opt) { + case 'C': + compatible_flag = 1; + break; + case 'R': // don't load eqnrc + load_startup_file = 0; + break; + case 'M': + config_macro_path.command_line_dir(optarg); + break; + case 'v': + printf("GNU eqn (groff) version %s\n", Version_string); + exit(EXIT_SUCCESS); + break; + case 'd': + if (optarg[0] == '\0' || optarg[1] == '\0') + error("'-d' option requires a two-character argument"); + else if (is_invalid_input_char(optarg[0])) + error("invalid delimiter (%1) in '-d' option argument", + input_char_description(optarg[0])); + else if (is_invalid_input_char(optarg[1])) + error("invalid delimiter (%1) in '-d' option argument", + input_char_description(optarg[1])); + else { + start_delim = optarg[0]; + end_delim = optarg[1]; + } + break; + case 'f': + set_gfont(optarg); + break; + case 'T': + device = optarg; + if (strcmp(device, "ps:html") == 0) { + device = "ps"; + html = 1; + } + else if (strcmp(device, "MathML") == 0) { + output_format = mathml; + load_startup_file = 0; + } + else if (strcmp(device, "mathml:xhtml") == 0) { + device = "MathML"; + output_format = mathml; + load_startup_file = 0; + xhtml = 1; + } + break; + case 's': + if (set_gsize(optarg)) + warning("option '-s' is deprecated"); + else + error("invalid size '%1' in '-s' option argument ", optarg); + break; + case 'p': + { + int n; + if (sscanf(optarg, "%d", &n) == 1) { + warning("option '-p' is deprecated"); + set_script_reduction(n); + } + else + error("invalid size '%1' in '-p' option argument ", optarg); + } + break; + case 'm': + { + int n; + if (sscanf(optarg, "%d", &n) == 1) + set_minimum_size(n); + else + error("invalid size '%1' in '-n' option argument", optarg); + } + break; + case 'r': + one_size_reduction_flag = 1; + break; + case 'N': + no_newline_in_delim_flag = 1; + break; + case CHAR_MAX + 1: // --help + usage(stdout); + exit(EXIT_SUCCESS); + break; + case '?': + usage(stderr); + exit(EXIT_FAILURE); + break; + default: + assert(0 == "unhandled getopt_long return value"); + } + init_table(device); + init_char_table(); + printf(".do if !dEQ .ds EQ\n" + ".do if !dEN .ds EN\n"); + if (output_format == troff) { + printf(".if !'\\*(.T'%s' " + ".if !'\\*(.T'html' " // the html device uses '-Tps' to render + // equations as images + ".tm warning: %s should have been given a '-T\\*(.T' option\n", + device, program_name); + printf(".if '\\*(.T'html' " + ".if !'%s'ps' " + ".tm warning: %s should have been given a '-Tps' option\n", + device, program_name); + printf(".if '\\*(.T'html' " + ".if !'%s'ps' " + ".tm warning: (it is advisable to invoke groff via: groff -Thtml -e)\n", + device); + } + if (load_startup_file) { + char *path; + FILE *fp = config_macro_path.open_file(STARTUP_FILE, &path); + if (fp) { + do_file(fp, path); + if (fclose(fp) < 0) + fatal("unable to close '%1': %2", STARTUP_FILE, + strerror(errno)); + free(path); + } + } + if (optind >= argc) + do_file(stdin, "-"); + else + for (int i = optind; i < argc; i++) + if (strcmp(argv[i], "-") == 0) + do_file(stdin, "-"); + else { + errno = 0; + FILE *fp = fopen(argv[i], "r"); + if (!fp) + fatal("unable to open '%1': %2", argv[i], strerror(errno)); + else { + do_file(fp, argv[i]); + if (fclose(fp) < 0) + fatal("unable to close '%1': %2", argv[i], strerror(errno)); + } + } + if (ferror(stdout)) + fatal("standard output stream is in an error state"); + if (fflush(stdout) < 0) + fatal("unable to flush standard output stream: %1", + strerror(errno)); + exit(EXIT_SUCCESS); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/mark.cpp b/src/preproc/eqn/mark.cpp new file mode 100644 index 0000000..9427b10 --- /dev/null +++ b/src/preproc/eqn/mark.cpp @@ -0,0 +1,120 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + +class mark_box : public pointer_box { +public: + mark_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +// we push down marks so that they don't interfere with spacing + +box *make_mark_box(box *p) +{ + list_box *b = p->to_list_box(); + if (b != 0) { + b->list.p[0] = make_mark_box(b->list.p[0]); + return b; + } + else + return new mark_box(p); +} + +mark_box::mark_box(box *pp) : pointer_box(pp) +{ +} + +void mark_box::output() +{ + p->output(); +} + +int mark_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + if (res) + error("multiple marks and lineups"); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " MARK_REG " 0\n"); + return FOUND_MARK; +} + +void mark_box::debug_print() +{ + fprintf(stderr, "mark { "); + p->debug_print(); + fprintf(stderr, " }"); +} + + +class lineup_box : public pointer_box { +public: + lineup_box(box *); + void output(); + int compute_metrics(int style); + void debug_print(); +}; + +// we push down lineups so that they don't interfere with spacing + +box *make_lineup_box(box *p) +{ + list_box *b = p->to_list_box(); + if (b != 0) { + b->list.p[0] = make_lineup_box(b->list.p[0]); + return b; + } + else + return new lineup_box(p); +} + +lineup_box::lineup_box(box *pp) : pointer_box(pp) +{ +} + +void lineup_box::output() +{ + p->output(); +} + +int lineup_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + if (res) + error("multiple marks and lineups"); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " MARK_REG " 0\n"); + return FOUND_LINEUP; +} + +void lineup_box::debug_print() +{ + fprintf(stderr, "lineup { "); + p->debug_print(); + fprintf(stderr, " }"); +} diff --git a/src/preproc/eqn/neqn.1.man b/src/preproc/eqn/neqn.1.man new file mode 100644 index 0000000..758a150 --- /dev/null +++ b/src/preproc/eqn/neqn.1.man @@ -0,0 +1,92 @@ +.TH @g@neqn @MAN1EXT@ "@MDATE@" "groff @VERSION@" +.SH Name +@g@neqn \- format equations for character-cell terminal output +. +. +.\" ==================================================================== +.\" Legal Terms +.\" ==================================================================== +.\" +.\" Copyright (C) 2001-2020 Free Software Foundation, Inc. +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Permission is granted to copy and distribute translations of this +.\" manual into another language, under the above conditions for +.\" modified versions, except that this permission notice may be +.\" included in translations approved by the Free Software Foundation +.\" instead of in the original English. +. +. +.\" Save and disable compatibility mode (for, e.g., Solaris 10/11). +.do nr *groff_neqn_1_man_C \n[.cp] +.cp 0 +. +.\" Define fallback for groff 1.23's MR macro if the system lacks it. +.nr do-fallback 0 +.if !\n(.f .nr do-fallback 1 \" mandoc +.if \n(.g .if !d MR .nr do-fallback 1 \" older groff +.if !\n(.g .nr do-fallback 1 \" non-groff *roff +.if \n[do-fallback] \{\ +. de MR +. ie \\n(.$=1 \ +. I \%\\$1 +. el \ +. IR \%\\$1 (\\$2)\\$3 +. . +.\} +.rr do-fallback +. +. +.\" ==================================================================== +.SH Synopsis +.\" ==================================================================== +. +.SY @g@neqn +.RI [ @g@eqn-argument \~.\|.\|.] +.YS +. +. +.\" ==================================================================== +.SH Description +.\" ==================================================================== +. +.I @g@neqn +invokes the +.MR @g@eqn @MAN1EXT@ +command with the +.B ascii +output device. +. +. +.LP +.I @g@eqn +does not support low-resolution, +typewriter-like devices, +although it may work adequately for very simple input. +. +. +.\" ==================================================================== +.SH "See also" +.\" ==================================================================== +. +.MR @g@eqn @MAN1EXT@ +. +. +.\" Restore compatibility mode (for, e.g., Solaris 10/11). +.cp \n[*groff_neqn_1_man_C] +.do rr *groff_neqn_1_man_C +. +. +.\" Local Variables: +.\" fill-column: 72 +.\" mode: nroff +.\" End: +.\" vim: set filetype=nroff textwidth=72: diff --git a/src/preproc/eqn/neqn.sh b/src/preproc/eqn/neqn.sh new file mode 100644 index 0000000..fb8d616 --- /dev/null +++ b/src/preproc/eqn/neqn.sh @@ -0,0 +1,26 @@ +#! /bin/sh +# Copyright (C) 2014-2020 Free Software Foundation, Inc. +# +# 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 2 of the License (GPL2). +# +# 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. +# +# The GPL2 license text is available in the internet at +# <http://www.gnu.org/licenses/gpl-2.0.txt>. + +# Provision of this shell script should not be taken to imply that use of +# GNU eqn with groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047 is supported. + +@GROFF_BIN_PATH_SETUP@ +PATH="$GROFF_RUNTIME$PATH" +export PATH +exec @g@eqn -Tascii ${1+"$@"} + +# eof diff --git a/src/preproc/eqn/other.cpp b/src/preproc/eqn/other.cpp new file mode 100644 index 0000000..1ddc8fb --- /dev/null +++ b/src/preproc/eqn/other.cpp @@ -0,0 +1,706 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> + +#include "eqn.h" +#include "pbox.h" + +class accent_box : public pointer_box { +private: + box *ab; +public: + accent_box(box *, box *); + ~accent_box(); + int compute_metrics(int); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_accent_box(box *p, box *q) +{ + return new accent_box(p, q); +} + +accent_box::accent_box(box *pp, box *qq) : pointer_box(pp), ab(qq) +{ +} + +accent_box::~accent_box() +{ + delete ab; +} + +#if 0 +int accent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_skew(); + ab->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, x_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]\n", + uid, ab->uid, uid); + return r; +} + +void accent_box::output() +{ + if (output_format == troff) { + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n[" + SKEW_FORMAT "]u'", + p->uid, ab->uid, p->uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + ab->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", ab->uid); + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u+\\n[" + SKEW_FORMAT "]u)'", + p->uid, ab->uid, p->uid); + p->output(); + } + else if (output_format == mathml) { + printf("<mover accent='true'>"); + p->output(); + ab->output(); + printf("</mover>") + } +} +#endif + +/* This version copes with the possibility of an accent's being wider +than its accentee. LEFT_WIDTH_FORMAT gives the distance from the +left edge of the resulting box to the middle of the accentee's box.*/ + +int accent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_skew(); + ab->compute_metrics(style); + printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2-\\n[" SKEW_FORMAT "])\n", + uid, p->uid, ab->uid, p->uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2+\\n[" SKEW_FORMAT "])" + "+\\n[" LEFT_WIDTH_FORMAT "]\n", + uid, p->uid, ab->uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, x_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]\n", + uid, ab->uid, uid); + if (r) + printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2)'\n", + uid, p->uid); + return r; +} + +void accent_box::output() +{ + if (output_format == troff) { + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u+\\n[" SKEW_FORMAT "]u" + "-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid, ab->uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + ab->output(); + printf(DELIMITER_CHAR); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); + } + else if (output_format == mathml) { + printf("<mover accent='true'>"); + p->output(); + ab->output(); + printf("</mover>"); + } +} + +void accent_box::check_tabs(int level) +{ + ab->check_tabs(level + 1); + p->check_tabs(level + 1); +} + +void accent_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } accent { "); + ab->debug_print(); + fprintf(stderr, " }"); +} + +class overline_char_box : public simple_box { +public: + overline_char_box(); + void output(); + void debug_print(); +}; + +overline_char_box::overline_char_box() +{ +} + +void overline_char_box::output() +{ + if (output_format == troff) { + printf("\\v'-%dM/2u-%dM'", 7*default_rule_thickness, x_height); + printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"), + accent_width); + printf("\\v'%dM/2u+%dM'", 7*default_rule_thickness, x_height); + } + else if (output_format == mathml) + printf("<mo>¯</mo>"); +} + +void overline_char_box::debug_print() +{ + fprintf(stderr, "<overline char>"); +} + +class overline_box : public pointer_box { +public: + overline_box(box *); + int compute_metrics(int); + void output(); + void debug_print(); +}; + +box *make_overline_box(box *p) +{ + if (p->is_char()) + return new accent_box(p, new overline_char_box); + else + return new overline_box(p); +} + +overline_box::overline_box(box *pp) : pointer_box(pp) +{ +} + +int overline_box::compute_metrics(int style) +{ + int r = p->compute_metrics(cramped_style(style)); + // 9 + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+%dM\n", + uid, p->uid, default_rule_thickness*5); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void overline_box::output() +{ + if (output_format == troff) { + // 9 + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" HEIGHT_FORMAT "]u-(%dM/2u)'", + p->uid, 7*default_rule_thickness); + if (draw_flag) + printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid); + else + printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid); + printf(DELIMITER_CHAR); + p->output(); + } + else if (output_format == mathml) { + printf("<mover accent='false'>"); + p->output(); + printf("<mo>¯</mo></mover>"); + } +} + +void overline_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } bar"); +} + +class uaccent_box : public pointer_box { + box *ab; +public: + uaccent_box(box *, box *); + ~uaccent_box(); + int compute_metrics(int); + void output(); + void compute_subscript_kern(); + void check_tabs(int); + void debug_print(); +}; + +box *make_uaccent_box(box *p, box *q) +{ + return new uaccent_box(p, q); +} + +uaccent_box::uaccent_box(box *pp, box *qq) +: pointer_box(pp), ab(qq) +{ +} + +uaccent_box::~uaccent_box() +{ + delete ab; +} + +int uaccent_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + ab->compute_metrics(style); + printf(".nr " LEFT_WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2)\n", + uid, p->uid, ab->uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]/2" + ">?(\\n[" WIDTH_FORMAT "]/2)" + "+\\n[" LEFT_WIDTH_FORMAT "]\n", + uid, p->uid, ab->uid, uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + "+\\n[" DEPTH_FORMAT "]\n", + uid, p->uid, ab->uid); + if (r) + printf(".nr " MARK_REG " +\\n[" LEFT_WIDTH_FORMAT "]" + "-(\\n[" WIDTH_FORMAT "]/2)'\n", + uid, p->uid); + return r; +} + +void uaccent_box::output() +{ + if (output_format == troff) { + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, ab->uid); + printf("\\v'\\n[" DEPTH_FORMAT "]u'", p->uid); + ab->output(); + printf(DELIMITER_CHAR); + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" LEFT_WIDTH_FORMAT "]u-(\\n[" WIDTH_FORMAT "]u/2u)'", + uid, p->uid); + p->output(); + printf(DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); + } + else if (output_format == mathml) { + printf("<munder accent='true'>"); + p->output(); + ab->output(); + printf("</munder>"); + } +} + +void uaccent_box::check_tabs(int level) +{ + ab->check_tabs(level + 1); + p->check_tabs(level + 1); +} + +void uaccent_box::compute_subscript_kern() +{ + box::compute_subscript_kern(); // want 0 subscript kern +} + +void uaccent_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } uaccent { "); + ab->debug_print(); + fprintf(stderr, " }"); +} + +class underline_char_box : public simple_box { +public: + underline_char_box(); + void output(); + void debug_print(); +}; + +underline_char_box::underline_char_box() +{ +} + +void underline_char_box::output() +{ + if (output_format == troff) { + printf("\\v'%dM/2u'", 7*default_rule_thickness); + printf((draw_flag ? "\\D'l%dM 0'" : "\\l'%dM\\&\\(ru'"), + accent_width); + printf("\\v'-%dM/2u'", 7*default_rule_thickness); + } + else if (output_format == mathml) + printf("<mo>_</mo>"); +} + +void underline_char_box::debug_print() +{ + fprintf(stderr, "<underline char>"); +} + + +class underline_box : public pointer_box { +public: + underline_box(box *); + int compute_metrics(int); + void output(); + void compute_subscript_kern(); + void debug_print(); +}; + +box *make_underline_box(box *p) +{ + if (p->is_char()) + return new uaccent_box(p, new underline_char_box); + else + return new underline_box(p); +} + +underline_box::underline_box(box *pp) : pointer_box(pp) +{ +} + +int underline_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + // 10 + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n", + uid, p->uid, default_rule_thickness*5); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + return r; +} + +void underline_box::output() +{ + if (output_format == troff) { + // 10 + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" DEPTH_FORMAT "]u+(%dM/2u)'", + p->uid, 7*default_rule_thickness); + if (draw_flag) + printf("\\D'l\\n[" WIDTH_FORMAT "]u 0'", p->uid); + else + printf("\\l'\\n[" WIDTH_FORMAT "]u\\&\\(ru'", p->uid); + printf(DELIMITER_CHAR); + p->output(); + } + else if (output_format == mathml) { + printf("<munder accent='true'>"); + p->output(); + printf("<mo>¯</mo></munder>"); + } +} + +// we want an underline box to have 0 subscript kern + +void underline_box::compute_subscript_kern() +{ + box::compute_subscript_kern(); +} + +void underline_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " } under"); +} + +size_box::size_box(char *s, box *pp) : pointer_box(pp), size(s) +{ +} + +int size_box::compute_metrics(int style) +{ + printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid); + printf(".ps %s\n", size); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid); + int r = p->compute_metrics(style); + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void size_box::output() +{ + if (output_format == troff) { + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + p->output(); + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + } + else if (output_format == mathml) { + printf("<mstyle mathsize='%s'>", size); + p->output(); + printf("</mstyle>"); + } +} + +size_box::~size_box() +{ + free(size); +} + +void size_box::debug_print() +{ + fprintf(stderr, "size %s { ", size); + p->debug_print(); + fprintf(stderr, " }"); +} + + +font_box::font_box(char *s, box *pp) : pointer_box(pp), f(s) +{ +} + +font_box::~font_box() +{ + free(f); +} + +int font_box::compute_metrics(int style) +{ + const char *old_roman_font = current_roman_font; + current_roman_font = f; + printf(".nr " FONT_FORMAT " \\n[.f]\n", uid); + printf(".ft %s\n", f); + int r = p->compute_metrics(style); + current_roman_font = old_roman_font; + printf(".ft \\n[" FONT_FORMAT "]\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void font_box::output() +{ + if (output_format == troff) { + printf("\\f[%s]", f); + const char *old_roman_font = current_roman_font; + current_roman_font = f; + p->output(); + current_roman_font = old_roman_font; + printf("\\f[\\n[" FONT_FORMAT "]]", uid); + } + else if (output_format == mathml) { + const char *mlfont = f; + // bold and italic are already in MathML; translate eqn roman here + switch (f[0]) { + case 'I': + case 'i': + mlfont = "italic"; + break; + case 'B': + case 'b': + mlfont = "bold"; + break; + case 'R': + case 'r': + default: + mlfont = "normal"; + break; + } + printf("<mstyle mathvariant='%s'>", mlfont); + p->output(); + printf("</mstyle>"); + } +} + +void font_box::debug_print() +{ + fprintf(stderr, "font %s { ", f); + p->debug_print(); + fprintf(stderr, " }"); +} + +fat_box::fat_box(box *pp) : pointer_box(pp) +{ +} + +int fat_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n", + uid, p->uid, fat_offset); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + return r; +} + +void fat_box::output() +{ + if (output_format == troff) { + p->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p->uid); + printf("\\h'%dM'", fat_offset); + p->output(); + } + else if (output_format == mathml) { + printf("<mstyle mathvariant='double-struck'>"); + p->output(); + printf("</mstyle>"); + } +} + + +void fat_box::debug_print() +{ + fprintf(stderr, "fat { "); + p->debug_print(); + fprintf(stderr, " }"); +} + + +vmotion_box::vmotion_box(int i, box *pp) : pointer_box(pp), n(i) +{ +} + +int vmotion_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + if (n > 0) { + printf(".nr " HEIGHT_FORMAT " %dM+\\n[" HEIGHT_FORMAT "]\n", + uid, n, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + } + else { + printf(".nr " DEPTH_FORMAT " %dM+\\n[" DEPTH_FORMAT "]>?0\n", + uid, -n, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", + uid, p->uid); + } + return r; +} + +void vmotion_box::output() +{ + if (output_format == troff) { + printf("\\v'%dM'", -n); + p->output(); + printf("\\v'%dM'", n); + } + else if (output_format == mathml) { + printf("<merror>eqn vertical motion cannot be expressed " + "in MathML</merror>"); + p->output(); + } +} + +void vmotion_box::debug_print() +{ + if (n >= 0) + fprintf(stderr, "up %d { ", n); + else + fprintf(stderr, "down %d { ", -n); + p->debug_print(); + fprintf(stderr, " }"); +} + +hmotion_box::hmotion_box(int i, box *pp) : pointer_box(pp), n(i) +{ +} + +int hmotion_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]+%dM\n", + uid, p->uid, n); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid); + if (r) + printf(".nr " MARK_REG " +%dM\n", n); + return r; +} + +void hmotion_box::output() +{ + if (output_format == troff) { + printf("\\h'%dM'", n); + p->output(); + } + else if (output_format == mathml) { + printf("<merror>eqn horizontal motion cannot be expressed " + "in MathML</merror>"); + p->output(); + } +} + +void hmotion_box::debug_print() +{ + if (n >= 0) + fprintf(stderr, "fwd %d { ", n); + else + fprintf(stderr, "back %d { ", -n); + p->debug_print(); + fprintf(stderr, " }"); +} + +vcenter_box::vcenter_box(box *pp) : pointer_box(pp) +{ +} + +int vcenter_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid); + printf(".nr " SUP_RAISE_FORMAT " \\n[" DEPTH_FORMAT "]-\\n[" + HEIGHT_FORMAT "]/2+%dM\n", + uid, p->uid, p->uid, axis_height); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]+\\n[" + SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]-\\n[" + SUP_RAISE_FORMAT "]>?0\n", uid, p->uid, uid); + + return r; +} + +void vcenter_box::output() +{ + if (output_format == troff) + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + p->output(); + if (output_format == troff) + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); +} + +void vcenter_box::debug_print() +{ + fprintf(stderr, "vcenter { "); + p->debug_print(); + fprintf(stderr, " }"); +} + diff --git a/src/preproc/eqn/over.cpp b/src/preproc/eqn/over.cpp new file mode 100644 index 0000000..6a9cd9b --- /dev/null +++ b/src/preproc/eqn/over.cpp @@ -0,0 +1,204 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + +class over_box : public box { +private: + int reduce_size; + box *num; + box *den; +public: + over_box(int small, box *, box *); + ~over_box(); + void debug_print(); + int compute_metrics(int); + void output(); + void check_tabs(int); +}; + +box *make_over_box(box *pp, box *qq) +{ + return new over_box(0, pp, qq); +} + +box *make_small_over_box(box *pp, box *qq) +{ + return new over_box(1, pp, qq); +} + +over_box::over_box(int is_small, box *pp, box *qq) +: reduce_size(is_small), num(pp), den(qq) +{ + spacing_type = INNER_TYPE; +} + +over_box::~over_box() +{ + delete num; + delete den; +} + +int over_box::compute_metrics(int style) +{ + if (reduce_size) { + style = script_style(style); + printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid); + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid); + } + int mark_uid = 0; + int res = num->compute_metrics(style); + if (res) + mark_uid = num->uid; + int r = den->compute_metrics(cramped_style(style)); + if (r && res) + error("multiple marks and lineups"); + else { + mark_uid = den->uid; + res = r; + } + if (reduce_size) + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + printf(".nr " WIDTH_FORMAT " (\\n[" WIDTH_FORMAT "]>?\\n[" WIDTH_FORMAT "]", + uid, num->uid, den->uid); + // allow for \(ru being wider than both the numerator and denominator + if (!draw_flag) + fputs(">?\\w" DELIMITER_CHAR "\\(ru" DELIMITER_CHAR, stdout); + printf(")+%dM\n", null_delimiter_space*2 + over_hang*2); + // 15b + printf(".nr " SUP_RAISE_FORMAT " %dM\n", + uid, (reduce_size ? num2 : num1)); + printf(".nr " SUB_LOWER_FORMAT " %dM\n", + uid, (reduce_size ? denom2 : denom1)); + + // 15d + printf(".nr " SUP_RAISE_FORMAT " +(\\n[" DEPTH_FORMAT + "]-\\n[" SUP_RAISE_FORMAT "]+%dM+(%dM/2)+%dM)>?0\n", + uid, num->uid, uid, axis_height, default_rule_thickness, + default_rule_thickness*(reduce_size ? 1 : 3)); + printf(".nr " SUB_LOWER_FORMAT " +(\\n[" HEIGHT_FORMAT + "]-\\n[" SUB_LOWER_FORMAT "]-%dM+(%dM/2)+%dM)>?0\n", + uid, den->uid, uid, axis_height, default_rule_thickness, + default_rule_thickness*(reduce_size ? 1 : 3)); + + + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]\n", + uid, uid, num->uid); + printf(".nr " DEPTH_FORMAT " \\n[" SUB_LOWER_FORMAT "]+\\n[" + DEPTH_FORMAT "]\n", + uid, uid, den->uid); + if (res) + printf(".nr " MARK_REG " +(\\n[" WIDTH_FORMAT "]-\\n[" + WIDTH_FORMAT "]/2)\n", uid, mark_uid); + return res; +} + +#define USE_Z + +void over_box::output() +{ + if (output_format == troff) { + if (reduce_size) + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + #ifdef USE_Z + printf("\\Z" DELIMITER_CHAR); + #endif + // move up to the numerator baseline + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + // move across so that it's centered + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, num->uid); + + // print the numerator + num->output(); + + #ifdef USE_Z + printf(DELIMITER_CHAR); + #else + // back again + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", num->uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, num->uid); + // down again + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + #endif + #ifdef USE_Z + printf("\\Z" DELIMITER_CHAR); + #endif + // move down to the denominator baseline + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + + // move across so that it's centered + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, den->uid); + + // print the denominator + den->output(); + + #ifdef USE_Z + printf(DELIMITER_CHAR); + #else + // back again + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", den->uid); + printf("\\h'-(\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u)'", + uid, den->uid); + // up again + printf("\\v'-\\n[" SUB_LOWER_FORMAT "]u'", uid); + #endif + if (reduce_size) + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + // draw the line + printf("\\h'%dM'", null_delimiter_space); + printf("\\v'-%dM'", axis_height); + fputs(draw_flag ? "\\D'l" : "\\l'", stdout); + printf("\\n[" WIDTH_FORMAT "]u-%dM", + uid, 2*null_delimiter_space); + fputs(draw_flag ? " 0'" : "\\&\\(ru'", stdout); + printf("\\v'%dM'", axis_height); + printf("\\h'%dM'", null_delimiter_space); + } + else if (output_format == mathml) { + // FIXME: passing a displaystyle attribute doesn't validate. + printf("<mfrac>"); + num->output(); + den->output(); + printf("</mfrac>"); + } +} + +void over_box::debug_print() +{ + fprintf(stderr, "{ "); + num->debug_print(); + if (reduce_size) + fprintf(stderr, " } smallover { "); + else + fprintf(stderr, " } over { "); + den->debug_print(); + fprintf(stderr, " }"); +} + +void over_box::check_tabs(int level) +{ + num->check_tabs(level + 1); + den->check_tabs(level + 1); +} diff --git a/src/preproc/eqn/pbox.h b/src/preproc/eqn/pbox.h new file mode 100644 index 0000000..b185419 --- /dev/null +++ b/src/preproc/eqn/pbox.h @@ -0,0 +1,140 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +extern int fat_offset; + +extern int over_hang; +extern int accent_width; + +extern int delimiter_factor; +extern int delimiter_shortfall; + +extern int null_delimiter_space; +extern int script_space; +extern int thin_space; +extern int medium_space; +extern int thick_space; + +extern int num1; +extern int num2; +// we don't use num3, because we don't have \atop +extern int denom1; +extern int denom2; +extern int axis_height; +extern int sup1; +extern int sup2; +extern int sup3; +extern int default_rule_thickness; +extern int sub1; +extern int sub2; +extern int sup_drop; +extern int sub_drop; +extern int x_height; +extern int big_op_spacing1; +extern int big_op_spacing2; +extern int big_op_spacing3; +extern int big_op_spacing4; +extern int big_op_spacing5; + +extern int baseline_sep; +extern int shift_down; +extern int column_sep; +extern int matrix_side_sep; + +// ms.eqn relies on this! + +#define LINE_STRING "10" +#define MARK_OR_LINEUP_FLAG_REG "MK" + +#define WIDTH_FORMAT PREFIX "w%d" +#define HEIGHT_FORMAT PREFIX "h%d" +#define DEPTH_FORMAT PREFIX "d%d" +#define TOTAL_FORMAT PREFIX "t%d" +#define SIZE_FORMAT PREFIX "z%d" +#define SMALL_SIZE_FORMAT PREFIX "Z%d" +#define SUP_RAISE_FORMAT PREFIX "p%d" +#define SUB_LOWER_FORMAT PREFIX "b%d" +#define SUB_KERN_FORMAT PREFIX "k%d" +#define FONT_FORMAT PREFIX "f%d" +#define SKEW_FORMAT PREFIX "s%d" +#define LEFT_WIDTH_FORMAT PREFIX "lw%d" +#define LEFT_DELIM_STRING_FORMAT PREFIX "l%d" +#define RIGHT_DELIM_STRING_FORMAT PREFIX "r%d" +#define SQRT_STRING_FORMAT PREFIX "sqr%d" +#define SQRT_WIDTH_FORMAT PREFIX "sq%d" +#define BASELINE_SEP_FORMAT PREFIX "bs%d" +// this needs two parameters, the uid and the column index +#define COLUMN_WIDTH_FORMAT PREFIX "cw%d,%d" + +#define BAR_STRING PREFIX "sqb" +#define TEMP_REG PREFIX "temp" +#define MARK_REG PREFIX "mark" +#define MARK_WIDTH_REG PREFIX "mwidth" +#define SAVED_MARK_REG PREFIX "smark" +#define MAX_SIZE_REG PREFIX "mxsz" +#define REPEAT_APPEND_STRING_MACRO PREFIX "ras" +#define TOP_HEIGHT_REG PREFIX "th" +#define TOP_DEPTH_REG PREFIX "td" +#define MID_HEIGHT_REG PREFIX "mh" +#define MID_DEPTH_REG PREFIX "md" +#define BOT_HEIGHT_REG PREFIX "bh" +#define BOT_DEPTH_REG PREFIX "bd" +#define EXT_HEIGHT_REG PREFIX "eh" +#define EXT_DEPTH_REG PREFIX "ed" +#define TOTAL_HEIGHT_REG PREFIX "tot" +#define DELTA_REG PREFIX "delta" +#define DELIM_STRING PREFIX "delim" +#define DELIM_WIDTH_REG PREFIX "dwidth" +#define SAVED_FONT_REG PREFIX "sfont" +#define SAVED_PREV_FONT_REG PREFIX "spfont" +#define SAVED_INLINE_FONT_REG PREFIX "sifont" +#define SAVED_INLINE_PREV_FONT_REG PREFIX "sipfont" +#define SAVED_SIZE_REG PREFIX "ssize" +#define SAVED_INLINE_SIZE_REG PREFIX "sisize" +#define SAVED_INLINE_PREV_SIZE_REG PREFIX "sipsize" +#define SAVE_FONT_STRING PREFIX "sfont" +#define RESTORE_FONT_STRING PREFIX "rfont" +#define INDEX_REG PREFIX "i" +#define TEMP_MACRO PREFIX "tempmac" + +#define DELIMITER_CHAR "\\(EQ" + +const int CRAMPED_SCRIPT_STYLE = 0; +const int SCRIPT_STYLE = 1; +const int CRAMPED_DISPLAY_STYLE = 2; +const int DISPLAY_STYLE = 3; + +extern int script_style(int); +extern int cramped_style(int); + +const int ORDINARY_TYPE = 0; +const int OPERATOR_TYPE = 1; +const int BINARY_TYPE = 2; +const int RELATION_TYPE = 3; +const int OPENING_TYPE = 4; +const int CLOSING_TYPE = 5; +const int PUNCTUATION_TYPE = 6; +const int INNER_TYPE = 7; +const int SUPPRESS_TYPE = 8; + +void set_script_size(); + +enum { HINT_PREV_IS_ITALIC = 01, HINT_NEXT_IS_ITALIC = 02 }; + +extern const char *current_roman_font; diff --git a/src/preproc/eqn/pile.cpp b/src/preproc/eqn/pile.cpp new file mode 100644 index 0000000..fcc2e92 --- /dev/null +++ b/src/preproc/eqn/pile.cpp @@ -0,0 +1,354 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +// piles and matrices + +#include <assert.h> + +#include "eqn.h" +#include "pbox.h" + +// SUP_RAISE_FORMAT gives the first baseline +// BASELINE_SEP_FORMAT gives the separation between baselines + +int pile_box::compute_metrics(int style) +{ + int i; + for (i = 0; i < col.len; i++) + col.p[i]->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0", uid); + for (i = 0; i < col.len; i++) + printf(">?\\n[" WIDTH_FORMAT "]", col.p[i]->uid); + printf("\n"); + printf(".nr " BASELINE_SEP_FORMAT " %dM", + uid, baseline_sep+col.space); + for (i = 1; i < col.len; i++) + printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)", + col.p[i-1]->uid, col.p[i]->uid, default_rule_thickness*5); + // round it so that it's a multiple of the vertical motion quantum + printf("+(\\n(.V/2)/\\n(.V*\\n(.V\n"); + + printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2" + "+%dM\n", + uid, uid, col.len-1, axis_height - shift_down); + printf(".nr " HEIGHT_FORMAT " \\n[" SUP_RAISE_FORMAT "]+\\n[" + HEIGHT_FORMAT "]\n", + uid, uid, col.p[0]->uid); + printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d+\\n[" + DEPTH_FORMAT "]-\\n[" SUP_RAISE_FORMAT "]\n", + uid, uid, col.len-1, col.p[col.len-1]->uid, uid); + return FOUND_NOTHING; +} + +void pile_box::output() +{ + if (output_format == troff) { + int i; + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + for (i = 0; i < col.len; i++) { + switch (col.align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, col.p[i]->uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, col.p[i]->uid); + break; + default: + assert(0); + } + col.p[i]->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", col.p[i]->uid); + switch (col.align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + col.p[i]->uid, uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + col.p[i]->uid, uid); + break; + default: + assert(0); + } + if (i != col.len - 1) + printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid); + } + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", col.len - 1, uid); + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); + } + else if (output_format == mathml) { + const char *av; + switch (col.align) { + case LEFT_ALIGN: + av = "left"; + break; + case RIGHT_ALIGN: + av = "right"; + break; + case CENTER_ALIGN: + av = "center"; + break; + default: + assert(0); + } + printf("<mtable columnalign='%s'>", av); + for (int i = 0; i < col.len; i++) { + printf("<mtr><mtd>"); + col.p[i]->output(); + printf("</mtd></mtr>"); + } + printf("</mtable>"); + } +} + +pile_box::pile_box(box *pp) : col(pp) +{ +} + +void pile_box::check_tabs(int level) +{ + col.list_check_tabs(level); +} + +void pile_box::debug_print() +{ + col.debug_print("pile"); +} + +int matrix_box::compute_metrics(int style) +{ + int i, j; + int max_len = 0; + int space = 0; + for (i = 0; i < len; i++) { + for (j = 0; j < p[i]->len; j++) + p[i]->p[j]->compute_metrics(style); + if (p[i]->len > max_len) + max_len = p[i]->len; + if (p[i]->space > space) + space = p[i]->space; + } + for (i = 0; i < len; i++) { + printf(".nr " COLUMN_WIDTH_FORMAT " 0", uid, i); + for (j = 0; j < p[i]->len; j++) + printf(">?\\n[" WIDTH_FORMAT "]", p[i]->p[j]->uid); + printf("\n"); + } + printf(".nr " WIDTH_FORMAT " %dM", + uid, column_sep*(len-1)+2*matrix_side_sep); + for (i = 0; i < len; i++) + printf("+\\n[" COLUMN_WIDTH_FORMAT "]", uid, i); + printf("\n"); + printf(".nr " BASELINE_SEP_FORMAT " %dM", + uid, baseline_sep+space); + for (i = 0; i < len; i++) + for (j = 1; j < p[i]->len; j++) + printf(">?(\\n[" DEPTH_FORMAT "]+\\n[" HEIGHT_FORMAT "]+%dM)", + p[i]->p[j-1]->uid, p[i]->p[j]->uid, default_rule_thickness*5); + // round it so that it's a multiple of the vertical motion quantum + printf("+(\\n(.V/2)/\\n(.V*\\n(.V\n"); + printf(".nr " SUP_RAISE_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d/2" + "+%dM\n", + uid, uid, max_len-1, axis_height - shift_down); + printf(".nr " HEIGHT_FORMAT " 0\\n[" SUP_RAISE_FORMAT "]+(0", + uid, uid); + for (i = 0; i < len; i++) + printf(">?\\n[" HEIGHT_FORMAT "]", p[i]->p[0]->uid); + printf(")>?0\n"); + printf(".nr " DEPTH_FORMAT " \\n[" BASELINE_SEP_FORMAT "]*%d-\\n[" + SUP_RAISE_FORMAT "]+(0", + uid, uid, max_len-1, uid); + for (i = 0; i < len; i++) + if (p[i]->len == max_len) + printf(">?\\n[" DEPTH_FORMAT "]", p[i]->p[max_len-1]->uid); + printf(")>?0\n"); + return FOUND_NOTHING; +} + +void matrix_box::output() +{ + if (output_format == troff) { + printf("\\h'%dM'", matrix_side_sep); + for (int i = 0; i < len; i++) { + int j; + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + for (j = 0; j < p[i]->len; j++) { + switch (p[i]->align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u/2u'", + uid, i, p[i]->p[j]->uid); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, i, p[i]->p[j]->uid); + break; + default: + assert(0); + } + p[i]->p[j]->output(); + printf("\\h'-\\n[" WIDTH_FORMAT "]u'", p[i]->p[j]->uid); + switch (p[i]->align) { + case LEFT_ALIGN: + break; + case CENTER_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u/2u'", + p[i]->p[j]->uid, uid, i); + break; + case RIGHT_ALIGN: + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" COLUMN_WIDTH_FORMAT "]u'", + p[i]->p[j]->uid, uid, i); + break; + default: + assert(0); + } + if (j != p[i]->len - 1) + printf("\\v'\\n[" BASELINE_SEP_FORMAT "]u'", uid); + } + printf("\\v'\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\v'-(%du*\\n[" BASELINE_SEP_FORMAT "]u)'", p[i]->len - 1, uid); + printf("\\h'\\n[" COLUMN_WIDTH_FORMAT "]u'", uid, i); + if (i != len - 1) + printf("\\h'%dM'", column_sep); + } + printf("\\h'%dM'", matrix_side_sep); + } + else if (output_format == mathml) { + int n = p[0]->len; // Each column must have the same number of rows in it + printf("<mtable>"); + for (int i = 0; i < n; i++) { + printf("<mtr>"); + for (int j = 0; j < len; j++) { + const char *av; + switch (p[j]->align) { + case LEFT_ALIGN: + av = "left"; + break; + case RIGHT_ALIGN: + av = "right"; + break; + case CENTER_ALIGN: + av = "center"; + break; + default: + assert(0); + } + printf("<mtd columnalign='%s'>", av); + p[j]->p[i]->output(); + printf("</mtd>"); + } + printf("</mtr>"); + } + printf("</mtable>"); + } +} + +matrix_box::matrix_box(column *pp) +{ + p = new column*[10]; + for (int i = 0; i < 10; i++) + p[i] = 0; + maxlen = 10; + len = 1; + p[0] = pp; +} + +matrix_box::~matrix_box() +{ + for (int i = 0; i < len; i++) + delete p[i]; + delete[] p; +} + +void matrix_box::append(column *pp) +{ + if (len + 1 > maxlen) { + column **oldp = p; + maxlen *= 2; + p = new column*[maxlen]; + memcpy(p, oldp, sizeof(column*)*len); + delete[] oldp; + } + p[len++] = pp; +} + +void matrix_box::check_tabs(int level) +{ + for (int i = 0; i < len; i++) + p[i]->list_check_tabs(level); +} + +void matrix_box::debug_print() +{ + fprintf(stderr, "matrix { "); + p[0]->debug_print("col"); + for (int i = 1; i < len; i++) { + fprintf(stderr, " "); + p[i]->debug_print("col"); + } + fprintf(stderr, " }"); +} + +column::column(box *pp) : box_list(pp), align(CENTER_ALIGN), space(0) +{ +} + +void column::set_alignment(alignment a) +{ + align = a; +} + +void column::set_space(int n) +{ + space = n; +} + +void column::debug_print(const char *s) +{ + char c = '\0'; // shut up -Wall + switch (align) { + case LEFT_ALIGN: + c = 'l'; + break; + case RIGHT_ALIGN: + c = 'r'; + break; + case CENTER_ALIGN: + c = 'c'; + break; + default: + assert(0); + } + fprintf(stderr, "%c%s %d { ", c, s, space); + list_debug_print(" above "); + fprintf(stderr, " }"); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/script.cpp b/src/preproc/eqn/script.cpp new file mode 100644 index 0000000..f485eb9 --- /dev/null +++ b/src/preproc/eqn/script.cpp @@ -0,0 +1,250 @@ +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include <assert.h> + +#include "eqn.h" +#include "pbox.h" + +class script_box : public pointer_box { +private: + box *sub; + box *sup; +public: + script_box(box *, box *, box *); + ~script_box(); + int compute_metrics(int); + void output(); + void debug_print(); + int left_is_italic(); + void hint(unsigned); + void check_tabs(int); +}; + +/* The idea is that the script should attach to the rightmost box +of a list. For example, given '2x sup 3', the superscript should +attach to 'x' rather than '2x'. */ + +box *make_script_box(box *nuc, box *sub, box *sup) +{ + list_box *b = nuc->to_list_box(); + if (b != 0) { + b->list.p[b->list.len-1] = make_script_box(b->list.p[b->list.len - 1], + sub, + sup); + return b; + } + else + return new script_box(nuc, sub, sup); +} + +script_box::script_box(box *pp, box *qq, box *rr) +: pointer_box(pp), sub(qq), sup(rr) +{ +} + +script_box::~script_box() +{ + delete sub; + delete sup; +} + +int script_box::left_is_italic() +{ + return p->left_is_italic(); +} + +int script_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + p->compute_subscript_kern(); + printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid); + if (!(style <= SCRIPT_STYLE && one_size_reduction_flag)) + set_script_size(); + printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid); + if (sub != 0) + sub->compute_metrics(cramped_style(script_style(style))); + if (sup != 0) + sup->compute_metrics(script_style(style)); + // 18a + if (p->is_char()) { + printf(".nr " SUP_RAISE_FORMAT " 0\n", uid); + printf(".nr " SUB_LOWER_FORMAT " 0\n", uid); + } + else { + printf(".nr " SUP_RAISE_FORMAT " \\n[" HEIGHT_FORMAT "]-%dM>?0\n", + uid, p->uid, sup_drop); + printf(".nr " SUB_LOWER_FORMAT " \\n[" DEPTH_FORMAT "]+%dM\n", + uid, p->uid, sub_drop); + } + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + if (sup == 0) { + assert(sub != 0); + // 18b + printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM>?(\\n[" + HEIGHT_FORMAT "]-(%dM*4/5))\n", + uid, uid, sub1, sub->uid, x_height); + } + else { + // sup != 0 + // 18c + int pos; + if (style == DISPLAY_STYLE) + pos = sup1; + else if (style & 1) // not cramped + pos = sup2; + else + pos = sup3; + printf(".nr " SUP_RAISE_FORMAT " \\n[" SUP_RAISE_FORMAT + "]>?%dM>?(\\n[" DEPTH_FORMAT "]+(%dM/4))\n", + uid, uid, pos, sup->uid, x_height); + // 18d + if (sub != 0) { + printf(".nr " SUB_LOWER_FORMAT " \\n[" SUB_LOWER_FORMAT "]>?%dM\n", + uid, uid, sub2); + // 18e + printf(".nr " TEMP_REG " \\n[" DEPTH_FORMAT "]-\\n[" + SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "]-\\n[" + SUB_LOWER_FORMAT "]+(4*%dM)\n", + sup->uid, uid, sub->uid, uid, default_rule_thickness); + printf(".if \\n[" TEMP_REG "] \\{"); + printf(".nr " SUB_LOWER_FORMAT " +\\n[" TEMP_REG "]\n", uid); + printf(".nr " TEMP_REG " (%dM*4/5)-\\n[" SUP_RAISE_FORMAT + "]+\\n[" DEPTH_FORMAT "]>?0\n", + x_height, uid, sup->uid); + printf(".nr " SUP_RAISE_FORMAT " +\\n[" TEMP_REG "]\n", uid); + printf(".nr " SUB_LOWER_FORMAT " -\\n[" TEMP_REG "]\n", uid); + printf(".\\}\n"); + } + } + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]", uid, p->uid); + if (sub != 0 && sup != 0) + printf("+((\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]>?\\n[" + WIDTH_FORMAT "])+%dM)>?0\n", + sub->uid, p->uid, sup->uid, script_space); + else if (sub != 0) + printf("+(\\n[" WIDTH_FORMAT "]-\\n[" SUB_KERN_FORMAT "]+%dM)>?0\n", + sub->uid, p->uid, script_space); + else if (sup != 0) + printf("+(\\n[" WIDTH_FORMAT "]+%dM)>?0\n", sup->uid, script_space); + else + printf("\n"); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]", + uid, p->uid); + if (sup != 0) + printf(">?(\\n[" SUP_RAISE_FORMAT "]+\\n[" HEIGHT_FORMAT "])", + uid, sup->uid); + if (sub != 0) + printf(">?(-\\n[" SUB_LOWER_FORMAT "]+\\n[" HEIGHT_FORMAT "])", + uid, sub->uid); + printf("\n"); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]", + uid, p->uid); + if (sub != 0) + printf(">?(\\n[" SUB_LOWER_FORMAT "]+\\n[" DEPTH_FORMAT "])", + uid, sub->uid); + if (sup != 0) + printf(">?(-\\n[" SUP_RAISE_FORMAT "]+\\n[" DEPTH_FORMAT "])", + uid, sup->uid); + printf("\n"); + return res; +} + +void script_box::output() +{ + if (output_format == troff) { + p->output(); + if (sup != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + sup->output(); + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + printf(DELIMITER_CHAR); + } + if (sub != 0) { + printf("\\Z" DELIMITER_CHAR); + printf("\\v'\\n[" SUB_LOWER_FORMAT "]u'", uid); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + printf("\\h'-\\n[" SUB_KERN_FORMAT "]u'", p->uid); + sub->output(); + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + printf(DELIMITER_CHAR); + } + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u'", + uid, p->uid); + } + else if (output_format == mathml) { + if (sup != 0 && sub != 0) { + printf("<msubsup>"); + p->output(); + sub->output(); + sup->output(); + printf("</msubsup>"); + } + else if (sup != 0) { + printf("<msup>"); + p->output(); + sup->output(); + printf("</msup>"); + } + else if (sub != 0) { + printf("<msub>"); + p->output(); + sub->output(); + printf("</msub>"); + } + } +} + +void script_box::hint(unsigned flags) +{ + p->hint(flags & ~HINT_NEXT_IS_ITALIC); +} + +void script_box::debug_print() +{ + fprintf(stderr, "{ "); + p->debug_print(); + fprintf(stderr, " }"); + if (sub) { + fprintf(stderr, " sub { "); + sub->debug_print(); + fprintf(stderr, " }"); + } + if (sup) { + fprintf(stderr, " sup { "); + sup->debug_print(); + fprintf(stderr, " }"); + } +} + +void script_box::check_tabs(int level) +{ + if (sup) + sup->check_tabs(level + 1); + if (sub) + sub->check_tabs(level + 1); + p->check_tabs(level); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: diff --git a/src/preproc/eqn/special.cpp b/src/preproc/eqn/special.cpp new file mode 100644 index 0000000..8ad8238 --- /dev/null +++ b/src/preproc/eqn/special.cpp @@ -0,0 +1,117 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + +#define STRING_FORMAT PREFIX "str%d" + +#define SPECIAL_STRING "0s" +#define SPECIAL_WIDTH_REG "0w" +#define SPECIAL_HEIGHT_REG "0h" +#define SPECIAL_DEPTH_REG "0d" +#define SPECIAL_SUB_KERN_REG "0skern" +#define SPECIAL_SKEW_REG "0skew" + +/* +For example: + +.de Cl +.ds 0s \Z'\\*[0s]'\v'\\n(0du'\D'l \\n(0wu -\\n(0hu-\\n(0du'\v'\\n(0hu' +.. +.EQ +define cancel 'special Cl' +.EN +*/ + + +class special_box : public pointer_box { + char *macro_name; +public: + special_box(char *, box *); + ~special_box(); + int compute_metrics(int); + void compute_subscript_kern(); + void compute_skew(); + void output(); + void debug_print(); +}; + +box *make_special_box(char *s, box *p) +{ + return new special_box(s, p); +} + +special_box::special_box(char *s, box *pp) : pointer_box(pp), macro_name(s) +{ +} + +special_box::~special_box() +{ + delete[] macro_name; +} + +int special_box::compute_metrics(int style) +{ + int r = p->compute_metrics(style); + p->compute_subscript_kern(); + p->compute_skew(); + printf(".ds " SPECIAL_STRING " \""); + p->output(); + printf("\n"); + printf(".nr " SPECIAL_WIDTH_REG " 0\\n[" WIDTH_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_HEIGHT_REG " \\n[" HEIGHT_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_DEPTH_REG " \\n[" DEPTH_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_SUB_KERN_REG " \\n[" SUB_KERN_FORMAT "]\n", p->uid); + printf(".nr " SPECIAL_SKEW_REG " 0\\n[" SKEW_FORMAT "]\n", p->uid); + printf(".%s\n", macro_name); + printf(".rn " SPECIAL_STRING " " STRING_FORMAT "\n", uid); + printf(".nr " WIDTH_FORMAT " 0\\n[" SPECIAL_WIDTH_REG "]\n", uid); + printf(".nr " HEIGHT_FORMAT " 0>?\\n[" SPECIAL_HEIGHT_REG "]\n", uid); + printf(".nr " DEPTH_FORMAT " 0>?\\n[" SPECIAL_DEPTH_REG "]\n", uid); + printf(".nr " SUB_KERN_FORMAT " 0>?\\n[" SPECIAL_SUB_KERN_REG "]\n", uid); + printf(".nr " SKEW_FORMAT " 0\\n[" SPECIAL_SKEW_REG "]\n", uid); + // User will have to change MARK_REG if appropriate. + return r; +} + +void special_box::compute_subscript_kern() +{ + // Already computed in compute_metrics(), so do nothing. +} + +void special_box::compute_skew() +{ + // Already computed in compute_metrics(), so do nothing. +} + +void special_box::output() +{ + if (output_format == troff) + printf("\\*[" STRING_FORMAT "]", uid); + else if (output_format == mathml) + printf("<merror>eqn specials cannot be expressed in MathML</merror>"); +} + +void special_box::debug_print() +{ + fprintf(stderr, "special %s { ", macro_name); + p->debug_print(); + fprintf(stderr, " }"); +} diff --git a/src/preproc/eqn/sqrt.cpp b/src/preproc/eqn/sqrt.cpp new file mode 100644 index 0000000..e8d7c77 --- /dev/null +++ b/src/preproc/eqn/sqrt.cpp @@ -0,0 +1,186 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#include "eqn.h" +#include "pbox.h" + + +class sqrt_box : public pointer_box { +public: + sqrt_box(box *); + int compute_metrics(int style); + void output(); + void debug_print(); + void check_tabs(int); +}; + +box *make_sqrt_box(box *pp) +{ + return new sqrt_box(pp); +} + +sqrt_box::sqrt_box(box *pp) : pointer_box(pp) +{ +} + +#define SQRT_CHAR "\\[sqrt]" +#define RADICAL_EXTENSION_CHAR "\\[sqrtex]" + +#define SQRT_CHAIN "\\[sqrt\\\\n[" INDEX_REG "]]" +#define BAR_CHAIN "\\[sqrtex\\\\n[" INDEX_REG "]]" + +int sqrt_box::compute_metrics(int style) +{ + // 11 + int r = p->compute_metrics(cramped_style(style)); + printf(".nr " TEMP_REG " \\n[" HEIGHT_FORMAT "]+\\n[" DEPTH_FORMAT + "]+%dM+(%dM/4)\n", + p->uid, p->uid, default_rule_thickness, + (style > SCRIPT_STYLE ? x_height : default_rule_thickness)); + printf(".nr " SIZE_FORMAT " \\n[.ps]\n", uid); + printf(".ds " SQRT_STRING_FORMAT " " SQRT_CHAR "\n", uid); + printf(".ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n"); + printf(".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR SQRT_CHAR DELIMITER_CHAR "\n", + uid); + printf(".if \\n[rst]-\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{", + default_rule_thickness); + + printf(".nr " INDEX_REG " 0\n" + ".de " TEMP_MACRO "\n" + ".ie c" SQRT_CHAIN " \\{" + ".ds " SQRT_STRING_FORMAT " " SQRT_CHAIN "\n" + ".ie c" BAR_CHAIN " .ds " BAR_STRING " " BAR_CHAIN "\n" + ".el .ds " BAR_STRING " " RADICAL_EXTENSION_CHAR "\n" + ".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR SQRT_CHAIN DELIMITER_CHAR "\n" + ".if \\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "] \\{" + ".nr " INDEX_REG " +1\n" + "." TEMP_MACRO "\n" + ".\\}\\}\n" + ".el .nr " INDEX_REG " 0-1\n" + "..\n" + "." TEMP_MACRO "\n", + uid, uid, default_rule_thickness); + + printf(".if \\n[" INDEX_REG "]<0 \\{"); + + // Determine the maximum point size + printf(".ps 1000\n"); + printf(".nr " MAX_SIZE_REG " \\n[.ps]\n"); + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + // We define a macro that will increase the current point size + // until we get a radical sign that's tall enough or we reach + // the maximum point size. + printf(".de " TEMP_MACRO "\n" + ".nr " SQRT_WIDTH_FORMAT + " 0\\w" DELIMITER_CHAR "\\*[" SQRT_STRING_FORMAT "]" DELIMITER_CHAR "\n" + ".if \\\\n[rst]" + "&(\\\\n[rst]-\\\\n[rsb]-%dM<\\n[" TEMP_REG "])" + "&(\\\\n[.ps]<\\n[" MAX_SIZE_REG "]) \\{" + ".ps +1\n" + "." TEMP_MACRO "\n" + ".\\}\n" + "..\n" + "." TEMP_MACRO "\n", + uid, uid, default_rule_thickness); + + printf(".\\}\\}\n"); + + printf(".nr " SMALL_SIZE_FORMAT " \\n[.ps]\n", uid); + // set TEMP_REG to the amount by which the radical sign is too big + printf(".nr " TEMP_REG " \\n[rst]-\\n[rsb]-%dM-\\n[" TEMP_REG "]\n", + default_rule_thickness); + // If TEMP_REG is negative, the bottom of the radical sign should + // be -TEMP_REG above the bottom of p. If it's positive, the bottom + // of the radical sign should be TEMP_REG/2 below the bottom of p. + // This calculates the amount by which the baseline of the radical + // should be raised. + printf(".nr " SUP_RAISE_FORMAT " (-\\n[" TEMP_REG "]>?(-\\n[" TEMP_REG "]/2))" + "-\\n[rsb]-\\n[" DEPTH_FORMAT "]\n", uid, p->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n", + uid, p->uid, uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?(-\\n[" SUP_RAISE_FORMAT "]-\\n[rsb])\n", + uid, p->uid, uid); + // Do this last, so we don't lose height and depth information on + // the radical sign. + // Remember that the width of the bar might be greater than the width of p. + + printf(".nr " TEMP_REG " " + "\\n[" WIDTH_FORMAT "]" + ">?\\w" DELIMITER_CHAR "\\*[" BAR_STRING "]" DELIMITER_CHAR "\n", + p->uid); + printf(".as " SQRT_STRING_FORMAT " " + "\\l'\\n[" TEMP_REG "]u\\&\\*[" BAR_STRING "]'\n", + uid); + printf(".nr " WIDTH_FORMAT " \\n[" TEMP_REG "]" + "+\\n[" SQRT_WIDTH_FORMAT "]\n", + uid, uid); + + if (r) + printf(".nr " MARK_REG " +\\n[" SQRT_WIDTH_FORMAT "]\n", uid); + // the top of the bar might be higher than the top of the radical sign + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?(\\n[" SUP_RAISE_FORMAT "]+\\n[rst])\n", + uid, p->uid, uid); + // put a bit of extra space above the bar + printf(".nr " HEIGHT_FORMAT " +%dM\n", uid, default_rule_thickness); + printf(".ps \\n[" SIZE_FORMAT "]u\n", uid); + return r; +} + +void sqrt_box::output() +{ + if (output_format == troff) { + printf("\\Z" DELIMITER_CHAR); + printf("\\s[\\n[" SMALL_SIZE_FORMAT "]u]", uid); + printf("\\v'-\\n[" SUP_RAISE_FORMAT "]u'", uid); + printf("\\*[" SQRT_STRING_FORMAT "]", uid); + printf("\\s[\\n[" SIZE_FORMAT "]u]", uid); + printf(DELIMITER_CHAR); + + printf("\\Z" DELIMITER_CHAR); + printf("\\h'\\n[" WIDTH_FORMAT "]u-\\n[" WIDTH_FORMAT "]u" + "+\\n[" SQRT_WIDTH_FORMAT "]u/2u'", + uid, p->uid, uid); + p->output(); + printf(DELIMITER_CHAR); + + printf("\\h'\\n[" WIDTH_FORMAT "]u'", uid); + } + else if (output_format == mathml) { + printf("<msqrt>"); + p->output(); + printf("</msqrt>"); + } +} + +void sqrt_box::debug_print() +{ + fprintf(stderr, "sqrt { "); + p->debug_print(); + fprintf(stderr, " }"); +} + +void sqrt_box::check_tabs(int level) +{ + p->check_tabs(level + 1); +} diff --git a/src/preproc/eqn/tests/diagnostics-report-correct-line-numbers.sh b/src/preproc/eqn/tests/diagnostics-report-correct-line-numbers.sh new file mode 100755 index 0000000..feb78c1 --- /dev/null +++ b/src/preproc/eqn/tests/diagnostics-report-correct-line-numbers.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Copyright (C) 2022 Free Software Foundation, Inc. +# +# 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 <http://www.gnu.org/licenses/>. +# + +eqn="${abs_top_builddir:-.}/eqn" +fail= + +wail () { + echo "...FAILED" >&2 + echo "$error" + fail=yes +} + +# Verify that correct input file line numbers are reported. + +echo "checking for absent line number in early EOF diagnostic" >&2 +input='.EQ' +error=$(printf "%s\n" "$input" | "$eqn" 2>&1 > /dev/null) +echo "$error" | grep -Eq '^[^:]+:[^:]+: fatal' || wail + +echo "checking for correct line number in nested 'EQ' diagnostic" >&2 +input='.EQ +.EQ' +error=$(printf "%s\n" "$input" | "$eqn" 2>&1 > /dev/null) +echo "$error" | grep -Eq '^[^:]+:[^:]+:2: fatal' || wail + +echo "checking for correct line number in invalid input character" \ + "diagnostic" >&2 +error=$(printf ".EQ\nx\n.EN\n\200\n" | "$eqn" 2>&1 > /dev/null) +echo "$error" | grep -Eq '^[^:]+:[^:]+:4: error' || wail + +echo "checking for correct line number in invalid input character" \ + "diagnostic when 'lf' request used beforehand" >&2 +error=$(printf ".EQ\nx\n.EN\n.lf 99\n\200\n" | "$eqn" 2>&1 > /dev/null) +echo "$error" | grep -Eq '^[^:]+:[^:]+:99: error' || wail + +test -z "$fail" + +# vim:set ai et sw=4 ts=4 tw=72: diff --git a/src/preproc/eqn/text.cpp b/src/preproc/eqn/text.cpp new file mode 100644 index 0000000..272f3fe --- /dev/null +++ b/src/preproc/eqn/text.cpp @@ -0,0 +1,957 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctype.h> +#include <stdlib.h> +#include "eqn.h" +#include "pbox.h" +#include "ptable.h" + +struct map { + const char *from; + const char *to; +}; + +struct map entity_table[] = { + // Classic troff special characters + {"%", "­"}, // ISOnum + {"'", "´"}, // ISOdia + {"!=", "≠"}, // ISOtech + {"**", "∗"}, // ISOtech + {"*a", "α"}, // ISOgrk3 + {"*A", "A"}, + {"*b", "β"}, // ISOgrk3 + {"*B", "B"}, + {"*d", "δ"}, // ISOgrk3 + {"*D", "Δ"}, // ISOgrk3 + {"*e", "ε"}, // ISOgrk3 + {"*E", "E"}, + {"*f", "φ"}, // ISOgrk3 + {"*F", "Φ"}, // ISOgrk3 + {"*g", "γ"}, // ISOgrk3 + {"*G", "Γ"}, // ISOgrk3 + {"*h", "θ"}, // ISOgrk3 + {"*H", "Θ"}, // ISOgrk3 + {"*i", "ι"}, // ISOgrk3 + {"*I", "I"}, + {"*k", "κ"}, // ISOgrk3 + {"*K", "K;"}, + {"*l", "λ"}, // ISOgrk3 + {"*L", "Λ"}, // ISOgrk3 + {"*m", "μ"}, // ISOgrk3 + {"*M", "M"}, + {"*n", "ν"}, // ISOgrk3 + {"*N", "N"}, + {"*o", "o"}, + {"*O", "O"}, + {"*p", "π"}, // ISOgrk3 + {"*P", "Π"}, // ISOgrk3 + {"*q", "ψ"}, // ISOgrk3 + {"*Q", "&PSI;"}, // ISOgrk3 + {"*r", "ρ"}, // ISOgrk3 + {"*R", "R"}, + {"*s", "σ"}, // ISOgrk3 + {"*S", "Σ"}, // ISOgrk3 + {"*t", "τ"}, // ISOgrk3 + {"*T", "Τ"}, // ISOgrk3 + {"*u", "υ"}, // ISOgrk3 + {"*U", "Υ"}, // ISOgrk3 + {"*w", "ω"}, // ISOgrk3 + {"*W", "Ω"}, // ISOgrk3 + {"*x", "χ"}, // ISOgrk3 + {"*X", "Χ"}, // ISOgrk3 + {"*y", "η"}, // ISOgrk3 + {"*Y", "Η"}, // ISOgrk3 + {"*z", "ζ"}, // ISOgrk3 + {"*Z", "Ζ"}, // ISOgrk3 + {"+-", "±"}, // ISOnum + {"->", "→"}, // ISOnum + {"12", "½"}, // ISOnum + {"14", "¼"}, // ISOnum + {"34", "¾"}, // ISOnum + {"<-", "←"}, // ISOnum + {"==", "≡"}, // ISOtech + {"Fi", "ffi"}, // ISOpub + {"Fl", "ffl"}, // ISOpub + {"aa", "´"}, // ISOdia + {"ap", "∼"}, // ISOtech + {"bl", "&phonexb;"}, // ISOpub + {"br", "│"}, // ISObox + {"bs", "☎"}, // ISOpub (for the Bell logo) + {"bu", "•"}, // ISOpub + {"bv", "|"}, // ISOnum + {"ca", "∩"}, // ISOtech + {"ci", "○"}, // ISOpub + {"co", "©"}, // ISOnum + {"ct", "¢"}, // ISOnum + {"cu", "∪"}, // ISOtech + {"da", "↓"}, // ISOnum + {"de", "°"}, // ISOnum + {"dg", "†"}, // ISOpub + {"dd", "‡"}, // ISOpub + {"di", "÷"}, // ISOnum + {"em", "—"}, // ISOpub + {"eq", "="}, // ISOnum + {"es", "∅"}, // ISOamso + {"ff", "ff"}, // ISOpub + {"fi", "fi"}, // ISOpub + {"fl", "fl"}, // ISOpub + {"fm", "′"}, // ISOtech + {"ge", "≥"}, // ISOtech + {"gr", "∇"}, // ISOtech + {"hy", "‐"}, // ISOnum + {"ib", "⊆"}, // ISOtech + {"if", "∞"}, // ISOtech + {"ip", "⊇"}, // ISOtech + {"is", "∫"}, // ISOtech + {"le", "≤"}, // ISOtech + // Some pile characters go here + {"mi", "−"}, // ISOtech + {"mo", "∈"}, // ISOtech + {"mu", "×"}, // ISOnum + {"no", "¬"}, // ISOnum + {"or", "|"}, // ISOnum + {"pl", "+"}, // ISOnum + {"pt", "∝"}, // ISOtech + {"rg", "™"}, // ISOnum + // More pile characters go here + {"rn", "¯"}, // ISOdia + {"ru", "_"}, // ISOnum + {"sb", "⊂"}, // ISOtech + {"sc", "§"}, // ISOnum + {"sl", "/"}, + {"sp", "⊃"}, // ISOtech + {"sq", "▪"}, // ISOpub + {"sr", "√"}, // ISOtech + {"ts", "ς"}, // ISOgrk3 + {"ua", "↑"}, // ISOnum + {"ul", "_"}, + {"~=", "≅"}, // ISOtech + // Extended specials supported by groff; see groff_char(7). + // These are listed in the order they occur on that man page. + {"-D", "Ð"}, // ISOlat: Icelandic uppercase eth + {"Sd", "ð"}, // ISOlat1: Icelandic lowercase eth + {"TP", "Þ"}, // ISOlat1: Icelandic uppercase thorn + {"Tp", "þ"}, // ISOlat1: Icelandic lowercase thorn + {"ss", "ß"}, // ISOlat1 + // Ligatures + // ff, fi, fl, ffi, ffl from old troff go here + {"AE", "Æ"}, // ISOlat1 + {"ae", "æ"}, // ISOlat1 + {"OE", "Œ"}, // ISOlat2 + {"oe", "œ"}, // ISOlat2 + {"IJ", "ij"}, // ISOlat2: Dutch IJ ligature + {"ij", "IJ"}, // ISOlat2: Dutch ij ligature + {".i", "ı"}, // ISOlat2,ISOamso + {".j", "&jnodot;"}, // ISOamso (undocumented but in 1.19) + // Accented characters + {"'A", "Á"}, // ISOlat1 + {"'C", "Ć"}, // ISOlat2 + {"'E", "É"}, // ISOlat1 + {"'I", "Í"}, // ISOlat1 + {"'O", "Ó"}, // ISOlat1 + {"'U", "Ú"}, // ISOlat1 + {"'Y", "Ý"}, // ISOlat1 + {"'a", "á"}, // ISOlat1 + {"'c", "ć"}, // ISOlat2 + {"'e", "é"}, // ISOlat1 + {"'i", "í"}, // ISOlat1 + {"'o", "ó"}, // ISOlat1 + {"'u", "ú"}, // ISOlat1 + {"'y", "ý"}, // ISOlat1 + {":A", "Ä"}, // ISOlat1 + {":E", "Ë"}, // ISOlat1 + {":I", "Ï"}, // ISOlat1 + {":O", "Ö"}, // ISOlat1 + {":U", "Ü"}, // ISOlat1 + {":Y", "Ÿ"}, // ISOlat2 + {":a", "ä"}, // ISOlat1 + {":e", "ë"}, // ISOlat1 + {":i", "ï"}, // ISOlat1 + {":o", "ö"}, // ISOlat1 + {":u", "ü"}, // ISOlat1 + {":y", "ÿ"}, // ISOlat1 + {"^A", "Â"}, // ISOlat1 + {"^E", "Ê"}, // ISOlat1 + {"^I", "Î"}, // ISOlat1 + {"^O", "Ô"}, // ISOlat1 + {"^U", "Û"}, // ISOlat1 + {"^a", "â"}, // ISOlat1 + {"^e", "ê"}, // ISOlat1 + {"^i", "î"}, // ISOlat1 + {"^o", "ô"}, // ISOlat1 + {"^u", "û"}, // ISOlat1 + {"`A", "À"}, // ISOlat1 + {"`E", "È"}, // ISOlat1 + {"`I", "Ì"}, // ISOlat1 + {"`O", "Ò"}, // ISOlat1 + {"`U", "Ù"}, // ISOlat1 + {"`a", "à"}, // ISOlat1 + {"`e", "è"}, // ISOlat1 + {"`i", "ì"}, // ISOlat1 + {"`o", "ò"}, // ISOlat1 + {"`u", "ù"}, // ISOlat1 + {"~A", "Ã"}, // ISOlat1 + {"~N", "Ñ"}, // ISOlat1 + {"~O", "Õ"}, // ISOlat1 + {"~a", "ã"}, // ISOlat1 + {"~n", "ñ"}, // ISOlat1 + {"~o", "õ"}, // ISOlat1 + {"vS", "Š"}, // ISOlat2 + {"vs", "š"}, // ISOlat2 + {"vZ", "Ž"}, // ISOlat2 + {"vz", "ž"}, // ISOlat2 + {",C", "Ç"}, // ISOlat1 + {",c", "ç"}, // ISOlat1 + {"/L", "Ł"}, // ISOlat2: Polish L with a slash + {"/l", "ł"}, // ISOlat2: Polish l with a slash + {"/O", "Ø"}, // ISOlat1 + {"/o", "ø"}, // ISOlat1 + {"oA", "Å"}, // ISOlat1 + {"oa", "å"}, // ISOlat1 + // Accents + {"a\"","˝"}, // ISOdia: double acute accent (Hungarian umlaut) + {"a-", "¯"}, // ISOdia: macron or bar accent + {"a.", "˙"}, // ISOdia: dot above + {"a^", "ˆ"}, // ISOdia: circumflex accent + {"aa", "´"}, // ISOdia: acute accent + {"ga", "`"}, // ISOdia: grave accent + {"ab", "˘"}, // ISOdia: breve accent + {"ac", "¸"}, // ISOdia: cedilla accent + {"ad", "¨"}, // ISOdia: umlaut or dieresis + {"ah", "ˇ"}, // ISOdia: caron (aka hacek accent) + {"ao", "˚"}, // ISOdia: ring or circle accent + {"a~", "˜"}, // ISOdia: tilde accent + {"ho", "˛"}, // ISOdia: hook or ogonek accent + {"ha", "^"}, // ASCII circumflex, hat, caret + {"ti", "~"}, // ASCII tilde, large tilde + // Quotes + {"Bq", "‚"}, // ISOpub: low double comma quote + {"bq", "„"}, // ISOpub: low single comma quote + {"lq", "“"}, // ISOnum + {"rq", "”"}, // ISOpub + {"oq", "‘"}, // ISOnum: single open quote + {"cq", "’"}, // ISOnum: single closing quote (ASCII 39) + {"aq", "&zerosp;'"}, // apostrophe quote + {"dq", "\""}, // double quote (ASCII 34) + {"Fo", "«"}, // ISOnum + {"Fc", "»"}, // ISOnum + //{"fo", "&fo;"}, + //{"fc", "&fc;"}, + // Punctuation + {"r!", "¡"}, // ISOnum + {"r?", "¿"}, // ISOnum + // Old troff \(em goes here + {"en", "–"}, // ISOpub: en dash + // Old troff \(hy goes here + // Brackets + {"lB", "["}, // ISOnum: left (square) bracket + {"rB", "]"}, // ISOnum: right (square) bracket + {"lC", "{"}, // ISOnum: left (curly) brace + {"rC", "}"}, // ISOnum: right (curly) brace + {"la", "⟨"}, // ISOtech: left angle bracket + {"ra", "⟩"}, // ISOtech: right angle bracket + // Old troff \(bv goes here + // Bracket-pile characters could go here. + // Arrows + // Old troff \(<- and \(-> go here + {"<>", "↔"}, // ISOamsa + {"da", "↓"}, // ISOnum + {"ua", "↑"}, // ISOnum + {"lA", "⇐"}, // ISOtech + {"rA", "⇒"}, // ISOtech + {"hA", "⇔"}, // ISOtech: horizontal double-headed arrow + {"dA", "⇓"}, // ISOamsa + {"uA", "⇑"}, // ISOamsa + {"vA", "⇕"}, // ISOamsa: vertical double-headed double arrow + //{"an", "&an;"}, + // Lines + {"-h", "ℏ"}, // ISOamso: h-bar (Planck's constant) + // Old troff \(or goes here + {"ba", "|"}, // ISOnum + // Old troff \(br, \{u, \(ul, \(bv go here + {"bb", "¦"}, // ISOnum + {"sl", "/"}, + {"rs", "\"}, // ISOnum + // Text markers + // Old troff \(ci, \(bu, \(dd, \(dg go here + {"lz", "◊"}, // ISOpub + // Old troff sq goes here + {"ps", "¶"}, // ISOnum: paragraph or pilcrow sign + {"sc", "§"}, // ISOnum (in old troff) + // Old troff \(lh, \{h go here + {"at", "@"}, // ISOnum + {"sh", "#"}, // ISOnum + //{"CR", "&CR;"}, + {"OK", "✓"}, // ISOpub + // Legalize + // Old troff \(co, \{g go here + {"tm", "™"}, // ISOnum + // Currency symbols + {"Do", "$"}, // ISOnum + {"ct", "¢"}, // ISOnum + {"eu", "€"}, + {"Eu", "€"}, + {"Ye", "¥"}, // ISOnum + {"Po", "£"}, // ISOnum + {"Cs", "¤"}, // ISOnum: currency sign + {"Fn", "&fnof"}, // ISOtech + // Units + // Old troff de goes here + {"%0", "‰"}, // ISOtech: per thousand, per mille sign + // Old troff \(fm goes here + {"sd", "″"}, // ISOtech + {"mc", "µ"}, // ISOnum + {"Of", "ª"}, // ISOnum + {"Om", "º"}, // ISOnum + // Logical symbols + {"AN", "∧"}, // ISOtech + {"OR", "∨"}, // ISOtech + // Old troff \(no goes here + {"te", "∃"}, // ISOtech: there exists, existential quantifier + {"fa", "∀"}, // ISOtech: for all, universal quantifier + {"st", "&bepsi"}, // ISOamsr: such that + {"3d", "∴"}, // ISOtech + {"tf", "∴"}, // ISOtech + // Mathematical symbols + // Old troff "12", "14", "34" goes here + {"S1", "¹"}, // ISOnum + {"S2", "²"}, // ISOnum + {"S3", "³"}, // ISOnum + // Old troff \(pl", \-, \(+- go here + {"t+-", "±"}, // ISOnum + {"-+", "∓"}, // ISOtech + {"pc", "·"}, // ISOnum + {"md", "·"}, // ISOnum + // Old troff \(mu goes here + {"tmu", "×"}, // ISOnum + {"c*", "⊗"}, // ISOamsb: multiply sign in a circle + {"c+", "⊕"}, // ISOamsb: plus sign in a circle + // Old troff \(di goes here + {"tdi", "÷"}, // ISOnum + {"f/", "―"}, // ISOnum: horizintal bar for fractions + // Old troff \(** goes here + {"<=", "≤"}, // ISOtech + {">=", "≥"}, // ISOtech + {"<<", "≪"}, // ISOamsr + {">>", "≫"}, // ISOamsr + {"!=", "≠"}, // ISOtech + // Old troff \(eq and \(== go here + {"=~", "≅"}, // ISOamsr + // Old troff \(ap goes here + {"~~", "≈"}, // ISOtech + // This appears to be an error in the groff table. + // It clashes with the Bell Labs use of ~= for a congruence sign + // {"~=", "≈"}, // ISOamsr + // Old troff \(pt, \(es, \(mo go here + {"nm", "∉"}, // ISOtech + {"nb", "⊄"}, // ISOamsr + {"nc", "⊅"}, // ISOamsn + {"ne", "≢"}, // ISOamsn + // Old troff \(sb, \(sp, \(ib, \(ip, \(ca, \(cu go here + {"/_", "∠"}, // ISOamso + {"pp", "⊥"}, // ISOtech + // Old troff \(is goes here + {"sum", "∑"}, // ISOamsb + {"product", "∏"}, // ISOamsb + {"gr", "∇"}, // ISOtech + // Old troff \(sr. \{n, \(if go here + {"Ah", "ℵ"}, // ISOtech + {"Im", "ℑ"}, // ISOamso: Fraktur I, imaginary + {"Re", "ℜ"}, // ISOamso: Fraktur R, real + {"wp", "℘"}, // ISOamso + {"pd", "∂"}, // ISOtech: partial differentiation sign + // Their table duplicates the Greek letters here. + // We list only the variant forms here, mapping them into + // the ISO Greek 4 variants (which may or may not be correct :-() + {"+f", "&b.phiv;"}, // ISOgrk4: variant phi + {"+h", "&b.thetas;"}, // ISOgrk4: variant theta + {"+p", "&b.omega;"}, // ISOgrk4: variant pi, looking like omega + // Card symbols + {"CL", "♣"}, // ISOpub: club suit + {"SP", "♠"}, // ISOpub: spade suit + {"HE", "♥"}, // ISOpub: heart suit + {"DI", "♦"}, // ISOpub: diamond suit +}; + +const char *special_to_entity(const char *sp) +{ + struct map *mp; + for (mp = entity_table; + mp < entity_table + sizeof(entity_table)/sizeof(entity_table[0]); + mp++) { + if (strcmp(mp->from, sp) == 0) + return mp->to; + } + return NULL; +} + +class char_box : public simple_box { + unsigned char c; + char next_is_italic; + char prev_is_italic; +public: + char_box(unsigned char); + void debug_print(); + void output(); + int is_char(); + int left_is_italic(); + int right_is_italic(); + void hint(unsigned); + void handle_char_type(int, int); +}; + +class special_char_box : public simple_box { + char *s; +public: + special_char_box(const char *); + ~special_char_box(); + void output(); + void debug_print(); + int is_char(); + void handle_char_type(int, int); +}; + +enum spacing_type { + s_ordinary, + s_operator, + s_binary, + s_relation, + s_opening, + s_closing, + s_punctuation, + s_inner, + s_suppress +}; + +const char *spacing_type_table[] = { + "ordinary", + "operator", + "binary", + "relation", + "opening", + "closing", + "punctuation", + "inner", + "suppress", + 0, +}; + +const int DIGIT_TYPE = 0; +const int LETTER_TYPE = 1; + +const char *font_type_table[] = { + "digit", + "letter", + 0, +}; + +struct char_info { + int spacing_type; + int font_type; + char_info(); +}; + +char_info::char_info() +: spacing_type(ORDINARY_TYPE), font_type(DIGIT_TYPE) +{ +} + +static char_info char_table[256]; + +declare_ptable(char_info) +implement_ptable(char_info) + +PTABLE(char_info) special_char_table; + +static int get_special_char_spacing_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->spacing_type : ORDINARY_TYPE; +} + +static int get_special_char_font_type(const char *ch) +{ + char_info *p = special_char_table.lookup(ch); + return p ? p->font_type : DIGIT_TYPE; +} + +static void set_special_char_type(const char *ch, int st, int ft) +{ + char_info *p = special_char_table.lookup(ch); + if (!p) { + p = new char_info[1]; + special_char_table.define(ch, p); + } + if (st >= 0) + p->spacing_type = st; + if (ft >= 0) + p->font_type = ft; +} + +void init_char_table() +{ + set_special_char_type("pl", s_binary, -1); + set_special_char_type("mi", s_binary, -1); + set_special_char_type("eq", s_relation, -1); + set_special_char_type("<=", s_relation, -1); + set_special_char_type(">=", s_relation, -1); + char_table['}'].spacing_type = s_closing; + char_table[')'].spacing_type = s_closing; + char_table[']'].spacing_type = s_closing; + char_table['{'].spacing_type = s_opening; + char_table['('].spacing_type = s_opening; + char_table['['].spacing_type = s_opening; + char_table[','].spacing_type = s_punctuation; + char_table[';'].spacing_type = s_punctuation; + char_table[':'].spacing_type = s_punctuation; + char_table['.'].spacing_type = s_punctuation; + char_table['>'].spacing_type = s_relation; + char_table['<'].spacing_type = s_relation; + char_table['*'].spacing_type = s_binary; + for (int i = 0; i < 256; i++) + if (csalpha(i)) + char_table[i].font_type = LETTER_TYPE; +} + +static int lookup_spacing_type(const char *type) +{ + for (int i = 0; spacing_type_table[i] != 0; i++) + if (strcmp(spacing_type_table[i], type) == 0) + return i; + return -1; +} + +static int lookup_font_type(const char *type) +{ + for (int i = 0; font_type_table[i] != 0; i++) + if (strcmp(font_type_table[i], type) == 0) + return i; + return -1; +} + +void box::set_spacing_type(char *type) +{ + int t = lookup_spacing_type(type); + if (t < 0) + error("unrecognised type '%1'", type); + else + spacing_type = t; + free(type); +} + +char_box::char_box(unsigned char cc) +: c(cc), next_is_italic(0), prev_is_italic(0) +{ + spacing_type = char_table[c].spacing_type; +} + +void char_box::hint(unsigned flags) +{ + if (flags & HINT_PREV_IS_ITALIC) + prev_is_italic = 1; + if (flags & HINT_NEXT_IS_ITALIC) + next_is_italic = 1; +} + +void char_box::output() +{ + if (output_format == troff) { + int font_type = char_table[c].font_type; + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + if (!prev_is_italic) + fputs("\\,", stdout); + if (c == '\\') + fputs("\\e", stdout); + else + putchar(c); + if (!next_is_italic) + fputs("\\/", stdout); + else + fputs("\\&", stdout); // suppress ligaturing and kerning + if (font_type != LETTER_TYPE) + fputs("\\fP", stdout); + } + else if (output_format == mathml) { + if (isdigit(c)) + printf("<mn>"); + else if (char_table[c].spacing_type) + printf("<mo>"); + else + printf("<mi>"); + if (c == '<') + printf("<"); + else if (c == '>') + printf(">"); + else if (c == '&') + printf("&"); + else + putchar(c); + if (isdigit(c)) + printf("</mn>"); + else if (char_table[c].spacing_type) + printf("</mo>"); + else + printf("</mi>"); + } +} + +int char_box::left_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::right_is_italic() +{ + int font_type = char_table[c].font_type; + return font_type == LETTER_TYPE; +} + +int char_box::is_char() +{ + return 1; +} + +void char_box::debug_print() +{ + if (c == '\\') { + putc('\\', stderr); + putc('\\', stderr); + } + else + putc(c, stderr); +} + +special_char_box::special_char_box(const char *t) +{ + s = strsave(t); + spacing_type = get_special_char_spacing_type(s); +} + +special_char_box::~special_char_box() +{ + free(s); +} + +void special_char_box::output() +{ + if (output_format == troff) { + int font_type = get_special_char_font_type(s); + if (font_type != LETTER_TYPE) + printf("\\f[%s]", current_roman_font); + printf("\\,\\[%s]\\/", s); + if (font_type != LETTER_TYPE) + printf("\\fP"); + } + else if (output_format == mathml) { + const char *entity = special_to_entity(s); + if (entity != NULL) + printf("<mo>%s</mo>", entity); + else + printf("<merror>unknown eqn/troff special char %s</merror>", s); + } +} + +int special_char_box::is_char() +{ + return 1; +} + +void special_char_box::debug_print() +{ + fprintf(stderr, "\\[%s]", s); +} + + +void char_box::handle_char_type(int st, int ft) +{ + if (st >= 0) + char_table[c].spacing_type = st; + if (ft >= 0) + char_table[c].font_type = ft; +} + +void special_char_box::handle_char_type(int st, int ft) +{ + set_special_char_type(s, st, ft); +} + +void set_char_type(const char *type, char *ch) +{ + assert(ch != 0); + int st = lookup_spacing_type(type); + int ft = lookup_font_type(type); + if (st < 0 && ft < 0) { + error("bad character type '%1'", type); + delete[] ch; + return; + } + box *b = split_text(ch); + b->handle_char_type(st, ft); + delete b; +} + +/* We give primes special treatment so that in "x' sub 2", the "2" +will be tucked under the prime */ + +class prime_box : public pointer_box { + box *pb; +public: + prime_box(box *); + ~prime_box(); + int compute_metrics(int style); + void output(); + void compute_subscript_kern(); + void debug_print(); + void handle_char_type(int, int); +}; + +box *make_prime_box(box *pp) +{ + return new prime_box(pp); +} + +prime_box::prime_box(box *pp) : pointer_box(pp) +{ + pb = new special_char_box("fm"); +} + +prime_box::~prime_box() +{ + delete pb; +} + +int prime_box::compute_metrics(int style) +{ + int res = p->compute_metrics(style); + pb->compute_metrics(style); + printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" WIDTH_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]" + ">?\\n[" HEIGHT_FORMAT "]\n", + uid, p->uid, pb->uid); + printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]" + ">?\\n[" DEPTH_FORMAT "]\n", + uid, p->uid, pb->uid); + return res; +} + +void prime_box::compute_subscript_kern() +{ + p->compute_subscript_kern(); + printf(".nr " SUB_KERN_FORMAT " 0\\n[" WIDTH_FORMAT "]" + "+\\n[" SUB_KERN_FORMAT "]>?0\n", + uid, pb->uid, p->uid); +} + +void prime_box::output() +{ + p->output(); + pb->output(); +} + +void prime_box::handle_char_type(int st, int ft) +{ + p->handle_char_type(st, ft); + pb->handle_char_type(st, ft); +} + +void prime_box::debug_print() +{ + p->debug_print(); + putc('\'', stderr); +} + +box *split_text(char *text) +{ + list_box *lb = 0; + box *fb = 0; + char *s = text; + while (*s != '\0') { + char c = *s++; + box *b = 0; + switch (c) { + case '+': + b = new special_char_box("pl"); + break; + case '-': + b = new special_char_box("mi"); + break; + case '=': + b = new special_char_box("eq"); + break; + case '\'': + b = new special_char_box("fm"); + break; + case '<': + if (*s == '=') { + b = new special_char_box("<="); + s++; + break; + } + goto normal_char; + case '>': + if (*s == '=') { + b = new special_char_box(">="); + s++; + break; + } + goto normal_char; + case '\\': + if (*s == '\0') { + lex_error("bad escape"); + break; + } + c = *s++; + switch (c) { + case '(': + { + char buf[3]; + if (*s != '\0') { + buf[0] = *s++; + if (*s != '\0') { + buf[1] = *s++; + buf[2] = '\0'; + b = new special_char_box(buf); + } + else { + lex_error("bad escape"); + } + } + else { + lex_error("bad escape"); + } + } + break; + case '[': + { + char *ch = s; + while (*s != ']' && *s != '\0') + s++; + if (*s == '\0') + lex_error("bad escape"); + else { + *s++ = '\0'; + b = new special_char_box(ch); + } + } + break; + case 'f': + case 'g': + case 'k': + case 'n': + case '*': + { + char *escape_start = s - 2; + switch (*s) { + case '(': + if (*++s != '\0') + ++s; + break; + case '[': + for (++s; *s != '\0' && *s != ']'; s++) + ; + break; + } + if (*s == '\0') + lex_error("bad escape"); + else { + ++s; + char *buf = new char[s - escape_start + 1]; + memcpy(buf, escape_start, s - escape_start); + buf[s - escape_start] = '\0'; + b = new quoted_text_box(buf); + } + } + break; + case '-': + case '_': + { + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + b = new special_char_box(buf); + } + break; + case '`': + b = new special_char_box("ga"); + break; + case '\'': + b = new special_char_box("aa"); + break; + case 'e': + case '\\': + b = new char_box('\\'); + break; + case '^': + case '|': + case '0': + { + char buf[3]; + buf[0] = '\\'; + buf[1] = c; + buf[2] = '\0'; + b = new quoted_text_box(strsave(buf)); + break; + } + default: + lex_error("unquoted escape"); + b = new quoted_text_box(strsave(s - 2)); + s = strchr(s, '\0'); + break; + } + break; + default: + normal_char: + b = new char_box(c); + break; + } + while (*s == '\'') { + if (b == 0) + b = new quoted_text_box(0); + b = new prime_box(b); + s++; + } + if (b != 0) { + if (lb != 0) + lb->append(b); + else if (fb != 0) { + lb = new list_box(fb); + lb->append(b); + } + else + fb = b; + } + } + free(text); + if (lb != 0) + return lb; + else if (fb != 0) + return fb; + else + return new quoted_text_box(0); +} + |