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()
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
//! 推导完整遍历、提前停止及归约中的失败处理义务。
|
||||
//! 未知长度归约合并初始与迭代摘要直到稳定,保留空输入的可能性。
|
||||
|
||||
use super::{State, flow};
|
||||
use crate::compiler::model::*;
|
||||
use crate::syntax::diagnostic::Diagnostic;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn collection(&mut self, collection: &Collection) -> Result<Flow, Diagnostic> {
|
||||
if collection
|
||||
.sequence
|
||||
.value_type
|
||||
.database_stream(self.definitions)
|
||||
&& let Some(definition) = self.database_error
|
||||
{
|
||||
let Shape::Choice(variants) = &self.definitions[definition].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
self.failures
|
||||
.extend((0..variants.len()).map(|variant| Failure {
|
||||
definition,
|
||||
variant,
|
||||
}));
|
||||
}
|
||||
let sequence = self.expression(&collection.sequence)?;
|
||||
if let Some(limit) = &collection.limit {
|
||||
self.expression(limit)?;
|
||||
}
|
||||
let extra = collection
|
||||
.extra
|
||||
.as_ref()
|
||||
.map(|value| self.expression(value))
|
||||
.transpose()?;
|
||||
let signature = &self.signatures[collection.handler];
|
||||
self.failures.extend(&signature.failures);
|
||||
let before = self.pending.clone();
|
||||
// Stream 的失败元素尚未产生;只在实际消费合同中引入义务,关闭不等于丢弃已读错误。
|
||||
let shape =
|
||||
if let Type::Stream(item) | Type::AsyncStream(item) = &collection.sequence.value_type {
|
||||
match &sequence.sequence {
|
||||
// 转发流可能没有现存错误 token,但未来拉取仍会产生错误义务。
|
||||
Some(Sequence::Unknown(value)) if value.tokens.is_empty() => {
|
||||
Sequence::Unknown(Box::new(self.unknown(item, 0)?))
|
||||
}
|
||||
Some(sequence) => sequence.clone(),
|
||||
None => Sequence::Unknown(Box::new(self.unknown(item, 0)?)),
|
||||
}
|
||||
} else {
|
||||
sequence
|
||||
.sequence
|
||||
.clone()
|
||||
.unwrap_or_else(|| Sequence::Unknown(Box::default()))
|
||||
};
|
||||
let mut produced = Vec::new();
|
||||
let mut state = extra.clone().unwrap_or_default();
|
||||
let mut fresh = BTreeMap::new();
|
||||
match &shape {
|
||||
Sequence::Known(items) => {
|
||||
for item in items {
|
||||
self.budget(0)?;
|
||||
let value =
|
||||
self.apply_handler(collection, item, &state, &mut BTreeMap::new())?;
|
||||
if collection.operation.reduction() {
|
||||
state = value;
|
||||
produced.push(state.clone());
|
||||
} else {
|
||||
produced.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Sequence::Unknown(item) => {
|
||||
if collection.operation.reduction() {
|
||||
// 初始状态参与合并,因为未知集合可以完全不执行 handler。
|
||||
loop {
|
||||
self.budget(0)?;
|
||||
let value = self.apply_handler(collection, item, &state, &mut fresh)?;
|
||||
let previous = state.clone();
|
||||
state.merge(&value);
|
||||
if previous == state {
|
||||
break;
|
||||
}
|
||||
}
|
||||
produced.push(state.clone());
|
||||
} else {
|
||||
produced.push(self.apply_handler(collection, item, &state, &mut fresh)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
let empty = matches!(&shape, Sequence::Known(items) if items.is_empty());
|
||||
let nonempty = matches!(&shape, Sequence::Known(items) if !items.is_empty());
|
||||
let partial = matches!(
|
||||
collection.operation,
|
||||
Operation::Find | Operation::ReduceUntil
|
||||
);
|
||||
self.pending = before;
|
||||
// 提前结束只保证访问首项,不能用选中的输出消费剩余元素的义务。
|
||||
if partial {
|
||||
if let Sequence::Known(items) = &shape
|
||||
&& let Some(first) = items.first()
|
||||
{
|
||||
self.pending.retain(|token| !first.tokens.contains(token));
|
||||
}
|
||||
} else {
|
||||
self.pending
|
||||
.retain(|token| !sequence.tokens.contains(token));
|
||||
}
|
||||
if let Some(extra) = &extra
|
||||
&& (nonempty || collection.operation.reduction())
|
||||
{
|
||||
self.pending.retain(|token| !extra.tokens.contains(token));
|
||||
}
|
||||
let mut output = match collection.operation {
|
||||
Operation::ParallelEach => Flow::default(),
|
||||
Operation::Each if signature.outputs.is_empty() => Flow::default(),
|
||||
Operation::Each => Flow::sequence(match shape {
|
||||
Sequence::Known(_) => Sequence::Known(produced),
|
||||
Sequence::Unknown(_) => Sequence::Unknown(Box::new(produced.pop().unwrap())),
|
||||
}),
|
||||
Operation::Filter if empty => Flow::sequence(Sequence::Known(Vec::new())),
|
||||
Operation::Filter => Flow::sequence(Sequence::Unknown(Box::new(shape.element()))),
|
||||
Operation::Find => {
|
||||
let item = if empty {
|
||||
return Ok(Flow::variants([(0, Flow::default())]));
|
||||
} else if matches!(&shape, Sequence::Known(items) if items.len() == 1) {
|
||||
shape.element()
|
||||
} else {
|
||||
self.unknown(&signature.inputs[0].1, 0)?
|
||||
};
|
||||
Flow::variants([(0, Flow::default()), (1, item)])
|
||||
}
|
||||
Operation::Sum => Flow::default(),
|
||||
Operation::Reduce => state,
|
||||
Operation::ReduceUntil => {
|
||||
if let Some((first, rest)) = produced.split_first() {
|
||||
let mut output = first.clone();
|
||||
for value in rest {
|
||||
output.merge(value);
|
||||
}
|
||||
output
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
};
|
||||
let retained: BTreeSet<_> = self.pending.intersection(&output.tokens).copied().collect();
|
||||
if !retained.is_empty() {
|
||||
let mut mapping = BTreeMap::new();
|
||||
for token in &output.tokens {
|
||||
let target = if retained.contains(token) {
|
||||
self.token()?
|
||||
} else {
|
||||
*token
|
||||
};
|
||||
mapping.insert(*token, BTreeSet::from([target]));
|
||||
}
|
||||
output = self.instantiate(&output, &mut mapping, 0)?;
|
||||
}
|
||||
self.pending.extend(&output.tokens);
|
||||
self.closed_streams.extend(sequence.streams);
|
||||
self.refresh_streams(&mut output);
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn apply_handler(
|
||||
&mut self,
|
||||
collection: &Collection,
|
||||
item: &Flow,
|
||||
extra: &Flow,
|
||||
fresh: &mut BTreeMap<usize, BTreeSet<usize>>,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let signature = &self.signatures[collection.handler];
|
||||
let mut mapping = fresh.clone();
|
||||
for template in &signature.input_flows {
|
||||
for token in &template.tokens {
|
||||
mapping.remove(token);
|
||||
}
|
||||
}
|
||||
for (template, actual) in signature.input_flows.iter().zip([item, extra]) {
|
||||
flow::bind(template, actual, &mut mapping);
|
||||
}
|
||||
let output = if let Some(value) = &signature.returns {
|
||||
self.instantiate(value, &mut mapping, 0)?
|
||||
} else {
|
||||
Flow::default()
|
||||
};
|
||||
*fresh = mapping;
|
||||
if collection.operation == Operation::ReduceUntil {
|
||||
Ok(output.fields.get(&0).cloned().unwrap_or_default())
|
||||
} else {
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
//! 按表达式求值规则传播失败摘要。
|
||||
//! 可能被短路跳过的处理不能解除已有义务;捕获调用的参数失败仍向外传播。
|
||||
|
||||
use super::State;
|
||||
use crate::compiler::model::*;
|
||||
use crate::syntax::{ast::Literal, diagnostic::Diagnostic};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn expression(&mut self, expression: &Expression) -> Result<Flow, Diagnostic> {
|
||||
self.budget(0)?;
|
||||
match &expression.kind {
|
||||
Node::Enqueue(call, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
let Shape::Choice(variants) = &self.definitions[call.error].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
self.failures
|
||||
.extend((0..variants.len()).map(|variant| Failure {
|
||||
definition: call.error,
|
||||
variant,
|
||||
}));
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Authorization(call, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
for definition in [call.api_error, call.database_error] {
|
||||
let Shape::Choice(variants) = &self.definitions[definition].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
self.failures
|
||||
.extend((0..variants.len()).map(|variant| Failure {
|
||||
definition,
|
||||
variant,
|
||||
}));
|
||||
}
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Auth(call, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
if call.operation != auth::Operation::Site {
|
||||
let Shape::Choice(variants) = &self.definitions[call.error].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
self.failures.extend(variants.iter().enumerate().filter_map(
|
||||
|(variant, value)| {
|
||||
matches!(
|
||||
value.name.as_str(),
|
||||
"Invalid" | "Unauthorized" | "Forbidden"
|
||||
)
|
||||
.then_some(Failure {
|
||||
definition: call.error,
|
||||
variant,
|
||||
})
|
||||
},
|
||||
));
|
||||
}
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Json(JsonOperation::Decode { error }, arguments) => {
|
||||
self.expression(&arguments[0])?;
|
||||
self.failures.insert(Failure {
|
||||
definition: *error,
|
||||
variant: 1,
|
||||
});
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Database(query, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
let Shape::Choice(variants) = &self.definitions[query.error].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
self.failures
|
||||
.extend((0..variants.len()).map(|variant| Failure {
|
||||
definition: query.error,
|
||||
variant,
|
||||
}));
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Literal(Literal::Null) => Ok(Flow::variants([(0, Flow::default())])),
|
||||
Node::Literal(_) => Ok(Flow::default()),
|
||||
Node::Variable(index) => {
|
||||
let mut value = self.values[*index].clone();
|
||||
self.refresh_streams(&mut value);
|
||||
Ok(value)
|
||||
}
|
||||
Node::Field(value, index) => Ok(self
|
||||
.expression(value)?
|
||||
.fields
|
||||
.get(index)
|
||||
.cloned()
|
||||
.unwrap_or_default()),
|
||||
Node::Some(value) => Ok(Flow::variants([(1, self.expression(value)?)])),
|
||||
Node::List(values) => Ok(Flow::sequence(Sequence::Known(
|
||||
values
|
||||
.iter()
|
||||
.map(|value| self.expression(value))
|
||||
.collect::<Result<_, _>>()?,
|
||||
))),
|
||||
Node::Map(entries) => {
|
||||
let mut values = Vec::new();
|
||||
for (key, value) in entries {
|
||||
values.push(Flow::fields([
|
||||
(0, self.expression(key)?),
|
||||
(1, self.expression(value)?),
|
||||
]));
|
||||
}
|
||||
Ok(Flow::sequence(Sequence::Known(values)))
|
||||
}
|
||||
Node::Access(operation, arguments) => {
|
||||
self.access(*operation, arguments, &expression.value_type)
|
||||
}
|
||||
Node::Net(operation, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
match operation {
|
||||
NetOperation::Connections => self.lazy_stream(),
|
||||
NetOperation::Chunks => {
|
||||
let Type::Named(index) = expression.value_type else {
|
||||
unreachable!("checked stream result");
|
||||
};
|
||||
Ok(Flow::variants([
|
||||
(0, Flow::fields([(0, self.lazy_stream()?)])),
|
||||
(1, self.unknown_variant(index, 1, 0)?),
|
||||
]))
|
||||
}
|
||||
_ => self.unknown(&expression.value_type, 0),
|
||||
}
|
||||
}
|
||||
Node::Http(HttpOperation::Open | HttpOperation::OpenStream, arguments) => {
|
||||
for argument in arguments {
|
||||
let value = self.expression(argument)?;
|
||||
// 上传源的失败由 HTTP 打开结果或响应流交付,不能留下重复义务。
|
||||
self.pending.retain(|token| !value.tokens.contains(token));
|
||||
}
|
||||
let Type::Named(index) = expression.value_type else {
|
||||
unreachable!("checked HTTP stream result");
|
||||
};
|
||||
// 下载尚未拉取;沿用 TCP 流的延迟失败证明,关闭不会伪造已读错误。
|
||||
Ok(Flow::variants([
|
||||
(
|
||||
0,
|
||||
Flow::fields([(0, Flow::fields([(2, self.lazy_stream()?)]))]),
|
||||
),
|
||||
(1, self.unknown_variant(index, 1, 0)?),
|
||||
]))
|
||||
}
|
||||
Node::Crypto(_, arguments)
|
||||
| Node::Assert(arguments)
|
||||
| Node::Calendar(_, arguments)
|
||||
| Node::Standard(_, arguments)
|
||||
| Node::Bytes(_, arguments)
|
||||
| Node::Http(_, arguments)
|
||||
| Node::Tls(_, arguments)
|
||||
| Node::Io(_, arguments)
|
||||
| Node::Upload(_, arguments)
|
||||
| Node::Json(_, arguments) => {
|
||||
for argument in arguments {
|
||||
self.expression(argument)?;
|
||||
}
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Variant(index, variant, values) => {
|
||||
let mut fields = Vec::new();
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
fields.push((index, self.expression(value)?));
|
||||
}
|
||||
let value = Flow::fields(fields);
|
||||
Ok(Flow::variants([(
|
||||
*variant,
|
||||
self.variant(*index, *variant, value)?,
|
||||
)]))
|
||||
}
|
||||
Node::Record(_, values) => {
|
||||
let mut fields = Vec::new();
|
||||
for (index, value) in values {
|
||||
fields.push((*index, self.expression(value)?));
|
||||
}
|
||||
Ok(Flow::fields(fields))
|
||||
}
|
||||
Node::Close(value) => {
|
||||
let value = self.expression(value)?;
|
||||
self.closed_streams.extend(value.streams);
|
||||
Ok(Flow::default())
|
||||
}
|
||||
Node::Unary(_, value)
|
||||
| Node::TestSecret(value)
|
||||
| Node::TestClock(value)
|
||||
| Node::TestDrain { limit: value, .. }
|
||||
| Node::Decimal(value)
|
||||
| Node::Id(value)
|
||||
| Node::IdText(value)
|
||||
| Node::Length(value)
|
||||
| Node::Sum(value) => {
|
||||
self.expression(value)?;
|
||||
Ok(Flow::default())
|
||||
}
|
||||
Node::Binary(operator, left, right) => {
|
||||
self.expression(left)?;
|
||||
if matches!(operator.as_str(), "and" | "or") {
|
||||
// 右侧仍需通过检查,但可能不执行,处理义务必须保留该路径。
|
||||
if let Node::Literal(Literal::Bool(value)) = left.kind {
|
||||
if value == (operator == "and") {
|
||||
self.expression(right)?;
|
||||
} else {
|
||||
let pending = self.pending.clone();
|
||||
let closed = self.closed_streams.clone();
|
||||
self.expression(right)?;
|
||||
self.pending = pending;
|
||||
self.closed_streams = closed;
|
||||
}
|
||||
} else {
|
||||
let pending = self.pending.clone();
|
||||
self.expression(right)?;
|
||||
self.pending.extend(pending);
|
||||
}
|
||||
} else {
|
||||
self.expression(right)?;
|
||||
}
|
||||
Ok(Flow::default())
|
||||
}
|
||||
Node::Call(index, arguments) => {
|
||||
let signature = &self.signatures[*index];
|
||||
let mut mapping = self.arguments(arguments, signature)?;
|
||||
self.failures.extend(&signature.failures);
|
||||
if let Some(value) = &signature.returns {
|
||||
self.instantiate(value, &mut mapping, 0)
|
||||
} else {
|
||||
Ok(Flow::default())
|
||||
}
|
||||
}
|
||||
Node::Schedule(operation, index, arguments) => {
|
||||
self.schedule(*operation, *index, arguments)
|
||||
}
|
||||
Node::Join { task, stop } => self.join(task, *stop),
|
||||
Node::Timeout {
|
||||
task,
|
||||
millis,
|
||||
fallback,
|
||||
} => self.timeout(task, millis, *fallback),
|
||||
Node::Race(tasks) => self.race(tasks),
|
||||
Node::Group(limit) => self.group(limit),
|
||||
Node::Stream(value) => {
|
||||
let mut value = self.expression(value)?;
|
||||
value.streams.insert(self.symbol()?);
|
||||
Ok(value)
|
||||
}
|
||||
Node::Ticks(millis) => {
|
||||
self.expression(millis)?;
|
||||
Ok(Flow {
|
||||
streams: [self.symbol()?].into(),
|
||||
..Flow::default()
|
||||
})
|
||||
}
|
||||
Node::Channel(capacity) => {
|
||||
self.expression(capacity)?;
|
||||
Ok(Flow::default())
|
||||
}
|
||||
Node::Send(channel, value) => {
|
||||
self.expression(channel)?;
|
||||
let value = self.expression(value)?;
|
||||
self.pending.retain(|token| !value.tokens.contains(token));
|
||||
Ok(Flow::default())
|
||||
}
|
||||
Node::Receive(channel) => {
|
||||
self.expression(channel)?;
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::GroupRun {
|
||||
group,
|
||||
target,
|
||||
arguments,
|
||||
} => self.group_run(group, *target, arguments),
|
||||
Node::Timer(_, millis) => {
|
||||
self.expression(millis)?;
|
||||
self.unknown(&expression.value_type, 0)
|
||||
}
|
||||
Node::Capture(capture) => self.capture(capture),
|
||||
Node::Collection(collection) => self.collection(collection),
|
||||
}
|
||||
}
|
||||
|
||||
fn capture(&mut self, capture: &Capture) -> Result<Flow, Diagnostic> {
|
||||
let mut possibilities = BTreeMap::<usize, Flow>::new();
|
||||
match &capture.call.kind {
|
||||
Node::Call(function, arguments) => {
|
||||
let signature = &self.signatures[*function];
|
||||
let mut mapping = self.arguments(arguments, signature)?;
|
||||
if let Some(value) = &signature.returns {
|
||||
let value = self.instantiate(value, &mut mapping, 0)?;
|
||||
let fields = if signature.outputs.len() == 1 {
|
||||
Flow::fields([(0, value)])
|
||||
} else {
|
||||
value
|
||||
};
|
||||
possibilities.insert(capture.success, fields);
|
||||
}
|
||||
}
|
||||
Node::Json(JsonOperation::Decode { .. }, arguments) => {
|
||||
self.expression(&arguments[0])?;
|
||||
let value = self.unknown(&capture.call.value_type, 0)?;
|
||||
possibilities.insert(capture.success, Flow::fields([(0, value)]));
|
||||
}
|
||||
_ => unreachable!("checked capture call"),
|
||||
}
|
||||
for (failure, target, wrapped) in &capture.targets {
|
||||
let source = self.unknown_variant(failure.definition, failure.variant, 0)?;
|
||||
let value = if *wrapped {
|
||||
self.variant(
|
||||
capture.definition,
|
||||
*target,
|
||||
Flow::fields([(0, Flow::variants([(failure.variant, source)]))]),
|
||||
)?
|
||||
} else {
|
||||
source
|
||||
};
|
||||
if let Some(previous) = possibilities.get_mut(target) {
|
||||
previous.merge(&value);
|
||||
} else {
|
||||
possibilities.insert(*target, value);
|
||||
}
|
||||
}
|
||||
Ok(Flow::variants(possibilities))
|
||||
}
|
||||
|
||||
pub(super) fn fail(&mut self, value: &Expression) -> Result<(), Diagnostic> {
|
||||
let flow = self.expression(value)?;
|
||||
let Type::Named(index) = value.value_type else {
|
||||
return Err(Diagnostic::new(
|
||||
"C005",
|
||||
"fail requires an error choice variant",
|
||||
value.span,
|
||||
));
|
||||
};
|
||||
let Shape::Choice(variants) = &self.definitions[index].shape else {
|
||||
return Err(Diagnostic::new(
|
||||
"C005",
|
||||
"fail requires an error choice variant",
|
||||
value.span,
|
||||
));
|
||||
};
|
||||
let possible = if flow.variants.is_empty() {
|
||||
(0..variants.len()).collect::<Vec<_>>()
|
||||
} else {
|
||||
flow.variants.keys().copied().collect()
|
||||
};
|
||||
for variant in possible {
|
||||
if !variants[variant].error {
|
||||
return Err(Diagnostic::new(
|
||||
"C005",
|
||||
"fail can receive a non-error variant",
|
||||
value.span,
|
||||
));
|
||||
}
|
||||
self.failures.insert(Failure {
|
||||
definition: index,
|
||||
variant,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
//! 构造、绑定和实例化嵌套值的失败摘要。
|
||||
//! 函数摘要中的义务编号在调用处重新映射,避免不同调用互相抵消错误。
|
||||
|
||||
use super::State;
|
||||
use crate::compiler::model::*;
|
||||
use crate::syntax::diagnostic::Diagnostic;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
pub(super) fn tracked_types(definitions: &[Definition]) -> Vec<bool> {
|
||||
fn tracked(kind: &Type, definitions: &[Definition], values: &mut [Option<bool>]) -> bool {
|
||||
match kind {
|
||||
Type::List(_) | Type::Map(_, _) | Type::AsyncStream(_) => true,
|
||||
Type::Optional(inner) => tracked(inner, definitions, values),
|
||||
Type::MapEntry(key, value) => {
|
||||
tracked(key, definitions, values) || tracked(value, definitions, values)
|
||||
}
|
||||
Type::Named(index) => {
|
||||
if let Some(value) = values[*index] {
|
||||
return value;
|
||||
}
|
||||
let value = !definitions[*index].related
|
||||
&& match &definitions[*index].shape {
|
||||
Shape::Record(fields) => fields
|
||||
.iter()
|
||||
.any(|(_, kind)| tracked(kind, definitions, values)),
|
||||
Shape::Choice(variants) => variants.iter().any(|variant| {
|
||||
variant.error
|
||||
|| variant
|
||||
.fields
|
||||
.iter()
|
||||
.any(|(_, kind)| tracked(kind, definitions, values))
|
||||
}),
|
||||
};
|
||||
values[*index] = Some(value);
|
||||
value
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
// 类型检查已排除值类型环;只为含集合、流或错误的记录建立结构摘要。
|
||||
let mut values = vec![None; definitions.len()];
|
||||
for index in 0..definitions.len() {
|
||||
tracked(&Type::Named(index), definitions, &mut values);
|
||||
}
|
||||
values.into_iter().map(Option::unwrap).collect()
|
||||
}
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn unknown(&mut self, kind: &Type, depth: usize) -> Result<Flow, Diagnostic> {
|
||||
self.budget(depth)?;
|
||||
if let Type::List(inner) | Type::AsyncStream(inner) = kind {
|
||||
let mut value =
|
||||
Flow::sequence(Sequence::Unknown(Box::new(self.unknown(inner, depth + 1)?)));
|
||||
if matches!(kind, Type::AsyncStream(_)) {
|
||||
value.streams.insert(self.symbol()?);
|
||||
}
|
||||
return Ok(value);
|
||||
}
|
||||
if let Type::Map(key, value) = kind {
|
||||
let entry = Type::MapEntry(key.clone(), value.clone());
|
||||
return Ok(Flow::sequence(Sequence::Unknown(Box::new(
|
||||
self.unknown(&entry, depth + 1)?,
|
||||
))));
|
||||
}
|
||||
// 没有业务错误的记录仍可能含集合或流,字段摘要不能因此丢弃。
|
||||
match kind {
|
||||
Type::Named(index) if !self.tracked_types[*index] => Ok(Flow::default()),
|
||||
Type::Named(index) => match &self.definitions[*index].shape {
|
||||
Shape::Record(fields) => {
|
||||
let mut values = Vec::new();
|
||||
for (index, (_, kind)) in fields.iter().enumerate() {
|
||||
values.push((index, self.unknown(kind, depth + 1)?));
|
||||
}
|
||||
Ok(Flow::fields(values))
|
||||
}
|
||||
Shape::Choice(variants) => {
|
||||
let mut values = Vec::new();
|
||||
for variant in 0..variants.len() {
|
||||
values.push((variant, self.unknown_variant(*index, variant, depth + 1)?));
|
||||
}
|
||||
Ok(Flow::variants(values))
|
||||
}
|
||||
},
|
||||
Type::Optional(inner) => Ok(Flow::variants([
|
||||
(0, Flow::default()),
|
||||
(1, self.unknown(inner, depth + 1)?),
|
||||
])),
|
||||
Type::MapEntry(key, value) => Ok(Flow::fields([
|
||||
(0, self.unknown(key, depth + 1)?),
|
||||
(1, self.unknown(value, depth + 1)?),
|
||||
])),
|
||||
_ => Ok(Flow::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unknown_variant(
|
||||
&mut self,
|
||||
index: usize,
|
||||
variant: usize,
|
||||
depth: usize,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
self.budget(depth)?;
|
||||
let Shape::Choice(variants) = &self.definitions[index].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut fields = Vec::new();
|
||||
for (field, (_, kind)) in variants[variant].fields.iter().enumerate() {
|
||||
fields.push((field, self.unknown(kind, depth + 1)?));
|
||||
}
|
||||
self.variant(index, variant, Flow::fields(fields))
|
||||
}
|
||||
|
||||
pub(super) fn variant(
|
||||
&mut self,
|
||||
index: usize,
|
||||
variant: usize,
|
||||
mut fields: Flow,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let Shape::Choice(variants) = &self.definitions[index].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
if variants[variant].error {
|
||||
fields.tokens.insert(self.token()?);
|
||||
fields.guaranteed = true;
|
||||
}
|
||||
Ok(fields)
|
||||
}
|
||||
|
||||
pub(super) fn instantiate(
|
||||
&mut self,
|
||||
template: &Flow,
|
||||
mapping: &mut BTreeMap<usize, BTreeSet<usize>>,
|
||||
depth: usize,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
self.budget(depth)?;
|
||||
// 子摘要按调用处重新映射,不先深拷贝再逐项覆盖。
|
||||
let mut value = Flow {
|
||||
guaranteed: template.guaranteed,
|
||||
task: template.task.clone(),
|
||||
..Flow::default()
|
||||
};
|
||||
for resource in &template.streams {
|
||||
if !mapping.contains_key(resource) {
|
||||
mapping.insert(*resource, BTreeSet::from([self.symbol()?]));
|
||||
}
|
||||
value.streams.extend(&mapping[resource]);
|
||||
}
|
||||
let mut tokens = BTreeSet::new();
|
||||
for token in &template.tokens {
|
||||
if !mapping.contains_key(token) {
|
||||
mapping.insert(*token, BTreeSet::from([self.token()?]));
|
||||
}
|
||||
tokens.extend(&mapping[token]);
|
||||
}
|
||||
self.pending.extend(&tokens);
|
||||
value.tokens = tokens;
|
||||
for (index, field) in &template.fields {
|
||||
value
|
||||
.fields
|
||||
.insert(*index, self.instantiate(field, mapping, depth + 1)?);
|
||||
}
|
||||
for (index, variant) in &template.variants {
|
||||
value
|
||||
.variants
|
||||
.insert(*index, self.instantiate(variant, mapping, depth + 1)?);
|
||||
}
|
||||
value.sequence =
|
||||
match &template.sequence {
|
||||
Some(Sequence::Known(values)) => Some(Sequence::Known(
|
||||
values
|
||||
.iter()
|
||||
.map(|value| self.instantiate(value, mapping, depth + 1))
|
||||
.collect::<Result<_, _>>()?,
|
||||
)),
|
||||
Some(Sequence::Unknown(value)) => Some(Sequence::Unknown(Box::new(
|
||||
self.instantiate(value, mapping, depth + 1)?,
|
||||
))),
|
||||
None => None,
|
||||
};
|
||||
self.refresh_streams(&mut value);
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bind(template: &Flow, actual: &Flow, mapping: &mut BTreeMap<usize, BTreeSet<usize>>) {
|
||||
for resource in &template.streams {
|
||||
mapping
|
||||
.entry(*resource)
|
||||
.or_default()
|
||||
.extend(&actual.streams);
|
||||
}
|
||||
for token in &template.tokens {
|
||||
mapping.entry(*token).or_default();
|
||||
}
|
||||
let mut nested: BTreeSet<_> = template
|
||||
.fields
|
||||
.values()
|
||||
.chain(template.variants.values())
|
||||
.flat_map(|value| value.tokens.iter().copied())
|
||||
.collect();
|
||||
if let Some(sequence) = &template.sequence {
|
||||
nested.extend(sequence.element().tokens);
|
||||
}
|
||||
for token in template.tokens.difference(&nested) {
|
||||
mapping.entry(*token).or_default().extend(&actual.tokens);
|
||||
}
|
||||
for (index, field) in &template.fields {
|
||||
bind(
|
||||
field,
|
||||
actual.fields.get(index).unwrap_or(&Flow::default()),
|
||||
mapping,
|
||||
);
|
||||
}
|
||||
for (index, variant) in &template.variants {
|
||||
bind(
|
||||
variant,
|
||||
actual.variants.get(index).unwrap_or(&Flow::default()),
|
||||
mapping,
|
||||
);
|
||||
}
|
||||
if let Some(sequence) = &template.sequence {
|
||||
let actual = actual.sequence.as_ref();
|
||||
match (sequence, actual) {
|
||||
(Sequence::Known(values), Some(Sequence::Known(actual)))
|
||||
if values.len() == actual.len() =>
|
||||
{
|
||||
for (value, actual) in values.iter().zip(actual) {
|
||||
bind(value, actual, mapping);
|
||||
}
|
||||
}
|
||||
_ => bind(
|
||||
&sequence.element(),
|
||||
&actual.map(Sequence::element).unwrap_or_default(),
|
||||
mapping,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn assign(target: &mut Flow, fields: &[usize], value: Flow) {
|
||||
let Some((field, remaining)) = fields.split_first() else {
|
||||
*target = value;
|
||||
return;
|
||||
};
|
||||
assign(target.fields.entry(*field).or_default(), remaining, value);
|
||||
*target = Flow::fields(std::mem::take(&mut target.fields));
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
//! 逐分句追踪业务失败的处理义务,并推导函数失败集合。
|
||||
//! 只有返回、传播或显式恢复才能解除义务;分析结果不改变运行时错误身份。
|
||||
|
||||
mod capture;
|
||||
mod collection;
|
||||
mod expression;
|
||||
mod flow;
|
||||
mod storage;
|
||||
mod stream;
|
||||
mod task;
|
||||
|
||||
use crate::compiler::model::*;
|
||||
use crate::syntax::{diagnostic::Diagnostic, source::Span};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
pub(super) fn check(
|
||||
function: &mut Function,
|
||||
definitions: &[Definition],
|
||||
signatures: &[Signature],
|
||||
database_error: Option<usize>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let tracked_types = flow::tracked_types(definitions);
|
||||
let mut next = 0;
|
||||
let mut failures = BTreeSet::new();
|
||||
let mut returns: Option<Flow> = None;
|
||||
let mut inputs: Vec<Option<Flow>> = vec![None; function.signature.inputs.len()];
|
||||
for clause in &function.clauses {
|
||||
let mut state = State {
|
||||
database_error,
|
||||
definitions,
|
||||
signatures,
|
||||
tracked_types: &tracked_types,
|
||||
values: vec![Flow::default(); clause.variables.len()],
|
||||
pending: BTreeSet::new(),
|
||||
closed_streams: BTreeSet::new(),
|
||||
failures: BTreeSet::new(),
|
||||
next,
|
||||
work: 0,
|
||||
span: clause.span,
|
||||
};
|
||||
for (position, (_, kind)) in function.signature.inputs.iter().enumerate() {
|
||||
let pattern = &clause.patterns[position];
|
||||
let value = match (&clause.variables[position].1, pattern) {
|
||||
(_, Pattern::Null) => Flow::default(),
|
||||
(Type::Named(index), _)
|
||||
if matches!(definitions[*index].shape, Shape::Choice(_)) =>
|
||||
{
|
||||
let Shape::Choice(variants) = &definitions[*index].shape else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut possibilities = Vec::new();
|
||||
for variant in 0..variants.len() {
|
||||
if super::clauses::accepts_variant(
|
||||
pattern,
|
||||
variant,
|
||||
function
|
||||
.clauses
|
||||
.iter()
|
||||
.map(|clause| clause.patterns[position].clone()),
|
||||
) {
|
||||
possibilities
|
||||
.push((variant, state.unknown_variant(*index, variant, 0)?));
|
||||
}
|
||||
}
|
||||
Flow::variants(possibilities)
|
||||
}
|
||||
(kind, _) => state.unknown(kind, 0)?,
|
||||
};
|
||||
let input = if matches!(kind, Type::Optional(_))
|
||||
&& matches!(pattern, Pattern::Present(_) | Pattern::Other)
|
||||
{
|
||||
Flow::variants([(1, value.clone())])
|
||||
} else if matches!(pattern, Pattern::Null) {
|
||||
Flow::variants([(0, Flow::default())])
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
if let Some(previous) = &mut inputs[position] {
|
||||
previous.merge(&input);
|
||||
} else {
|
||||
inputs[position] = Some(input);
|
||||
}
|
||||
state.values[position] = value;
|
||||
}
|
||||
for binding in &clause.bindings {
|
||||
let Pattern::Variant(variant) = clause.patterns[binding.input].inner() else {
|
||||
unreachable!()
|
||||
};
|
||||
state.values[binding.variable] = state.values[binding.input]
|
||||
.variants
|
||||
.get(variant)
|
||||
.and_then(|value| value.fields.get(&binding.field))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
}
|
||||
for statement in &clause.body {
|
||||
match statement {
|
||||
Statement::Assign {
|
||||
variable,
|
||||
fields,
|
||||
value,
|
||||
} => {
|
||||
let value = state.expression(value)?;
|
||||
flow::assign(&mut state.values[*variable], fields, value);
|
||||
}
|
||||
Statement::Action(value) => {
|
||||
state.expression(value)?;
|
||||
}
|
||||
Statement::Fail(value) => state.fail(value)?,
|
||||
}
|
||||
}
|
||||
if !clause.terminates {
|
||||
let mut output = match clause.outputs.as_slice() {
|
||||
[] => Flow::default(),
|
||||
[index] => state.values[*index].clone(),
|
||||
outputs => Flow::fields(
|
||||
outputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(field, index)| (field, state.values[*index].clone())),
|
||||
),
|
||||
};
|
||||
state.refresh_streams(&mut output);
|
||||
if function.signature.recovery.is_none()
|
||||
&& !output.guaranteed
|
||||
&& !state.pending.is_subset(&output.tokens)
|
||||
{
|
||||
return Err(Diagnostic::new(
|
||||
"C012",
|
||||
"failure is not handled: return, propagate, or declare an explicit recovery policy",
|
||||
clause.span,
|
||||
));
|
||||
}
|
||||
if let Some(previous) = &mut returns {
|
||||
previous.merge(&output);
|
||||
} else {
|
||||
returns = Some(output);
|
||||
}
|
||||
}
|
||||
next = state.next;
|
||||
failures.extend(state.failures);
|
||||
}
|
||||
function.signature.failures = failures.into_iter().collect();
|
||||
function.signature.input_flows = inputs.into_iter().map(Option::unwrap).collect();
|
||||
function.signature.returns = returns;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct State<'program> {
|
||||
database_error: Option<usize>,
|
||||
definitions: &'program [Definition],
|
||||
signatures: &'program [Signature],
|
||||
tracked_types: &'program [bool],
|
||||
values: Vec<Flow>,
|
||||
/// 尚未由当前分句处理或转移出去的错误义务。
|
||||
pending: BTreeSet<usize>,
|
||||
closed_streams: BTreeSet<usize>,
|
||||
failures: BTreeSet<Failure>,
|
||||
next: usize,
|
||||
work: usize,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
pub(super) fn prototype(
|
||||
signature: &mut Signature,
|
||||
definitions: &[Definition],
|
||||
span: Span,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let tracked_types = flow::tracked_types(definitions);
|
||||
let mut state = State {
|
||||
database_error: None,
|
||||
definitions,
|
||||
signatures: &[],
|
||||
tracked_types: &tracked_types,
|
||||
values: Vec::new(),
|
||||
pending: BTreeSet::new(),
|
||||
closed_streams: BTreeSet::new(),
|
||||
failures: BTreeSet::new(),
|
||||
next: 0,
|
||||
work: 0,
|
||||
span,
|
||||
};
|
||||
signature.input_flows = signature
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|(_, kind)| state.unknown(kind, 0))
|
||||
.collect::<Result<_, _>>()?;
|
||||
signature.returns = Some(match signature.outputs.as_slice() {
|
||||
[] => Flow::default(),
|
||||
[(_, kind)] => state.unknown(kind, 0)?,
|
||||
fields => Flow::fields(
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (_, kind))| Ok((index, state.unknown(kind, 0)?)))
|
||||
.collect::<Result<Vec<_>, Diagnostic>>()?,
|
||||
),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl State<'_> {
|
||||
fn symbol(&mut self) -> Result<usize, Diagnostic> {
|
||||
self.budget(0)?;
|
||||
let symbol = self.next;
|
||||
self.next += 1;
|
||||
Ok(symbol)
|
||||
}
|
||||
|
||||
fn token(&mut self) -> Result<usize, Diagnostic> {
|
||||
let token = self.symbol()?;
|
||||
self.pending.insert(token);
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
fn budget(&mut self, depth: usize) -> Result<(), Diagnostic> {
|
||||
// 达到分析边界就报告失败,不能把未完成的证明当作合同通过。
|
||||
self.work += 1;
|
||||
if self.work > 16_384 || depth > 128 {
|
||||
return Err(Diagnostic::new(
|
||||
"C015",
|
||||
"failure analysis exceeds its structural limit",
|
||||
self.span,
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arguments(
|
||||
&mut self,
|
||||
arguments: &[Expression],
|
||||
signature: &Signature,
|
||||
) -> Result<BTreeMap<usize, BTreeSet<usize>>, Diagnostic> {
|
||||
self.call_inputs(arguments, signature, true)
|
||||
}
|
||||
|
||||
fn call_inputs(
|
||||
&mut self,
|
||||
arguments: &[Expression],
|
||||
signature: &Signature,
|
||||
consume: bool,
|
||||
) -> Result<BTreeMap<usize, BTreeSet<usize>>, Diagnostic> {
|
||||
let mut mapping = BTreeMap::new();
|
||||
let values = arguments
|
||||
.iter()
|
||||
.map(|argument| self.expression(argument))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
for (value, template) in values.iter().zip(&signature.input_flows) {
|
||||
flow::bind(template, value, &mut mapping);
|
||||
if consume {
|
||||
self.pending.retain(|token| !value.tokens.contains(token));
|
||||
}
|
||||
}
|
||||
Ok(mapping)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//! 追踪 List/Map 存取对失败义务的影响。
|
||||
//! 读取、覆盖或删除部分元素不等于处理整个容器中的业务错误。
|
||||
|
||||
use super::State;
|
||||
use crate::compiler::model::*;
|
||||
use crate::syntax::diagnostic::Diagnostic;
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn access(
|
||||
&mut self,
|
||||
operation: Access,
|
||||
arguments: &[Expression],
|
||||
kind: &Type,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let mut values = arguments
|
||||
.iter()
|
||||
.map(|argument| self.expression(argument))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let container = std::mem::take(&mut values[0]);
|
||||
match operation {
|
||||
Access::Contains => Ok(Flow::default()),
|
||||
Access::Entries => Ok(container),
|
||||
Access::Append => {
|
||||
let value = std::mem::take(&mut values[1]);
|
||||
let guaranteed = value.guaranteed || container.guaranteed;
|
||||
let sequence = match container.sequence.expect("List flow") {
|
||||
Sequence::Known(mut items) => {
|
||||
items.push(value);
|
||||
Sequence::Known(items)
|
||||
}
|
||||
Sequence::Unknown(mut item) => {
|
||||
item.merge(&value);
|
||||
Sequence::Unknown(item)
|
||||
}
|
||||
};
|
||||
let mut output = Flow::sequence(sequence);
|
||||
output.guaranteed = guaranteed;
|
||||
Ok(output)
|
||||
}
|
||||
Access::First => {
|
||||
if let Some(Sequence::Known(items)) = container.sequence {
|
||||
if let Some(item) = items.into_iter().next() {
|
||||
let Type::List(inner) = &arguments[0].value_type else {
|
||||
unreachable!()
|
||||
};
|
||||
return Ok(if matches!(inner.as_ref(), Type::Optional(_)) {
|
||||
item
|
||||
} else {
|
||||
Flow::variants([(1, item)])
|
||||
});
|
||||
}
|
||||
Ok(Flow::variants([(0, Flow::default())]))
|
||||
} else {
|
||||
self.unknown(kind, 0)
|
||||
}
|
||||
}
|
||||
Access::Get => self.unknown(kind, 0),
|
||||
Access::Remove => {
|
||||
if matches!(&container.sequence, Some(Sequence::Known(items)) if items.is_empty()) {
|
||||
Ok(container)
|
||||
} else {
|
||||
self.unknown(kind, 0)
|
||||
}
|
||||
}
|
||||
Access::Put => {
|
||||
let entry = Flow::fields([
|
||||
(0, std::mem::take(&mut values[1])),
|
||||
(1, std::mem::take(&mut values[2])),
|
||||
]);
|
||||
if matches!(&container.sequence, Some(Sequence::Known(items)) if items.is_empty()) {
|
||||
return Ok(Flow::sequence(Sequence::Known(vec![entry])));
|
||||
}
|
||||
let mut output = self.unknown(kind, 0)?;
|
||||
let mut item = output.sequence.take().expect("Map flow").element();
|
||||
item.merge(&entry);
|
||||
output = Flow::sequence(Sequence::Unknown(Box::new(item)));
|
||||
output.guaranteed = entry.guaranteed;
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//! 异步流别名只需在错误证明中跟踪:关闭的源不能再兑现处理原元素的承诺。
|
||||
//! 这些编号和集合不会进入生成程序;普通 List 复制仍保留独立可消费的值。
|
||||
|
||||
use super::State;
|
||||
use crate::compiler::model::{Flow, Sequence};
|
||||
use crate::syntax::diagnostic::Diagnostic;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn lazy_stream(&mut self) -> Result<Flow, Diagnostic> {
|
||||
Ok(Flow {
|
||||
streams: [self.symbol()?].into(),
|
||||
..Flow::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn refresh_streams(&self, value: &mut Flow) -> bool {
|
||||
if !value.streams.is_disjoint(&self.closed_streams) {
|
||||
value.tokens.clear();
|
||||
value.guaranteed = false;
|
||||
value.sequence = Some(Sequence::Known(Vec::new()));
|
||||
return true;
|
||||
}
|
||||
let mut nested = BTreeSet::new();
|
||||
let mut changed = false;
|
||||
for child in value.fields.values_mut().chain(value.variants.values_mut()) {
|
||||
nested.extend(&child.tokens);
|
||||
changed |= self.refresh_streams(child);
|
||||
}
|
||||
if let Some(sequence) = &mut value.sequence {
|
||||
nested.extend(sequence.element().tokens);
|
||||
match sequence {
|
||||
Sequence::Known(items) => {
|
||||
for item in items {
|
||||
changed |= self.refresh_streams(item);
|
||||
}
|
||||
}
|
||||
Sequence::Unknown(item) => changed |= self.refresh_streams(item),
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
value.tokens.retain(|token| !nested.contains(token));
|
||||
value.tokens.extend(
|
||||
value
|
||||
.fields
|
||||
.values()
|
||||
.chain(value.variants.values())
|
||||
.flat_map(|child| child.tokens.iter().copied()),
|
||||
);
|
||||
if let Some(sequence) = &value.sequence {
|
||||
value.tokens.extend(sequence.element().tokens);
|
||||
}
|
||||
value.guaranteed = false;
|
||||
}
|
||||
changed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
//! 提交只保存处理承诺;正常 wait 才完成输入处理并引入输出错误义务。
|
||||
//! stop 可能在工作开始前取消,不能把已存在的错误当成已经被子任务处理。
|
||||
|
||||
use super::State;
|
||||
use crate::compiler::model::{failure::Deferred, *};
|
||||
use crate::syntax::diagnostic::Diagnostic;
|
||||
|
||||
impl State<'_> {
|
||||
pub(super) fn schedule(
|
||||
&mut self,
|
||||
operation: Schedule,
|
||||
target: usize,
|
||||
arguments: &[Expression],
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let signature = &self.signatures[target];
|
||||
let mut mapping = self.call_inputs(arguments, signature, operation != Schedule::Run)?;
|
||||
let before = self.pending.clone();
|
||||
let inputs = mapping.values().flatten().copied().collect();
|
||||
let output = if let Some(value) = &signature.returns {
|
||||
self.instantiate(value, &mut mapping, 0)?
|
||||
} else {
|
||||
Flow::default()
|
||||
};
|
||||
if operation == Schedule::Run {
|
||||
self.pending = before;
|
||||
Ok(Flow {
|
||||
task: Some(Box::new(Deferred {
|
||||
inputs,
|
||||
output,
|
||||
failures: signature.failures.iter().copied().collect(),
|
||||
})),
|
||||
..Flow::default()
|
||||
})
|
||||
} else {
|
||||
self.failures.extend(&signature.failures);
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn join(&mut self, task: &Expression, stop: bool) -> Result<Flow, Diagnostic> {
|
||||
let value = self.expression(task)?;
|
||||
let deferred = value.task.expect("checked affine handle");
|
||||
self.failures.extend(&deferred.failures);
|
||||
if !stop {
|
||||
self.pending
|
||||
.retain(|token| !deferred.inputs.contains(token));
|
||||
}
|
||||
self.pending.extend(&deferred.output.tokens);
|
||||
Ok(deferred.output)
|
||||
}
|
||||
|
||||
pub(super) fn timeout(
|
||||
&mut self,
|
||||
task: &Expression,
|
||||
millis: &Expression,
|
||||
fallback: usize,
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let flow = self.expression(task)?;
|
||||
self.expression(millis)?;
|
||||
let deferred = flow.task.expect("Task originates from run");
|
||||
let Type::Task(target) = task.value_type else {
|
||||
unreachable!("checked Task");
|
||||
};
|
||||
self.failures.extend(&self.signatures[target].failures);
|
||||
let signature = &self.signatures[fallback];
|
||||
self.failures.extend(&signature.failures);
|
||||
let fallback = signature
|
||||
.returns
|
||||
.as_ref()
|
||||
.map(|value| self.instantiate(value, &mut Default::default(), 0))
|
||||
.transpose()?;
|
||||
if fallback.is_none() {
|
||||
self.pending
|
||||
.retain(|token| !deferred.inputs.contains(token));
|
||||
}
|
||||
let mut output = self.signatures[target]
|
||||
.returns
|
||||
.as_ref()
|
||||
.map(|_| deferred.output);
|
||||
if let Some(fallback) = fallback {
|
||||
if let Some(output) = &mut output {
|
||||
output.merge(&fallback);
|
||||
} else {
|
||||
output = Some(fallback);
|
||||
}
|
||||
}
|
||||
let output = output.unwrap_or_default();
|
||||
self.pending.extend(&output.tokens);
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub(super) fn race(&mut self, tasks: &[Expression]) -> Result<Flow, Diagnostic> {
|
||||
let mut output: Option<Flow> = None;
|
||||
let mut handled: Option<std::collections::BTreeSet<usize>> = None;
|
||||
for task in tasks {
|
||||
let value = self.expression(task)?;
|
||||
let deferred = value.task.expect("Task originates from run");
|
||||
let Type::Task(target) = task.value_type else {
|
||||
unreachable!("checked Task");
|
||||
};
|
||||
self.failures.extend(&self.signatures[target].failures);
|
||||
if self.signatures[target].returns.is_some() {
|
||||
// 只有每个可能胜出的任务都处理的输入,才能在正常返回时解除义务。
|
||||
if let Some(handled) = &mut handled {
|
||||
handled.retain(|token| deferred.inputs.contains(token));
|
||||
} else {
|
||||
handled = Some(deferred.inputs);
|
||||
}
|
||||
if let Some(output) = &mut output {
|
||||
output.merge(&deferred.output);
|
||||
} else {
|
||||
output = Some(deferred.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(handled) = handled {
|
||||
self.pending.retain(|token| !handled.contains(token));
|
||||
}
|
||||
let output = output.unwrap_or_default();
|
||||
self.pending.extend(&output.tokens);
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub(super) fn group(&mut self, limit: &Expression) -> Result<Flow, Diagnostic> {
|
||||
self.expression(limit)?;
|
||||
Ok(Flow {
|
||||
task: Some(Box::new(Deferred {
|
||||
inputs: Default::default(),
|
||||
output: Flow::default(),
|
||||
failures: Default::default(),
|
||||
})),
|
||||
..Flow::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn group_run(
|
||||
&mut self,
|
||||
group: &Expression,
|
||||
target: usize,
|
||||
arguments: &[Expression],
|
||||
) -> Result<Flow, Diagnostic> {
|
||||
let Node::Variable(index) = group.kind else {
|
||||
unreachable!("checked local Group");
|
||||
};
|
||||
let pending = self
|
||||
.schedule(Schedule::Run, target, arguments)?
|
||||
.task
|
||||
.expect("checked run");
|
||||
let group = self.values[index].task.as_mut().expect("checked Group");
|
||||
group.inputs.extend(pending.inputs);
|
||||
group.failures.extend(pending.failures);
|
||||
// 提交时可能观察到已有子任务的失败;正常 wait 才兑现所有输入处理承诺。
|
||||
self.failures.extend(&group.failures);
|
||||
Ok(Flow::default())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user