Skip to content
This repository was archived by the owner on Oct 25, 2025. It is now read-only.

Commit d337239

Browse files
committed
add doc string checker grader
1 parent 8b49237 commit d337239

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

lib/schema.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ const StyleGrader = BaseScorer.extend({
163163
penalty: z.number()
164164
*/
165165
})
166+
});
167+
168+
const DocChkGrader = BaseScorer.extend({
169+
type: z.literal("docchk"),
170+
config: z.strictObject({
171+
function: z.string(),
172+
}),
166173
})
167174

168175
const ImageArtifactGrader = BaseArtist.extend({
@@ -171,7 +178,7 @@ const ImageArtifactGrader = BaseArtist.extend({
171178
generator: z.string(),
172179
name: z.string(),
173180
}),
174-
})
181+
});
175182

176183
export const Grader = z.discriminatedUnion("type", [
177184
WellFormedGrader,
@@ -185,6 +192,7 @@ export const Grader = z.discriminatedUnion("type", [
185192
FeedbotGrader,
186193
ProgramInspectorGrader,
187194
StyleGrader,
195+
DocChkGrader,
188196
ImageArtifactGrader,
189197
]);
190198

src/graders/docchk.arr

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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

src/input.arr

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import npm("pyret-autograder", "main.arr") as A
2424
import mk-feedbot from file("./graders/feedbot.arr")
2525
import mk-program-inspector from file("./graders/program-inspector.arr")
2626
import mk-style from file("./graders/style.arr")
27+
import mk-docchk from file("./graders/docchk.arr")
28+
2729

2830
provide:
2931
process-spec,
@@ -167,6 +169,11 @@ fun convert-grader(
167169
| typ == "style" then:
168170
points = grader.get("points").and-then(expect-num).or-else(0)
169171
mk-style(id, deps, entry, entry-opt.value, points)
172+
| typ == "docchk" then:
173+
points = grader.get("points").and-then(expect-num).or-else(0)
174+
config = grader.get-value("config") ^ expect-obj
175+
fn-name = config.get-value("function") ^ expect-str
176+
mk-docchk(id, deps, entry, fn-name, points)
170177
| typ == "image-artifact" then:
171178
out = grader.get-value("out") ^ expect-str
172179
config = grader.get-value("config") ^ expect-obj

tests/scripts/shim.arr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ input = io.get-stdin()
2727
result = P.grade-pawtograder-spec(input)
2828

2929
debugging.print-json(result.serialize())
30-
debugging.print-json(J.tojson(result.native().get-value("tests").get(0).get-value("extra_data")).serialize())
30+
#debugging.print-json(J.tojson(result))

0 commit comments

Comments
 (0)