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

+167
View File
@@ -0,0 +1,167 @@
//! 为 result(call) 选择唯一且名义身份匹配的 choice。
//! 仅载荷形状相同不足以匹配业务错误;数值 Fault 不进入捕获集合。
use crate::compiler::{check::Context, model::*};
use crate::syntax::{ast, diagnostic::Diagnostic};
use std::collections::BTreeMap;
impl Context<'_> {
pub(crate) fn capture(
&mut self,
call: &ast::Expression,
expected: Option<&Type>,
source: &ast::Expression,
) -> Result<Expression, Diagnostic> {
let target = match expected {
Some(Type::Optional(inner)) => Some(inner.as_ref()),
expected => expected,
};
// 泛型解码直接使用结果 Choice 的成功字段作为类型上下文,不生成包装函数。
let decoded = if matches!(&call.kind, ast::ExpressionKind::Call(target, _)
if target.path().as_deref() == Some("dever.json.decode"))
{
let Some(Type::Named(index)) = target else {
return Err(self.error(
source,
"result(dever.json.decode(...)) requires an explicit result choice",
));
};
let Shape::Choice(variants) = &self.definitions[*index].shape else {
return Err(self.error(source, "JSON decode result requires a choice"));
};
let mut successes = variants.iter().filter(|variant| !variant.error);
let (Some(success), None) = (successes.next(), successes.next()) else {
return Err(self.error(
source,
"JSON decode result requires exactly one success variant",
));
};
let [(_, kind)] = success.fields.as_slice() else {
return Err(self.error(source, "JSON decode success variant requires one value"));
};
Some(kind.clone())
} else {
None
};
let value = self.expression(call, decoded.as_ref())?;
let decoded_failure;
let (origin, failures) = match &value.kind {
Node::Call(function, _) => {
let signature = &self.signatures[*function];
(signature.source, signature.failures.as_slice())
}
Node::Json(JsonOperation::Decode { error }, _) => {
decoded_failure = [Failure {
definition: *error,
variant: 1,
}];
(self.definitions[*error].source, decoded_failure.as_slice())
}
_ => {
return Err(self.error(
source,
"result requires a function call or typed JSON decode",
));
}
};
let mut candidates = Vec::new();
for (index, definition) in self.definitions.iter().enumerate() {
if definition.source != self.source && !self.definition_names.exported(index) {
continue;
}
if target.is_none()
&& definition.source != self.source
&& definition.source != origin
&& !failures.iter().any(|failure| failure.definition == index)
{
continue;
}
if target.is_some_and(|kind| *kind != Type::Named(index)) {
continue;
}
let Shape::Choice(variants) = &definition.shape else {
continue;
};
let successes = variants
.iter()
.enumerate()
.filter(|(_, variant)| !variant.error)
.collect::<Vec<_>>();
let [(success, variant)] = successes.as_slice() else {
continue;
};
let outputs_match = match &value.value_type {
Type::Unit => variant.fields.is_empty(),
Type::Outputs(outputs) => variant
.fields
.iter()
.map(|(_, kind)| kind)
.eq(outputs.iter().map(|(_, kind)| kind)),
kind => matches!(variant.fields.as_slice(), [(_, field)] if field == kind),
};
if !outputs_match {
continue;
}
if let Some(targets) = targets(index, variants, failures, self.definitions) {
candidates.push((index, *success, targets));
}
}
if candidates.len() != 1 {
return Err(Diagnostic::new(
"C005",
"result needs one unambiguous choice with matching success outputs and all declared errors",
source.span,
));
}
let (definition, success, targets) = candidates.pop().unwrap();
let capture = Capture {
call: value,
definition,
success,
targets,
};
self.coerce(
Expression {
kind: Node::Capture(Box::new(capture)),
value_type: Type::Named(definition),
span: source.span,
},
expected,
)
}
}
fn targets(
index: usize,
variants: &[Variant],
actual: &[Failure],
definitions: &[Definition],
) -> Option<Vec<(Failure, usize, bool)>> {
let mut mapping = BTreeMap::new();
for (position, variant) in variants.iter().enumerate().filter(|(_, value)| value.error) {
let direct = Failure {
definition: index,
variant: position,
};
if actual.contains(&direct) {
mapping.insert(direct, (position, false));
}
if let [(_, Type::Named(inner))] = variant.fields.as_slice()
&& let Shape::Choice(_) = &definitions[*inner].shape
{
for failure in actual.iter().filter(|failure| failure.definition == *inner) {
if mapping.insert(*failure, (position, true)).is_some() {
return None;
}
}
}
}
actual
.iter()
.map(|failure| {
mapping
.get(failure)
.map(|(variant, wrapped)| (*failure, *variant, *wrapped))
})
.collect()
}