1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
# This script does some consistency checks of the manual.
#
# Run it in the 'doc/ref' directory
#
#
Read( "makedocreldata.g" );
doc := ComposedXMLString(
GAPInfo.ManualDataRef.pathtodoc,
GAPInfo.ManualDataRef.main,
GAPInfo.ManualDataRef.files,
true);;
# Detect which ManSection should be used to document obj. Returns one of
# "Func", "Oper", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass"
#
# See PRINT_OPERATION where some of the code below is borrowed
#
ManSectionType:=function( op )
local class, flags, types, catok, repok, propok, seenprop, t;
if IsInfoClass( op ) then
return "InfoClass";
elif IsFamily( op ) then
return "Fam";
elif not IsFunction( op ) then
return "Var";
elif IsFunction( op ) and not IsOperation( op ) then
return "Func";
elif IsOperation( op ) then
class := "Oper";
if IS_IDENTICAL_OBJ(op,IS_OBJECT) then
class := "Filt";
elif IS_CONSTRUCTOR(op) then
class := "Constructor"; # seem to never get one
elif IsFilter(op) then
class := "Filt";
flags := TRUES_FLAGS(FLAGS_FILTER(op));
types := [];
for t in flags do
AddSet(types, INFO_FILTERS[t]);
od;
catok := true;
repok := true;
propok := true;
seenprop := false;
for t in types do
if not t in FNUM_REPS then
repok := false;
fi;
if not t in FNUM_CATS then
catok := false;
fi;
if not t in FNUM_PROS and not t in FNUM_TPRS then
propok := false;
fi;
if t in FNUM_PROS then
seenprop := true;
fi;
od;
if seenprop and propok then
class := "Prop";
elif catok then
class := "Filt"; # in PRINT_OPERATION - "Category";
elif repok then
class := "Filt"; # in PRINT_OPERATION - "Representation";
fi;
elif Tester(op) <> false then
# op is an attribute
class := "Attr";
fi;
return class;
else
return fail;
fi;
end;
#
# Checks whether ManSections are using the right kind of elements
#
CheckManSectionTypes := function( doc, verbose... )
local types, type, r, s, t, x, y, yint, elt, stats, name, pos, obj, man,
matches, matches2, match, errcount, referrcount, warncount, display_warnings;
if Length( verbose ) = 0 then
display_warnings := false;
else
display_warnings := verbose[1];
fi;
types:=[ "Func", "Oper", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass" ];
r := ParseTreeXMLString(doc[1]);;
CheckAndCleanGapDocTree(r);
x := XMLElements( r, types );;
errcount:=0;
Print( "****************************************************************\n" );
Print( "*** Checking types in ManSections \n" );
Print( "****************************************************************\n" );
for elt in x do
name := elt.attributes.Name;
if not name in [ "IsBound", "Unbind", "Info", "Assert", "TryNextMethod", "QUIT", "-infinity" ] then
if EvalString( Concatenation("IsBound(", name, ")") ) <> true then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : ", name, " is unbound \n" );
errcount:=errcount+1;
else
obj := EvalString( name );
man := ManSectionType( obj );
# we allow to use "Meth" for "Oper", "Attr", "Prop" but issue a warning
# if there is no at least one "Oper", "Attr" or "Prop" for any "Meth"
if ( man <> elt.name ) and not ( man in ["Attr","Prop","Oper"] and elt.name="Meth") then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : ", name, " uses ", elt.name, " instead of ", man, "\n");
errcount:=errcount+1;
fi;
if elt.name="Meth" then
if Number( x, t -> t.attributes.Name=name and t.name in ["Attr","Prop","Oper"] ) = 0 then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : ", name, " uses Meth with no matching Oper/Attr/Prop\n" );
errcount:=errcount+1;
fi;
fi;
fi;
fi;
od;
Print( "****************************************************************\n" );
Print( "*** Checking types in cross-references \n" );
Print( "****************************************************************\n" );
# get all ref elements
y := XMLElements( r, [ "Ref" ] );
Print( "Found ", Length(y), " Ref elements " );
# select only those which point to the reference manual
yint := Filtered( y, elt ->
not IsBound(elt.attributes.BookName) or
(IsBound(elt.attributes.BookName) and elt.attributes.BookName="ref"));
Print( "including ", Length(yint), " within the Reference manual\n" );
# select only those which refer to one of these given in the list `types`:
# "Func", "Oper", "Meth", "Filt", "Prop", "Attr", "Var", "Fam", "InfoClass"
y := Filtered( yint, elt -> ForAny( types, t -> IsBound(elt.attributes.(t))));
referrcount:=0;
warncount:=0;
for elt in y do
type := First( types, t -> IsBound(elt.attributes.(t)));
if type <> fail then
matches := Filtered(x, t -> t.attributes.Name=elt.attributes.(type));
if Length(matches) = 0 then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : no match for ", type , ":=", elt.attributes.(type), "\n" );
referrcount:=referrcount+1;
continue;
elif Length(matches) = 1 then
match := matches[1];
elif IsBound(elt.attributes.Label) then
matches := Filtered( matches, t -> IsBound(t.attributes.Label));
matches := Filtered( matches, t -> t.attributes.Label=elt.attributes.Label);
if Length(matches) > 1 then
Error("Multiple labels - this should not happen!");
fi;
match := matches[1];
else
matches2 := Filtered( matches, t -> not IsBound(t.attributes.Label));
if Length(matches2)=0 then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : no match (wrong type or missing label?) for ", type , ":=", elt.attributes.(type), "\n" );
Print(" Suggestions: \n");
matches := Filtered( matches, t -> IsBound(t.attributes.Label));
for t in matches do
Print( "Use ", t.name, " with Label:=\"", t.attributes.Label, "\" (for Arg:=\"", t.attributes.Arg, "\")\n");
od;
referrcount:=referrcount+1;
continue;
elif Length(matches2) > 1 then
Error("Multiple labels - this should not happen!");
else
match := matches2[1];
fi;
fi;
if match.name <> type then
pos:=OriginalPositionDocument(doc[2],elt.start);
Print( pos[1], ":", pos[2], " : Ref to ", elt.attributes.(type), " uses ", type, " instead of ", match.name, "\n" );
Print( "./fixconsistency.sh '", ReplacedString(elt.attributes.(type), """\""", """\\\"""), "' ", type, " ", match.name, " ", pos[1], " ", pos[2], "\n" );
warncount:=warncount+1;
fi;
fi;
od;
Print( "****************************************************************\n" );
stats:=Collected(List(x, elt -> elt.name));
Print("Selected ", Length(x), " ManSections of the following types:\n");
for s in stats do
Print( s[1], " - ", s[2], "\n");
od;
Print( "Found ", errcount, " errors in ManSection types \n");
Print( "Selected ", Length(y), " Ref elements referring to ManSections \n" );
Print( "Found ", referrcount, " errors and ", warncount, " warnings in Ref elements \n");
Print( "****************************************************************\n" );
return referrcount=0 and warncount=0;
end;
CheckDocCoverage := function( doc )
local r, x, with, without, mansect, pos, y;
r := ParseTreeXMLString(doc[1]);
CheckAndCleanGapDocTree(r);
x:=XMLElements( r, ["ManSection"] );;
with:=0;
without:=0;
Print( "****************************************************************\n" );
Print( "*** Looking for ManSections having no examples \n" );
Print( "****************************************************************\n" );
for mansect in x do
pos:=OriginalPositionDocument(doc[2],mansect.start);
y := XMLElements( mansect, ["Example"] );
if Length(y)=0 then
if IsBound(mansect.content[1].attributes) and
IsBound(mansect.content[1].attributes.Name) then
Print( pos[1], ":", pos[2], " : ", mansect.content[1].attributes.Name );
elif IsBound(mansect.content[2].attributes) and
IsBound(mansect.content[2].attributes.Name) then
Print( pos[1], ":", pos[2], " : ", mansect.content[2].attributes.Name );
else
Print( pos[1], ":", pos[2], " : ", mansect.content[1].content[1].content );
fi;
without := without + 1;
Print("\n");
else
with := with + 1;
fi;
od;
Print( "****************************************************************\n" );
Print( "*** Doc coverage report \n");
Print( "****************************************************************\n" );
Print( Length(x), " mansections \n");
Print( with, " with examples \n");
Print( without , " without examples \n");
end;
# Uncomment next line to add ManSections without examples to the test log
# CheckDocCoverage(doc);
QuitGap( CheckManSectionTypes(doc) );
|