| 42 |
42 |
|
use syn::{Ident, Result};
|
| 43 |
43 |
|
|
| 44 |
44 |
|
use crate::ast::{
|
| 45 |
|
- |
Arg, Declaration, Emission, Guard, Hole, HoleRoot, Interpolated, Item, Param, Pattern,
|
| 46 |
|
- |
Predicate, RegionKind, Source, StrPart,
|
|
45 |
+ |
Arg, Declaration, Emission, Guard, Hole, HoleRoot, Interpolated, Item, Param, Predicate,
|
|
46 |
+ |
RegionKind, Source, StrPart,
|
| 47 |
47 |
|
};
|
| 48 |
48 |
|
|
| 49 |
49 |
|
/// The flag that asks for a staged twin.
|
| 180 |
180 |
|
body: Vec<Fill>,
|
| 181 |
181 |
|
},
|
| 182 |
182 |
|
/// Another shape's filler, called where its markup was spliced in.
|
| 183 |
|
- |
Include { callee: syn::Path, args: Vec<Arg> },
|
|
183 |
+ |
///
|
|
184 |
+ |
/// `site` is the include's ordinal in the shape that holds it, which is
|
|
185 |
+ |
/// what the staged twin hands `Plan::enter`. The filler passes the same
|
|
186 |
+ |
/// number to `Cursor::enter`, so a caller's walk stops at the boundary
|
|
187 |
+ |
/// rather than answering the callee's holes with its own.
|
|
188 |
+ |
Include {
|
|
189 |
+ |
callee: syn::Path,
|
|
190 |
+ |
args: Vec<Arg>,
|
|
191 |
+ |
site: u16,
|
|
192 |
+ |
},
|
| 184 |
193 |
|
}
|
| 185 |
194 |
|
|
| 186 |
195 |
|
/// Numbering, one counter per thing a plan answers.
|
| 383 |
392 |
|
Ok(match source {
|
| 384 |
393 |
|
Source::Str(text) => Source::Str(interpolated(text, counters)),
|
| 385 |
394 |
|
Source::Hole(hole) => Source::Hole(value(hole, counters)),
|
| 386 |
|
- |
Source::Choose {
|
| 387 |
|
- |
arms,
|
| 388 |
|
- |
otherwise,
|
| 389 |
|
- |
scrutinee: _,
|
| 390 |
|
- |
} => {
|
| 391 |
|
- |
let id = counters.arms_next();
|
| 392 |
|
- |
let arms = arms
|
| 393 |
|
- |
.iter()
|
| 394 |
|
- |
.enumerate()
|
| 395 |
|
- |
.map(|(at, (_, value))| {
|
| 396 |
|
- |
Ok((
|
| 397 |
|
- |
Pattern::Int(i64::try_from(at).unwrap_or(i64::MAX)),
|
| 398 |
|
- |
self::source(value, counters)?,
|
| 399 |
|
- |
))
|
| 400 |
|
- |
})
|
| 401 |
|
- |
.collect::<Result<Vec<_>>>()?;
|
| 402 |
|
- |
Source::Choose {
|
| 403 |
|
- |
scrutinee: plan_read("arm", id),
|
| 404 |
|
- |
arms,
|
| 405 |
|
- |
otherwise: Box::new(self::source(otherwise, counters)?),
|
| 406 |
|
- |
}
|
|
395 |
+ |
// A dispatch has no residual, and staging one bakes an arm. The
|
|
396 |
+ |
// `Emission::Given` arm below carries the whole reason.
|
|
397 |
+ |
Source::Choose { scrutinee, .. } => {
|
|
398 |
+ |
return Err(syn::Error::new(
|
|
399 |
+ |
scrutinee.span(),
|
|
400 |
+ |
"a staged shape cannot dispatch: only one arm reaches the \
|
|
401 |
+ |
residual, so the others are lost rather than compiled. Say \
|
|
402 |
+ |
the arms as guards -- `A when x; B unless x` -- which renders \
|
|
403 |
+ |
the same and derives a branch for each",
|
|
404 |
+ |
));
|
| 407 |
405 |
|
}
|
| 408 |
406 |
|
})
|
| 409 |
407 |
|
}
|
| 410 |
408 |
|
|
| 411 |
|
- |
impl Counters {
|
| 412 |
|
- |
/// A dispatch is numbered out of the guard counter's neighbour, and the
|
| 413 |
|
- |
/// plan keeps arms in their own list.
|
| 414 |
|
- |
fn arms_next(&mut self) -> u16 {
|
| 415 |
|
- |
let id = self.holes;
|
| 416 |
|
- |
// Arms and holes do not share a namespace in the plan; the counter is
|
| 417 |
|
- |
// reused so that a shape's numbering stays a single pass over its body.
|
| 418 |
|
- |
self.holes += 1;
|
| 419 |
|
- |
id
|
| 420 |
|
- |
}
|
| 421 |
|
- |
}
|
| 422 |
|
- |
|
| 423 |
409 |
|
/// A value a request would have brought, which becomes one sentinel.
|
| 424 |
410 |
|
///
|
| 425 |
411 |
|
/// A hole rooted at a path is left alone. `REGION` and `PATH` are consts, so
|
| 745 |
731 |
|
inner,
|
| 746 |
732 |
|
}
|
| 747 |
733 |
|
}
|
| 748 |
|
- |
// The arms become integers, so the plan chooses one by position and the
|
| 749 |
|
- |
// scrutinee's own type never has to be reproduced. That is also why an
|
| 750 |
|
- |
// `otherwise` stops being optional here: a match on a `usize` against
|
| 751 |
|
- |
// integer literals is not exhaustive without one, and rustc would
|
| 752 |
|
- |
// report that against generated code rather than against the
|
| 753 |
|
- |
// declaration that caused it.
|
| 754 |
|
- |
Emission::Given {
|
| 755 |
|
- |
scrutinee,
|
| 756 |
|
- |
arms,
|
| 757 |
|
- |
otherwise,
|
| 758 |
|
- |
} => {
|
| 759 |
|
- |
let Some(otherwise) = otherwise else {
|
| 760 |
|
- |
return Err(syn::Error::new(
|
| 761 |
|
- |
scrutinee.span(),
|
| 762 |
|
- |
"a staged dispatch needs an `otherwise`: its arms are chosen by \
|
| 763 |
|
- |
position, and a position nothing answers has to land somewhere",
|
| 764 |
|
- |
));
|
| 765 |
|
- |
};
|
| 766 |
|
- |
let id = counters.arms_next();
|
| 767 |
|
- |
let arms = arms
|
| 768 |
|
- |
.iter()
|
| 769 |
|
- |
.enumerate()
|
| 770 |
|
- |
.map(|(at, (_, emission))| {
|
| 771 |
|
- |
Ok((
|
| 772 |
|
- |
Pattern::Int(i64::try_from(at).unwrap_or(i64::MAX)),
|
| 773 |
|
- |
Box::new(self::emission(emission, counters)?),
|
| 774 |
|
- |
))
|
| 775 |
|
- |
})
|
| 776 |
|
- |
.collect::<Result<Vec<_>>>()?;
|
| 777 |
|
- |
Emission::Given {
|
| 778 |
|
- |
scrutinee: plan_read("arm", id),
|
| 779 |
|
- |
arms,
|
| 780 |
|
- |
otherwise: Some(Box::new(self::emission(otherwise, counters)?)),
|
| 781 |
|
- |
}
|
|
734 |
+ |
// A dispatch has no residual, and staging one is silently wrong.
|
|
735 |
+ |
//
|
|
736 |
+ |
// The derivation finds a branch by rendering the screen twice with one
|
|
737 |
+ |
// guard changed and reading the difference, and it finds a loop the same
|
|
738 |
+ |
// way with a row count. A dispatch is neither: its arms replace each
|
|
739 |
+ |
// other at one position, so there is no insertion to measure and no
|
|
740 |
+ |
// deletion to put back, and every arm would claim the same bytes.
|
|
741 |
+ |
//
|
|
742 |
+ |
// What happened instead was worse than a refusal. The staged twin reads
|
|
743 |
+ |
// `plan.arm(id)`, which answers zero, so exactly one arm rendered and
|
|
744 |
+ |
// the residual held it as a literal with the others simply gone. MNW's
|
|
745 |
+ |
// forum settings pane compiled to "You haven't joined any forum
|
|
746 |
+ |
// communities yet." and would have served that to every reader.
|
|
747 |
+ |
//
|
|
748 |
+ |
// Refused rather than modelled. `Op::Arms` and a derivation that renders
|
|
749 |
+ |
// once per arm is the general answer and is a feature rather than a fix;
|
|
750 |
+ |
// every dispatch in the tree today is two-way over a bool, which is two
|
|
751 |
+ |
// guards, which the residual already has a shape for.
|
|
752 |
+ |
Emission::Given { scrutinee, .. } => {
|
|
753 |
+ |
return Err(syn::Error::new(
|
|
754 |
+ |
scrutinee.span(),
|
|
755 |
+ |
"a staged shape cannot dispatch: only one arm reaches the \
|
|
756 |
+ |
residual, so the others are lost rather than compiled. Say \
|
|
757 |
+ |
the arms as guards -- `A when x; B unless x` -- which renders \
|
|
758 |
+ |
the same and derives a branch for each",
|
|
759 |
+ |
));
|
| 782 |
760 |
|
}
|
| 783 |
761 |
|
})
|
| 784 |
762 |
|
}
|
| 850 |
828 |
|
// The filler calls the shape's own name, not the twin's: the twin renders
|
| 851 |
829 |
|
// markup and the filler fills it, and they are two different functions
|
| 852 |
830 |
|
// beside one declaration.
|
|
831 |
+ |
let site = counters.sites;
|
|
832 |
+ |
counters.sites += 1;
|
|
833 |
+ |
|
| 853 |
834 |
|
counters.wrote(Fill::Include {
|
| 854 |
835 |
|
callee: path.clone(),
|
| 855 |
836 |
|
args: args.clone(),
|
|
837 |
+ |
site,
|
| 856 |
838 |
|
});
|
| 857 |
839 |
|
|
| 858 |
840 |
|
let mut path = path.clone();
|
| 862 |
844 |
|
.ok_or_else(|| syn::Error::new(hole.span(), "an empty path"))?;
|
| 863 |
845 |
|
last.ident = staged_name(&last.ident);
|
| 864 |
846 |
|
|
| 865 |
|
- |
let site = counters.sites;
|
| 866 |
|
- |
counters.sites += 1;
|
| 867 |
|
- |
|
| 868 |
847 |
|
Ok(Hole {
|
| 869 |
848 |
|
root: HoleRoot::Call {
|
| 870 |
849 |
|
path,
|