-- Copyright (c) 1991-2002, The Numerical Algorithms Group Ltd. -- All rights reserved. -- Copyright (C) 2007-2013, Gabriel Dos Reis. -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are -- met: -- -- - Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- -- - Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in -- the documentation and/or other materials provided with the -- distribution. -- -- - Neither the name of The Numerical Algorithms Group Ltd. nor the -- names of its contributors may be used to endorse or promote products -- derived from this software without specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import c_-util import daase namespace BOOT $checkPrenAlist == [[char "(",:char ")"],[char "{",:char "}"],[char "[",:char "]"]] batchExecute() == _/RF_-1 '(GENCON INPUT) ++ returns the documentation for operator `op' with given ++ `modemap', supposedly defined in domain or category ++ constructed from `conName'. getDoc(conName,op,modemap) == [dc,target,sl,pred,D] := simplifyModemap modemap sig := [target,:sl] cons? dc => sig := MSUSBT('$,dc,sig) sig := applySubst(pairList(dc.args,$FormalMapVariableList),sig) getDocForDomain(conName,op,sig) if argList := IFCDR getOfCategoryArgument pred then applySubst(pairList(argList,$FormalMapArgumentList),sig) sig := MSUBST('$,dc,sig) getDocForCategory(conName,op,sig) ++ Given a predicate `pred' for a modemap, returns the first ++ argument to the ofCategory predicate it contains. Return ++ nil otherwise. getOfCategoryArgument pred == pred is [fn,:.] and fn in '(AND OR NOT) => or/[getOfCategoryArgument x for x in rest pred] pred is ['ofCategory,'_*1,form] => form nil getDocForCategory(name,op,sig) == getOpDoc(getConstructorAbbreviationFromDB name,op,sig) or or/[getOpDoc(getConstructorAbbreviationFromDB x,op,sig) for x in whatCatCategories name] getDocForDomain(name,op,sig) == getOpDoc(getConstructorAbbreviationFromDB name,op,sig) or or/[getOpDoc(getConstructorAbbreviationFromDB x,op,sig) for x in whatCatExtDom name] ++ returns the documentation, known to the global DB, for a operator ++ `op' and given signature `sigPart'. The operator `op' is assumed ++ to have been defined in the domain or catagory `abb'. getOpDoc(abb,op,:sigPart) == u := symbolTarget(op,getConstructorDocumentationFromDB abb) $argList : local := $FormalMapVariableList _$: local := '_$ sigPart is [sig] => or/[d for [s,:d] in u | sig = s] u ++ Parse the content of source file `fn' only for the purpose of ++ documentation. readForDoc fn == $bootStrapMode: local:= true _/RQ_-LIB_-1 [fn,'SPAD] ++ record the documentation location for an operator wih given ++ signature. recordSignatureDocumentation(opSig,lineno) == recordDocumentation(rest postTransform opSig,lineno) recordAttributeDocumentation(['ATTRIBUTE,att],lineno) == name := opOf att upperCase? stringChar(symbolName name,0) => nil recordDocumentation([name,['attribute,:IFCDR postTransform att]],lineno) recordDocumentation(key,lineno) == recordHeaderDocumentation lineno u:= collectComBlock lineno --record nil to mean "there was no documentation" $maxSignatureLineNumber := lineno $docList := [[key,:u],:$docList] -- leave first of $docList alone as required by collectAndDeleteAssoc recordHeaderDocumentation lineno == if $maxSignatureLineNumber = 0 then al := [p for (p := [n,:u]) in $COMBLOCKLIST | null n or null lineno or n < lineno] $COMBLOCKLIST := SETDIFFERENCE($COMBLOCKLIST,al) $headerDocumentation := ASSOCRIGHT al if $headerDocumentation then $maxSignatureLineNumber := 1 --see postDef $headerDocumentation collectComBlock x == $COMBLOCKLIST is [[=x,:val],:.] => u := [:val,:collectAndDeleteAssoc x] $COMBLOCKLIST := rest $COMBLOCKLIST u collectAndDeleteAssoc x collectAndDeleteAssoc x == --u is (.. (x . a) .. (x . b) .. ) ==> (a b ..) deleting entries from u --assumes that the first element is useless for y in tails $COMBLOCKLIST | (s := rest y) repeat while s and first s is [=x,:r] repeat res := [:res,:r] s := rest s y.rest := s res instantiateNiladicsInDoc! docList == for [x,:.] in docList | x is [.,:.] repeat x.opSig := instantiateNiladicsInList! x.opSig docList finalizeDocumentation db == ctor := dbConstructor db unusedCommentLineNumbers := [x for (x := [n,:r]) in $COMBLOCKLIST | r] docList := instantiateNiladicsInDoc! substitute!("$","%",transDocList($op,$docList)) if u := [sig for [sig,:doc] in docList | null doc] then for y in u repeat y is 'constructor => noHeading := true y is [x,b] and b is ['attribute,:r] => attributes := [[x,:r],:attributes] signatures := [y,:signatures] if noHeading or signatures or attributes or unusedCommentLineNumbers then sayKeyedMsg("S2CD0001",nil) bigcnt := 1 if noHeading or signatures or attributes then sayKeyedMsg("S2CD0002",[strconc(STRINGIMAGE bigcnt,'"."),ctor]) bigcnt := bigcnt + 1 litcnt := 1 if noHeading then sayKeyedMsg("S2CD0003", [strconc('"(",toString litcnt,'")"),ctor]) litcnt := litcnt + 1 if signatures then sayKeyedMsg("S2CD0004", [strconc('"(",toString litcnt,'")")]) litcnt := litcnt + 1 for [op,sig] in signatures repeat s := formatOpSignature(op,sig) sayMSG s isnt [.,:.] => ['%x9,s] ['%x9,:s] if attributes then sayKeyedMsg("S2CD0005", [strconc('"(",toString litcnt,'")")]) litcnt := litcnt + 1 for x in attributes repeat a := form2String x sayMSG a isnt [.,:.] => ['%x9,a] ['%x9,:a] if unusedCommentLineNumbers then sayKeyedMsg("S2CD0006",[strconc(toString bigcnt,'"."),ctor]) for [n,r] in unusedCommentLineNumbers repeat sayMSG ['" ",:bright n,'" ",r] form := dbConstructorForm db hn [[:fn(sig,$e,form.args),:doc] for [sig,:doc] in docList] where fn(x,e,args) == x isnt [.,:.] => [x,nil] if #x > 2 then x := take(2,x) applySubst(pairList(args,$FormalMapVariableList),macroExpand(x,e)) hn u == -- ((op,sig,doc), ...) --> ((op ((sig doc) ...)) ...) opList := removeDuplicates ASSOCLEFT u [[op,:[[sig,doc] for [op1,sig,doc] in u | op = op1]] for op in opList] --======================================================================= -- Transformation of ++ comments --======================================================================= transDocList($constructorName,doclist) == --returns ((key line)...) --called ONLY by finalizeDocumentation --if $exposeFlag then messages go to file $outStream; flag=nil by default sayBrightly ['" Processing ",$constructorName,'" for Browser database:"] commentList := transDoc($constructorName,doclist) acc := nil for entry in commentList repeat entry is ['constructor,x] => conEntry => checkDocError ['"Spurious comments: ",x] conEntry := entry acc := [entry,:acc] conEntry => [conEntry,:acc] checkDocError1 ['"Missing Description"] acc $attribute? := nil ++ Given a functor `conname', and a list of documenation strings, ++ sanity-check the documentation. In particular extract information ++ such as `Description', etc. transDoc(conname,doclist) == --$exposeFlag and not isExposedConstructor conname => nil --skip over unexposed constructors when checking system files $x: local := nil rlist := reverse doclist for [$x,:lines] in rlist repeat $attribute? : local := $x is [.,[key]] and key is 'attribute null lines => $attribute? => nil checkDocError1 ['"Not documented!!!!"] u := checkTrim($x,(string? lines => [lines]; $x is 'constructor => first lines; lines)) $argl : local := nil --set by checkGetArgs -- tpd: related domain information doesn't exist -- if v := checkExtract('"Related Domains:",u) then -- $lisplibRelatedDomains:=[w for x in gn(v) | w := fn(x)] where -- gn(v) == --note: unabbrev checks for correct number of arguments -- s := checkExtractItemList v -- parse := ncParseFromString s --is a single conform or a tuple -- null parse => nil -- parse is ['Tuple,:r] => r -- [parse] -- fn(x) == -- expectedNumOfArgs := checkNumOfArgs x -- null expectedNumOfArgs => -- checkDocError ['"Unknown constructor name?: ",opOf x] -- x -- expectedNumOfArgs ~= (n := #(IFCDR x)) => -- n = 0 => checkDocError1 -- ['"You must give arguments to the _"Related Domain_": ",x] -- checkDocError -- ['"_"Related Domain_" has wrong number of arguments: ",x] -- nil -- n=0 and x isnt [.,:.] => [x] -- x longline := $x is 'constructor => v :=checkExtract('"Description:",u) or u and checkExtract('"Description:", [strconc('"Description: ",first u),:rest u]) transformAndRecheckComments('constructor,v or u) transformAndRecheckComments($x,u) acc := [[$x,longline],:acc] --processor assumes a list of lines reverse! acc checkExtractItemList l == --items are separated by commas or end of line acc := nil --l is list of remaining lines while l repeat --stop when you get to a line with a colon m := maxIndex first l k := charPosition(char ":",first l,0) k <= m => return nil acc := [first l,:acc] l := rest l strconc/[x for x in reverse! acc] ++ Translate '%' in signature to '%%' for proper printing. escapePercent x == x is [y, :z] => y1 := escapePercent y z1 := escapePercent z sameObject?(y, y1) and sameObject?(z, z1) => x [y1, :z1] x = "%" => "%%" x transformAndRecheckComments(name,lines) == $checkingXmptex? := false $x : local := name $name : local := 'GlossaryPage $origin : local := 'gloss $recheckingFlag : local := false $exposeFlagHeading : local := name isnt [.,:.] => ['" -- ",name] concat('" --",formatOpSignature(name.0, escapePercent name.1)) if not $exposeFlag then sayBrightly $exposeFlagHeading u := checkComments(name,lines) $recheckingFlag := true checkRewrite(name,[u]) $recheckingFlag := false u checkRewrite(name,lines) == main where --similar to checkComments from c-doc main() == $checkErrorFlag: local := true margin := 0 lines := checkRemoveComments lines u := lines if $checkingXmptex? then u := [checkAddIndented(x,margin) for x in u] $argl := checkGetArgs first u --set $argl u2 := nil verbatim := nil for x in u repeat w := newString2Words x verbatim => w and first w is '"\end{verbatim}" => verbatim := false u2 := append(u2, w) u2 := append(u2, [x]) w and first w is '"\begin{verbatim}" => verbatim := true u2 := append(u2, w) u2 := append(u2, w) u := u2 u := checkAddSpaces u u := checkSplit2Words u u := checkAddMacros u u := checkTexht u -- checkBalance u okBefore := not $checkErrorFlag checkArguments u if $checkErrorFlag then u := checkFixCommonProblem u checkRecordHash u -- u := checkTranVerbatim u checkDecorateForHt u checkTexht u == count := 0 acc := nil while u repeat x := first u if x is '"\texht" and (u := IFCDR u) then if not (IFCAR u = $charLbrace) then checkDocError '"First left brace after \texht missing" count := 1 -- drop first argument including braces of \texht while ((y := IFCAR (u := rest u))~= $charRbrace or count > 1) repeat if y = $charLbrace then count := count + 1 if y = $charRbrace then count := count - 1 x := IFCAR (u := rest u) -- drop first right brace of 1st arg if x is '"\httex" and (u := IFCDR u) and (IFCAR u = $charLbrace) then acc := [IFCAR u,:acc] --left brace: add it while (y := IFCAR (u := rest u)) ~= $charRbrace repeat (acc := [y,:acc]) acc := [IFCAR u,:acc] --right brace: add it x := IFCAR (u := rest u) --left brace: forget it while IFCAR (u := rest u) ~= $charRbrace repeat 'skip x := IFCAR (u := rest u) --forget right brace: move to next char acc := [x,:acc] u := rest u reverse! acc checkRecordHash u == while u repeat x := first u if string? x and stringChar(x,0) = $charBack then if member(x,$HTlinks) and (u := checkLookForLeftBrace IFCDR u) and (u := checkLookForRightBrace IFCDR u) and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then htname := intern IFCAR u entry := tableValue($htHash,htname) or [nil] tableValue($htHash,htname) := [first entry,:[[$name,:$origin],:rest entry]] else if member(x,$HTlisplinks) and (u := checkLookForLeftBrace IFCDR u) and (u := checkLookForRightBrace IFCDR u) and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then htname := intern checkGetLispFunctionName checkGetStringBeforeRightBrace u entry := tableValue($lispHash,htname) or [nil] tableValue($lispHash,htname) := [first entry,:[[$name,:$origin],:rest entry]] else if ((p := member(x,'("\gloss" "\spadglos"))) or (q := member(x,'("\glossSee" "\spadglosSee")))) and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then if q then u := checkLookForRightBrace u u := checkLookForLeftBrace IFCDR u u := IFCDR u htname := intern checkGetStringBeforeRightBrace u entry := tableValue($glossHash,htname) or [nil] tableValue($glossHash,htname) := [first entry,:[[$name,:$origin],:rest entry]] else if x is '"\spadsys" and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then s := checkGetStringBeforeRightBrace u if stringChar(s,0) = char ")" then s := subString(s,1) parse := checkGetParse s null parse => checkDocError ['"Unparseable \spadtype: ",s] not member(opOf parse,$currentSysList) => checkDocError ['"Bad system command: ",s] parse isnt [.,:.] or (parse isnt ['set,arg]) => 'ok ---assume ok not spadSysChoose($setOptions,arg) => checkDocError ['"Incorrect \spadsys: ",s] entry := tableValue($sysHash,htname) or [nil] tableValue($sysHash,htname) := [first entry,:[[$name,:$origin],:rest entry]] else if x is '"\spadtype" and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then s := checkGetStringBeforeRightBrace u parse := checkGetParse s null parse => checkDocError ['"Unparseable \spadtype: ",s] n := checkNumOfArgs parse null n => checkDocError ['"Unknown \spadtype: ", s] parse isnt [.,:.] and n > 0 => 'skip null (key := checkIsValidType parse) => checkDocError ['"Unknown \spadtype: ", s] key isnt [.,:.] => 'ok checkDocError ['"Wrong number of arguments: ",form2HtString key] else if x in '("\spadop" "\keyword") and (u := checkLookForLeftBrace IFCDR u) and (u := IFCDR u) then x := intern checkGetStringBeforeRightBrace u not (property(x,'Led) or property(x,'Nud)) => checkDocError ['"Unknown \spadop: ",x] u := rest u 'done checkGetParse s == ncParseFromString removeBackslashes s ++ remove non-leading backslash characters from string `s'. removeBackslashes s == s is '"" => '"" (k := charPosition($charBack,s,0)) < #s => k = 0 => removeBackslashes subString(s,1) strconc(subString(s,0,k),removeBackslashes subString(s,k + 1)) s ++ returns the arity (as known to the global DB) of the functor ++ instantiated by `conform'. Returns nil when `conform' does ++ not imply aknown functor. checkNumOfArgs conform == conname := opOf conform constructor? conname or (conname := abbreviation? conname) => getConstructorArity conname nil --signals error ++ returns ok if correct, form if wrong number of arguments, nil if unknown ++ The check is down recursively on the argument to the instantiated functor. checkIsValidType form == main where main() == form isnt [.,:.] => 'ok [op,:args] := form op = ":" => args is [.,t] and checkIsValidType t builtinConstructor? op => and/[checkIsValidType t for t in args] conname := (constructor? op => op; abbreviation? op) null conname => nil fn(form,getDualSignature conname) fn(form,coSig) == #form ~= #coSig => form or/[null checkIsValidType x for x in form.args for flag in rest coSig | flag] => nil 'ok checkGetLispFunctionName s == n := #s (k := charPosition(char "|",s,1)) and k < n and (j := charPosition(char "|",s,k + 1)) and j < n => subString(s,k + 1,j-k-1) checkDocError ['"Ill-formed lisp expression : ",s] 'illformed checkGetStringBeforeRightBrace u == acc := nil while u repeat x := first u x = $charRbrace => return strconc/(reverse! acc) acc := [x,:acc] u := rest u -- checkTranVerbatim u == -- acc := nil -- while u repeat -- x := first u -- x is '"\begin" and checkTranVerbatimMiddle u is [middle,:r] => -- acc := [$charRbrace,:middle,$charLbrace,'"\spadpaste",:acc] -- u := r -- if x is '"\spadcommand" then x := '"\spadpaste" -- acc := [x,:acc] -- u := rest u -- reverse! acc -- -- checkTranVerbatimMiddle u == -- (y := IFCAR (v := IFCDR u)) = $charLbrace and -- (y := IFCAR (v := IFCDR v)) is '"verbatim" and -- (y := IFCAR (v := IFCDR v)) = $charRbrace => -- w := IFCDR v -- middle := nil -- while w and (z := first w) isnt '"\end" repeat -- middle := [z,:middle] -- w := rest w -- if (y := IFCAR (w := IFCDR w)) = $charLbrace and -- (y := IFCAR (w := IFCDR w)) is '"verbatim" and -- (y := IFCAR (w := IFCDR w)) = $charRbrace then -- u := IFCDR w -- else -- checkDocError '"Missing \end{verbatim}" -- u := w -- [middle,:u] -- -- checkTranVerbatim1 u == -- acc := nil -- while u repeat -- x := first u -- x is '"\begin" and (y := IFCAR (v := IFCDR u)) = $charLbrace and -- (y := IFCAR (v := IFCDR v)) is '"verbatim" and -- (y := IFCAR (v := IFCDR v)) = $charRbrace => -- w := IFCDR v -- middle := nil -- while w and (z := first w) isnt '"\end" repeat -- middle := [z,:middle] -- w := rest w -- if (y := IFCAR (w := IFCDR w)) = $charLbrace and -- (y := IFCAR (w := IFCDR w)) is '"verbatim" and -- (y := IFCAR (w := IFCDR w)) = $charRbrace then -- u := IFCDR w -- acc := [$charRbrace,:middle,$charLbrace,'"\spadpaste",:acc] -- if x is '"\spadcommand" then x := '"\spadpaste" -- acc := [x,:acc] -- u := rest u -- reverse! acc appendOver [head,:tail] == acc := lastNode head for x in tail repeat end := lastNode x acc.rest := x acc := end head checkRemoveComments lines == while lines repeat do line := checkTrimCommented first lines if firstNonBlankPosition line >= 0 then acc := [line,:acc] lines := rest lines reverse! acc ++ return the part of `line' that is not a comment. A comment ++ is introduced by a leading percent character (%), or a double ++ percent character (%%). checkTrimCommented line == n := #line k := htcharPosition(char "%",line,0) --line beginning with % is a comment k = 0 => '"" --remarks beginning with %% are comments k >= n - 1 or line.(k + 1) ~= char "%" => line k < #line => subString(line,0,k) line htcharPosition(char,line,i) == m := #line k := charPosition(char,line,i) k = m => k k > 0 => line.(k - 1) ~= $charBack => k htcharPosition(char,line,k + 1) 0 checkAddMacros u == acc := nil verbatim := false while u repeat x := first u acc := x is '"\end{verbatim}" => verbatim := false [x, :acc] verbatim => [x, :acc] x is '"\begin{verbatim}" => verbatim := true [x, :acc] y := LASSOC(x,$HTmacs) => [:y,:acc] [x,:acc] u := rest u reverse! acc checkComments(nameSig,lines) == main where main() == $checkErrorFlag: local := false margin := checkGetMargin lines if null $attribute? and nameSig isnt 'constructor then lines := [checkTransformFirsts(first nameSig,first lines,margin),:rest lines] u := checkIndentedLines(lines, margin) $argl := checkGetArgs first u --set $argl u2 := nil verbatim := nil for x in u repeat w := newString2Words x verbatim => w and first w is '"\end{verbatim}" => verbatim := false u2 := append(u2, w) u2 := append(u2, [x]) w and first w is '"\begin{verbatim}" => verbatim := true u2 := append(u2, w) u2 := append(u2, w) u := u2 u := checkAddSpaces u u := checkIeEg u u := checkSplit2Words u checkBalance u okBefore := null $checkErrorFlag checkArguments u if $checkErrorFlag then u := checkFixCommonProblem u v := checkDecorate u res := strconc/[y for y in v] res := checkAddPeriod res if $checkErrorFlag then pp res res checkIndentedLines(u, margin) == verbatim := false u2 := nil for x in u repeat k := firstNonBlankPosition x k = -1 => verbatim => u2 := [:u2, $charFauxNewline] u2 := [:u2, '"\blankline "] s := subString(x, k) s is '"\begin{verbatim}" => verbatim := true u2 := [:u2, s] s is '"\end{verbatim}" => verbatim := false u2 := [:u2, s] verbatim => u2 := [:u2, subString(x, margin)] margin = k => u2 := [:u2, s] u2 := [:u2, strconc('"\indented{",toString(k-margin),'"}{",checkAddSpaceSegments(s,0),'"}")] u2 newString2Words l == not string? l => [l] m := maxIndex l m = -1 => nil i := 0 [w while newWordFrom(l,i,m) is [w,i]] newWordFrom(l,i,m) == while i <= m and stringChar(l,i) = char " " repeat i := i + 1 i > m => nil buf := '"" ch := stringChar(l,i) ch = $charFauxNewline => [$stringFauxNewline, i+ 1] done := false while i <= m and not done repeat ch := stringChar(l,i) ch = $charBlank or ch = $charFauxNewline => done := true buf := strconc(buf, charString ch) i := i + 1 [buf,i] checkAddPeriod s == --No, just leave blank at the end (rdj: 10/18/91) m := maxIndex s lastChar := stringChar(s,m) lastChar = char "!" or lastChar = char "?" or lastChar = char "." => s lastChar = char "," or lastChar = char ";" => stringChar(s,m) := char "." s s checkGetArgs u == not string? u => nil m := maxIndex u k := firstNonBlankPosition(u) k > 0 => checkGetArgs subString(u,k) stringPrefix?('"\spad{",u) => k := getMatchingRightPren(u,6,char "{",char "}") or m checkGetArgs subString(u,6,k-6) (i := charPosition(char "(",u,0)) > m => nil stringChar(u,m) ~= char ")" => nil while (k := charPosition($charComma,u,i + 1)) < m repeat acc := [trimString subString(u,i + 1,k - i - 1),:acc] i := k reverse! [subString(u,i + 1,m - i - 1),:acc] checkGetMargin lines == while lines repeat do x := first lines k := firstNonBlankPosition x k = -1 => nil margin := (margin => MIN(margin,k); k) lines := rest lines margin or 0 firstNonBlankPosition(x,:options) == start := IFCAR options or 0 k := -1 for i in start..maxIndex x repeat if stringChar(x,i) ~= $charBlank then return (k := i) k checkAddIndented(x,margin) == k := firstNonBlankPosition x k = -1 => '"\blankline " margin = k => x strconc('"\indented{",toString(k-margin),'"}{",checkAddSpaceSegments(subString(x,k),0),'"}") checkAddSpaceSegments(u,k) == m := maxIndex u i := charPosition($charBlank,u,k) m < i => u j := i while (j := j + 1) < m and stringChar(u,j) = char " " repeat 'continue n := j - i --number of blanks n > 1 => strconc(subString(u,0,i),'"\space{", toString n,'"}",checkAddSpaceSegments(subString(u,i + n),0)) checkAddSpaceSegments(u,j) checkTrim($x,lines) == main where main() == s := [wherePP first lines] for x in rest lines repeat j := wherePP x if not scalarMember?(j,s) then checkDocError [$x,'" has varying indentation levels"] s := [j,:s] [trim y for y in lines] wherePP(u) == k := charPosition($charPlus,u,0) k = #u or charPosition($charPlus,u,k + 1) ~= k + 1 => systemError '" Improper comment found" k trim(s) == k := wherePP(s) return subString(s,k + 2) m := maxIndex s n := k + 2 for j in (k + 2)..m while stringChar(s,j) = $charBlank repeat (n := n + 1) subString(s,n) checkExtract(header,lines) == while lines repeat line := first lines k := firstNonBlankPosition line --k gives margin of Description: substring?(header,line,k) => return nil lines := rest lines null lines => nil u := first lines j := charPosition(char ":",u,k) margin := k firstLines := (k := firstNonBlankPosition(u,j + 1)) ~= -1 => [subString(u,j + 1),:rest lines] rest lines --now look for another header; if found skip all rest of these lines acc := nil for line in firstLines repeat do m := #line (k := firstNonBlankPosition line) = -1 => 'skip --include if blank k > margin => 'skip --include if idented not upperCase? stringChar(line,k) => 'skip --also if not upcased (j := charPosition(char ":",line,k)) = m => 'skip --or if not colon, or (i := charPosition(char " ",line,k+1)) < j => 'skip --blank before colon return nil acc := [line,:acc] reverse! acc checkFixCommonProblem u == acc := nil while u repeat x := first u x = $charLbrace and member(next := IFCAR rest u,$HTspadmacros) and (IFCAR IFCDR rest u ~= $charLbrace) => checkDocError ['"Reversing ",next,'" and left brace"] acc := [$charLbrace,next,:acc] --reverse order of brace and command u := rest rest u acc := [x,:acc] u := rest u reverse! acc checkDecorate u == count := 0 -- number of enclosing opening braces spadflag := false --means OK to wrap single letter words with \s{} mathSymbolsOk := false acc := nil verbatim := false -- true if inside `verbatim' environment while u repeat x := first u if not verbatim then if x is '"\em" then if count > 0 then mathSymbolsOk := count - 1 spadflag := count - 1 else checkDocError ['"\em must be enclosed in braces"] if string? x and x in '("\spadpaste" "\spad" "\spadop") then mathSymbolsOk := count if string? x and x in '("\s" "\spadtype" "\spadsys" "\example" "\andexample" "\spadop" "\spad" "\spadignore" "\spadpaste" "\spadcommand" "\footnote") then spadflag := count else if x = $charLbrace then count := count + 1 else if x = $charRbrace then count := count - 1 if mathSymbolsOk = count then mathSymbolsOk := false if spadflag = count then spadflag := false else if not mathSymbolsOk and x in '("+" "*" "=" "==" "->") then if $checkingXmptex? then checkDocError ["Symbol ",x,'" appearing outside \spad{}"] acc := x is '"\end{verbatim}" => verbatim := false [x, :acc] verbatim => [x, :acc] x is '"\begin{verbatim}" => verbatim := true [x, :acc] x is '"\begin" and first(v := IFCDR u) = $charLbrace and first(v := IFCDR v) is '"detail" and first(v := IFCDR v) = $charRbrace => u := v ['"\blankline ",:acc] x is '"\end" and first(v := IFCDR u) = $charLbrace and first(v := IFCDR v) is '"detail" and first(v := IFCDR v) = $charRbrace => u := v acc char? x and x = char "$" or x is '"$" => ['"\$",:acc] char? x and x = char "%" or x is '"%" => ['"\%",:acc] char? x and x = char "," or x is '"," => spadflag => ['",",:acc] ['",{}",:acc] x is '"\spad" => ['"\spad",:acc] string? x and digit? stringChar(x,0) => [x,:acc] not spadflag and (char? x and alphabetic? x and not charMember?(x,$charExclusions) or member(x,$argl)) => [$charRbrace,x,$charLbrace,'"\spad",:acc] not spadflag and string? x and ((x.0 ~= $charBack and digit?(x.(maxIndex x))) or x in '("true" "false")) => [$charRbrace,x,$charLbrace,'"\spad",:acc] --wrap x1, alpha3, etc xcount := (string? x => # x; 0) xcount = 3 and x.1 = char "t" and x.2 = char "h" => ['"th",$charRbrace,x.0,$charLbrace,'"\spad",:acc] xcount = 4 and x.1 = char "-" and x.2 = char "t" and x.3 = char "h" => ['"-th",$charRbrace,x.0,$charLbrace,'"\spad",:acc] not spadflag and (xcount = 2 and x.1 = char "i" or --wrap ei, xi, hi xcount > 0 and xcount < 4 and not x in '("th" "rd" "st") and hasNoVowels x) => --wrap words with no vowels [$charRbrace,x,$charLbrace,'"\spad",:acc] [checkAddBackSlashes x,:acc] u := rest u reverse! acc hasNoVowels x == max := maxIndex x stringChar(x,max) = char "y" => false and/[not isVowel stringChar(x,i) for i in 0..max] isVowel c == c=char "a" or c=char "e" or c=char "i" or c=char "o" or c=char "u" or c=char "A" or c=char "E" or c=char "I" or c=char "O" or c=char "U" checkAddBackSlashes s == (char? s and (c := s)) or (#s = 1 and (c := stringChar(s,0))) => charMember?(s,$charEscapeList) => strconc($charBack,charString c) s k := 0 m := maxIndex s insertIndex := nil while k <= m repeat do c := stringChar(s,k) c = $charBack => k := k + 2 charMember?(c,$charEscapeList) => return (insertIndex := k) k := k + 1 insertIndex => checkAddBackSlashes strconc(subString(s,0,insertIndex),$charBack,s.k,subString(s,insertIndex + 1)) s checkAddSpaces u == null u => nil null rest u => u space := $charBlank u2 := nil for i in 1.. for f in u repeat -- want newlines before and after begin/end verbatim and between lines -- since this might be written to a file, we can't really use -- newline characters. The Browser and HD will do the translation -- later. if f is '"\begin{verbatim}" then space := $charFauxNewline if null u2 then u2 := [space] if i > 1 then u2 := [:u2, space, f] else u2 := [:u2, f] if f is '"\end{verbatim}" then u2 := [:u2, space] space := $charBlank u2 checkIeEg u == acc := nil verbatim := false while u repeat x := first u acc := x is '"\end{verbatim}" => verbatim := false [x, :acc] verbatim => [x, :acc] x is '"\begin{verbatim}" => verbatim := true [x, :acc] z := checkIeEgfun x => [:reverse! z,:acc] [x,:acc] u := rest u reverse! acc checkIeEgfun x == CHARP x => nil x is '"" => nil m := maxIndex x for k in 0..(m - 3) repeat x.(k + 1) = $charPeriod and x.(k + 3) = $charPeriod and (x.k = char "i" and x.(k + 2) = char "e" and (key := '"that is") or x.k = char "e" and x.(k + 2) = char "g" and (key := '"for example")) => firstPart := (k > 0 => [subString(x,0,k)]; nil) result := [:firstPart,'"\spadignore{",subString(x,k,4),'"}", :checkIeEgfun subString(x,k+4)] result checkSplit2Words u == acc := nil while u repeat x := first u acc := x is '"\end{verbatim}" => verbatim := false [x, :acc] verbatim => [x, :acc] x is '"\begin{verbatim}" => verbatim := true [x, :acc] z := checkSplitBrace x => [:reverse! z,:acc] [x,:acc] u := rest u reverse! acc checkSplitBrace x == CHARP x => [x] #x = 1 => [x.0] (u := checkSplitBackslash x) and rest u => "append"/[checkSplitBrace y for y in u] m := maxIndex x (u := checkSplitOn x) and rest u => "append"/[checkSplitBrace y for y in u] (u := checkSplitPunctuation x) and rest u => "append"/[checkSplitBrace y for y in u] [x] checkSplitBackslash x == not string? x => [x] m := maxIndex x (k := charPosition($charBack,x,0)) < m => m = 1 or alphabetic?(x . (k + 1)) => --starts with a backslash so.. (k := charPosition($charBack,x,1)) < m => --..see if there is another [subString(x,0,k),:checkSplitBackslash subString(x,k)] -- yup [x] --no, just return line k = 0 => --starts with backspace but x.1 is not a letter; break it up [subString(x,0,2),:checkSplitBackslash subString(x,2)] u := subString(x,0,k) v := subString(x,k,2) k + 1 = m => [u,v] [u,v,:checkSplitBackslash subString(x,k + 2)] [x] checkSplitPunctuation x == not string? x => [x] m := maxIndex x m < 1 => [x] lastchar := x.m lastchar = $charPeriod and stringChar(x,m - 1) = $charPeriod => m = 1 => [x] m > 3 and stringChar(x,m-2) = $charPeriod => [:checkSplitPunctuation subString(x,0,m-2),'"..."] [:checkSplitPunctuation subString(x,0,m-1),'".."] lastchar = $charPeriod or lastchar = $charSemiColon or lastchar = $charComma => [subString(x,0,m),lastchar] m > 1 and stringChar(x,m - 1) = $charQuote => [subString(x,0,m - 1),subString(x,m-1)] (k := charPosition($charBack,x,0)) < m => k = 0 => m = 1 or tableValue($htMacroTable,x) or alphabetic? x.1 => [x] v := subString(x,2) [subString(x,0,2),:checkSplitPunctuation v] u := subString(x,0,k) v := subString(x,k) [:checkSplitPunctuation u,:checkSplitPunctuation v] (k := charPosition($charDash,x,1)) < m => u := subString(x,k + 1) [subString(x,0,k),$charDash,:checkSplitPunctuation u] [x] checkSplitOn(x) == not string? x => [x] l := $charSplitList m := maxIndex x while l repeat char := first l do m = 0 and stringChar(x,0) = char => return (k := -1) --special exit k := charPosition(char,x,0) k > 0 and x.(k - 1) = $charBack => [x] k <= m => return k l := rest l null l => [x] k = -1 => [char] k = 0 => [char,subString(x,1)] k = maxIndex x => [subString(x,0,k),char] [subString(x,0,k),char,:checkSplitOn subString(x,k + 1)] checkBalance u == checkBeginEnd u stack := nil while u is [x,:u] repeat not char? x => nil closer := scalarTarget(x,$checkPrenAlist) => --is it an open bracket? stack := [closer,:stack] --yes, push the open bracket open := rassoc(x,$checkPrenAlist) => --it is a close bracket! stack is [top,:restStack] => --does corresponding open bracket match? x = top => stack := restStack --yes: just pop the stack checkDocError ['"Mismatch: left ",checkSayBracket open,'" matches right ",checkSayBracket x] checkDocError ['"Missing left ",checkSayBracket x] stack ~= nil => for x in reverse! stack repeat checkDocError ['"Missing right ",checkSayBracket x] nil ++ returns the class of the parenthesis x ++ pren ::= '(' | ')' ++ brace ::= '{' | '}' ++ bracket ::= '[' | ']' checkSayBracket x == x = char "(" or x = char ")" => '"pren" x = char "{" or x = char "}" => '"brace" '"bracket" checkBeginEnd u == beginEndStack := nil while u repeat do x := first u string? x and x.0 = $charBack and #x > 2 and not tableValue($htMacroTable,x) and not (x is '"\spadignore") and IFCAR IFCDR u = $charLbrace and not (substring?('"\radiobox",x,0) or substring?('"\inputbox",x,0))=> --allow 0 argument guys to pass through checkDocError ["Unexpected HT command: ",x] x is '"\beginitems" => beginEndStack := ["items",:beginEndStack] x is '"\begin" => u is [.,=$charLbrace,y,:r] and first r = $charRbrace => if not member(y,$beginEndList) then checkDocError ['"Unknown begin type: \begin{",y,'"}"] beginEndStack := [y,:beginEndStack] u := r checkDocError ['"Improper \begin command"] x is '"\item" => member(IFCAR beginEndStack,'("items" "menu")) => nil null beginEndStack => checkDocError ['"\item appears outside a \begin-\end"] checkDocError ['"\item appears within a \begin{",IFCAR beginEndStack,'"}.."] x is '"\end" => u is [.,=$charLbrace,y,:r] and first r = $charRbrace => y = IFCAR beginEndStack => beginEndStack := rest beginEndStack u := r checkDocError ['"Trying to match \begin{",IFCAR beginEndStack,'"} with \end{",y,"}"] checkDocError ['"Improper \end command"] u := rest u beginEndStack => checkDocError ['"Missing \end{",first beginEndStack,'"}"] 'ok checkArguments u == while u repeat do x := first u null (k := tableValue($htMacroTable,x)) => 'skip k = 0 => 'skip k > 0 => checkHTargs(x,rest u,k,nil) checkHTargs(x,rest u,-k,true) u := rest u u checkHTargs(keyword,u,nargs,integerValue?) == --u should start with an open brace ... nargs = 0 => 'ok if not (u := checkLookForLeftBrace u) then return checkDocError ['"Missing argument for ",keyword] if not (u := checkLookForRightBrace IFCDR u) then return checkDocError ['"Missing right brace for ",keyword] checkHTargs(keyword,rest u,nargs - 1,integerValue?) checkLookForLeftBrace(u) == --return line beginning with left brace while u repeat x := first u if x = $charLbrace then return u x ~= $charBlank => return nil u := rest u u checkLookForRightBrace(u) == --return line beginning with right brace count := 0 while u repeat x := first u do x = $charRbrace => count = 0 => return (found := u) count := count - 1 x = $charLbrace => count := count + 1 u := rest u found checkInteger s == CHARP s => false s is '"" => false and/[digit? stringChar(s,i) for i in 0..maxIndex s] checkTransformFirsts(opname,u,margin) == --case 1: \spad{... --case 2: form(args) --case 3: form arg --case 4: op arg --case 5: arg op arg namestring := opname is ['Zero] => '"0" opname is ['One] => '"1" symbolName opname margin > 0 => s := leftTrim u strconc(fillerSpaces margin,checkTransformFirsts(opname,s,0)) m := maxIndex u m < 2 => u stringChar(u,0) = $charBack => u alphabetic? u.0 => i := checkSkipToken(u,0,m) or return u j := checkSkipBlanks(u,i,m) or return u open := stringChar(u,j) open = char "[" and (close := char "]") or open = char "(" and (close := char ")") => k := getMatchingRightPren(u,j + 1,open,close) namestring ~= (firstWord := subString(u,0,i)) => checkDocError ['"Improper first word in comments: ",firstWord] u null k => if open = char "[" then checkDocError ['"Missing close bracket on first line: ", u] else checkDocError ['"Missing close parenthesis on first line: ", u] u strconc('"\spad{",subString(u,0,k + 1),'"}",subString(u,k + 1)) k := checkSkipToken(u,j,m) or return u infixOp := makeSymbol subString(u,j,k - j) null property(infixOp,'Led) => --case 3 namestring ~= (firstWord := subString(u,0,i)) => checkDocError ['"Improper first word in comments: ",firstWord] u #(p := symbolName infixOp) = 1 and (open := p.0) and (close := scalarTarget(open,$checkPrenAlist)) => --have an open bracket l := getMatchingRightPren(u,k + 1,open,close) if l > maxIndex u then l := k - 1 strconc('"\spad{",subString(u,0,l + 1),'"}",subString(u,l + 1)) strconc('"\spad{",subString(u,0,k),'"}",subString(u,k)) l := checkSkipBlanks(u,k,m) or return u n := checkSkipToken(u,l,m) or return u namestring ~= symbolName infixOp => checkDocError ['"Improper initial operator in comments: ",infixOp] u strconc('"\spad{",subString(u,0,n),'"}",subString(u,n)) --case 5 true => -- not alphabetic? u.0 => i := checkSkipToken(u,0,m) or return u namestring ~= (firstWord := subString(u,0,i)) => checkDocError ['"Improper first word in comments: ",firstWord] u prefixOp := makeSymbol subString(u,0,i) not property(prefixOp,'Nud) => u ---what could this be? j := checkSkipBlanks(u,i,m) or return u u.j = char "(" => --case 4 j := getMatchingRightPren(u,j + 1,char "(",char ")") j > m => u strconc('"\spad{",subString(u,0,j + 1),'"}",subString(u,j + 1)) k := checkSkipToken(u,j,m) or return u namestring ~= (firstWord := subString(u,0,i)) => checkDocError ['"Improper first word in comments: ",firstWord] u strconc('"\spad{",subString(u,0,k),'"}",subString(u,k)) getMatchingRightPren(u,j,open,close) == count := 0 m := maxIndex u for i in j..m repeat c := stringChar(u,i) do c = close => count = 0 => return (found := i) count := count - 1 c = open => count := count + 1 found checkSkipBlanks(u,i,m) == while i < m and stringChar(u,i) = $charBlank repeat i := i + 1 i = m => nil i checkSkipToken(u,i,m) == alphabetic? stringChar(u,i) => checkSkipIdentifierToken(u,i,m) checkSkipOpToken(u,i,m) checkSkipOpToken(u,i,m) == while i < m and (not(checkAlphabetic(u.i)) and not(member(u.i,$charDelimiters))) repeat i := i + 1 i = m => nil i checkSkipIdentifierToken(u,i,m) == while i < m and checkAlphabetic u.i repeat i := i + 1 i = m => nil i ++ returns true if character `c' is alphabetic. checkAlphabetic c == alphabetic? c or digit? c or charMember?(c,$charIdentifierEndings) --======================================================================= -- Code for creating a personalized report for ++ comments --======================================================================= docreport(nam) == --creates a report for person "nam" using file "whofiles" removeFile '"docreport.input" runCommand strconc('"echo _")bo setOutStream('",STRINGIMAGE nam,'")_" > temp.input") runCommand '"cat docreport.header temp.input > docreport.input" runCommand strconc('"awk '/",STRINGIMAGE nam,'"/ {printf(_")co %s.spad\n_",$2)}' whofiles > temp.input") runCommand '"cat docreport.input temp.input > temp1.input" runCommand '"cat temp1.input docreport.trailer > docreport.input" removeFile '"temp.input" removeFile '"temp1.input" $editFile := '"docreport.input" _/RQ() setOutStream nam == filename := strconc('"/tmp/",STRINGIMAGE nam,".docreport") $outStream := MAKE_-OUTSTREAM filename whoOwns(con) == null $exposeFlag => nil --con=constructor name (id beginning with a capital), returns owner as a string filename := getConstructorSourceFileFromDB con quoteChar := char "_"" runCommand strconc('"awk '$2 == ",quoteChar,filename,quoteChar,'" {print $1}' whofiles > /tmp/temp") instream := MAKE_-INSTREAM '"/tmp/temp" value := readLine instream SHUT instream value ~= %nothing => value nil --======================================================================= -- Report Documentation Error --======================================================================= ++ True if we are compiling only documentation. $compileDocumentation := false checkDocError1 u == --when compiling for documentation, ignore certain errors $compileDocumentation => nil checkDocError u checkDocError u == $checkErrorFlag := true msg := $recheckingFlag => $constructorName => checkDocMessage u concat('"> ",u) $constructorName => checkDocMessage u u if $exposeFlag and $exposeFlagHeading then sayBrightly1($exposeFlagHeading,$outStream) sayBrightly $exposeFlagHeading $exposeFlagHeading := nil sayBrightly msg if $exposeFlag then sayBrightly1(msg,$outStream) --if called by checkDocFile (see file checkdoc.boot) ++ Augment `u' with information about the owner of the source file ++ containing the current functor definition being processed. checkDocMessage u == sourcefile := getConstructorSourceFileFromDB $constructorName person := whoOwns $constructorName or '"---" middle := not null $x => ['"(",$x,'"): "] ['": "] concat(person,'">",sourcefile,'"-->",$constructorName,middle,u) checkDecorateForHt u == count := 0 spadflag := false --means OK to wrap single letter words with \s{} while u repeat x := first u do if x is '"\em" then if count > 0 then spadflag := count - 1 else checkDocError ['"\em must be enclosed in braces"] if x in '("\s" "\spadop" "\spadtype" "\spad" "\spadpaste" "\spadcommand" "\footnote") then spadflag := count else if x = $charLbrace then count := count + 1 else if x = $charRbrace then count := count - 1 if spadflag = count then spadflag := false else if not spadflag and x in '("+" "*" "=" "==" "->") then if $checkingXmptex? then checkDocError ["Symbol ",x,'" appearing outside \spad{}"] x is '"$" or x is '"%" => checkDocError ['"Unescaped ",x] -- not spadflag and string? x and (member(x,$argl) or #x = 1 -- and alphabetic? x.0) and not (x in '("a" "A")) => -- checkDocError1 ['"Naked ",x] -- not spadflag and string? x and (not x.0 = $charBack and not digit?(x.0) and digit? stringChar(x,maxIndex x) or x in '("true" "false")) -- => checkDocError1 ["Naked ",x] u := rest u u