Skip to content

Commit

Permalink
changed exercise format to use Zipper.t instead of strings, and creat…
Browse files Browse the repository at this point in the history
…ed exercise export functionality for instructors
  • Loading branch information
cyrus- committed Sep 21, 2022
1 parent 943b1fb commit eb8584b
Show file tree
Hide file tree
Showing 17 changed files with 6,272 additions and 214 deletions.
Empty file added .ocamlformat
Empty file.
1 change: 1 addition & 0 deletions opam.export
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ installed: [
"ocaml_intrinsics.v0.15.2"
"ocamlbuild.0.14.1"
"ocamlfind.1.9.5"
"ocamlformat.0.24.1"
"ocamlformat-rpc-lib.0.22.4"
"ocplib-endian.1.2"
"octavius.1.2.2"
Expand Down
12 changes: 12 additions & 0 deletions src/haz3lcore/zipper/Zipper.re
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ let init: int => t =
// col_target: 0,
};

let next_blank = id => {
(id + 1, init(id));
};

[@deriving (show({with_path: false}), sexp, yojson)]
type state = (t, IdGen.state);

Expand Down Expand Up @@ -310,3 +314,11 @@ let caret_point = (measured, z: t): Measured.Point.t => {
let Measured.Point.{row, col} = base_point(measured, z);
{row, col: col + Caret.offset(z.caret)};
};

let serialize = (z: t): string => {
sexp_of_t(z) |> Sexplib.Sexp.to_string;
};

