Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -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()
|
||||
}
|
||||
Reference in new issue
Block a user