use super::{
BenchCaptureError, BenchmarkDeclaration, Budgets, ContentionPosture, Measurement, References,
Reporter, Row, WorkFormula,
};
use crate::descriptor::{
CaptureCause, DeclarationError, FunctionName, Grammar, ModuleName, Name, SupportName,
};
use crate::token::{CapturedDelimiter, CapturedTokenTree, SpanHandle};
const SUPPORT: &str = "support";
const TABLE_FUNCTION: &str = "table_function";
const TABLE: &str = "table";
const REPORTER: &str = "reporter";
const NAMED: &str = "named";
const WORKLOAD: &str = "workload";
const PREFLIGHT: &str = "preflight";
const PLANTED_WORSE: &str = "planted_worse";
const COMPLEXITY: &str = "complexity";
const AXIS: &str = "axis";
const SAMPLES: &str = "samples";
const WARMUPS: &str = "warmups";
const RATIO_NUMERATOR: &str = "ratio_numerator";
const RATIO_DENOMINATOR: &str = "ratio_denominator";
const FORMULA: &str = "formula";
const OBSERVE: &str = "observe";
const DECLARABLE: [&str; 4] = [SUPPORT, TABLE_FUNCTION, TABLE, REPORTER];
const DECLARABLE_ROW: [&str; 11] = [
WORKLOAD,
PREFLIGHT,
PLANTED_WORSE,
COMPLEXITY,
AXIS,
SAMPLES,
WARMUPS,
RATIO_NUMERATOR,
RATIO_DENOMINATOR,
FORMULA,
OBSERVE,
];
pub fn captured(
body: &[&CapturedTokenTree],
at: SpanHandle,
grammar: Grammar,
) -> Result<BenchmarkDeclaration, BenchCaptureError> {
let clauses = declaration_clauses(grammar, body)?;
let support = SupportName::declared(identifier(grammar, &clauses, SUPPORT, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table_function = FunctionName::declared(identifier(grammar, &clauses, TABLE_FUNCTION, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table = named_reference(grammar, &clauses, TABLE, at)?;
let reporter_module = ModuleName::declared(identifier(grammar, &clauses, REPORTER, at)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let mut rows: Vec<Row> = Vec::new();
for clause in &clauses {
if let Clause::Row {
lens,
body: stated,
at: site,
} = clause
{
rows.push(row(grammar, lens, stated, *site)?);
}
}
BenchmarkDeclaration::declared(
support,
table_function,
table,
rows,
Reporter::declared(reporter_module),
)
.map_err(|refusal| carried(grammar, refusal, at))
}
const fn refused(grammar: Grammar, cause: CaptureCause, at: SpanHandle) -> BenchCaptureError {
BenchCaptureError::grammar_refused(grammar, cause, at)
}
const fn carried(grammar: Grammar, refusal: DeclarationError, at: SpanHandle) -> BenchCaptureError {
BenchCaptureError::vocabulary_refused(grammar, refusal, at)
}
enum Clause<'trees> {
Assigned {
key: &'trees str,
value: Vec<&'trees CapturedTokenTree>,
at: SpanHandle,
},
Row {
lens: &'trees str,
body: Vec<&'trees CapturedTokenTree>,
at: SpanHandle,
},
}
fn declaration_clauses<'trees>(
grammar: Grammar,
body: &[&'trees CapturedTokenTree],
) -> Result<Vec<Clause<'trees>>, BenchCaptureError> {
let mut clauses: Vec<Clause<'trees>> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in body {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
close(grammar, &group, &mut clauses)?;
group.clear();
} else {
group.push(tree);
}
}
close(grammar, &group, &mut clauses)?;
distinct(grammar, &clauses)?;
Ok(clauses)
}
fn close<'trees>(
grammar: Grammar,
group: &[&'trees CapturedTokenTree],
clauses: &mut Vec<Clause<'trees>>,
) -> Result<(), BenchCaptureError> {
let Some((head, rest)) = group.split_first() else {
return Ok(());
};
if let [body] = rest
&& let Some(lens) = head.word()
&& let Some((CapturedDelimiter::Brace, inner)) = body.group()
{
clauses.push(Clause::Row {
lens,
body: inner.iter().collect(),
at: head.span(),
});
return Ok(());
}
clauses.push(assignment(grammar, head, rest, &DECLARABLE)?);
Ok(())
}
fn assignment<'trees>(
grammar: Grammar,
head: &'trees CapturedTokenTree,
rest: &[&'trees CapturedTokenTree],
declarable: &[&str],
) -> Result<Clause<'trees>, BenchCaptureError> {
let opening = head.span();
let Some(key) = head.word() else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening));
};
let Some((assigned_by, value)) = rest.split_first() else {
return Err(refused(grammar, CaptureCause::ClauseUnread, opening));
};
if assigned_by.punct() != Some('=') || value.is_empty() {
return Err(refused(
grammar,
CaptureCause::ClauseUnread,
assigned_by.span(),
));
}
if !declarable.contains(&key) {
return Err(refused(grammar, CaptureCause::ClauseUndeclared, opening));
}
Ok(Clause::Assigned {
key,
value: value.to_vec(),
at: opening,
})
}
fn distinct(grammar: Grammar, clauses: &[Clause<'_>]) -> Result<(), BenchCaptureError> {
for (position, clause) in clauses.iter().enumerate() {
let Clause::Assigned { key, at, .. } = clause else {
continue;
};
let earlier = clauses.iter().take(position).any(|other| match *other {
Clause::Assigned { key: seen, .. } => seen == *key,
Clause::Row { .. } => false,
});
if earlier {
return Err(refused(grammar, CaptureCause::ClauseDoubled, *at));
}
}
Ok(())
}
fn assigned<'trees, 'clauses>(
clauses: &'clauses [Clause<'trees>],
key: &str,
) -> Option<(&'clauses [&'trees CapturedTokenTree], SpanHandle)> {
clauses.iter().find_map(|clause| match *clause {
Clause::Assigned {
key: named,
ref value,
at,
} if named == key => Some((value.as_slice(), at)),
Clause::Assigned { .. } | Clause::Row { .. } => None,
})
}
fn identifier<'trees>(
grammar: Grammar,
clauses: &[Clause<'trees>],
key: &str,
at: SpanHandle,
) -> Result<&'trees str, BenchCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [only] = value else {
return Err(refused(grammar, CaptureCause::ClauseUnread, clause));
};
only.word()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, only.span()))
}
fn count<Number: core::str::FromStr>(
grammar: Grammar,
clauses: &[Clause<'_>],
key: &str,
at: SpanHandle,
) -> Result<Number, BenchCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [only] = value else {
return Err(refused(grammar, CaptureCause::ClauseUnread, clause));
};
number(grammar, only)
}
fn number<Number: core::str::FromStr>(
grammar: Grammar,
tree: &CapturedTokenTree,
) -> Result<Number, BenchCaptureError> {
let spelling = tree
.number()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, tree.span()))?;
if !spelling.bytes().all(|byte| byte.is_ascii_digit()) {
return Err(refused(grammar, CaptureCause::ClauseUnread, tree.span()));
}
spelling
.parse::<Number>()
.map_err(|_| refused(grammar, CaptureCause::NumberBeyondSeat, tree.span()))
}
fn named_reference(
grammar: Grammar,
clauses: &[Clause<'_>],
key: &str,
at: SpanHandle,
) -> Result<Name, BenchCaptureError> {
let (value, clause) =
assigned(clauses, key).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
named_value(grammar, value, clause)
}
fn named_value(
grammar: Grammar,
value: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<Name, BenchCaptureError> {
let [word, arguments] = value else {
return Err(refused(grammar, CaptureCause::ReferenceUnread, at));
};
if word.word() != Some(NAMED) {
return Err(refused(grammar, CaptureCause::ReferenceUnread, word.span()));
}
let Some((CapturedDelimiter::Parenthesis, inner)) = arguments.group() else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
let parts: Vec<&CapturedTokenTree> = inner.iter().collect();
let [namespace, separator, stem] = parts.as_slice() else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
if separator.punct() != Some(',') {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
separator.span(),
));
}
let (Some(owner), Some(spelling)) = (namespace.text(), stem.text()) else {
return Err(refused(
grammar,
CaptureCause::ReferenceUnread,
arguments.span(),
));
};
Name::named(owner, spelling).map_err(|refusal| carried(grammar, refusal, arguments.span()))
}
fn axis(
grammar: Grammar,
clauses: &[Clause<'_>],
at: SpanHandle,
) -> Result<Vec<u64>, BenchCaptureError> {
let (value, clause) =
assigned(clauses, AXIS).ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [bracketed] = value else {
return Err(refused(grammar, CaptureCause::RosterUnread, clause));
};
let Some((CapturedDelimiter::Bracket, inner)) = bracketed.group() else {
return Err(refused(
grammar,
CaptureCause::RosterUnread,
bracketed.span(),
));
};
let mut sizes: Vec<u64> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in inner {
if tree.punct() == Some(',') {
match group.as_slice() {
[] => {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
[only] => sizes.push(number::<u64>(grammar, only)?),
[first, ..] => {
return Err(refused(grammar, CaptureCause::RosterUnread, first.span()));
}
}
group.clear();
} else {
group.push(tree);
}
}
match group.as_slice() {
[] => {}
[only] => sizes.push(number::<u64>(grammar, only)?),
[first, ..] => return Err(refused(grammar, CaptureCause::RosterUnread, first.span())),
}
Ok(sizes)
}
fn formula(
grammar: Grammar,
clauses: &[Clause<'_>],
) -> Result<Option<WorkFormula>, BenchCaptureError> {
let Some((value, at)) = assigned(clauses, FORMULA) else {
return Ok(None);
};
let [only] = value else {
return Err(refused(grammar, CaptureCause::ClauseUnread, at));
};
let text = only
.text()
.ok_or_else(|| refused(grammar, CaptureCause::ClauseUnread, only.span()))?;
WorkFormula::encoded(text.as_bytes().to_vec())
.map(Some)
.map_err(|refusal| carried(grammar, refusal, only.span()))
}
fn observations(
grammar: Grammar,
clauses: &[Clause<'_>],
at: SpanHandle,
) -> Result<Vec<Name>, BenchCaptureError> {
let (value, at) = assigned(clauses, OBSERVE)
.ok_or_else(|| refused(grammar, CaptureCause::ClauseAbsent, at))?;
let [bracketed] = value else {
return Err(refused(grammar, CaptureCause::RosterUnread, at));
};
let Some((CapturedDelimiter::Bracket, inner)) = bracketed.group() else {
return Err(refused(
grammar,
CaptureCause::RosterUnread,
bracketed.span(),
));
};
let mut observed: Vec<Name> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in inner {
if tree.punct() == Some(',') {
if group.is_empty() {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
}
observed.push(named_value(grammar, &group, bracketed.span())?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
observed.push(named_value(grammar, &group, bracketed.span())?);
}
Ok(observed)
}
fn row(
grammar: Grammar,
lens: &str,
body: &[&CapturedTokenTree],
at: SpanHandle,
) -> Result<Row, BenchCaptureError> {
let named = FunctionName::declared(lens).map_err(|refusal| carried(grammar, refusal, at))?;
let clauses = row_clauses(grammar, body)?;
let references = References {
workload: named_reference(grammar, &clauses, WORKLOAD, at)?,
correctness_preflight: named_reference(grammar, &clauses, PREFLIGHT, at)?,
planted_worse: named_reference(grammar, &clauses, PLANTED_WORSE, at)?,
complexity_claim: named_reference(grammar, &clauses, COMPLEXITY, at)?,
};
let sizes = axis(grammar, &clauses, at)?;
let measurement = Measurement {
budgets: Budgets {
samples: count(grammar, &clauses, SAMPLES, at)?,
warmups: count(grammar, &clauses, WARMUPS, at)?,
ratio_numerator: count(grammar, &clauses, RATIO_NUMERATOR, at)?,
ratio_denominator: count(grammar, &clauses, RATIO_DENOMINATOR, at)?,
},
contention: ContentionPosture::NoDeclaredContention,
work_formula: formula(grammar, &clauses)?,
};
Row::declared(
named,
references,
sizes,
measurement,
observations(grammar, &clauses, at)?,
)
.map_err(|refusal| carried(grammar, refusal, at))
}
fn row_clauses<'trees>(
grammar: Grammar,
body: &[&'trees CapturedTokenTree],
) -> Result<Vec<Clause<'trees>>, BenchCaptureError> {
let mut clauses: Vec<Clause<'trees>> = Vec::new();
let mut group: Vec<&CapturedTokenTree> = Vec::new();
for tree in body {
if tree.punct() == Some(',') {
let Some((head, rest)) = group.split_first() else {
return Err(refused(
grammar,
CaptureCause::SeparatorDangling,
tree.span(),
));
};
clauses.push(assignment(grammar, head, rest, &DECLARABLE_ROW)?);
group.clear();
} else {
group.push(tree);
}
}
if let Some((head, rest)) = group.split_first() {
clauses.push(assignment(grammar, head, rest, &DECLARABLE_ROW)?);
}
distinct(grammar, &clauses)?;
Ok(clauses)
}