let deserialize = (data: string): t => {
Sexplib.Sexp.of_string(data) |> t_of_sexp;
};
26 changes: 13 additions & 13 deletions src/haz3lweb/Grading.re
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ let score_view = ((earned: points, max: points)) => {
module TestValidationReport = {
type t = {
test_results: option(Interface.test_results),
num_required: int,
minimum: int,
required: int,
provided: int,
};

let mk =
(eds: SchoolExercise.eds, test_results: option(Interface.test_results)) => {
{
test_results,
num_required: eds.your_tests.num_required,
minimum: eds.your_tests.minimum,
required: eds.your_tests.required,
provided: eds.your_tests.provided,
};
};

Expand All @@ -42,20 +42,20 @@ module TestValidationReport = {
| None => 0.0
| Some(test_results) =>
let num_tests = float_of_int(test_results.total);
let num_required = float_of_int(report.num_required);
let minimum = float_of_int(report.minimum);
let required = float_of_int(report.required);
let provided = float_of_int(report.provided);
let num_passing = float_of_int(test_results.passing);

num_required -. minimum <= 0.0 || num_tests <= 0.0
required -. provided <= 0.0 || num_tests <= 0.0
? 0.0
: num_passing
/. num_tests
*. (
Float.max(
0.,
Float.min(num_tests -. minimum, num_required -. minimum),
Float.min(num_tests -. provided, required -. provided),
)
/. (num_required -. minimum)
/. (required -. provided)
);
};
};
Expand All @@ -79,13 +79,13 @@ module TestValidationReport = {
| Some(test_results) => [
{
let total_tests = test_results.total;
let num_required = report.num_required;
let required = report.required;
let num_tests_message =
total_tests >= num_required
? "at least " ++ string_of_int(num_required)
total_tests >= required
? "at least " ++ string_of_int(required)
: string_of_int(test_results.total)
++ " of "
++ string_of_int(report.num_required);
++ string_of_int(report.required);
text(
"Entered "
++ num_tests_message
Expand Down
136 changes: 97 additions & 39 deletions src/haz3lweb/SchoolExercise.re
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type hidden_tests('code) = {
[@deriving (show({with_path: false}), sexp, yojson)]
type your_tests('code) = {
tests: 'code,
num_required: int,
minimum: int,
required: int,
provided: int,
};

[@deriving (show({with_path: false}), sexp, yojson)]
Expand All @@ -38,7 +38,9 @@ type p('code) = {
next_id: Id.t,
title: string,
version: int,
prompt: [@opaque] Node.t,
module_name: string,
prompt:
[@printer (fmt, _) => Format.pp_print_string(fmt, "prompt")] [@opaque] Node.t,
point_distribution,
prelude: 'code,
correct_impl: 'code,
Expand Down Expand Up @@ -70,7 +72,7 @@ type pos =
| HiddenTests;

[@deriving (show({with_path: false}), sexp, yojson)]
type spec = p(CodeString.t);
type spec = p(Zipper.t);

[@deriving (show({with_path: false}), sexp, yojson)]
type eds = p(Editor.t);
Expand Down Expand Up @@ -226,17 +228,14 @@ let switch_editor = (idx: int, {eds, _}) => {
eds,
};

let editor_of_code = (init_id, code) =>
switch (EditorUtil.editor_of_code(init_id, code)) {
| None => failwith("Exercise error: invalid code")
| Some(x) => x
};
let editor_of_serialization = zipper => Editor.init(zipper);
let eds_of_spec: spec => eds =
(
{
next_id,
title,
version,
module_name,
prompt,
point_distribution,
prelude,
Expand All @@ -247,39 +246,29 @@ let eds_of_spec: spec => eds =
hidden_tests,
},
) => {
let id = next_id;
let (id, prelude) = editor_of_code(id, prelude);
let (id, correct_impl) = editor_of_code(id, correct_impl);
let (id, your_tests) = {
let (id, tests) = editor_of_code(id, your_tests.tests);
(
id,
{
tests,
num_required: your_tests.num_required,
minimum: your_tests.minimum,
},
);
let prelude = editor_of_serialization(prelude);
let correct_impl = editor_of_serialization(correct_impl);
let your_tests = {
let tests = editor_of_serialization(your_tests.tests);
{tests, required: your_tests.required, provided: your_tests.provided};
};
let (id, your_impl) = editor_of_code(id, your_impl);
let (id, hidden_bugs) =
List.fold_left(
((id, acc), {impl, hint}) => {
let (id, impl) = editor_of_code(id, impl);
(id, acc @ [{impl, hint}]);
},
(id, []),
hidden_bugs,
);
let (id, hidden_tests) = {
let your_impl = editor_of_serialization(your_impl);
let hidden_bugs =
hidden_bugs
|> List.map(({impl, hint}) => {
let impl = editor_of_serialization(impl);
{impl, hint};
});
let hidden_tests = {
let {tests, hints} = hidden_tests;
let (id, tests) = editor_of_code(id, tests);
(id, {tests, hints});
let tests = editor_of_serialization(tests);
{tests, hints};
};
{
next_id: id,
next_id,
title,
version,
module_name,
prompt,
point_distribution,
prelude,
Expand Down Expand Up @@ -336,7 +325,7 @@ let unpersist_state =
if (visible_in(pos, ~instructor_mode)) {
(id, Editor.init(List.assoc(pos, positioned_zippers)));
} else {
editor_of_code(id, default);
(next_id, editor_of_serialization(default));
};
let id = next_id;
let (id, prelude) = lookup(id, Prelude, spec.prelude);
Expand All @@ -363,14 +352,15 @@ let unpersist_state =
next_id: id,
title: spec.title,
version: spec.version,
module_name: spec.module_name,
prompt: spec.prompt,
point_distribution: spec.point_distribution,
prelude,
correct_impl,
your_tests: {
tests: your_tests_tests,
num_required: spec.your_tests.num_required,
minimum: spec.your_tests.minimum,
required: spec.your_tests.required,
provided: spec.your_tests.provided,
},
your_impl,
hidden_bugs,
Expand Down Expand Up @@ -618,3 +608,71 @@ let focus = (state: state, stitched_dynamics: stitched(DynamicsItem.t)) => {
};
(focal_zipper, focal_info_map);
};

// Module Export

let editor_pp = (fmt, editor: Editor.t) => {
let zipper = editor.state.zipper;
let serialization = Zipper.show(zipper);
// let string_literal = "\"" ++ String.escaped(serialization) ++ "\"";
Format.pp_print_string(fmt, serialization);
};

let export_module = (module_name, {eds, _}: state) => {
let prefix =
"let prompt = "
++ module_name
++ "_prompt.prompt\n"
++ "let exercise: SchoolExercise.spec = ";
let record = show_p(editor_pp, eds);
let data = prefix ++ record;
print_endline(data);
data;
};

let blank_spec =
(
~title,
~module_name,
~point_distribution,
~required_tests,
~provided_tests,
~num_wrong_impls,
) => {
let id = 0;
let (id, prelude) = Zipper.next_blank(id);
let (id, correct_impl) = Zipper.next_blank(id);
let (id, your_tests_tests) = Zipper.next_blank(id);
let (id, your_impl) = Zipper.next_blank(id);
let (id, hidden_bugs) =
Util.ListUtil.init_fold(
num_wrong_impls,
id,
(i, id) => {
let (id, zipper) = Zipper.next_blank(id);
(id, {impl: zipper, hint: "TODO: hint " ++ string_of_int(i)});
},
);
let (id, hidden_tests_tests) = Zipper.next_blank(id);
{
next_id: id,
title,
version: 1,
module_name,
prompt: Node.text("TODO: prompt"),
point_distribution,
prelude,
correct_impl,
your_tests: {
tests: your_tests_tests,
required: required_tests,
provided: provided_tests,
},
your_impl,
hidden_bugs,
hidden_tests: {
tests: hidden_tests_tests,
hints: [],
},
};
};
5 changes: 3 additions & 2 deletions src/haz3lweb/SchoolSettings_base.re
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
let filename = "haz3l-demo";
let exercises: list(SchoolExercise.spec) = [
OddExercise.exercise,
FibExercise.exercise,
Ex_OddlyRecursive.exercise,
Ex_RecursiveFibonacci.exercise,
// FibExercise.exercise,
];
12 changes: 12 additions & 0 deletions src/haz3lweb/exercises/BlankTemplate.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(* copy this file, filling in the details below, to create a blank exercise that you can then edit in Hazel
and export, overwriting your copy of the below
for prompts, you need to define a separate file {module_name}_prompt.re (see examples)
*)

let exercise =
SchoolExercise.blank_spec ~title:"Recursive Fibonacci"
~module_name:"Ex_RecursiveFibonacci"
~point_distribution:
{ test_validation = 10; mutation_testing = 40; impl_grading = 50 }
~required_tests:5 ~provided_tests:0 ~num_wrong_impls:2
Loading

0 comments on commit eb8584b

Please sign in to comment.