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 ComposeXML.gi GAPDoc Frank Lübeck ## ## #Y Copyright (C) 2000, Frank Lübeck, Lehrstuhl D für Mathematik, #Y RWTH Aachen ## ## The files ComposeXML.gi/.gd contain a function which allows to construct ## a GAPDoc-XML document from several source files. ## These tools can also be used for collection/extracting other types of ## documents. ## ## <#GAPDoc Label="ComposedDocument"> ## <ManSection > ## <Func Arg="tagname, path, main, source[, info]" Name="ComposedDocument" /> ## <Func Arg="path, main, source[, info]" Name="ComposedXMLString" /> ## <Returns>a document as string, or a list with this string and ## information about the source positions</Returns> ## <Description> ## ## The argument <A>tagname</A> is the string used for the pseudo elements ## which mark the pieces of a document to collect. (In <Ref Sect="DistrConv"/> ## we used <C>GAPDoc</C> as <A>tagname</A>. The second function ## <Ref Func="ComposedXMLString"/><C>( ... )</C> is an abbreviation for ## <Ref Func="ComposedDocument"/><C>("GAPDoc", ... )</C>.<P/> ## ## The argument <A>path</A> must be a path to some directory (as ## string or directory object), <A>main</A> the name of a file and ## <A>source</A> a list of file names. These file names are relative ## to <A>path</A>, except they start with <C>"/"</C> to specify an ## absolute path or they start with <C>"gap://"</C> to specify a ## file relative to the &GAP; roots (see <Ref Func="FilenameGAP" ## />). The document is constructed via the mechanism described in ## Section <Ref Sect="DistrConv"/>.<P/> ## ## First the files given in <A>source</A> are scanned for ## chunks of the document marked by <C><#<A>tagname</A> ## Label="..."></C> and <C></#<A>tagname</A>></C> pairs. ## Then the file <A>main</A> is read and all <C><#Include ... ## ></C>-tags are substituted recursively by other files or chunks ## of documentation found in the first step, respectively.<P/> ## ## If the optional argument <A>info</A> is given and set to ## <K>true</K> this function returns a list <C>[str, origin]</C>, ## where <C>str</C> is a string containing the composed document and ## <C>origin</C> is a sorted list of entries of the form <C>[pos, ## filename, line]</C>. Here <C>pos</C> runs through all character ## positions of starting lines or text pieces from different files in ## <C>str</C>. The <C>filename</C> and <C>line</C> describe the ## origin of this part of the collected document.<P/> ## ## Without the fourth argument only the string <C>str</C> is returned. ## <P/> ## ## By default <Ref Func="ComposedDocument"/> runs into an error if an ## <C><#Include ...></C>-tag cannot be substituted (because a file ## or chunk is missing). This behaviour can be changed by setting ## <C>DOCCOMPOSEERROR := false;</C>. Then the missing parts are ## substituted by a short note about what is missing. Of course, this ## feature is only useful if the resulting document is a valid XML ## document (e.g., when the missing pieces are complete paragraphs or ## sections).<P/> ## ## <Log> ## gap> doc := ComposedDocument("GAPDoc", "/my/dir", "manual.xml", ## > ["../lib/func.gd", "../lib/func.gi"], true);; ## </Log> ## </Description> ## </ManSection> ## <#/GAPDoc> ## # reset this if not found files or chunks should not run into an error DOCCOMPOSEERROR := true; InstallGlobalFunction(ComposedDocument, function(arg) local path, main, source, info, tagname, btag, etag, pieces, origin, fname, str, posnl, i, j, pre, pos, name, piece, b, len, Collect, res, src, f, a, usedpieces, lenb, NormalizedFilename; # get arguments, 5th arg is optional for compatibility with older versions tagname := arg[1]; btag := Concatenation("<#", tagname, " Label=\""); lenb := Length(btag); etag := Concatenation("<#/", tagname, ">"); path := arg[2]; main := arg[3]; source := arg[4]; if Length(arg) > 4 and arg[5] = true then info := true; else info := false; fi; if IsString(path) then path := Directory(path); fi; # utility NormalizedFilename := function(str) local res; if Length(str) > 6 and str{[1..6]} = "gap://" then res := FilenameGAP(str); elif Length(str) = 0 or str[1] <> '/' then res := Filename(path, str); else res := str; fi; if res = fail then res := str; fi; return res; end; # first we fetch the chunks from the source files pieces := rec(); origin := rec(); for f in source do fname := NormalizedFilename(f); Info(InfoGAPDoc, 2, "#I ComposedDocument: Searching for chunks in ", fname, "\n"); str := StringFile(fname); if str = fail then Info(InfoGAPDoc, 1, "#W WARNING: no file ", fname, " to compose document.\n"); continue; fi; posnl := Positions(str, '\n'); i := PositionSublist(str, btag); while i <> fail do j := i-1; while j > 0 and str[j] <> '\n' do j := j-1; od; pre := str{[j+1..i-1]}; pos := Position(str, '\"', i+lenb-1); if pos=fail then Error(f, ": File ends within <#", tagname, " tag.\n"); fi; name := str{[i+lenb..pos-1]}; i := Position(str, '\n', pos); if i=fail then Error(f, ": File ends within <#", tagname, " piece.\n"); fi; pos := PositionSublist(str, etag, i); if pos=fail then Error(f, ": File ends within <#", tagname, " piece.\n"); fi; while str[pos-1] <> '\n' do pos := pos-1; od; piece := SplitString(str{[i+1..pos-1]}, "\n", ""); for a in [1..Length(piece)] do b := 1; len := Minimum(Length(piece[a]), Length(pre)); while b <= len and pre[b] = piece[a][b] do b := b+1; od; if b > 1 then piece[a] := piece[a]{[b..Length(piece[a])]}; fi; od; for a in piece do Add(a, '\n'); od; Info(InfoGAPDoc, 3, "Found piece ", name, "\n"); if IsBound(pieces.(name)) then Info(InfoGAPDoc, 1, "#W WARNING: overwriting piece with label \"", name,"\"\n#W Previous occurrence: ",origin.(name)[1], " line ", origin.(name)[2],"\n", "#W New occurrence: ",fname," line ",PositionSorted(posnl, i+1),"\n"); fi; pieces.(name) := Concatenation(piece); # for each found piece store the filename and number of the first # line of the piece in that file origin.(name) := [fname, PositionSorted(posnl, i+1)]; i := PositionSublist(str, btag, pos); od; od; # we do some bookkeeping which pieces are actually used usedpieces := []; # recursive substitution of files and chunks from above # In this helper [cont, from] is a pair [piece, orig] from above # or a pair [filename, 0]. Collect := function(res, src, cont, from) local posnl, pos, i, len, new, p, j, piece, fname; # if piece is a whole file we simulate info as in 'pieces' if from = 0 then fname := cont; cont := StringFile(fname); if cont = fail and DOCCOMPOSEERROR = true then Error("Cannot include file ", fname, ".\n"); elif cont = fail then cont := Concatenation("MISSING FILE ", fname, "\n"); from := [fname, 1]; else from := [fname, 1]; fi; fi; posnl := Positions(cont, '\n'); pos := 0; while pos <> fail do i := PositionSublist(cont, "<#Include ", pos); if i = fail then # in this case add the rest to res i := Length(cont) + 1; fi; len := Length(res); new := cont{[pos+1..i-1]}; Append(res, new); p := PositionSorted(posnl, pos+1) + from[2] - 1; # add entry to 'src' for first character from current piece Add(src, [len+1, from[1], p]); j := Position(new, '\n'); while j <> fail and j < Length(new) do # further entries to 'src' for each new line in current piece Add(src, [len+j+1, from[1], p+1]); j := Position(new, '\n', j); p := p+1; od; # now include by recursive call of this function if i <= Length(cont) then pos := Position(cont, '>', i); if pos = fail then Error("Input ends within <#Include ... tag."); fi; piece := SplitString(cont{[i+9..pos-1]}, "", "\"= "); if piece[1]="SYSTEM" then Collect(res, src, NormalizedFilename(piece[2]), 0); elif piece[1]="Label" then if not IsBound(pieces.(piece[2])) and DOCCOMPOSEERROR=true then Error("Did not find chunk ", piece[2]); elif not IsBound(pieces.(piece[2])) then pieces.(piece[2]) := Concatenation("MISSING CHUNK ", piece[2]); origin.(piece[2]) := [Concatenation("MISSINGCHUNK ",piece[2]),1]; fi; Add(usedpieces, piece[2]); Collect(res, src, pieces.(piece[2]), origin.(piece[2])); fi; else pos := fail; fi; od; end; res := ""; src := []; # now start the recursion as #Include of the main file in empty string Collect(res, src, NormalizedFilename(main), 0); Info(InfoGAPDoc, 2, "#I Labels of chunks which were not used: ", Difference(RecNames(pieces), usedpieces), "\n"); if info then return [res, src]; else # we allow this for compatibility with former versions return res; fi; end); InstallGlobalFunction(ComposedXMLString, function(arg) return CallFuncList(ComposedDocument, Concatenation(["GAPDoc"], arg)); end); ## <#GAPDoc Label="OriginalPositionDocument"> ## <ManSection > ## <Func Arg="srcinfo, pos" Name="OriginalPositionDocument" /> ## <Returns>A pair <C>[filename, linenumber]</C>.</Returns> ## <Description> ## Here <A>srcinfo</A> must be a data structure as returned as ## second entry by <Ref Func="ComposedDocument" /> called with ## <A>info</A>=<K>true</K>. It returns for a given position <A>pos</A> in ## the composed document the file name and line number from which that ## text was collected. ## </Description> ## </ManSection> ## <#/GAPDoc> InstallGlobalFunction(OriginalPositionDocument, function(srcinfo, pos) local r; r := PositionSorted(srcinfo, [pos]); if not IsBound(srcinfo[r]) or srcinfo[r][1] > pos then r := r-1; fi; return [srcinfo[r][2], srcinfo[r][3]]; end); ## Utility for file names ## <#GAPDoc Label="FilenameGAP"/> ## <ManSection > ## <Func Arg="fname" Name="FilenameGAP"/> ## <Returns>file name as string or fail</Returns> ## <Description> ## ## This functions returns the full path of a file with name <A>fname</A> ## relative to a &GAP; root path, or <K>fail</K> if such a file does not ## exist. The argument <A>fname</A> can optionally start with the prefix ## <C>"gap://"</C> which will be removed. ## ## <Log> ## gap> FilenameGAP("hsdkfhs.g"); ## fail ## gap> FilenameGAP("lib/system.g"); ## "/usr/local/gap4/lib/system.g" ## gap> FilenameGAP("gap://lib/system.g"); ## "/usr/local/gap4/lib/system.g" ## </Log> ## </Description> ## </ManSection> ## <#/GAPDoc> InstallGlobalFunction(FilenameGAP, function(fpath) if Length(fpath) > 5 and fpath{[1..6]} = "gap://" then fpath := fpath{[7..Length(fpath)]}; fi; return Filename(List(GAPInfo.RootPaths, Directory), fpath); end);