1+ #|
2+ Copyright (C) 2025 dbp <dbp@dbpmail.net>
3+
4+ This file is part of pyret-autograder-pawtograder.
5+
6+ pyret-autograder-pawtograder is free software: you can redistribute it and/or
7+ modify it under the terms of the GNU Lesser General Public License as
8+ published by the Free Software Foundation, either version 3 of the License,
9+ or (at your option) any later version.
10+
11+ pyret-autograder-pawtograder is distributed in the hope that it will be
12+ useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14+ General Public License for more details.
15+
16+ You should have received a copy of the GNU Lesser General Public License
17+ with pyret-autograder-pawtograder. If not, see <http://www.gnu.org/licenses/>.
18+ |#
19+
20+ import npm( "pyret-autograder" , "main.arr" ) as AG
21+ import ast as A
22+ import npm( "pyret-autograder" , "common/ast.arr" ) as CA
23+ import npm( "pyret-lang" , "../../src/arr/compiler/ast-util.arr" ) as AU
24+ import json as J
25+ import string-dict as SD
26+ import filesystem as FS
27+ import lists as L
28+
29+ include either
30+
31+
32+ provide :
33+ data DocsInfo ,
34+ mk-docchk,
35+ end
36+
37+ MIN-DOC-LEN = 10
38+
39+ data DocsInfo :
40+ | parser-error( err :: CA . ParsePathErr ) # this should've been caught by wf
41+ | fn-not-defined( name :: String ) # this should have been caught by function-defined
42+ | docs-missing-or-short( name :: String )
43+ | docs-present
44+ end
45+
46+ fun check-docs(
47+ path :: String ,
48+ name :: String
49+ ) -> DocsInfo :
50+ cases ( Either ) CA . parse-path( path) :
51+ | left( err) => parser-error( err)
52+ | right( ast) =>
53+ ast-ended = AU . append-nothing-if-necessary( ast)
54+ cases ( A . Program ) ast-ended:
55+ | s-program( _, _, _, _, _, _, body) =>
56+ cases ( A . Expr ) body:
57+ | s-block( _, stmts) => find-docs( stmts, name)
58+ | else => fn-not-defined( name)
59+ end
60+ end
61+ end
62+ end
63+
64+ fun find-docs(
65+ stmts :: List <A . Expr >,
66+ fn-name :: String
67+ ) -> DocsInfo :
68+ cases ( List ) stmts:
69+ | empty => fn-not-defined( fn-name)
70+ | link( st, rest) =>
71+ cases ( A . Expr ) st:
72+ | s-fun( _, actual-name, _, args, _, doc, _, _, _, _) =>
73+ if actual-name == fn-name:
74+ if string-length( doc) >= MIN-DOC-LEN:
75+ docs-present
76+ else :
77+ docs-missing-or-short( fn-name)
78+ end
79+ else :
80+ find-docs( rest, fn-name)
81+ end
82+ | else => find-docs( rest, fn-name)
83+ end
84+ end
85+ end
86+
87+ fun fmt-docs( score, info :: DocsInfo ) -> AG . ComboAggregate :
88+ general = cases ( DocsInfo ) info:
89+ | parse-err( _) => AG . output-text( "couldn't parse" )
90+ | fn-not-defined( _) => AG . output-text( "function missing" )
91+ | docs-missing-or-short( _) =>
92+ AG . output-markdown( "Function either missing docstring, or docstring not sufficient." )
93+ | docs-present =>
94+ AG . output-markdown( "Doc string present." )
95+ end
96+ staff = none
97+ { general; staff}
98+ end
99+
100+ fun mk-docchk(
101+ id :: AG . Id , deps :: List <AG . Id >, path :: String , fn-name :: String , max-score :: Number
102+ ) :
103+ name = "Doc String Checker"
104+ scorer = lam () :
105+ info = check-docs( path, fn-name)
106+ cases ( DocsInfo ) info:
107+ | docs-present => right( { 1 ; docs-present} )
108+ | else => right( { 0 ; info} )
109+ end
110+ end
111+ fmter = fmt-docs
112+ part = some( fn-name)
113+ AG . mk-simple-scorer( id, deps, scorer, name, max-score, fmter, part)
114+ end
0 commit comments