Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
Project: cocalc-sagemath-dev-slelievre
Views: 418346############################################################################# ## #W GAPDoc2Text.gi GAPDoc Frank Lübeck ## ## #Y Copyright (C) 2000, Frank Lübeck, Lehrstuhl D für Mathematik, #Y RWTH Aachen ## ## The files GAPDoc2Text.g{d,i} contain a conversion program which ## produces from a GAPDoc XML-document a text version for viewing the ## document on screen (GAP online help). ## ## ## We add a link to the document root to all recursively called functions ## by adding .root entries. The toc-, index- and bib-information is ## collected in the root. ## ## The set of elements is partitioned into two subsets - those which ## contain whole paragraphs and those which don't. ## ## The handler of a paragraph containing element (see ## GAPDoc2TextProcs.ParEls below) gets a list as argument to which it adds ## entries pairwise: the first of such a pair is the paragraph counter ## (like [3,2,1,5] meaning Chap.3, Sec.2, Subsec.1, Par.5) and the second ## is the formatted text of this paragraph. ## ## Some handlers of paragraph containing elements do the formatting ## themselves (e.g., .List), the others are handled in the main recursion ## function 'GAPDoc2TextContent'. ## ## We produce a full version of the document in text format, including ## title page, abstract and other front matter, table of contents, ## bibliography (via BibTeX-data files) and index. Highlighting with ## colors for ANSI color sequence capable terminals is included. ## ## In this text converter we also produce the manual.six data. For ## getting page numbers in .dvi and .pdf versions, the LaTeX-converter ## and LaTeX must be run first. This produces a .pnr file. ## InstallValue(GAPDoc2TextProcs, rec()); ## Some text attributes for display on ANSI terminals ## We do all the formatting with not used escape sequences of form ## <ESC>[<num>X. These are substituted by user configurable sequences ## before an actual display. ## ## the actual themes are in the file TextThemes.g ## ## <#GAPDoc Label="SetGAPDocTextTheme"> ## <ManSection > ## <Func Arg="[optrec1[, optrec2] ...]" Name="SetGAPDocTextTheme" /> ## <Returns>nothing</Returns> ## <Description> ## This utility function is for readers of the screen version of &GAP; ## manuals which are generated by the &GAPDoc; package. It allows to ## configure the color and attribute layout of the displayed text. ## There is a default which can be reset by calling this function ## without argument. <P/> ## ## As an abbreviation the arguments <A>optrec1</A> and so on can be ## strings for the known name of a theme. Information about valid ## names is shown with <C>SetGAPDocTextTheme("");</C>. <P/> ## ## Otherwise, <A>optrec1</A> and so on must be a record. Its entries ## overwrite the corresponding entries in the default and in previous ## arguments. To construct valid markup you ## can use <Ref Var="TextAttr"/>. Entries must be either pairs of ## strings, which are put before and after the corresponding text, or ## as an abbreviation it can be a single string. In the latter case, ## the second string is implied; if the string contains an escape ## sequence the second string is <C>TextAttr.reset</C>, otherwise the ## given string is used. The following components are recognized: ## ## <List> ## <Mark><C>flush</C></Mark><Item><C>"both"</C> for left-right justified ## paragraphs, and <C>"left"</C> for ragged right ones</Item> ## <Mark><C>Heading</C></Mark><Item>chapter and (sub-)section headings </Item> ## <Mark><C>Func</C></Mark><Item>function, operation, ... names </Item> ## <Mark><C>Arg</C></Mark><Item>argument names in descriptions</Item> ## <Mark><C>Example</C></Mark><Item>example code</Item> ## <Mark><C>Package</C></Mark><Item>package names</Item> ## <Mark><C>Returns</C></Mark><Item>Returns-line in descriptions</Item> ## <Mark><C>URL</C></Mark><Item>URLs</Item> ## <Mark><C>Mark</C></Mark><Item>Marks in description lists</Item> ## <Mark><C>K</C></Mark><Item>&GAP; keywords</Item> ## <Mark><C>C</C></Mark><Item>code or text to type</Item> ## <Mark><C>F</C></Mark><Item>file names</Item> ## <Mark><C>B</C></Mark><Item>buttons</Item> ## <Mark><C>M</C></Mark><Item>simplified math elements</Item> ## <Mark><C>Math</C></Mark><Item>normal math elements</Item> ## <Mark><C>Display</C></Mark><Item>displayed math elements</Item> ## <Mark><C>Emph</C></Mark><Item>emphasized text</Item> ## <Mark><C>Q</C></Mark><Item>quoted text</Item> ## <Mark><C>Ref</C></Mark><Item>reference text</Item> ## <Mark><C>Prompt</C></Mark><Item>&GAP; prompt in examples</Item> ## <Mark><C>BrkPrompt</C></Mark><Item>&GAP; break prompt in examples</Item> ## <Mark><C>GAPInput</C></Mark><Item>&GAP; input in examples</Item> ## <Mark><C>reset</C></Mark><Item>reset to default, don't change this </Item> ## <Mark><C>BibAuthor</C></Mark><Item>author names in bibliography</Item> ## <Mark><C>BibTitle</C></Mark><Item>titles in bibliography</Item> ## <Mark><C>BibJournal</C></Mark><Item>journal names in bibliography</Item> ## <Mark><C>BibVolume</C></Mark><Item>volume number in bibliography</Item> ## <Mark><C>BibLabel</C></Mark><Item>labels for bibliography entries</Item> ## <Mark><C>BibReset</C></Mark><Item>reset for bibliography, ## don't change</Item> ## <Mark><C>ListBullet</C></Mark><Item>bullet for simple lists (2 ## visible characters long)</Item> ## <Mark><C>EnumMarks</C></Mark><Item>one visible character before and ## after the number in enumerated lists</Item> ## <Mark><C>DefLineMarker</C></Mark><Item>marker before function and variable ## definitions (2 visible characters long)</Item> ## <Mark><C>FillString</C></Mark><Item>for filling in definitions and ## example separator lines</Item> ## </List> ## ## <Example> ## gap> # use no colors for GAP examples and ## gap> # change display of headings to bold green ## gap> SetGAPDocTextTheme("noColorPrompt", ## > rec(Heading:=Concatenation(TextAttr.bold, TextAttr.2))); ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc> ## # used fields GAPDoc2TextProcs.TextAttrFields := [ "reset", "Heading", "Func", "Arg", "Example", "Package", "Returns", "URL", "Mark", "K", "C", "F", "B", "Emph", "Ref", "BibReset", "BibAuthor", "BibTitle", "BibJournal", "BibVolume", "BibLabel", "Q", "M", "Math", "Display", "Prompt", "BrkPrompt", "GAPInput", "GAPOutput", "DefLineMarker", "ListBullet", "EnumMarks", "FillString", "format", "flush" ]; ## We produce a text document with markup that is ignored by terminals, ## this can be substituted on the fly before display according to the ## current GAPDocTextTheme. GAPDoc2TextProcs.TextAttr := rec(); GAPDoc2TextProcs.TextAttr.f := function() local i, l, fs; fs := GAPDoc2TextProcs.TextAttrFields; l := Length(fs); # begin and end markers ESC[(i)X and ESC[(i+100)X for i in [1..l] do GAPDoc2TextProcs.TextAttr.(fs[i]) := [Concatenation(TextAttr.CSI, String(i-1), "X"), Concatenation(TextAttr.CSI, String(100+i-1), "X")]; od; end; GAPDoc2TextProcs.TextAttr.f(); Unbind(GAPDoc2TextProcs.TextAttr.f); GAPDoc2TextProcs.ParEls := [ "Display", "Example", "Log", "Listing", "List", "Enum", "Item", "Table", "TitlePage", "Address", "TitleComment", "Abstract", "Copyright", "Acknowledgements", "Colophon", "TableOfContents", "Bibliography", "TheIndex", "Subsection", "ManSection", "Description", "Returns", "Section", "Chapter", "Appendix", "Body", "Book", "WHOLEDOCUMENT", "Attr", "Fam", "Filt", "Func", "Heading", "InfoClass", "Meth", "Oper", "Constr", "Prop", "Var", "Verb" ]; ## arg: a list of strings ## nothing for now, may be enhanced and documented later. SetGapDocTxtOptions := function(arg) local gdp; gdp := GAPDoc2TextProcs; return; end; ## here we collect paragraphs to whole chapters and remember line numbers ## of subsections for the .six information GAPDoc2TextProcs.PutFilesTogether := function(l, six) local countandshift, concat, files, n, i, p, a, tmp; # count number of lines in txt and add 2 spaces in the beginning of # each line, returns [newtxt, nrlines] countandshift := function(txt) local new, ind, n, p, pos; # sometimes there may occur paragraph elements inside text elements # (like list in list item) - we concatenate the text here. concat := function(txt) local new, a; if not IsString(txt) then new := ""; for a in txt do if IsChar(a) then Add(new, a); elif IsList(a) then Append(new, concat(a)); fi; od; else new := txt; fi; ConvertToStringRep(new); return new; end; txt := concat(txt); new := " "; ind := " "; n := 0; p := 0; pos := Position(txt, '\n'); while pos <> fail do Append(new, txt{[p+1..pos]}); if pos < Length(txt) then Append(new, ind); fi; n := n+1; p := pos; pos := Position(txt, '\n', p); od; if p < Length(txt) then Append(new, txt{[p+1..Length(txt)]}); fi; return [new, n]; end; # putting the paragraphs together (one string (file) for each chapter) files := rec(); for n in Set(List([2,4..Length(l)], i-> l[i-1][1])) do files.(n) := rec(text := "", ssnr := [], linenr := [], len := 0); od; for i in [2,4..Length(l)] do n := files.(l[i-1][1]); p := countandshift(l[i]); if Length(n.ssnr)=0 or l[i-1]{[1..3]} <> n.ssnr[Length(n.ssnr)] then Add(n.ssnr, l[i-1]{[1..3]}); Add(n.linenr, n.len+1); fi; Append(n.text, p[1]); n.len := n.len + p[2]; od; # - add line numbers to six information # - add simplified strings for searching # - add hash strings of the latter for links in HTML and PDF version # (such that links remain stable as long as the (sub)section exists # and stays in the same chapter) Info(InfoGAPDoc, 1, "#I Producing simplified search strings and labels ", "for hyperlinks . . .\n"); tmp := ShallowCopy(six); SortParallel(List([1..Length(tmp)], i-> [tmp[i][3],i]), tmp); for i in [1..Length(six)] do a := tmp[i]; p := Position(files.(a[3][1]).ssnr, a[3]); if p = fail then Error("don't find subsection ", a[3], " in text documention"); fi; Add(a, files.(a[3][1]).linenr[p]); a[6] := SIMPLE_STRING(StripEscapeSequences(a[1])); NormalizeWhitespace(a[6]); # the 'X' is to start with a proper letter, since this will be used # for ID type attributes; we use the same label for all entries with # the same subsection number if i > 1 and a[3] = tmp[i-1][3] then a[7] := tmp[i-1][7]; else a[7] := Concatenation("X", HexStringInt(CrcText(a[6])+2^31), HexStringInt(CrcText(Reversed(a[6]))+2^31)); fi; od; return files; end; ## <#GAPDoc Label="GAPDoc2Text"> ## <ManSection > ## <Func Arg="tree[, bibpath][, width]" Name="GAPDoc2Text" /> ## <Returns>record containing text files as strings and other ## information</Returns> ## <Description> ## The argument <A>tree</A> for this function is a tree ## describing a &GAPDoc; XML document as returned by <Ref ## Func="ParseTreeXMLString" /> (probably also checked with <Ref ## Func="CheckAndCleanGapDocTree" />). This function produces a text ## version of the document which can be used with &GAP;'s online ## help (with the <C>"screen"</C> viewer, see <Ref BookName="Ref" ## Func="SetHelpViewer" />). It includes title page, bibliography ## and index. The bibliography is made from BibXMLext or &BibTeX; ## databases, see <Ref Chap="ch:bibutil"/>. ## Their location must be given with the argument <A>bibpath</A> (as ## string or directory object).<P/> ## ## The output is a record with one component for each chapter ## (with names <C>"0"</C>, <C>"1"</C>, ..., <C>"Bib"</C> and ## <C>"Ind"</C>). Each such component is again a record with ## the following components: ## ## <List > ## <Mark><C>text</C></Mark> ## <Item>the text of the whole chapter as a string</Item> ## <Mark><C>ssnr</C></Mark> ## <Item>list of subsection numbers in this chapter (like <C>[3, 2, ## 1]</C> for chapter 3, section 2, subsection 1) ## </Item> ## <Mark><C>linenr</C></Mark> ## <Item>corresponding list of line numbers where the subsections ## start</Item> ## <Mark><C>len</C></Mark> ## <Item>number of lines of this chapter</Item> ## </List> ## ## The result can be written into files with the command <Ref ## Func="GAPDoc2TextPrintTextFiles" />.<P/> ## ## As a side effect this function also produces the ## <F>manual.six</F> information which is used for searching in ## &GAP;'s online help. This is stored in <C><A>tree</A>.six</C> ## and can be printed into a <F>manual.six</F> file with ## <Ref Func="PrintSixFile" /> (preferably after producing a ## &LaTeX; version of the document as well and adding the ## page number information to <C><A>tree</A>.six</C>, see <Ref ## Func="GAPDoc2LaTeX" /> and <Ref Func="AddPageNumbersToSix" ## />).<P/> ## ## The text produced by this function contains some markup via ## ANSI escape sequences. The sequences used here are usually ## ignored by terminals. But the &GAP; help system will substitute ## them by interpreted color and attribute sequences (see <Ref ## Var="TextAttr"/>) before displaying them. There is a default ## markup used for this but it can also be configured by the ## user, see <Ref Func="SetGAPDocTextTheme"/>. Furthermore, the text ## produced is in UTF-8 encoding. The encoding is also translated on ## the fly, if <C>GAPInfo.TermEncoding</C> is set to some encoding ## supported by <Ref Func="Encode"/>, e.g., <C>"ISO-8859-1"</C> or ## <C>"latin1"</C>.<P/> ## ## With the optional argument <A>width</A> a different length of the ## output text lines can be chosen. The default is 76 and all lines ## in the resulting text start with two spaces. This looks good on a ## terminal with a standard width of 80 characters and you probably ## don't want to use this argument. ## </Description> ## </ManSection> ## <#/GAPDoc> ## # the basic call, used recursively with a result r from GetElement # and a string str or list l to which the output should be appended # arg: r[, linelength] (then a list is returned, only for whole document) # or: r, str[, linelength] (then the output is appended to string or # list str) InstallGlobalFunction(GAPDoc2Text, function(arg) local r, str, linelength, name; r := arg[1]; if Length(arg)=2 and (IsList(arg[2]) or (r.name = "WHOLEDOCUMENT" and IsDirectory(arg[2]))) then str := arg[2]; linelength := 76; elif Length(arg)=2 and IsInt(arg[2]) then linelength := arg[2]; str := ""; elif Length(arg)=3 then str := arg[2]; linelength := arg[3]; else str := ""; linelength := 76; fi; if r.name = "WHOLEDOCUMENT" then r.linelength := linelength; r.indent := ""; if IsDirectory(str) then r.bibpath := str; else if Length(str) = 0 then str := "."; fi; r.bibpath := Directory(str); fi; str := ""; fi; name := r.name; if not IsBound(GAPDoc2TextProcs.(name)) then Info(InfoGAPDoc, 1, "#W WARNING: Don't know how to process element ", name, " ---- ignored\n"); else GAPDoc2TextProcs.(r.name)(r, str); fi; if r.name = "WHOLEDOCUMENT" then # generate sorted list of counts from .six entries, makes LaTeX and HTML # converter much faster on large documents r.sixcount := List(r.six, a-> a[3]); r.sixindex := [1..Length(r.six)]; SortParallel(r.sixcount, r.sixindex); # put final record together and return it, also add line numbers to # .six entries return GAPDoc2TextProcs.PutFilesTogether(str, r.six); fi; return str; end); ## to format paragraph which can be reformatted before display GAPDoc2TextProcs.MarkAndFormat := function(str, flush, indlen, len) local f, fnr, res, par; # only handle non-empty strings if flush = "auto" then f := "both"; fnr := 0; elif flush = "left" then f := flush; fnr := 1; elif flush = "center" then f := flush; fnr := 2; fi; par := FormatParagraph(str, len - indlen, f, [RepeatedString(" ", indlen), ""], WidthUTF8String); if Length(par) = 0 then return ""; fi; res := Concatenation(TextAttr.CSI,String(fnr),";",String(indlen),"Y", par{[indlen+1..Length(par)-1]}); res := Concatenation(par{[1..indlen]}, WrapTextAttribute(res, GAPDoc2TextProcs.TextAttr.format), "\n"); return res; end; ## recursion through the tree and formatting paragraphs BindGlobal("GAPDoc2TextContent", function(r, l) local tmp, par, cont, count, s, a; # inline needed Alt elements if IsList(r.content) and ForAny(r.content, a-> IsRecord(a) and a.name = "Alt") then tmp := r.content; r := ShallowCopy(r); r.content := []; for a in tmp do if IsRecord(a) and a.name = "Alt" then if GAPDoc2TextProcs.AltYes(a) then Append(r.content, a.content); fi; else Add(r.content, a); fi; od; fi; # utility: append counter and formatted paragraph to l par := function(s, name) local sn, ss, pos; # extra call for each part until a forced line break pos := Position(s, '\004'); sn := ""; while pos <> 0 do if pos = fail then ss := s; pos := 0; else ss := s{[1..pos-1]}; s := s{[pos+1..Length(s)]}; pos := Position(s, '\004'); fi; Append(sn, GAPDoc2TextProcs.MarkAndFormat(ss, "auto", Length(r.root.indent), r.root.linelength)); od; if Length(sn)>0 then GAPDoc2TextProcs.P(0, sn); Add(l, count); Add(l, sn); fi; end; # if not containing paragraphs, then l is string to append to if not r.name in GAPDoc2TextProcs.ParEls then for a in r.content do GAPDoc2Text(a, l); od; return; fi; # otherwise we have to collect text and paragraphs cont := r.content; count := r.count; s := ""; for a in cont do if a.count <> count then par(s, a.name); count := a.count; s := ""; fi; if a.name in GAPDoc2TextProcs.ParEls then # recursively collect paragraphs GAPDoc2Text(a, l); else # collect text for current paragraph GAPDoc2Text(a, s); fi; od; if Length(s)>0 then par(s, 0); fi; end); ## write head and foot of Txt file. GAPDoc2TextProcs.WHOLEDOCUMENT := function(r, par) local i, pi, t, el, str, dat, datbt, bib, b, keys, need, labels, tmp, pos, j, diff, text, stream, a, ansi, k, l, ind; ## add paragraph numbers to all nodes of the document AddParagraphNumbersGapDocTree(r); ## add a link .root to the root of the document to all nodes ## (then we can collect information about indexing and so on ## there) AddRootParseTree(r); r.index := []; r.toc := ""; r.labels := rec(); r.labeltexts := rec(); r.bibkeys := []; # and the .six information for the online help index # will contain pairs [string, [chap, sect, subsect]] r.six := []; ## checking for processing instructions before the book starts ## example: <?Txt option1="value1" ?> i := 1; pi := rec(); while not r.content[i].name = "Book" do if r.content[i].name = "XMLPI" then t := r.content[i].content; if Length(t) > 3 and t{[1..4]} = "Txt " then el := GetSTag(Concatenation("<", t, ">"), 2); for a in NamesOfComponents(el.attributes) do pi.(a) := el.attributes.(a); od; fi; fi; i := i+1; od; ## Now the actual work starts, we give the processing instructions found ## so far to the Book handler. ## We call the Book handler twice and produce index, bibliography, toc ## in between. Info(InfoGAPDoc, 1, "#I First run, collecting cross references, ", "index, toc, bib and so on . . .\n"); # with this flag we avoid unresolved references warnings in first run GAPDoc2TextProcs.FirstRun := true; GAPDoc2TextProcs.Book(r.content[i], "", pi); GAPDoc2TextProcs.FirstRun := false; # now the toc is ready Info(InfoGAPDoc, 1, "#I Table of contents complete.\n"); r.toctext := r.toc; # .index has entries of form [sorttext, subsorttext, numbertext, # entrytext, count[, subtext]] Info(InfoGAPDoc, 1, "#I Producing the index . . .\n"); SortBy(r.index, a-> [a[1],STRING_LOWER(a[2]), List(SplitString(a[3],".-",""), Int)]); str := ""; ind := r.index; k := 1; while k <= Length(ind) do if k > 1 and ind[k][4] = ind[k-1][4] then Append(str, " "); else Append(str, ind[k][4]); fi; if IsBound(ind[k][6]) then if k = 1 or ind[k][4] <> ind[k-1][4] then Append(str, ", "); fi; Append(str, ind[k][6]); elif Length(ind[k][2]) > 0 then if k = 1 or ind[k][4] <> ind[k-1][4] then Append(str, ", "); fi; Append(str, ind[k][2]); fi; l := k; while l <= Length(ind) and ind[l][4] = ind[k][4] and ((IsBound(ind[k][6]) and IsBound(ind[l][6]) and ind[k][6] = ind[l][6]) or (not IsBound(ind[k][6]) and not IsBound(ind[l][6]) and ind[k][2] = ind[l][2])) do Append(str, Concatenation(" ", ind[l][3], " ")); l := l+1; od; Append(str, "\n"); k := l; od; r.indextext := str; if Length(r.bibkeys) > 0 then GAPDocAddBibData(r); Info(InfoGAPDoc, 1, "#I Writing bibliography . . .\n"); need := List(r.bibentries, a-> RecBibXMLEntry(a, "Text", r.bibstrings)); # copy the unique labels for a in [1..Length(need)] do need[a].key := r.biblabels[a]; od; text := ""; ansi := rec( Bib_author := GAPDoc2TextProcs.TextAttr.BibAuthor, Bib_title := GAPDoc2TextProcs.TextAttr.BibTitle, Bib_journal := GAPDoc2TextProcs.TextAttr.BibJournal, Bib_volume := GAPDoc2TextProcs.TextAttr.BibVolume, Bib_Label := GAPDoc2TextProcs.TextAttr.BibLabel, Bib_reset := GAPDoc2TextProcs.TextAttr.BibReset[1]); for a in need do Append(text, StringBibAsText(a, ansi)); od; r.bibtext := text; fi; # second run r.six := []; r.index := []; Info(InfoGAPDoc, 1, "#I Second run through document . . .\n"); GAPDoc2TextProcs.Book(r.content[i], par, pi); # adding .six entries from index for a in r.index do if Length(a[2]) > 0 then Add(r.six, [Concatenation(a[4], " ", a[2]), a[3], a[5]]); else Add(r.six, [a[4], a[3], a[5]]); fi; od; ## remove the links to the root ??? ## RemoveRootParseTree(r); end; ## comments and processing instructions are in general ignored GAPDoc2TextProcs.XMLPI := function(r, str) return; end; GAPDoc2TextProcs.XMLCOMMENT := function(r, str) return; end; # do nothing with Ignore GAPDoc2TextProcs.Ignore := function(arg) end; # just process content ??? putting together here GAPDoc2TextProcs.Book := function(r, par, pi) r.root.Name := r.attributes.Name; GAPDoc2TextContent(r, par); end; ## Body is sectioning element GAPDoc2TextProcs.Body := GAPDoc2TextContent; ## the title page, the most complicated looking function GAPDoc2TextProcs.TitlePage := function(r, par) local strn, l, s, a, aa, cont, ss, st, stmp, ind, len; strn := "\n\n"; # the .six entry Add(r.root.six, [GAPDocTexts.d.Titlepage, GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"), r.count{[1..3]}]); # title l := Filtered(r.content, a-> a.name = "Title"); s := ""; GAPDoc2TextContent(l[1], s); s := FormatParagraph(WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.Heading), r.root.linelength, "center", WidthUTF8String); Append(strn, s); Append(strn, "\n\n"); # subtitle l := Filtered(r.content, a-> a.name = "Subtitle"); if Length(l)>0 then s := ""; GAPDoc2TextContent(l[1], s); s := FormatParagraph(WrapTextAttribute(s,GAPDoc2TextProcs.TextAttr.Heading), r.root.linelength, "center", WidthUTF8String); Append(strn, s); Append(strn, "\n\n"); fi; # version l := Filtered(r.content, a-> a.name = "Version"); if Length(l)>0 then s := ""; GAPDoc2TextContent(l[1], s); while Length(s)>0 and s[Length(s)] in WHITESPACE do Unbind(s[Length(s)]); od; s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String); Append(strn, s); Append(strn, "\n\n"); fi; # date l := Filtered(r.content, a-> a.name = "Date"); if Length(l)>0 then s := ""; GAPDoc2TextContent(l[1], s); s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String); Append(strn, s); Append(strn, "\n\n"); fi; # author name(s) l := Filtered(r.content, a-> a.name = "Author"); for a in l do s := ""; aa := ShallowCopy(a); aa.content := Filtered(a.content, b-> not b.name in ["Email", "Homepage", "Address"]); GAPDoc2TextContent(aa, s); s := FormatParagraph(s, r.root.linelength, "center", WidthUTF8String); Append(strn, s); Append(strn, "\n"); od; Append(strn, "\n\n"); # short comment for front page l := Filtered(r.content, a-> a.name = "TitleComment"); if Length(l)>0 then # format narrower than later text st := ""; r.root.linelength := r.root.linelength - 10; s := r.root.indent; r.root.indent := Concatenation(s, " "); GAPDoc2TextContent(l[1], st); r.root.indent := s; r.root.linelength := r.root.linelength + 10; Append(strn, st); Append(strn, "\n\n"); fi; # email and WWW-homepage of author(s), if given l := Filtered(r.content, a-> a.name = "Author"); for a in l do cont := List(a.content, b-> b.name); if "Email" in cont or "Homepage" in cont then s := ""; aa := ShallowCopy(a); aa.content := Filtered(a.content, b-> not b.name in ["Email", "Homepage", "Address"]); GAPDoc2TextContent(aa, s); NormalizeWhitespace(s); Append(strn, s); if "Email" in cont then Append(strn, Concatenation("\n ", GAPDocTexts.d.Email, ": ")); GAPDoc2Text(a.content[Position(cont, "Email")], strn); fi; if "Homepage" in cont then Append(strn, "\n"); Append(strn, Concatenation(" ", GAPDocTexts.d.Homepage, ": ")); GAPDoc2Text(a.content[Position(cont, "Homepage")], strn); fi; if "Address" in cont then Append(strn, "\n"); stmp := ""; ind := a.root.indent; len := Length(GAPDocTexts.d.Address); a.root.indent := Concatenation(ind, RepeatedString(' ', len+7)); GAPDoc2TextContent(a.content[Position(cont, "Address")], stmp); a.root.indent := ind; stmp[2]{Length(ind)+[1..len+7]} := Concatenation(" ", GAPDocTexts.d.Address, ": "); Append(strn, stmp); fi; Append(strn, "\n"); fi; od; # if an address is given outside the <Author> element(s) l := Filtered(r.content, a-> a.name = "Address"); if Length(l) > 0 then Append(strn, "\n\n"); stmp := ""; ind := r.root.indent; len := Length(GAPDocTexts.d.Address); r.root.indent := Concatenation(ind, RepeatedString(' ', len+2)); GAPDoc2TextContent(l[1], stmp); r.root.indent := ind; stmp[2]{Length(ind)+[1..len+2]} := Concatenation(GAPDocTexts.d.Address, ": "); Append(strn, stmp); fi; Append(strn, "\n-------------------------------------------------------\n"); Add(par, r.count); Add(par, strn); # abstract, copyright page, acknowledgements, colophon for ss in ["Abstract", "Copyright", "Acknowledgements", "Colophon" ] do l := Filtered(r.content, a-> a.name = ss); if Length(l)>0 then # the .six entry Add(r.root.six, [GAPDocTexts.d.(ss), GAPDoc2TextProcs.SectionNumber(l[1].count, "Subsection"), l[1].count{[1..3]}]); Add(par, l[1].count); Add(par, Concatenation(WrapTextAttribute(GAPDocTexts.d.(ss), GAPDoc2TextProcs.TextAttr.Heading), "\n")); GAPDoc2TextContent(l[1], par); Append(par[Length(par)], "\n-------------------------------------------------------\n"); fi; od; end; ## these produce text for an URL ## arg: r, str[, pre] GAPDoc2TextProcs.Link := GAPDoc2TextContent; GAPDoc2TextProcs.LinkText := GAPDoc2TextContent; GAPDoc2TextProcs.URL := function(arg) local r, str, pre, rr, txt, s; r := arg[1]; str := arg[2]; if Length(arg)>2 then pre := arg[3]; else pre := ""; fi; rr := First(r.content, a-> a.name = "LinkText"); if rr <> fail then txt := ""; GAPDoc2TextContent(rr, txt); rr := First(r.content, a-> a.name = "Link"); if rr = fail then Info(InfoGAPDoc, 1, "#W missing <Link> element for text ", txt, "\n"); s := "MISSINGLINK"; else s := ""; GAPDoc2TextContent(rr, s); fi; else s := ""; GAPDoc2TextContent(r, s); if IsBound(r.attributes.Text) then txt := r.attributes.Text; else txt := s; fi; fi; NormalizeWhitespace(s); NormalizeWhitespace(txt); pre := WrapTextAttribute(Concatenation(pre, s), GAPDoc2TextProcs.TextAttr.URL); if txt=s then Append(str, pre); else Append(str, Concatenation(txt, " (", pre, ")")); fi; end; GAPDoc2TextProcs.Homepage := GAPDoc2TextProcs.URL; GAPDoc2TextProcs.Email := function(r, str) # we add the 'mailto://' phrase GAPDoc2TextProcs.URL(r, str, "mailto:"); end; GAPDoc2TextProcs.Address := function(r, str) # just process the text GAPDoc2TextContent(r, str); end; ## utility: generate a chapter or (sub)section-number string GAPDoc2TextProcs.SectionNumber := function(count, sect) local res; res := ""; if IsString(count[1]) or count[1]>0 then Append(res, String(count[1])); else res := ""; fi; if sect="Chapter" or sect="Appendix" then return res; fi; Add(res, '.'); if count[2]>0 then Append(res, String(count[2])); fi; if sect="Section" then return res; fi; if count[3]>0 then Append(res, Concatenation("-", String(count[3]))); fi; if sect="Par" then Append(res, Concatenation(".", String(count[4]))); return res; fi; # default is SubSection or ManSection number return res; end; ## the sectioning commands are just translated and labels are ## generated, if given as attribute GAPDoc2TextProcs.ChapSect := function(r, par, sect) local num, posh, s, ind, strn, sm, sms, t; # section number as string num := GAPDoc2TextProcs.SectionNumber(r.count, sect); # the heading posh := Position(List(r.content, a-> a.name), "Heading"); if posh <> fail then s := ""; # first the .six entry GAPDoc2TextProcs.Heading1(r.content[posh], s); # reset to heading markup if overwritten by inner elements sm := s; if PositionSublist(s{[5..Length(s)-8]}, "\033[1") <> fail then for t in [ "K", "B", "M", "C", "F", "Func", "Math", "Emph", "Q", "Package", "Arg" ] do sm := SubstitutionSublist(sm, GAPDoc2TextProcs.TextAttr.(t)[2], Concatenation(GAPDoc2TextProcs.TextAttr.(t)[2], GAPDoc2TextProcs.TextAttr.Heading[2],"\027", GAPDoc2TextProcs.TextAttr.Heading[1],"\027")); od; fi; # in six entry we don't want the extra indent on reformatting sms := SubstitutionSublist(sm, "\033[0;0Y", "\033[0;-2Y"); Add(r.root.six, [WrapTextAttribute(NormalizedWhitespace(sms), GAPDoc2TextProcs.TextAttr.Heading), num, r.count{[1..3]}]); # label entry, if present if IsBound(r.attributes.Label) then r.root.labels.(r.attributes.Label) := num; r.root.labeltexts.(r.attributes.Label) := s; fi; # the heading text sm := Concatenation(num, " ", sm); Add(par, r.count); # here we assume that r.indent = "" Add(par, Concatenation("\n", FormatParagraph(sm, r.root.linelength, GAPDoc2TextProcs.TextAttr.Heading, WidthUTF8String), "\n")); # table of contents entry if sect="Section" then ind := " "; elif sect="Subsection" then ind := " "; else ind := ""; fi; # here s without heading markup Append(r.root.toc, FormatParagraph(Concatenation(num, " ", s), r.root.linelength-Length(ind), "left", [ind, ""], WidthUTF8String)); fi; # the actual content GAPDoc2TextContent(r, par); end; ## this really produces the content of the heading GAPDoc2TextProcs.Heading1 := function(r, str) local a, where; a := ""; GAPDoc2TextContent(r, a); if a = "" then if IsBound(r.root) and IsBound(r.root.inputorigins) then where := OriginalPositionDocument(r.root.inputorigins,r.start); where := Concatenation("(file ",where[1], ", line ",String(where[2]),")"); else where := ""; fi; Error("Empty <Heading> element is not supported ",where); return; fi; Append(str, a[2]); end; ## and this ignores the heading (for simpler recursion) GAPDoc2TextProcs.Heading := function(r, str) end; GAPDoc2TextProcs.Chapter := function(r, par) GAPDoc2TextProcs.ChapSect(r, par, "Chapter"); end; GAPDoc2TextProcs.Appendix := function(r, par) GAPDoc2TextProcs.ChapSect(r, par, "Appendix"); end; GAPDoc2TextProcs.Section := function(r, par) GAPDoc2TextProcs.ChapSect(r, par, "Section"); end; GAPDoc2TextProcs.Subsection := function(r, par) GAPDoc2TextProcs.ChapSect(r, par, "Subsection"); end; ## table of contents, just puts "TOC" in first run GAPDoc2TextProcs.TableOfContents := function(r, par) local s; # the .six entry Add(r.root.six, [GAPDocTexts.d.TableofContents, GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"), r.count{[1..3]}]); Add(par, r.count); if IsBound(r.root.toctext) then s := WrapTextAttribute(Concatenation(GAPDocTexts.d.Contents," (", r.root.Name, ")"), GAPDoc2TextProcs.TextAttr.Heading); Add(par, Concatenation("\n\n", s, "\n\n", r.root.toctext, "\n\n", GAPDoc2TextProcs.TextAttr.FillString[1],"\n")); else Add(par,"TOC\n-----------\n"); fi; end; ## bibliography, just "BIB" in first run, store databases in root GAPDoc2TextProcs.Bibliography := function(r, par) local s; # .six entries s := GAPDoc2TextProcs.SectionNumber(r.count, "Chapter"); Add(r.root.six, [GAPDocTexts.d.Bibliography, s, r.count{[1..3]}]); if GAPDocTexts.d.Bibliography <> GAPDocTexts.d.References then Add(r.root.six, [GAPDocTexts.d.References, s, r.count{[1..3]}]); fi; r.root.bibdata := r.attributes.Databases; if IsBound(r.attributes.Style) then r.root.bibstyle := r.attributes.Style; fi; Add(par, r.count); if IsBound(r.root.bibtext) then Add(par, Concatenation("\n\n", WrapTextAttribute(GAPDocTexts.d.References, GAPDoc2TextProcs.TextAttr.Heading), "\n\n", r.root.bibtext, "\n\n", GAPDoc2TextProcs.TextAttr.FillString[1], "\n")); else Add(par,"BIB\n-----------\n"); fi; end; ## inside <M> element we don't want this filtering GAPDoc2TextProcs.PCDATANOFILTER := function(r, str) Append(str, r.content); end; ## default is with filter ???changed??? GAPDoc2TextProcs.PCDATAFILTER := GAPDoc2TextProcs.PCDATANOFILTER; GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER; ## end of paragraph (end with double newline) GAPDoc2TextProcs.P := function(r, str) local l, i; l := Length(str); if l>0 and str[l] <> '\n' then Append(str, "\n\n"); elif l>1 and str[l-1] <> '\n' then Add(str, '\n'); else # remove too many line breaks i := l-2; while i>0 and str[i] = '\n' do Unbind(str[i+2]); i := i-1; od; fi; end; ## end of line, same as with .P, but no empty line GAPDoc2TextProcs.Br := function(r, str) # we use character \004 to mark forced line breaks, used in 'par' above Add(str, '\004'); ## GAPDoc2TextProcs.P(r, str); ## if Length(str) > 0 and str[Length(str)] = '\n' then ## Unbind(str[Length(str)]); ## fi; end; ## wrapping text attributes GAPDoc2TextProcs.WrapAppend := function(str, s, a) Append(str, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.(a))); end; GAPDoc2TextProcs.WrapAttr := function(r, str, a) local s; s := ""; GAPDoc2TextContent(r, s); GAPDoc2TextProcs.WrapAppend(str, s, a); end; ## GAP keywords GAPDoc2TextProcs.K := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "K"); end; ## verbatim GAP code GAPDoc2TextProcs.C := function(r, str) local s; s := ""; GAPDoc2TextContent(r, s); # <A> elements are allowed inside <C>, so maybe we need to repeat the markup # (with a char 23 such that it can be ignored in case of visible markup # substitution) s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.Arg[2], Concatenation(GAPDoc2TextProcs.TextAttr.Arg[2], GAPDoc2TextProcs.TextAttr.C[1],"\027")); GAPDoc2TextProcs.WrapAppend(str, s, "C"); end; ## file names GAPDoc2TextProcs.F := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "F"); end; ## argument names (same as Arg) GAPDoc2TextProcs.A := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "Arg"); end; ## simple maths, here we try to substitute TeX command to something which ## looks ok in text mode GAPDoc2TextProcs.M := function(r, str) local s; s := ""; GAPDoc2TextContent(r, s); s := TextM(s); GAPDoc2TextProcs.WrapAppend(str, s, "M"); end; ## in Txt this is shown in TeX format GAPDoc2TextProcs.Math := function(r, str) local s; s := ""; GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATANOFILTER; GAPDoc2TextContent(r, s); GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER; GAPDoc2TextProcs.WrapAppend(str, s, "Math"); end; ## displayed maths (also in TeX format, but printed as paragraph enclosed ## by "\[" and "\]") GAPDoc2TextProcs.Display := function(r, par) local s; s := ""; GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATANOFILTER; GAPDoc2TextContent(r, s); GAPDoc2TextProcs.PCDATA := GAPDoc2TextProcs.PCDATAFILTER; s := Concatenation(Filtered(s, IsString)); if IsBound(r.attributes.Mode) and r.attributes.Mode = "M" then s := TextM(s); fi; s := WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.Display); # change the indentation value in formatting escape sequence s := SubstitutionSublist(s, "\033[0;0Y", "\033[0;6Y", "one"); s := Concatenation("\n", s, "\n\n"); Add(par, r.count); Add(par, s); end; ## emphazised text GAPDoc2TextProcs.Emph := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "Emph"); end; ## quoted text GAPDoc2TextProcs.Q := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "Q"); end; ## Package names GAPDoc2TextProcs.Package := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "Package"); end; ## menu items GAPDoc2TextProcs.B := function(r, str) GAPDoc2TextProcs.WrapAttr(r, str, "B"); end; GAPDoc2TextProcs.AddColorPromptMarkup := function(cont) local res, pos, s; res := []; for s in cont do if Length(s) > 4 and s{[1..5]} = "gap> " then Add(res, Concatenation(WrapTextAttribute("gap>", GAPDoc2TextProcs.TextAttr.Prompt), " ", WrapTextAttribute(s{[6..Length(s)]}, GAPDoc2TextProcs.TextAttr.GAPInput))); elif Length(s) > 1 and s{[1,2]} = "> " then Add(res, Concatenation(WrapTextAttribute(">", GAPDoc2TextProcs.TextAttr.Prompt), " ", WrapTextAttribute(s{[3..Length(s)]}, GAPDoc2TextProcs.TextAttr.GAPInput))); elif Length(s) > 2 and s{[1..3]} = "brk" then pos := Position(s, ' '); if pos <> fail then Add(res, Concatenation(WrapTextAttribute(s{[1..pos-1]}, GAPDoc2TextProcs.TextAttr.BrkPrompt), " ", WrapTextAttribute(s{[pos+1..Length(s)]}, GAPDoc2TextProcs.TextAttr.GAPInput))); else Add(res, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.BrkPrompt)); fi; else Add(res, WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.GAPOutput)); fi; od; return res; end; GAPDoc2TextProcs.ExampleLike := function(r, par, label) local str, cont, a, s, len, l1; if Length(label) = 0 then str := Concatenation(r.root.indent, GAPDoc2TextProcs.TextAttr.FillString[1]); else str := Concatenation(r.root.indent, GAPDoc2TextProcs.TextAttr.FillString[1], " ", label, " ", GAPDoc2TextProcs.TextAttr.FillString[1]); fi; str := WrapTextAttribute(str, GAPDoc2TextProcs.TextAttr.Example); Add(str, '\n'); cont := ""; for a in r.content do # here we try to avoid reformatting if IsString(a.content) then Append(cont, a.content); else s := ""; GAPDoc2Text(a, s); Append(cont, s); fi; od; cont := SplitString(cont, "\n", ""); # delete first line, if whitespace only if Length(cont) > 0 and ForAll(cont[1], x-> x in WHITESPACE) then cont := cont{[2..Length(cont)]}; fi; # color prompt markup in <Example> and <Log> elements if label in [GAPDocTexts.d.Example, GAPDocTexts.d.Log] then cont := GAPDoc2TextProcs.AddColorPromptMarkup(cont); fi; cont := Concatenation(List(cont, a-> Concatenation(r.root.indent, " ", WrapTextAttribute(a, GAPDoc2TextProcs.TextAttr.Example), "\n"))); Append(str, cont); Append(str, Concatenation(r.root.indent, WrapTextAttribute(GAPDoc2TextProcs.TextAttr.FillString[1], GAPDoc2TextProcs.TextAttr.Example), "\n\n")); Add(par, r.count); Add(par, str); end; ## log of session and GAP code is typeset the same way as <Example> GAPDoc2TextProcs.Example := function(r, par) GAPDoc2TextProcs.ExampleLike(r, par, GAPDocTexts.d.Example); end; GAPDoc2TextProcs.Log := function(r, par) GAPDoc2TextProcs.ExampleLike(r, par, GAPDocTexts.d.Log); end; GAPDoc2TextProcs.Listing := function(r, par) if IsBound(r.attributes.Type) then GAPDoc2TextProcs.ExampleLike(r, par, r.attributes.Type); else GAPDoc2TextProcs.ExampleLike(r, par, ""); fi; end; ## Verb is without any formatting GAPDoc2TextProcs.Verb := function(r, par) local cont, s, pos, a; cont := ""; for a in r.content do # here we try to avoid reformatting if IsString(a.content) then Append(cont, a.content); else s := ""; GAPDoc2Text(a, s); Append(cont, s); fi; od; # delete first line if it contains only whitespace pos := Position(cont, '\n'); if pos <> fail and ForAll(cont{[1..pos]}, x-> x in WHITESPACE) then cont := cont{[pos+1..Length(cont)]}; fi; # adjust trailing newlines GAPDoc2TextProcs.P(0, cont); Append(par, [r.count, cont]); end; ## explicit labels GAPDoc2TextProcs.Label := function(r, str) r.root.labels.(r.attributes.Name) := GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"); end; ## citations GAPDoc2TextProcs.Cite := function(r, str) local key, pos; key := r.attributes.Key; pos := Position(r.root.bibkeys, key); if pos = fail then Add(r.root.bibkeys, key); Append(str, Concatenation("[?", key, "?]")); elif not IsBound(r.root.biblabels) then Append(str, Concatenation("[?", key, "?]")); else Append(str, Concatenation("[", r.root.biblabels[pos])); if IsBound(r.attributes.Where) then Append(str, ", "); Append(str, r.attributes.Where); fi; Add(str, ']'); fi; end; ## explicit index entries GAPDoc2TextProcs.Subkey := GAPDoc2TextContent; GAPDoc2TextProcs.Index := function(r, str) local s, sub, entry, a; s := ""; sub := ""; for a in r.content do if a.name = "Subkey" then GAPDoc2Text(a, sub); else GAPDoc2Text(a, s); fi; od; NormalizeWhitespace(s); NormalizeWhitespace(sub); if IsBound(r.attributes.Key) then entry := [STRING_LOWER(r.attributes.Key)]; else entry := [STRING_LOWER(StripEscapeSequences(s))]; fi; if IsBound(r.attributes.Subkey) then Add(entry, r.attributes.Subkey); else Add(entry, STRING_LOWER(StripEscapeSequences(sub))); fi; Add(entry, GAPDoc2TextProcs.SectionNumber(r.count, "Subsection")); Add(entry, s); Add(entry, r.count{[1..3]}); if Length(sub) > 0 then Add(entry, sub); fi; Add(r.root.index, entry); end; ## helper to add markup to the args GAPDoc2TextProcs.WrapArgs := function(argstr) local res, noletter, c; res := ""; noletter := true; for c in argstr do if noletter then if not c in ", []" then noletter := false; Append(res, GAPDoc2TextProcs.TextAttr.Arg[1]); fi; elif c in ", []" then noletter := true; Append(res, GAPDoc2TextProcs.TextAttr.Arg[2]); fi; Add(res, c); od; if not noletter then Append(res, GAPDoc2TextProcs.TextAttr.Arg[2]); fi; return res; end; ## this produces an implicit index entry and a label entry GAPDoc2TextProcs.LikeFunc := function(r, par, typ) local str, s, name, lab, comma, i, entry; # for very long lines we allow left flushed paragraph formatting s := Concatenation(GAPDoc2TextProcs.TextAttr.format[1], "\033[1;0Y", GAPDoc2TextProcs.TextAttr.DefLineMarker[1], WrapTextAttribute(r.attributes.Name, GAPDoc2TextProcs.TextAttr.Func)); if IsBound(r.attributes.Arg) then Append(s, "( "); Append(s, GAPDoc2TextProcs.WrapArgs(NormalizedArgList(r.attributes.Arg))); Append(s, " ) "); fi; # label (if not given, the default is the Name) if IsBound(r.attributes.Label) then lab := r.attributes.Label; comma := ", "; else lab := ""; comma := ""; fi; GAPDoc2TextProcs.Label(rec(count := r.count, attributes := rec(Name := Concatenation(r.attributes.Name, comma, lab)), root := r.root), par); # index entry name := r.attributes.Name; entry := [STRING_LOWER(name), "", GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"), WrapTextAttribute(name, GAPDoc2TextProcs.TextAttr.Func), r.count{[1..3]}]; if Length(lab) > 0 then entry[2] := STRING_LOWER(StripEscapeSequences(lab)); Add(entry, lab); fi; Add(r.root.index, entry); # some hint about the type of the variable Append(s, GAPDoc2TextProcs.TextAttr.FillString[1]); Append(s, Concatenation(" ",typ,GAPDoc2TextProcs.TextAttr.format[2],"\n")); Add(par, r.count); Add(par, s); end; GAPDoc2TextProcs.Func := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Func); end; GAPDoc2TextProcs.Oper := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Oper); end; GAPDoc2TextProcs.Constr := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Constr); end; GAPDoc2TextProcs.Meth := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Meth); end; GAPDoc2TextProcs.Filt := function(r, str) # r.attributes.Type could be "representation", "category", ... if IsBound(r.attributes.Type) then GAPDoc2TextProcs.LikeFunc(r, str, r.attributes.Type); else GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Filt); fi; end; GAPDoc2TextProcs.Prop := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Prop); end; GAPDoc2TextProcs.Attr := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Attr); end; GAPDoc2TextProcs.Var := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Var); end; GAPDoc2TextProcs.Fam := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.Fam); end; GAPDoc2TextProcs.InfoClass := function(r, str) GAPDoc2TextProcs.LikeFunc(r, str, GAPDocTexts.d.InfoClass); end; ## using the HelpData(.., .., "ref") interface GAPDoc2TextProcs.ResolveExternalRef := function(bookname, label, nr) local info, match, res; info := HELP_BOOK_INFO(bookname); if info = fail then return fail; fi; match := Concatenation(HELP_GET_MATCHES(info, SIMPLE_STRING(label), true)); if Length(match) < nr then return fail; fi; res := GetHelpDataRef(info, match[nr][2]); res[1] := SubstitutionSublist(res[1], " (not loaded): ", ": ", "one"); return res; end; GAPDoc2TextProcs.Ref := function(r, str) local funclike, int, txt, ref, lab, sectlike; # function like cases funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass" ]; int := Intersection(funclike, NamesOfComponents(r.attributes)); if Length(int)>0 then txt := r.attributes.(int[1]); if IsBound(r.attributes.Label) then lab := Concatenation(txt, ", ", r.attributes.Label); else lab := txt; fi; if IsBound(r.attributes.BookName) then ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1); if ref = fail then if GAPDoc2TextProcs.FirstRun <> true then Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ", r.attributes, "\n"); fi; ref := Concatenation(lab, "???"); else # the search text for online help including book name ref := ref[1]; fi; else if IsBound(r.root.labels.(lab)) then ref := r.root.labels.(lab); else if GAPDoc2TextProcs.FirstRun <> true then Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ", r.attributes, "\n"); fi; ref := Concatenation("???", lab, "???"); fi; fi; Append(str, WrapTextAttribute(txt, GAPDoc2TextProcs.TextAttr.Func)); # add reference by subsection number or text if external, # but only if it does not point to current subsection if GAPDoc2TextProcs.SectionNumber(r.count, "Subsection") <> ref then Append(str, Concatenation(" (", WrapTextAttribute(ref, GAPDoc2TextProcs.TextAttr.Ref), ")")); fi; return; fi; # section like cases sectlike := ["Chap", "Sect", "Subsect", "Appendix"]; int := Intersection(sectlike, NamesOfComponents(r.attributes)); if Length(int)>0 then txt := r.attributes.(int[1]); if IsBound(r.attributes.Label) then lab := Concatenation(txt, r.attributes.Label); else lab := txt; fi; if IsBound(r.attributes.BookName) then ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1); if ref = fail then if GAPDoc2TextProcs.FirstRun <> true then Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ", r.attributes, "\n"); fi; ref := Concatenation(lab, "???"); else # the search text for online help including book name ref := Concatenation("'", StripBeginEnd(ref[1], " "), "'"); fi; else # with sectioning references Label must be given lab := r.attributes.(int[1]); # default is printing section number, but we allow a Style="Text" # attribute if IsBound(r.attributes.Style) and r.attributes.Style = "Text" and IsBound(r.root.labeltexts.(lab)) then ref := Concatenation("'", StripBeginEnd( r.root.labeltexts.(lab), WHITESPACE), "'"); elif IsBound(r.root.labels.(lab)) then ref := r.root.labels.(lab); else if GAPDoc2TextProcs.FirstRun <> true then Info(InfoGAPDoc, 1, "#W WARNING: non resolved reference: ", r.attributes, "\n"); fi; ref := Concatenation("???", lab, "???"); fi; fi; Append(str, WrapTextAttribute(ref, GAPDoc2TextProcs.TextAttr.Ref)); return; fi; # neutral reference to a label lab := r.attributes.Label; if IsBound(r.attributes.BookName) then ref := GAPDoc2TextProcs.ResolveExternalRef(r.attributes.BookName, lab, 1); if ref = fail then ref := Concatenation(lab, "???"); else # the search text for online help including book name ref := ref[1]; fi; else if IsBound(r.root.labels.(lab)) then ref := r.root.labels.(lab); else ref := Concatenation("???", lab, "???"); fi; fi; Append(str, WrapTextAttribute(ref, GAPDoc2TextProcs.TextAttr.Ref)); return; end; GAPDoc2TextProcs.Description := function(r, par) local l, tmp; l := ""; GAPDoc2TextContent(r, l); # Add an empty line in front if not yet there if Length(par) > 0 and Length(par[Length(par)]) > 1 then tmp := par[Length(par)]; else tmp := ""; fi; if tmp[Length(tmp)-1] <> '\n' then Add(tmp, '\n'); fi; Append(par, l); end; GAPDoc2TextProcs.Returns := function(r, par) local l, ind, lr, pos; l := ""; ind := r.root.indent; r.root.indent := Concatenation(ind, " "); GAPDoc2TextContent(r, l); if Length(l) > 0 then lr := Length(GAPDocTexts.d.Returns)+1; pos := PositionSublist(l[2], RepeatedString(" ",lr), Length(ind)); l[2] := Concatenation(l[2]{[1..pos-1]}, WrapTextAttribute(Concatenation(GAPDocTexts.d.Returns,":"), GAPDoc2TextProcs.TextAttr.Returns), l[2]{[pos+lr..Length(l[2])]}); Append(par, l); fi; r.root.indent := ind; end; GAPDoc2TextProcs.ManSection := function(r, par) local funclike, i, num, s, strn; # if there is a Heading then handle as subsection if ForAny(r.content, a-> IsRecord(a) and a.name = "Heading") then GAPDoc2TextProcs.ChapSect(r, par, "Subsection"); return; fi; strn := ""; # function like elements funclike := [ "Func", "Oper", "Constr", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass" ]; # heading comes from name of first function like element i := 1; while not r.content[i].name in funclike do i := i+1; od; num := GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"); s := Concatenation(num, " ", r.content[i].attributes.Name); Add(par, r.count); Add(par, Concatenation(WrapTextAttribute(s, GAPDoc2TextProcs.TextAttr.Heading), "\n\n")); # append to TOC as subsection Append(r.root.toc, Concatenation(" ", s, "\n")); # label entry, if present if IsBound(r.attributes.Label) then r.root.labels.(r.attributes.Label) := num; r.root.labeltexts.(r.attributes.Label) := s; fi; GAPDoc2TextContent(r, par); end; GAPDoc2TextProcs.Mark := function(r, str) local s; s := ""; GAPDoc2TextProcs.WrapAttr(r, s, "Mark"); # allow for <C> and <A> elements in <Mark> s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.Arg[2], Concatenation(GAPDoc2TextProcs.TextAttr.Arg[2], GAPDoc2TextProcs.TextAttr.Mark[1],"\027")); s := SubstitutionSublist(s, GAPDoc2TextProcs.TextAttr.C[2], Concatenation(GAPDoc2TextProcs.TextAttr.C[2], GAPDoc2TextProcs.TextAttr.Mark[1],"\027")); Append(str, r.root.indent); Append(str, NormalizedWhitespace(s)); Append(str, "\n"); end; GAPDoc2TextProcs.Item := function(r, str) local s; # s := ""; s := r.root.indent; r.root.indent := Concatenation(s, " "); GAPDoc2TextContent(r, str); r.root.indent := s; # s:= FormatParagraph(s, r.root.linelength-6, "both", [" ", ""]); # Append(str, s); end; # must do the complete formatting GAPDoc2TextProcs.List := function(r, par) local s, ss, pos, a, i, start; if "Mark" in List(r.content, a-> a.name) then for a in r.content do if a.name = "Mark" then s := ""; GAPDoc2TextProcs.Mark(a, s); Append(par, [a.count, s]); elif a.name = "Item" then GAPDoc2TextProcs.Item(a, par); fi; od; else for a in Filtered(r.content, a-> a.name = "Item") do ss := ""; GAPDoc2TextProcs.Item(a, ss); if ss <> "" then # ignore empty <Item> # insert bullet start := ss[2]{[1..Length(r.root.indent)]}; ss[2] := ss[2]{[Length(r.root.indent)+1..Length(ss[2])]}; for i in [1..2] do Remove(ss[2], Position(ss[2],' ')); od; ss[2] := Concatenation(start, GAPDoc2TextProcs.TextAttr.ListBullet[1], ss[2]); Append(par, ss); fi; od; fi; end; GAPDoc2TextProcs.Enum := function(r, par) local i, ss, num, a, j, start; i := 1; for a in Filtered(r.content, a-> a.name = "Item") do ss := ""; GAPDoc2TextProcs.Item(a, ss); # merge in the counter start := ss[2]{[1..Length(r.root.indent)]}; ss[2] := ss[2]{[Length(r.root.indent)+1..Length(ss[2])]}; for j in [1..Length(String(i))+2] do Remove(ss[2], Position(ss[2],' ')); od; num := WrapTextAttribute(String(i), GAPDoc2TextProcs.TextAttr.EnumMarks); ss[2] := Concatenation(start, num, ss[2]); Append(par, ss); i := i+1; od; end; GAPDoc2TextProcs.TheIndex := function(r, par) local s; # .six entry s := GAPDoc2TextProcs.SectionNumber(r.count, "Chapter"); Add(r.root.six, [GAPDocTexts.d.Index, s, r.count{[1..3]}]); # the text, if available Add(par, r.count); if IsBound(r.root.indextext) then Add(par, Concatenation("\n\n", WrapTextAttribute(GAPDocTexts.d.Index, GAPDoc2TextProcs.TextAttr.Heading), "\n\n", r.root.indextext, "\n\n-------------------------------------------------------\n")); else Add(par,"INDEX\n-----------\n"); fi; end; GAPDoc2TextProcs.AltYes := function(r) if (not IsBound(r.attributes.Only) and not IsBound(r.attributes.Not)) or (IsBound(r.attributes.Only) and "Text" in SplitString(r.attributes.Only, "", " ,")) or (IsBound(r.attributes.Not) and not "Text" in SplitString(r.attributes.Not, "", " ,")) then return true; else return false; fi; end; GAPDoc2TextProcs.Alt := function(r, str) if GAPDoc2TextProcs.AltYes(r) then GAPDoc2TextContent(r, str); fi; end; # copy a few entries with two element names GAPDoc2TextProcs.E := GAPDoc2TextProcs.Emph; GAPDoc2TextProcs.Keyword := GAPDoc2TextProcs.K; GAPDoc2TextProcs.Code := GAPDoc2TextProcs.C; GAPDoc2TextProcs.File := GAPDoc2TextProcs.F; GAPDoc2TextProcs.Button := GAPDoc2TextProcs.B; GAPDoc2TextProcs.Arg := GAPDoc2TextProcs.A; GAPDoc2TextProcs.Quoted := GAPDoc2TextProcs.Q; GAPDoc2TextProcs.Par := GAPDoc2TextProcs.P; # like PCDATA GAPDoc2TextProcs.EntityValue := GAPDoc2TextProcs.PCDATA; GAPDoc2TextProcs.Table := function(r, str) local a, align, bc, t, z, b, l, s, pos, lens, m, d, ind, hline, cap, i, j; if not GAPDoc2TextProcs.AltYes(r) then return; fi; # head part of table and tabular if IsBound(r.attributes.Label) then r.root.labels.(r.attributes.Label) := GAPDoc2TextProcs.SectionNumber(r.count, "Subsection"); fi; # add spaces as separators of colums if no "|" is given # first, add a dummy character at the end of the input to # simplify handling of the last position a := Concatenation(r.attributes.Align, " "); align := ""; for i in [1..Length(a)-1] do if a[i] in "crl" then Add(align, a[i]); if a[i+1] <> '|' then Add(align, ' '); fi; elif a[i] = '|' then Add(align, '|'); fi; od; # make all odd positions separator descriptions if not align[1] in " |" then align := Concatenation(" ", align); fi; # box characters bc := List([1..11], i-> BOXCHARS{[3*i-2..3*i]}); # collect entries t := []; # the rows of the table for a in r.content do if a.name = "Row" then z := []; b := Filtered(a.content, x-> x.name = "Item"); for i in [1..Length(align)] do if i mod 2 = 1 then if align[i] = '|' then Add(z, Concatenation(" ", bc[2], " ")); else Add(z, Concatenation(" ", align{[i]}, " ")); fi; elif IsBound(b[i/2]) then l := ""; GAPDoc2TextProcs.Item(b[i/2], l); s := Concatenation(l{[2,4..Length(l)]}); NormalizeWhitespace(s); # do not reformat paragraphs later pos := PositionSublist(s, GAPDoc2TextProcs.TextAttr.format[1]); if pos = 1 then s := s{[Position(s, 'Y')+1..PositionSublist(s, GAPDoc2TextProcs.TextAttr.format[2])-1]}; fi; Add(z, s); else Add(z, ""); fi; od; Add(t, z); elif a.name = "HorLine" then Add(t, List(align, x-> "")); fi; od; # equalize width of entries in columns lens := []; for i in [2,4..2*QuoInt(Length(align), 2)] do a := List(t, b-> WidthUTF8String(StripEscapeSequences( SubstituteEscapeSequences(b[i], GAPDocTextTheme)))); m := Maximum(a); lens[i] := m; z := ""; for b in [1..m] do Add(z, ' '); od; if align[i] = 'r' then for j in [1..Length(t)] do t[j][i] := Concatenation(z{[1..m-a[j]]}, t[j][i]); od; elif align[i] = 'l' then for j in [1..Length(t)] do t[j][i] := Concatenation(t[j][i], z{[1..m-a[j]]}); od; else for j in [1..Length(t)] do d := m - a[j]; t[j][i] := Concatenation(z{[1..QuoInt(d, 2)]}, t[j][i], z{[1..d - QuoInt(d, 2)]}); od; fi; od; # put lines together for i in [1..Length(t)] do if Length(t[i][1])=0 then t[i] := ["-"]; fi; od; t := List(t, Concatenation); hline := function(t,l,m,r) local z, i, j; # Process first column z := " "; if align[1] = '|' then Append(z, l); Append(z, t); else Append(z, " "); fi; # Process all columns excluding the first and last one for i in [2..Length(align)-1] do if i mod 2 = 0 then for j in [1..lens[i]] do Append(z, t); od; elif align[i] = '|' then Append(z, t); Append(z, m); Append(z, t); else Append(z, " "); fi; od; # Process last column if align[Length(align)] = '|' then Append(z, t); Append(z, r); else Append(z, " "); fi; Add(z, '\n'); return z; end; for i in [1..Length(t)] do if t[i][1] = '-' then if i = 1 then t[i] := hline(bc[1],bc[3],bc[4],bc[5]); elif i = Length(t) then t[i] := hline(bc[1],bc[9],bc[10],bc[11]); else t[i] := hline(bc[1],bc[6],bc[7],bc[8]); fi; else t[i] := Concatenation(" ", t[i], "\n"); fi; od; t := Concatenation(t); Add(t, '\n'); # the caption, if given cap := Filtered(r.content, a-> a.name = "Caption"); if Length(cap) > 0 then s := ""; GAPDoc2TextProcs.Caption1(cap[1], s); Append(t, s); Append(t, "\n\n"); fi; Add(str, r.count); Add(str, t); end; # do nothing, we call .Caption1 directly in .Table GAPDoc2TextProcs.Caption := function(r, str) return; end; # here the caption for a table text is produced GAPDoc2TextProcs.Caption1 := function(r, str) local s; s := Concatenation(GAPDocTexts.d.Table,":"); s := WrapTextAttribute(s,GAPDoc2TextProcs.TextAttr.Heading); Append(s, " "); GAPDoc2TextContent(r, s); Append(str, FormatParagraph(s, r.root.linelength - 10, "both", [" ", ""], WidthUTF8String)); end; ## ## <#GAPDoc Label="GAPDoc2TextPrintTextFiles"> ## <ManSection > ## <Func Arg="t[, path]" Name="GAPDoc2TextPrintTextFiles" /> ## <Returns>nothing</Returns> ## <Description> ## The first argument must be a result returned by <Ref ## Func="GAPDoc2Text"/>. The second argument is a path for the files ## to write, it can be given as string or directory object. The text ## of each chapter is written into a separate file with name ## <F>chap0.txt</F>, <F>chap1.txt</F>, ..., <F>chapBib.txt</F>, and ## <F>chapInd.txt</F>.<P/> ## ## If you want to make your document accessible via the &GAP; online ## help you must put at least these files for the text version ## into a directory, together with the file <F>manual.six</F>, ## see <Ref Func="PrintSixFile" />. Then specify the path to the ## <F>manual.six</F> file in the packages <F>PackageInfo.g</F> file, ## see <Ref Sect="The PackageInfo.g File" BookName="reference"/>. ## <P/> ## ## Optionally you can add the <C>dvi</C>- and <C>pdf</C>-versions of ## the document which are produced with <Ref Func="GAPDoc2LaTeX" ## /> to this directory. The files must have the names ## <F>manual.dvi</F> and <F>manual.pdf</F>, respectively. Also ## you can add the files of the HTML version produced with ## <Ref Func="GAPDoc2HTML" /> to this directory, see <Ref ## Func="GAPDoc2HTMLPrintHTMLFiles" />. The handler functions in ## &GAP; for this help format detect automatically which of the ## optional formats of a book are actually available. ## ## </Description> ## </ManSection> ## <#/GAPDoc> ## # arg: t (result of GAPDoc2Text)[, directory] InstallGlobalFunction(GAPDoc2TextPrintTextFiles, function(arg) local t, dir, a; t := arg[1]; if Length(arg)>1 then dir := arg[2]; if IsString(dir) then dir := Directory(dir); fi; else dir := Directory(""); fi; for a in NamesOfComponents(t) do FileString(Filename(dir,Concatenation("chap",a,".txt")), t.(a).text); od; end);