aimx/values/
eval.rs

1//! Evaluation scoring for AIMX expressions.
2//!
3//! Provides the [`Eval`] type and [`parse_eval`] for representing and parsing
4//! success/attempt statistics used in workflows.
5
6use crate::{
7    aim::{WriterLike, Writer},
8    literals::{Literal, parse_unsigned},
9    values::Value,
10};
11use nom::{
12    IResult, Parser,
13    character::complete::{char, multispace0},
14    combinator::map,
15    sequence::delimited,
16};
17use std::{
18    fmt,
19};
20
21/// Evaluation scoring statistics.
22///
23/// Represents success/attempt counts used for simple performance metrics.
24#[derive(Debug, Clone, PartialEq)]
25pub struct Eval {
26    score: u32,
27    count: u32,
28}
29
30impl Eval {
31    /// Create a new evaluation value.
32    pub fn as_value(score: u32, count: u32) -> Value {
33        Value::Eval(Eval { score, count })
34    }
35
36    /// Record a successful evaluation attempt.
37    pub fn to_pass(&self) -> Value {
38        Self::as_value(self.score + 1, self.count + 1)
39    }
40
41    /// Record a failed evaluation attempt.
42    pub fn to_fail(&self) -> Value {
43        Self::as_value(self.score, self.count + 1)
44    }
45
46    pub fn score(&self) -> u32 {
47        self.score
48    }
49
50    pub fn count(&self) -> u32 {
51        self.count
52    }
53
54    /// Return success ratio as `Value::Literal(Literal::Number)`.
55    pub fn ratio(&self) -> Value {
56        if self.count == 0 {
57            Value::Literal(Literal::Number(0.0))
58        } else {
59            Value::Literal(Literal::Number(self.score as f64 / self.count as f64))
60        }
61    }
62
63    /// Write as `"score / count"`.
64    pub fn print(&self, writer: &mut Writer) {
65        writer.write_unsigned(self.score);
66        writer.write_str(" / ");
67        writer.write_unsigned(self.count);
68    }
69
70    /// Return the formula-string representation (round-trippable by the parser).
71    pub fn to_formula(&self) -> String {
72        let mut writer = Writer::formulizer();
73        self.print(&mut writer);
74        writer.finish()
75    }
76}
77
78impl WriterLike for Eval {
79    fn write(&self, writer: &mut Writer) {
80        self.print(writer);
81    }
82}
83
84impl fmt::Display for Eval {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        write!(f, "{}", self.to_stringized())
87    }
88}
89
90/// Parse an `Eval` from `"score / count"`.
91pub fn parse_eval(input: &str) -> IResult<&str, Eval> {
92    map(
93        (
94            parse_unsigned,
95            delimited(multispace0, char('/'), multispace0),
96            parse_unsigned,
97        ),
98        |(score, _, count)| Eval { score, count },
99    )
100    .parse(input)
101}