[TeX::] TeX Format. To provide for weaving in the standard maths and science typesetting software, TeX. @h Creation. = void TeX::create(void) { @; @; @; } @ = weave_format *wf = Formats::create_weave_format(I"TeX", I".tex"); @; @ = weave_format *wf = Formats::create_weave_format(I"DVI", I".tex"); @; METHOD_ADD(wf, POST_PROCESS_POS_MTID, TeX::post_process_DVI); METHOD_ADD(wf, POST_PROCESS_POS_MTID, TeX::post_process_report); METHOD_ADD(wf, POST_PROCESS_SUBSTITUTE_POS_MTID, TeX::post_process_substitute); METHOD_ADD(wf, PRESERVE_MATH_MODE_FOR_MTID, TeX::yes); @ = weave_format *wf = Formats::create_weave_format(I"PDF", I".tex"); METHOD_ADD(wf, PARA_MACRO_FOR_MTID, TeX::para_macro_PDF_1); @; METHOD_ADD(wf, PARA_MACRO_FOR_MTID, TeX::para_macro_PDF_2); METHOD_ADD(wf, CHANGE_COLOUR_FOR_MTID, TeX::change_colour_PDF); METHOD_ADD(wf, FIGURE_FOR_MTID, TeX::figure_PDF); METHOD_ADD(wf, POST_PROCESS_POS_MTID, TeX::post_process_PDF); METHOD_ADD(wf, POST_PROCESS_SUBSTITUTE_POS_MTID, TeX::post_process_substitute); METHOD_ADD(wf, INDEX_PDFS_POS_MTID, TeX::yes); METHOD_ADD(wf, PRESERVE_MATH_MODE_FOR_MTID, TeX::yes); @ = METHOD_ADD(wf, TOP_FOR_MTID, TeX::top); METHOD_ADD(wf, SUBHEADING_FOR_MTID, TeX::subheading); METHOD_ADD(wf, TOC_FOR_MTID, TeX::toc); METHOD_ADD(wf, CHAPTER_TP_FOR_MTID, TeX::chapter_title_page); METHOD_ADD(wf, PARAGRAPH_HEADING_FOR_MTID, TeX::paragraph_heading); METHOD_ADD(wf, SOURCE_CODE_FOR_MTID, TeX::source_code); METHOD_ADD(wf, INLINE_CODE_FOR_MTID, TeX::inline_code); METHOD_ADD(wf, DISPLAY_LINE_FOR_MTID, TeX::display_line); METHOD_ADD(wf, ITEM_FOR_MTID, TeX::item); METHOD_ADD(wf, BAR_FOR_MTID, TeX::bar); METHOD_ADD(wf, PARA_MACRO_FOR_MTID, TeX::para_macro); METHOD_ADD(wf, PAGEBREAK_FOR_MTID, TeX::pagebreak); METHOD_ADD(wf, BLANK_LINE_FOR_MTID, TeX::blank_line); METHOD_ADD(wf, AFTER_DEFINITIONS_FOR_MTID, TeX::after_definitions); METHOD_ADD(wf, CHANGE_MATERIAL_FOR_MTID, TeX::change_material); METHOD_ADD(wf, ENDNOTE_FOR_MTID, TeX::endnote); METHOD_ADD(wf, COMMENTARY_TEXT_FOR_MTID, TeX::commentary_text); METHOD_ADD(wf, LOCALE_FOR_MTID, TeX::locale); METHOD_ADD(wf, TAIL_FOR_MTID, TeX::tail); METHOD_ADD(wf, PREFORM_DOCUMENT_FOR_MTID, TeX::preform_document); METHOD_ADD(wf, POST_PROCESS_SUBSTITUTE_POS_MTID, TeX::post_process_substitute); METHOD_ADD(wf, PRESERVE_MATH_MODE_FOR_MTID, TeX::yes); @h Methods. For documentation, see "Weave Fornats". = int TeX::yes(weave_format *self) { return TRUE; } @ = void TeX::top(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *comment) { WRITE("%% %S\n", comment); @; } @ We don't use TeX's |\input| mechanism for macros because it is so prone to failures when searching directories (especially those with spaces in the names) and then locking TeX into a repeated prompt for help from |stdin| which is rather hard to escape from. Instead we paste the entire text of our macros file into the woven TeX: @ = filename *Macros = Patterns::obtain_filename(wv->pattern, I"inweb-macros.tex"); FILE *MACROS = Filenames::fopen(Macros, "r"); if (MACROS == NULL) Errors::fatal_with_file("can't open file of TeX macros", Macros); while (TRUE) { int c = fgetc(MACROS); if (c == EOF) break; PUT(c); } fclose(MACROS); @ = void TeX::subheading(weave_format *self, text_stream *OUT, weave_target *wv, int level, text_stream *comment, text_stream *head) { switch (level) { case 1: WRITE("\\par\\noindent{\\bf %S}\\mark{%S}\\medskip\n", comment, head); break; case 2: WRITE("\\smallskip\\par\\noindent{\\it %S}\\smallskip\\noindent\n", comment); if (head) Formats::text(OUT, wv, head); break; } } @ = void TeX::toc(weave_format *self, text_stream *OUT, weave_target *wv, int stage, text_stream *text1, text_stream *text2, paragraph *P) { switch (stage) { case 1: if (wv->pattern->show_abbrevs) WRITE("\\medskip\\hrule\\smallskip\\par\\noindent{\\usagefont %S.", text1); else WRITE("\\medskip\\hrule\\smallskip\\par\\noindent{\\usagefont "); break; case 2: WRITE("; "); break; case 3: WRITE("%S~%S", text1, text2); break; case 4: WRITE("}\\par\\medskip\\hrule\\bigskip\n"); break; } } @ = void TeX::chapter_title_page(weave_format *self, text_stream *OUT, weave_target *wv, chapter *C) { WRITE("%S\\medskip\n", C->md->rubric); section *S; LOOP_OVER_LINKED_LIST(S, section, C->sections) { WRITE("\\smallskip\\noindent "); if (wv->pattern->number_sections) WRITE("%d. ", S->printed_number); if (wv->pattern->show_abbrevs) WRITE("|%S|: ", S->sect_range); WRITE("{\\it %S}\\qquad\n%S", S->md->sect_title, S->sect_purpose); } } @ = text_stream *P_literal = NULL; void TeX::paragraph_heading(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *TeX_macro, section *S, paragraph *P, text_stream *heading_text, text_stream *chaptermark, text_stream *sectionmark, int weight) { if (P_literal == NULL) P_literal = Str::new_from_wide_string(L"P"); text_stream *orn = (P)?(P->ornament):P_literal; text_stream *N = (P)?(P->paragraph_number):NULL; TEMPORARY_TEXT(mark); WRITE_TO(mark, "%S%S\\quad$\\%S$%S", chaptermark, sectionmark, orn, N); TEMPORARY_TEXT(modified); Str::copy(modified, heading_text); match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, modified, L"(%c*?): (%c*)")) { Str::clear(modified); WRITE_TO(modified, "{\\sinchhigh %S}\\quad %S", mr.exp[0], mr.exp[1]); } if ((weight == 2) && ((S->md->is_a_singleton) || (wv->pattern->show_abbrevs == FALSE))) WRITE("\\%S{%S}{%S}{%S}{\\%S}{%S}%%\n", TeX_macro, N, modified, mark, orn, NULL); else WRITE("\\%S{%S}{%S}{%S}{\\%S}{%S}%%\n", TeX_macro, N, modified, mark, orn, S->sect_range); DISCARD_TEXT(mark); DISCARD_TEXT(modified); Regexp::dispose_of(&mr); } @ Code is typeset by TeX within vertical strokes; these switch a sort of typewriter-type verbatim mode on and off. To get an actual stroke, we must escape from code mode, escape it using a backslash, then re-enter code mode once again: = void TeX::source_code(weave_format *self, text_stream *OUT, weave_target *wv, int tab_stops_of_indentation, text_stream *prefatory, text_stream *matter, text_stream *colouring, text_stream *concluding_comment, int starts, int finishes, int code_mode) { if (code_mode == FALSE) WRITE("\\smallskip\\par\\noindent"); if (starts) { @; if (Str::len(prefatory) > 0) WRITE("{\\ninebf %S} ", prefatory); WRITE("|"); } int current_colour = PLAIN_COLOUR, colour_wanted = PLAIN_COLOUR; for (int i=0; i < Str::len(matter); i++) { colour_wanted = Str::get_at(colouring, i); @; if (Str::get_at(matter, i) == '|') WRITE("|\\||"); else WRITE("%c", Str::get_at(matter, i)); } colour_wanted = PLAIN_COLOUR; @; if (finishes) { WRITE("|"); if (Str::len(concluding_comment) > 0) { if ((Str::len(matter) > 0) || (!starts)) WRITE("\\hfill\\quad "); WRITE("{\\ttninepoint\\it %S}", concluding_comment); } WRITE("\n"); } } @ We actually use |\qquad| horizontal spaces rather than risk using TeX's messy alignment system: @ = for (int i=0; i = if (colour_wanted != current_colour) { Formats::change_colour(OUT, wv, colour_wanted, TRUE); current_colour = colour_wanted; } @ = void TeX::inline_code(weave_format *self, text_stream *OUT, weave_target *wv, int enter) { WRITE("|"); } @ = void TeX::change_colour_PDF(weave_format *self, text_stream *OUT, weave_target *wv, int col, int in_code) { char *inout = ""; if (in_code) inout = "|"; switch (col) { case MACRO_COLOUR: WRITE("%s\\pdfliteral direct{1 1 0 0 k}%s", inout, inout); break; case FUNCTION_COLOUR: WRITE("%s\\pdfliteral direct{0 1 1 0 k}%s", inout, inout); break; case PLAIN_COLOUR: WRITE("%s\\special{PDF:0 g}%s", inout, inout); break; case EXTRACT_COLOUR: WRITE("%s\\special{PDF:0 g}%s", inout, inout); break; } } @ = void TeX::display_line(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *text) { WRITE("\\quotesource{%S}\n", text); } @ = void TeX::item(weave_format *self, text_stream *OUT, weave_target *wv, int depth, text_stream *label) { if (Str::len(label) > 0) { if (depth == 1) WRITE("\\item{(%S)}", label); else WRITE("\\itemitem{(%S)}", label); } else { if (depth == 1) WRITE("\\item{}"); else WRITE("\\itemitem{}"); } } @ = void TeX::bar(weave_format *self, text_stream *OUT, weave_target *wv) { WRITE("\\par\\medskip\\noindent\\hrule\\medskip\\noindent\n"); } @ TeX itself has an almost defiant lack of support for anything pictorial, which is one reason it didn't live up to its hope of being the definitive basis for typography; even today the loose confederation of TeX-like programs and extensions lack standard approaches. Here we're going to use |pdftex| features, having nothing better. All we're trying for is to insert a picture, scaled to a given width, into the text at the current position. = void TeX::figure_PDF(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *figname, int w, int h) { WRITE("\\pdfximage"); if (w >= 0) WRITE(" width %d cm{../Figures/%S}\n", w, figname); else if (h >= 0) WRITE(" height %d cm{../Figures/%S}\n", h, figname); else WRITE("{../Figures/%S}\n", figname); WRITE("\\smallskip\\noindent" "\\hbox to\\hsize{\\hfill\\pdfrefximage \\pdflastximage\\hfill}" "\\smallskip\n"); } @ Any usage of angle-macros is highlighted in several cute ways: first, we make use of colour and we drop in the paragraph number of the definition of the macro in small type; and second, we use cross-reference links. In the PDF format, these three are all called, in sequence below; in TeX or DVI, only the middle one is. = void TeX::para_macro_PDF_1(weave_format *self, text_stream *OUT, weave_target *wv, para_macro *pmac, int defn) { if (defn) WRITE("|\\pdfdest num %d fit ", pmac->allocation_id + 100); else WRITE("|\\pdfstartlink attr{/C [0.9 0 0] /Border [0 0 0]} goto num %d ", pmac->allocation_id + 100); } void TeX::para_macro(weave_format *self, text_stream *OUT, weave_target *wv, para_macro *pmac, int defn) { WRITE("$\\langle${\\xreffont"); Formats::change_colour(OUT, wv, MACRO_COLOUR, FALSE); WRITE("%S ", pmac->macro_name); WRITE("{\\sevenss %S}}", pmac->defining_paragraph->paragraph_number); Formats::change_colour(OUT, wv, PLAIN_COLOUR, FALSE); WRITE("$\\rangle$ "); } void TeX::para_macro_PDF_2(weave_format *self, text_stream *OUT, weave_target *wv, para_macro *pmac, int defn) { if (defn) WRITE("$\\equiv$|"); else WRITE("\\pdfendlink|"); } @ = void TeX::pagebreak(weave_format *self, text_stream *OUT, weave_target *wv) { WRITE("\\vfill\\eject\n"); } @ = void TeX::blank_line(weave_format *self, text_stream *OUT, weave_target *wv, int in_comment) { if (in_comment) WRITE("\\smallskip\\par\\noindent%%\n"); else WRITE("\\smallskip\n"); } @ = void TeX::after_definitions(weave_format *self, text_stream *OUT, weave_target *wv) { WRITE("\\smallskip\n"); } @ = void TeX::endnote(weave_format *self, text_stream *OUT, weave_target *wv, int end) { if (end == 1) { WRITE("\\par\\noindent\\penalty10000\n"); WRITE("{\\usagefont "); } else { WRITE("}\\smallskip\n"); } } @ = void TeX::commentary_text(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *id) { int math_mode = FALSE; for (int i=0; i < Str::len(id); i++) { switch (Str::get_at(id, i)) { case '$': math_mode = (math_mode)?FALSE:TRUE; WRITE("%c", Str::get_at(id, i)); break; case '_': if (math_mode) WRITE("_"); else WRITE("\\_"); break; case '"': if ((Str::get_at(id, i) == '"') && ((i==0) || (Str::get_at(id, i-1) == ' ') || (Str::get_at(id, i-1) == '('))) WRITE("``"); else WRITE("''"); break; default: WRITE("%c", Str::get_at(id, i)); break; } } } @ = void TeX::locale(weave_format *self, text_stream *OUT, weave_target *wv, paragraph *par1, paragraph *par2) { WRITE("$\\%S$%S", par1->ornament, par1->paragraph_number); if (par2) WRITE("-%S", par2->paragraph_number); } @ = void TeX::change_material(weave_format *self, text_stream *OUT, weave_target *wv, int old_material, int new_material, int content) { if (old_material != new_material) { switch (old_material) { case REGULAR_MATERIAL: switch (new_material) { case CODE_MATERIAL: WRITE("\\beginlines\n"); break; case DEFINITION_MATERIAL: WRITE("\\beginlines\n"); break; case MACRO_MATERIAL: WRITE("\\beginlines\n"); break; } break; default: if (new_material == REGULAR_MATERIAL) WRITE("\\endlines\n"); break; } } } @ = void TeX::tail(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *comment, section *S) { WRITE("%% %S\n", comment); WRITE("\\end\n"); } @ The following is called only when the language is InC, and the weave is of the special Preform grammar document. = int TeX::preform_document(weave_format *self, text_stream *OUT, web *W, weave_target *wv, chapter *C, section *S, source_line *L, text_stream *matter, text_stream *concluding_comment) { if (L->preform_nonterminal_defined) { preform_production_count = 0; @; return TRUE; } else { if (L->category == PREFORM_GRAMMAR_LCAT) { @; return TRUE; } } return FALSE; } @ = WRITE("\\nonterminal{%S} |::=|", L->preform_nonterminal_defined->unangled_name); if (L->preform_nonterminal_defined->as_function) { WRITE("\\quad{\\it internal definition"); if (L->preform_nonterminal_defined->voracious) WRITE(" (voracious)"); else if (L->preform_nonterminal_defined->min_word_count == L->preform_nonterminal_defined->min_word_count) WRITE(" (%d word%s)", L->preform_nonterminal_defined->min_word_count, (L->preform_nonterminal_defined->min_word_count != 1)?"s":""); WRITE("}"); } WRITE("\n"); @ = TEMPORARY_TEXT(problem); match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, matter, L"Issue (%c*?) problem")) Str::copy(problem, mr.exp[0]); else if (Regexp::match(&mr, matter, L"FAIL_NONTERMINAL %+")) WRITE_TO(problem, "fail and skip"); else if (Regexp::match(&mr, matter, L"FAIL_NONTERMINAL")) WRITE_TO(problem, "fail"); preform_production_count++; WRITE_TO(matter, "|%S|", L->text_operand); while (Regexp::match(&mr, matter, L"(%c+?)|(%c+)")) { Str::clear(matter); WRITE_TO(matter, "%S___stroke___%S", mr.exp[0], mr.exp[1]); } while (Regexp::match(&mr, matter, L"(%c*?)___stroke___(%c*)")) { Str::clear(matter); WRITE_TO(matter, "%S|\\||%S", mr.exp[0], mr.exp[1]); } while (Regexp::match(&mr, matter, L"(%c*)<(%c*?)>(%c*)")) { Str::clear(matter); WRITE_TO(matter, "%S|\\nonterminal{%S}|%S", mr.exp[0], mr.exp[1], mr.exp[2]); } TEMPORARY_TEXT(label); int N = preform_production_count; int L = ((N-1)%26) + 1; if (N <= 26) WRITE_TO(label, "%c", 'a'+L-1); else if (N <= 52) WRITE_TO(label, "%c%c", 'a'+L-1, 'a'+L-1); else if (N <= 78) WRITE_TO(label, "%c%c%c", 'a'+L-1, 'a'+L-1, 'a'+L-1); else { int n = (N-1)/26; WRITE_TO(label, "%c${}^{%d}$", 'a'+L-1, n); } WRITE("\\qquad {\\hbox to 0.4in{\\it %S\\hfil}}%S", label, matter); if (Str::len(problem) > 0) WRITE("\\hfill$\\longrightarrow$ {\\ttninepoint\\it %S}", problem); else if (Str::len(concluding_comment) > 0) { WRITE(" \\hfill{\\ttninepoint\\it "); if (concluding_comment) Formats::text(OUT, wv, concluding_comment); WRITE("}"); } WRITE("\n"); DISCARD_TEXT(label); DISCARD_TEXT(problem); Regexp::dispose_of(&mr); @h Post-processing. = void TeX::post_process_PDF(weave_format *self, weave_target *wv, int open) { RunningTeX::post_process_weave(wv, open, FALSE); } void TeX::post_process_DVI(weave_format *self, weave_target *wv, int open) { RunningTeX::post_process_weave(wv, open, TRUE); } @ = void TeX::post_process_report(weave_format *self, weave_target *wv) { RunningTeX::report_on_post_processing(wv); } @ = int TeX::post_process_substitute(weave_format *self, text_stream *OUT, weave_target *wv, text_stream *detail, weave_pattern *pattern) { return RunningTeX::substitute_post_processing_data(OUT, wv, detail); } @h Removing math mode. "Math mode", in TeX jargon, is what happens when a mathematical formula is written inside dollar signs: in |Answer is $x+y^2$|, the math mode content is |x+y^2|. But since math mode doesn't (easily) exist in HTML, for example, we want to strip it out if the format is not TeX-related. To do this, the weaver calls the following. = void TeX::remove_math_mode(OUTPUT_STREAM, text_stream *text) { TEMPORARY_TEXT(math_matter); TeX::remove_math_mode_range(math_matter, text, 0, Str::len(text)-1); WRITE("%S", math_matter); DISCARD_TEXT(math_matter); } void TeX::remove_math_mode_range(OUTPUT_STREAM, text_stream *text, int from, int to) { for (int i=from; i <= to; i++) { @; } for (int i=from; i <= to; i++) { @; @; } int math_mode = FALSE; for (int i=from; i <= to; i++) { switch (Str::get_at(text, i)) { case '$': if (Str::get_at(text, i+1) == '$') i++; math_mode = (math_mode)?FALSE:TRUE; break; case '~': if (math_mode) WRITE(" "); else WRITE("~"); break; case '\\': @; break; default: PUT(Str::get_at(text, i)); break; } } } @ Here we remove |{{top}\over{bottom}}|, converting it to |((top) / (bottom))|. @ = if ((Str::get_at(text, i) == '\\') && (Str::get_at(text, i+1) == 'o') && (Str::get_at(text, i+2) == 'v') && (Str::get_at(text, i+3) == 'e') && (Str::get_at(text, i+4) == 'r') && (Str::get_at(text, i+5) == '{')) { int bl = 1; int j = i-1; for (; j >= from; j--) { wchar_t c = Str::get_at(text, j); if (c == '{') { bl--; if (bl == 0) break; } if (c == '}') bl++; } TeX::remove_math_mode_range(OUT, text, from, j-1); WRITE("(("); TeX::remove_math_mode_range(OUT, text, j+2, i-2); WRITE(") / ("); j=i+6; bl = 1; for (; j <= to; j++) { wchar_t c = Str::get_at(text, j); if (c == '}') { bl--; if (bl == 0) break; } if (c == '{') bl++; } TeX::remove_math_mode_range(OUT, text, i+6, j-1); WRITE("))"); TeX::remove_math_mode_range(OUT, text, j+2, to); return; } @ Here we remove |{\rm text}|, converting it to |text|, and similarly |\it|. @ = if ((Str::get_at(text, i) == '{') && (Str::get_at(text, i+1) == '\\') && (((Str::get_at(text, i+2) == 'r') && (Str::get_at(text, i+3) == 'm')) || ((Str::get_at(text, i+2) == 'i') && (Str::get_at(text, i+3) == 't'))) && (Str::get_at(text, i+4) == ' ')) { TeX::remove_math_mode_range(OUT, text, from, i-1); int j=i+5; for (; j <= to; j++) if (Str::get_at(text, j) == '}') break; TeX::remove_math_mode_range(OUT, text, i+5, j-1); TeX::remove_math_mode_range(OUT, text, j+1, to); return; } @ Here we remove |\sqrt{N}|, converting it to |sqrt(N)|. As a special case, we also look out for |{}^3\sqrt{N}| for cube root. @ = if ((Str::get_at(text, i) == '\\') && (Str::get_at(text, i+1) == 's') && (Str::get_at(text, i+2) == 'q') && (Str::get_at(text, i+3) == 'r') && (Str::get_at(text, i+4) == 't') && (Str::get_at(text, i+5) == '{')) { if ((Str::get_at(text, i-4) == '{') && (Str::get_at(text, i-3) == '}') && (Str::get_at(text, i-2) == '^') && (Str::get_at(text, i-1) == '3')) { TeX::remove_math_mode_range(OUT, text, from, i-5); WRITE(" curt("); } else { TeX::remove_math_mode_range(OUT, text, from, i-1); WRITE(" sqrt("); } int j=i+6, bl = 1; for (; j <= to; j++) { wchar_t c = Str::get_at(text, j); if (c == '}') { bl--; if (bl == 0) break; } if (c == '{') bl++; } TeX::remove_math_mode_range(OUT, text, i+6, j-1); WRITE(")"); TeX::remove_math_mode_range(OUT, text, j+1, to); return; } @ = TEMPORARY_TEXT(macro); i++; while ((i < Str::len(text)) && (Characters::isalpha(Str::get_at(text, i)))) PUT_TO(macro, Str::get_at(text, i++)); if (Str::eq(macro, I"not")) @ else @; DISCARD_TEXT(macro); i--; @ = if (Str::eq(macro, I"leq")) WRITE("<="); else if (Str::eq(macro, I"geq")) WRITE(">="); else if (Str::eq(macro, I"sim")) WRITE("~"); else if (Str::eq(macro, I"hbox")) WRITE(""); else if (Str::eq(macro, I"left")) WRITE(""); else if (Str::eq(macro, I"right")) WRITE(""); else if (Str::eq(macro, I"Rightarrow")) WRITE("=>"); else if (Str::eq(macro, I"Leftrightarrow")) WRITE("<=>"); else if (Str::eq(macro, I"to")) WRITE("-->"); else if (Str::eq(macro, I"rightarrow")) WRITE("-->"); else if (Str::eq(macro, I"longrightarrow")) WRITE("-->"); else if (Str::eq(macro, I"leftarrow")) WRITE("<--"); else if (Str::eq(macro, I"longleftarrow")) WRITE("<--"); else if (Str::eq(macro, I"lbrace")) WRITE("{"); else if (Str::eq(macro, I"mid")) WRITE("|"); else if (Str::eq(macro, I"rbrace")) WRITE("}"); else if (Str::eq(macro, I"cdot")) WRITE("."); else if (Str::eq(macro, I"cdots")) WRITE("..."); else if (Str::eq(macro, I"dots")) WRITE("..."); else if (Str::eq(macro, I"times")) WRITE("*"); else if (Str::eq(macro, I"quad")) WRITE(" "); else if (Str::eq(macro, I"qquad")) WRITE(" "); else if (Str::eq(macro, I"TeX")) WRITE("TeX"); else if (Str::eq(macro, I"neq")) WRITE("!="); else if (Str::eq(macro, I"noteq")) WRITE("!="); else if (Str::eq(macro, I"ell")) WRITE("l"); else if (Str::eq(macro, I"log")) WRITE("log"); else if (Str::eq(macro, I"exp")) WRITE("exp"); else if (Str::eq(macro, I"sin")) WRITE("sin"); else if (Str::eq(macro, I"cos")) WRITE("cos"); else if (Str::eq(macro, I"tan")) WRITE("tan"); else if (Str::eq(macro, I"top")) WRITE("T"); else if (Str::eq(macro, I"Alpha")) PUT((wchar_t) 0x0391); else if (Str::eq(macro, I"Beta")) PUT((wchar_t) 0x0392); else if (Str::eq(macro, I"Gamma")) PUT((wchar_t) 0x0393); else if (Str::eq(macro, I"Delta")) PUT((wchar_t) 0x0394); else if (Str::eq(macro, I"Epsilon")) PUT((wchar_t) 0x0395); else if (Str::eq(macro, I"Zeta")) PUT((wchar_t) 0x0396); else if (Str::eq(macro, I"Eta")) PUT((wchar_t) 0x0397); else if (Str::eq(macro, I"Theta")) PUT((wchar_t) 0x0398); else if (Str::eq(macro, I"Iota")) PUT((wchar_t) 0x0399); else if (Str::eq(macro, I"Kappa")) PUT((wchar_t) 0x039A); else if (Str::eq(macro, I"Lambda")) PUT((wchar_t) 0x039B); else if (Str::eq(macro, I"Mu")) PUT((wchar_t) 0x039C); else if (Str::eq(macro, I"Nu")) PUT((wchar_t) 0x039D); else if (Str::eq(macro, I"Xi")) PUT((wchar_t) 0x039E); else if (Str::eq(macro, I"Omicron")) PUT((wchar_t) 0x039F); else if (Str::eq(macro, I"Pi")) PUT((wchar_t) 0x03A0); else if (Str::eq(macro, I"Rho")) PUT((wchar_t) 0x03A1); else if (Str::eq(macro, I"Varsigma")) PUT((wchar_t) 0x03A2); else if (Str::eq(macro, I"Sigma")) PUT((wchar_t) 0x03A3); else if (Str::eq(macro, I"Tau")) PUT((wchar_t) 0x03A4); else if (Str::eq(macro, I"Upsilon")) PUT((wchar_t) 0x03A5); else if (Str::eq(macro, I"Phi")) PUT((wchar_t) 0x03A6); else if (Str::eq(macro, I"Chi")) PUT((wchar_t) 0x03A7); else if (Str::eq(macro, I"Psi")) PUT((wchar_t) 0x03A8); else if (Str::eq(macro, I"Omega")) PUT((wchar_t) 0x03A9); else if (Str::eq(macro, I"alpha")) PUT((wchar_t) 0x03B1); else if (Str::eq(macro, I"beta")) PUT((wchar_t) 0x03B2); else if (Str::eq(macro, I"gamma")) PUT((wchar_t) 0x03B3); else if (Str::eq(macro, I"delta")) PUT((wchar_t) 0x03B4); else if (Str::eq(macro, I"epsilon")) PUT((wchar_t) 0x03B5); else if (Str::eq(macro, I"zeta")) PUT((wchar_t) 0x03B6); else if (Str::eq(macro, I"eta")) PUT((wchar_t) 0x03B7); else if (Str::eq(macro, I"theta")) PUT((wchar_t) 0x03B8); else if (Str::eq(macro, I"iota")) PUT((wchar_t) 0x03B9); else if (Str::eq(macro, I"kappa")) PUT((wchar_t) 0x03BA); else if (Str::eq(macro, I"lambda")) PUT((wchar_t) 0x03BB); else if (Str::eq(macro, I"mu")) PUT((wchar_t) 0x03BC); else if (Str::eq(macro, I"nu")) PUT((wchar_t) 0x03BD); else if (Str::eq(macro, I"xi")) PUT((wchar_t) 0x03BE); else if (Str::eq(macro, I"omicron")) PUT((wchar_t) 0x03BF); else if (Str::eq(macro, I"pi")) PUT((wchar_t) 0x03C0); else if (Str::eq(macro, I"rho")) PUT((wchar_t) 0x03C1); else if (Str::eq(macro, I"varsigma")) PUT((wchar_t) 0x03C2); else if (Str::eq(macro, I"sigma")) PUT((wchar_t) 0x03C3); else if (Str::eq(macro, I"tau")) PUT((wchar_t) 0x03C4); else if (Str::eq(macro, I"upsilon")) PUT((wchar_t) 0x03C5); else if (Str::eq(macro, I"phi")) PUT((wchar_t) 0x03C6); else if (Str::eq(macro, I"chi")) PUT((wchar_t) 0x03C7); else if (Str::eq(macro, I"psi")) PUT((wchar_t) 0x03C8); else if (Str::eq(macro, I"omega")) PUT((wchar_t) 0x03C9); else if (Str::eq(macro, I"exists")) PUT((wchar_t) 0x2203); else if (Str::eq(macro, I"in")) PUT((wchar_t) 0x2208); else if (Str::eq(macro, I"forall")) PUT((wchar_t) 0x2200); else if (Str::eq(macro, I"cap")) PUT((wchar_t) 0x2229); else if (Str::eq(macro, I"emptyset")) PUT((wchar_t) 0x2205); else if (Str::eq(macro, I"subseteq")) PUT((wchar_t) 0x2286); else if (Str::eq(macro, I"land")) PUT((wchar_t) 0x2227); else if (Str::eq(macro, I"lor")) PUT((wchar_t) 0x2228); else if (Str::eq(macro, I"lnot")) PUT((wchar_t) 0x00AC); else if (Str::eq(macro, I"sum")) PUT((wchar_t) 0x03A3); else if (Str::eq(macro, I"prod")) PUT((wchar_t) 0x03A0); else { if (Str::len(macro) > 0) PRINT("Passing through unknown TeX macro \\%S: %S", macro, text); WRITE("\\%S", macro); } @ For Inform's purposes, we need to deal with just |\not\exists| and |\not\forall|. @ = if (Str::get_at(text, i) == '\\') { Str::clear(macro); i++; while ((i < Str::len(text)) && (Characters::isalpha(Str::get_at(text, i)))) PUT_TO(macro, Str::get_at(text, i++)); if (Str::eq(macro, I"exists")) PUT((wchar_t) 0x2204); else if (Str::eq(macro, I"forall")) { PUT((wchar_t) 0x00AC); PUT((wchar_t) 0x2200); } else { PRINT("Don't know how to apply '\\not' to '\\%S'\n", macro); } } else { PRINT("Don't know how to apply '\\not' here\n"); }