Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,179 @@
|
||||
use super::checked;
|
||||
|
||||
const HANDLERS: &str = "double(value: Int) (answer: Int) { answer = value * 2 }\npositive(value: Int) (answer: Bool) { answer = value > 0 }\nadd(value: Int, state: Int) (next: Int) { next = value + state }\nstop(value: Int, state: Int) (next: Int, stop: Bool) { next = value + state\n stop = next >= 3 }\n";
|
||||
const ERRORS: &str = "type Result { Ready(value: Int)\n error Failed(message: Text) }\nidentity(value: Result) (answer: Result) { answer = value }\nhandled(value: Result) (answer: Bool) recover(\"optional probe failure is explicitly discarded\") { answer = true }\ncontext(value: Int, state: Result) () recover(\"optional context probe is discarded\") {}\nkeep(value: Int, state: Result) (next: Result) { next = state }\nconsume(value: Result, state: Int) (next: Int) recover(\"count failed probes\") { next = state + 1 }\npartial(value: Result, state: Int) (next: Int, stop: Bool) recover(\"count failed probes\") { next = state + 1\n stop = true }\n";
|
||||
|
||||
#[test]
|
||||
fn checks_collection_handler_types_and_cycles() {
|
||||
for body in [
|
||||
"main() (answer: List<Int>) { answer = each(double, []) }",
|
||||
"main() (answer: List<Int>) { answer = filter(positive, [1, -1]) }",
|
||||
"main() (answer: Int?) { answer = find(positive, []) }",
|
||||
"main() (answer: Int) { answer = sum(double, []) }",
|
||||
"main() (answer: Float) { answer = sum([]) }",
|
||||
"main() (answer: Decimal?) { answer = sum([]) }",
|
||||
"main() (answer: Int) { answer = reduce(add, [], 10) }",
|
||||
"main() (answer: Int) { answer = reduceUntil(stop, [1, 2, 3], 0) }",
|
||||
] {
|
||||
let result = checked(&(HANDLERS.to_owned() + body));
|
||||
assert!(result.is_ok(), "{body}: {result:?}");
|
||||
}
|
||||
for body in [
|
||||
"main() (answer: List<Int>) { answer = each(positive, [1]) }",
|
||||
"main() (answer: List<Int>) { answer = filter(double, [1]) }",
|
||||
"main() (answer: Int?) { answer = find(positive, [\"one\"]) }",
|
||||
"main() (answer: Int) { answer = sum(positive, [1]) }",
|
||||
"main() (answer: Int) { answer = reduce(add, [1], true) }",
|
||||
"main() (answer: Int) { answer = reduceUntil(add, [1], 0) }",
|
||||
"main() (answer: Int) { answer = reduce(add, [1], 0, 1) }",
|
||||
"main() (answer: List<Int>) { double = 2\n answer = each(double, [1]) }",
|
||||
"main() () { handler = double }",
|
||||
] {
|
||||
assert!(checked(&(HANDLERS.to_owned() + body)).is_err(), "{body}");
|
||||
}
|
||||
assert_eq!(
|
||||
checked("cycle(value: Int) (answer: Int) { answer = sum(cycle, [value]) }")
|
||||
.unwrap_err()
|
||||
.code,
|
||||
"C007"
|
||||
);
|
||||
assert_eq!(checked("first(value: Int) (answer: Int) { answer = sum(second, [value]) }\nsecond(value: Int) (answer: Int) { answer = first(value) }").unwrap_err().code, "C007");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collections_preserve_error_obligations() {
|
||||
for body in [
|
||||
"main(values: List<Result>) (answer: Int) { copies = each(identity, values)\n answer = length(copies) }",
|
||||
"main(values: List<Result>) (answer: Result?) { answer = find(handled, values) }",
|
||||
"main(values: List<Result>) (answer: Int) { answer = reduceUntil(partial, values, 0) }",
|
||||
"main() (answer: Int) { answer = reduceUntil(partial, [Result.Failed(\"one\"), Result.Failed(\"two\")], 0) }",
|
||||
"main(state: Result) () { each(context, [], state) }",
|
||||
"main(values: List<Int>, state: Result) () { each(context, values, state) }",
|
||||
"main(state: Result) (answer: Int) { kept = reduce(keep, [], state)\n answer = 1 }",
|
||||
"main() (answer: List<Result>) { lost = Result.Failed(\"lost\")\n answer = each(identity, []) }",
|
||||
] {
|
||||
let result = checked(&(ERRORS.to_owned() + body));
|
||||
assert_eq!(result.unwrap_err().code, "C012", "{body}");
|
||||
}
|
||||
for body in [
|
||||
"main(values: List<Result>) (answer: List<Result>) { answer = each(identity, values) }",
|
||||
"main(values: List<Result>) (answer: Int) { answer = reduce(consume, values, 0) }",
|
||||
"main(state: Result) () { each(context, [1], state) }",
|
||||
"main() (answer: Result?) { answer = find(handled, [Result.Failed(\"one\")]) }",
|
||||
"main() (answer: Int) { answer = reduceUntil(partial, [Result.Failed(\"one\")], 0) }",
|
||||
"main(state: Result) (answer: Result) { answer = reduce(keep, [], state) }",
|
||||
] {
|
||||
let result = checked(&(ERRORS.to_owned() + body));
|
||||
assert!(result.is_ok(), "{body}: {result:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collection_error_summaries_survive_calls_and_repeated_state() {
|
||||
let prefix = ERRORS.to_owned()
|
||||
+ "copy(values: List<Result>) (answer: List<Result>) { answer = each(identity, values) }\nreplace(value: Result, state: Result) (next: Result) recover(\"last probe replaces previous probe\") { next = value }\n";
|
||||
for body in [
|
||||
"main(values: List<Result>) (answer: Int) { copies = copy(values)\n answer = length(copies) }",
|
||||
"main(values: List<Result>, state: Result) (answer: Int) { last = reduce(replace, values, state)\n answer = 1 }",
|
||||
"main(state: Result) () { copies = copy([])\n count = length(copies) }",
|
||||
] {
|
||||
assert_eq!(
|
||||
checked(&(prefix.clone() + body)).unwrap_err().code,
|
||||
"C012",
|
||||
"{body}"
|
||||
);
|
||||
}
|
||||
let source = prefix
|
||||
+ "main(values: List<Result>, state: Result) (answer: Result) { answer = reduce(replace, values, state) }";
|
||||
assert!(checked(&source).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_traversal_cannot_transfer_unvisited_obligations() {
|
||||
let prefix = ERRORS.to_owned()
|
||||
+ "takeContext(value: Int, context: Result) (answer: Result) { answer = context }\nfirst(value: Result, state: Result) (next: Result, stop: Bool) recover(\"replace previous state\") { next = value\n stop = true }\n";
|
||||
for body in [
|
||||
"main(values: List<Int>, state: Result) (answer: List<Result>) { answer = each(takeContext, values, state) }",
|
||||
"main(values: List<Result>) (answer: Result) { answer = reduceUntil(first, values, Result.Ready(0)) }",
|
||||
] {
|
||||
assert_eq!(
|
||||
checked(&(prefix.clone() + body)).unwrap_err().code,
|
||||
"C012",
|
||||
"{body}"
|
||||
);
|
||||
}
|
||||
assert!(checked(&(prefix + "main(state: Result) (answer: List<Result>) { answer = each(takeContext, [1], state) }")).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replacing_record_fields_still_checks_discarded_failures() {
|
||||
let source = ERRORS.to_owned()
|
||||
+ "type Row { result: Result\n count: Int }\nreplace(value: Row) (answer: Row) { answer = value\n answer.result = Result.Ready(1)\n answer.count = 0 }";
|
||||
assert_eq!(checked(&source).unwrap_err().code, "C012");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_length_map_builds_reserve_once_and_update_in_place() {
|
||||
for (kind, item, value, sequence) in [
|
||||
("Bytes", "Int", "value", "values"),
|
||||
("List<Int>", "Int", "value", "values"),
|
||||
(
|
||||
"Map<Int, Int>",
|
||||
"MapEntry<Int, Int>",
|
||||
"value.value",
|
||||
"entries(values)",
|
||||
),
|
||||
] {
|
||||
let text = format!(
|
||||
"insert(value: {item}, state: Map<Int, Int>) (next: Map<Int, Int>) {{ next = put(state, length(state), {value}) }}\nmain(values: {kind}) (answer: Map<Int, Int>) {{ answer = reduce(insert, {sequence}, {{}}) }}"
|
||||
);
|
||||
let source = dever::syntax::source::Source::new(0, "case.dever", text);
|
||||
let program = checked(&source.text).unwrap();
|
||||
let generated = dever::compiler::emit::generate(&program, &[source], &[1].into());
|
||||
let main = generated.split("pub fn function1(").nth(1).unwrap();
|
||||
let main = main.split("pub fn ").next().unwrap();
|
||||
assert!(
|
||||
main.contains("Map::with_capacity(sequence.len())"),
|
||||
"{main}"
|
||||
);
|
||||
assert!(main.contains("state.insert("), "{main}");
|
||||
assert!(!main.contains("state.clone()"), "{main}");
|
||||
assert!(!main.contains(".to_owned()"), "{main}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_reductions_keep_a_single_growing_buffer() {
|
||||
let text = "type State { values: List<Int>\n count: Int }\nstep(value: Int, state: State) (next: State) { values = append(state.values, value)\n next = State { values = values\n count = state.count + 1 } }\nmain(input: Bytes) (answer: State) { answer = reduce(step, input, State { values = []\n count = 0 }) }";
|
||||
let source = dever::syntax::source::Source::new(0, "case.dever", text.into());
|
||||
let program = checked(text).unwrap();
|
||||
let generated = dever::compiler::emit::generate(&program, &[source], &[1].into());
|
||||
let main = generated.split("pub fn function1(").nth(1).unwrap();
|
||||
let main = main.split("\npub ").next().unwrap();
|
||||
assert!(!main.contains(".clone()"), "{main}");
|
||||
let iteration = main.split("for byte in sequence").nth(1).unwrap();
|
||||
let iteration = iteration.split("Type0 {").next().unwrap();
|
||||
assert!(!iteration.contains(".reserve("), "{main}");
|
||||
assert!(iteration.contains(".push(element)"), "{main}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_selection_copies_only_matching_entries() {
|
||||
for (operation, result) in [
|
||||
("filter", "List<MapEntry<Text, Text>>"),
|
||||
("find", "MapEntry<Text, Text>?"),
|
||||
] {
|
||||
let text = format!(
|
||||
"matches(entry: MapEntry<Text, Text>, target: Text) (answer: Bool) {{ answer = entry.value == target }}\nmain(values: Map<Text, Text>, target: Text) (answer: {result}) {{ answer = {operation}(matches, entries(values), target) }}"
|
||||
);
|
||||
let source = dever::syntax::source::Source::new(0, "case.dever", text.clone());
|
||||
let program = checked(&text).unwrap();
|
||||
let generated = dever::compiler::emit::generate(&program, &[source], &[1].into());
|
||||
let main = generated.split("pub fn function1(").nth(1).unwrap();
|
||||
let main = main.split("\npub ").next().unwrap();
|
||||
let iteration = main.split("for (key, value) in sequence").nth(1).unwrap();
|
||||
let (inspection, selected) = iteration.split_once("if ").unwrap();
|
||||
assert!(!inspection.contains(".clone()"), "{main}");
|
||||
assert!(selected.contains("(key.clone(), value.clone())"), "{main}");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user