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

+138
View File
@@ -0,0 +1,138 @@
use dever::compiler::{check, model::Program};
use dever::syntax::{diagnostic::Diagnostic, source::Source};
fn linked(text: &str, identity: &str) -> Result<Program, Diagnostic> {
let sources = [
Source::new(0, "main.dever", text.into()),
Source::new(
1,
"lib/dever/io.dever",
include_str!("../../../../lib/dever/io.dever").into(),
),
];
let modules = sources
.iter()
.map(dever::syntax::parse)
.collect::<Result<Vec<_>, _>>()?;
check::check(modules, &[identity.into(), "dever.io".into()])
}
#[test]
fn pure_rejects_direct_transitive_captured_and_handler_io() {
let prefix = "write(value: Text) (answer: Text) { dever.io.println(value)\n answer = value }\napply(route: handler(value: Text) (answer: Text), value: Text) (answer: Text) { answer = route(value) }\n";
for source in [
"main(path: Text) () pure { file = dever.io.open(path) }".into(),
"main(path: Text) (answer: dever.io.OpenResult) pure { answer = result(dever.io.open(path)) }".into(),
"main() () pure { dever.io.println(\"hello\") }".into(),
prefix.to_owned() + "main() (answer: Text) pure { answer = write(\"hello\") }",
prefix.to_owned() + "main() (answer: Text) pure { answer = apply(write, \"hello\") }",
prefix.to_owned() + "main() (answer: List<Text>) pure { answer = each(write, [\"hello\"]) }",
"unused(route: handler() ()) () pure { route() }".into(),
] {
let error = linked(&source, "main").unwrap_err();
assert_eq!(error.code, "C013", "{source}: {error:?}");
}
let source = "identity(value: Text) (answer: Text) { answer = value }\napply(route: handler(value: Text) (answer: Text), value: Text) (answer: Text) { answer = route(value) }\nmain() (answer: Text) pure { answer = apply(identity, \"hello\") }";
assert!(linked(source, "main").is_ok());
}
#[test]
fn resource_equality_cannot_hide_inside_value_containers() {
for source in [
"main(file: dever.system.File) (answer: Bool) { answer = file == file }",
"main(file: dever.system.File) (answer: Bool) { answer = [file] == [file] }",
"type Box { file: dever.system.File }\nmain(value: Box) (answer: Bool) { answer = value == value }",
"type Box { Some(file: dever.system.File)\n None }\nmain(value: Box) (answer: Bool) { answer = value == value }",
"main(file: dever.system.File?) (answer: Bool) { answer = file == null }",
"main(files: Map<dever.system.File, Int>) () {}",
] {
assert_eq!(linked(source, "main").unwrap_err().code, "C005", "{source}");
}
assert!(linked("type Box { files: List<dever.system.File> }\nmain(value: Box) (answer: Box) { answer = value }", "main").is_ok());
}
#[test]
fn file_errors_keep_nominal_identity_and_handling_obligations() {
let source =
"main(path: Text) (answer: dever.io.OpenResult) { answer = result(dever.io.open(path)) }";
assert!(linked(source, "main").is_ok());
assert_eq!(
linked(
"main(path: Text) () { lost = result(dever.io.open(path)) }",
"main"
)
.unwrap_err()
.code,
"C012"
);
assert_eq!(linked("type OpenResult { Opened(file: dever.system.File)\n error Failed(message: Text) }\nmain(path: Text) (answer: OpenResult) { answer = result(dever.io.open(path)) }", "main").unwrap_err().code, "C005");
}
#[test]
fn raw_file_intrinsics_and_unmediated_app_effects_are_rejected() {
assert_eq!(
linked(
"main() () { value = dever.system.fileOpen(\"file\") }",
"main"
)
.unwrap_err()
.code,
"C006"
);
for identity in ["app.docs.app.files", "app.docs.domain.files"] {
let source = "helper(path: Text) (file: dever.system.File) { file = dever.io.open(path) }\nmain(path: Text) (file: dever.system.File) { file = helper(path) }";
assert_eq!(linked(source, identity).unwrap_err().code, "C013");
}
}
#[test]
fn stream_operations_remain_lazy_and_effectful() {
let prefix = "count(item: Int, state: Int) (next: Int) { next = state + item }\n";
for source in [
"main(stream: Stream<Int>) () pure { close(stream) }".into(),
prefix.to_owned()
+ "main(stream: Stream<Int>) (answer: Int) pure { answer = reduce(count, stream, 0) }",
] {
assert_eq!(
linked(&source, "main").unwrap_err().code,
"C013",
"{source}"
);
}
for source in [
"same(item: Int) (answer: Int) { answer = item }\nmain(stream: Stream<Int>) (answer: List<Int>) { answer = each(same, stream) }",
"yes(item: Int) (answer: Bool) { answer = true }\nmain(stream: Stream<Int>) (answer: Int?) { answer = find(yes, stream) }",
"main(stream: Stream<Int>) (answer: Int) { answer = length(stream) }",
"main(stream: Stream<Int>) (answer: Bool) { answer = stream == stream }",
"main(file: dever.system.File) () { close(file) }",
] {
assert_eq!(linked(source, "main").unwrap_err().code, "C005", "{source}");
}
assert!(linked("main(file: dever.system.File) () { stream = dever.io.chunks(file, 1)\n close(stream) }", "main").is_ok());
}
#[test]
fn stream_events_cannot_drop_failures_or_leak_private_types() {
for source in [
"ignore(event: dever.io.ReadEvent) () {}\nmain(stream: Stream<dever.io.ReadEvent>) () { each(ignore, stream) }",
"ignore(event: dever.io.ReadEvent, state: Int) (next: Int) { next = state }\nmain(stream: Stream<dever.io.ReadEvent>) (answer: Int) { answer = reduce(ignore, stream, 0) }",
"main(file: dever.system.File) () { lost = result(dever.io.chunks(file, 0)) }",
] {
assert_eq!(linked(source, "main").unwrap_err().code, "C012", "{source}");
}
assert_eq!(
linked(
"type Hidden { value: Int }\npublic type Visible { stream: Stream<Hidden> }",
"main"
)
.unwrap_err()
.code,
"C006"
);
assert_eq!(
linked("type Cycle { stream: Stream<Cycle> }", "main")
.unwrap_err()
.code,
"C007"
);
}