use super::{
BenchCaptureError, BenchmarkDeclaration, Budgets, ContentionPosture, Measurement, References,
Reporter, Row, WorkFormula,
};
use crate::descriptor::clause::{
Clause, assigned, assignment_clauses, declaration_clauses, identifier, named_reference,
named_value, number,
};
use crate::descriptor::{
CaptureCause, DeclarationError, FunctionName, Grammar, ModuleName, Name, SupportName,
};
use crate::token::{CapturedDelimiter, CapturedTokenTree, SpanHandle};
use core::convert::Infallible;
const SUPPORT: &str = "support";
const TABLE_FUNCTION: &str = "table_function";
const TABLE: &str = "table";
const REPORTER: &str = "reporter";
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,
&DECLARABLE,
|_grammar, group| Ok(row_clause(group)),
refused,
)?;
let support = SupportName::declared(identifier(grammar, &clauses, SUPPORT, at, refused)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table_function =
FunctionName::declared(identifier(grammar, &clauses, TABLE_FUNCTION, at, refused)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let table = named_reference(grammar, &clauses, TABLE, at, refused, carried)?;
let reporter_module =
ModuleName::declared(identifier(grammar, &clauses, REPORTER, at, refused)?)
.map_err(|refusal| carried(grammar, refusal, at))?;
let mut rows: Vec<Row> = Vec::new();
for clause in &clauses {
if let Some(stated) = clause.nested_value() {
rows.push(row(grammar, stated.lens, &stated.body, stated.at)?);
}
}
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)
}
struct RowClause<'trees> {
lens: &'trees str,
body: Vec<&'trees CapturedTokenTree>,
at: SpanHandle,
}
fn row_clause<'trees>(group: &[&'trees CapturedTokenTree]) -> Option<RowClause<'trees>> {
if let [head, body] = group
&& let Some(lens) = head.word()
&& let Some((CapturedDelimiter::Brace, inner)) = body.group()
{
return Some(RowClause {
lens,
body: inner.iter().collect(),
at: head.span(),
});
}
None
}
fn axis_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 axis(
grammar: Grammar,
clauses: &[Clause<'_, Infallible>],
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(axis_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(axis_number::<u64>(grammar, only)?),
[first, ..] => return Err(refused(grammar, CaptureCause::RosterUnread, first.span())),
}
Ok(sizes)
}
fn formula(
grammar: Grammar,
clauses: &[Clause<'_, Infallible>],
) -> 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<'_, Infallible>],
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(),
refused,
carried,
)?);
group.clear();
} else {
group.push(tree);
}
}
if !group.is_empty() {
observed.push(named_value(
grammar,
&group,
bracketed.span(),
refused,
carried,
)?);
}
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 = assignment_clauses(grammar, body, &DECLARABLE_ROW, refused)?;
let references = References {
workload: named_reference(grammar, &clauses, WORKLOAD, at, refused, carried)?,
correctness_preflight: named_reference(grammar, &clauses, PREFLIGHT, at, refused, carried)?,
planted_worse: named_reference(grammar, &clauses, PLANTED_WORSE, at, refused, carried)?,
complexity_claim: named_reference(grammar, &clauses, COMPLEXITY, at, refused, carried)?,
};
let sizes = axis(grammar, &clauses, at)?;
let measurement = Measurement {
budgets: Budgets {
samples: number(grammar, &clauses, SAMPLES, at, refused)?,
warmups: number(grammar, &clauses, WARMUPS, at, refused)?,
ratio_numerator: number(grammar, &clauses, RATIO_NUMERATOR, at, refused)?,
ratio_denominator: number(grammar, &clauses, RATIO_DENOMINATOR, at, refused)?,
},
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))
}