Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+184
View File
@@ -0,0 +1,184 @@
use dever::{
compiler::{check, model::Program},
syntax::{diagnostic::Diagnostic, source::Source},
};
fn linked(text: &str) -> Result<Program, Diagnostic> {
let inputs = [
("main", text),
("dever.json", include_str!("../../../lib/dever/json.dever")),
(
"dever.json.value",
include_str!("../../../lib/dever/json/value.dever"),
),
];
let modules = inputs
.iter()
.enumerate()
.map(|(index, (_, text))| {
dever::syntax::parse(&Source::new(
index,
if index == 0 {
"main.dever".into()
} else {
format!("library{index}.dever")
},
(*text).into(),
))
})
.collect::<Result<Vec<_>, _>>()?;
check::check(
modules,
&inputs
.iter()
.map(|(package, _)| (*package).into())
.collect::<Vec<_>>(),
)
}
#[test]
fn encode_rejects_non_wire_types_and_untyped_empty_values() {
for source in [
"main(value: Secret) (answer: Json) { answer = dever.json.encode(value) }",
"type Row { value: Secret }\nmain(value: Secret) (answer: Json) { row = Row { value = value } answer = dever.json.encode(row) }",
"type Row { private values: List<Int> }\nmain(values: List<Int>) (answer: Json) { row = Row { values = values } answer = dever.json.encode(row) }",
"type Row { private value: Int }\nmain(value: Row) (answer: Json) { answer = dever.json.encode(value) }",
"type Row { value: Int > 0 }\nmain(values: List<Row?>) (answer: Json) { answer = dever.json.encode(values) }",
"type State { Ready(value: Int) }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }",
"type State { Ready\n error Failed }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }",
"main(value: Map<Text, Int>) (answer: Json) { answer = dever.json.encode(value) }",
"main(value: dever.system.File) (answer: Json) { answer = dever.json.encode(value) }",
"main() (answer: Json) { answer = dever.json.encode([]) }",
"main() (answer: Json) { answer = dever.json.encode(null) }",
] {
assert!(linked(source).is_err(), "{source}");
}
assert!(linked("type Row { amount: Decimal }\nmain(values: List<Row?>) (answer: Json) pure { answer = dever.json.encode(values) }").is_ok());
}
#[test]
fn payload_free_choices_use_the_existing_wire_codec() {
assert!(linked("type State { Ready\n Busy }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }").is_ok());
assert!(linked("type State { Ready\n Busy }\ntype Row { state: State\n optional: State?\n states: List<State> }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }").is_ok());
}
#[test]
fn typed_json_decode_can_be_captured_without_an_intermediate_function() {
let prefix = "type Row { name: Text }\ntype Result { Decoded(value: Row)\n error Invalid(cause: dever.json.JsonResult) }\n";
let program = linked(&format!("{prefix}main(payload: Json) (output: Result) {{ output = result(dever.json.decode(payload)) }}")).unwrap();
let main = program
.functions
.iter()
.find(|function| function.signature.name == "main")
.unwrap();
assert!(main.signature.failures.is_empty());
assert!(
linked(&format!(
"{prefix}main(payload: Json) () {{ lost: Result = result(dever.json.decode(payload)) }}"
))
.unwrap_err()
.code
== "C012"
);
let source = format!(
"{prefix}type Failure {{ error Missing }}\nraise() (payload: Json) {{ fail(Failure.Missing) }}\nmain() (output: Result) {{ output = result(dever.json.decode(raise())) }}"
);
let program = linked(&source).unwrap();
let main = program
.functions
.iter()
.find(|function| function.signature.name == "main")
.unwrap();
assert_eq!(main.signature.failures.len(), 1);
assert_eq!(
program.definitions[main.signature.failures[0].definition].name,
"Failure"
);
for source in [
format!("{prefix}main(payload: Json) () {{ lost = result(dever.json.decode(payload)) }}"),
"type Wrong { Done(value: Int)\n error Invalid(message: Text) }\nmain(payload: Json) (output: Wrong) { output = result(dever.json.decode(payload)) }".into(),
"type Limited { Done(value: Int > 0)\n error Invalid(cause: dever.json.JsonResult) }\nmain(payload: Json) (output: Limited) { output = result(dever.json.decode(payload)) }".into(),
] {
assert!(linked(&source).is_err(), "{source}");
}
}
#[test]
fn json_failures_preserve_capture_and_argument_propagation() {
let source = "type Failure { error Missing }\nraise() (value: Int) { fail(Failure.Missing) }\nmain() (answer: dever.json.JsonResult) { answer = result(dever.json.encode(raise())) }";
let program = linked(source).unwrap();
let main = program
.functions
.iter()
.find(|function| function.signature.name == "main")
.unwrap();
assert_eq!(main.signature.failures.len(), 1);
assert_eq!(
program.definitions[main.signature.failures[0].definition].name,
"Failure"
);
for expression in [
"dever.json.encode(1)",
"dever.json.propertyValue(dever.json.fromText(\"42\"), \"a\")",
] {
let source = format!("main() () {{ lost = result({expression}) }}");
assert_eq!(linked(&source).unwrap_err().code, "C012");
}
}
#[test]
fn typed_decode_requires_an_explicit_wire_type_and_propagates_errors() {
let source = "type Row { names: List<Text>\noptional: Int? }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }";
let program = linked(source).unwrap();
let main = program
.functions
.iter()
.find(|function| function.signature.name == "main")
.unwrap();
assert_eq!(main.signature.failures.len(), 1);
for source in [
"main(value: Json) () { row = dever.json.decode(value) }",
"main(value: Text) (row: Int) { row = dever.json.decode(value) }",
"type Row { private secret: Text }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }",
] {
assert!(linked(source).is_err(), "{source}");
}
}
#[test]
fn local_type_annotations_supply_context_without_wrapper_functions() {
assert!(linked("type Row { names: List<Text> }\nmain(value: Json) (names: List<Text>) { row: Row = dever.json.decode(value)\n names = row.names }").is_ok());
assert!(linked("main() (answer: Int) { values: List<Int> = []\n values = [1]\n answer = length(values) }").is_ok());
for (source, code) in [
("main(value: Int) () { value: Int = 1 }", "C002"),
("main() () { value = 1\n value: Int = 2 }", "C002"),
("main() () { value: Int = true }", "C005"),
("main() () { value: Int = 1\n value = true }", "C005"),
(
"main() () { value: Channel<Int?> = channel(Int?, 1) }",
"C013",
),
] {
assert_eq!(linked(source).unwrap_err().code, code, "{source}");
}
}
#[test]
fn document_is_a_nominal_source_type_and_intrinsics_remain_private() {
assert!(
linked("main(document: dever.json.value.Document) (value: Int) { value = document.root }")
.is_ok()
);
assert!(linked("type Document { root: Int }\nmain(document: Document) (value: Text) { value = dever.json.stringify(document) }").is_err());
for expression in [
"dever.system.jsonFromText(\"{}\")",
"dever.system.jsonObject({})",
] {
assert_eq!(
linked(&format!("main() () {{ value = {expression} }}"))
.unwrap_err()
.code,
"C006"
);
}
}