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

+256
View File
@@ -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)
}
}