kalosm-language 0.3.3

A set of pretrained language models
Documentation
use kalosm_sample::{ArcParser, ParseResult};
use kalosm_sample::{
    CreateParserState, FloatParser, LiteralParser, ParseStatus, Parser, ParserExt,
};
use once_cell::sync::Lazy;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Arc;

use crate::tool::Tool;

use super::IndexParser;

#[derive(Debug)]
struct LazyParser<T>(Box<Lazy<T>>);

impl<T: CreateParserState> CreateParserState for LazyParser<T> {
    fn create_parser_state(&self) -> <Self as Parser>::PartialState {
        let _self: &T = self.0.deref();
        _self.create_parser_state()
    }
}

impl<T: Parser + Default> Default for LazyParser<T> {
    fn default() -> Self {
        Self(Box::new(Lazy::new(Default::default)))
    }
}

impl<T: Parser> Parser for LazyParser<T> {
    type Output = T::Output;

    type PartialState = T::PartialState;

    fn parse<'a>(
        &self,
        state: &Self::PartialState,
        input: &'a [u8],
    ) -> ParseResult<ParseStatus<'a, Self::PartialState, Self::Output>> {
        self.0.parse(state, input)
    }
}

/// A parser for mathematical equations
pub struct EquationParser {
    parser: ArcParser,
}

impl CreateParserState for EquationParser {
    fn create_parser_state(&self) -> <Self as Parser>::PartialState {
        EquationParserState {
            state: self.parser.create_parser_state(),
            current_text: String::new(),
        }
    }
}

impl Default for EquationParser {
    fn default() -> Self {
        let number = FloatParser::new(f64::MIN..=f64::MAX);
        let function = IndexParser::new(vec![
            LiteralParser::new("sqrt"),
            LiteralParser::new("abs"),
            LiteralParser::new("exp"),
            LiteralParser::new("ln"),
            LiteralParser::new("sin"),
            LiteralParser::new("cos"),
            LiteralParser::new("tan"),
            LiteralParser::new("asin"),
            LiteralParser::new("acos"),
            LiteralParser::new("atan"),
            LiteralParser::new("atan2"),
            LiteralParser::new("sinh"),
            LiteralParser::new("cosh"),
            LiteralParser::new("tanh"),
            LiteralParser::new("asinh"),
            LiteralParser::new("acosh"),
            LiteralParser::new("atanh"),
            LiteralParser::new("floor"),
            LiteralParser::new("ceil"),
            LiteralParser::new("round"),
            LiteralParser::new("signum"),
            LiteralParser::new("pi"),
            LiteralParser::new("e"),
        ]);

        let addition = LiteralParser::new(" + ");
        let subtraction = LiteralParser::new(" - ");
        let multiplication = LiteralParser::new(" * ");
        let division = LiteralParser::new(" / ");

        let operation = addition
            .otherwise(subtraction)
            .otherwise(multiplication)
            .otherwise(division);
        let binary_operation = LiteralParser::new("(")
            .then(LazyParser::<Self>::default())
            .then(operation)
            .then(LazyParser::<Self>::default())
            .then(LiteralParser::new(")"));

        let function_call = function
            .then(LiteralParser::new("("))
            .then(LazyParser::<Self>::default())
            .then(LiteralParser::new(")"));

        let expression = number.otherwise(function_call).otherwise(binary_operation);

        Self {
            parser: expression.map_output(|_| ()).boxed(),
        }
    }
}

/// An error that can occur while parsing an equation
#[derive(Debug, Clone, Copy)]
pub struct EquationParserParseError;

impl std::fmt::Display for EquationParserParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("EquationParserParseError")
    }
}

impl std::error::Error for EquationParserParseError {}

impl Parser for EquationParser {
    type Output = String;

    type PartialState = EquationParserState;

    fn parse<'a>(
        &self,
        state: &Self::PartialState,
        input: &'a [u8],
    ) -> ParseResult<ParseStatus<'a, Self::PartialState, Self::Output>> {
        self.parser.parse(&state.state, input).map(|result| {
            let new_text = state.current_text.clone() + std::str::from_utf8(input).unwrap();
            match result {
                ParseStatus::Incomplete {
                    new_state,
                    required_next,
                } => ParseStatus::Incomplete {
                    new_state: EquationParserState {
                        state: new_state,
                        current_text: new_text,
                    },
                    required_next,
                },
                ParseStatus::Finished { remaining, .. } => ParseStatus::Finished {
                    remaining,
                    result: new_text,
                },
            }
        })
    }
}

/// The state of an equation parser.
#[derive(Clone)]
pub struct EquationParserState {
    state: Arc<dyn std::any::Any + Send + Sync>,
    current_text: String,
}

impl Debug for EquationParserState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("EquationParserState")
            .field("current_text", &self.current_text)
            .finish()
    }
}

#[test]
fn literal_equation() {
    let parser = EquationParser::default();

    let state = parser.create_parser_state();

    let result = parser.parse(&state, "2".as_bytes());
    println!("{:?}", result);
    assert!(result.is_ok());
}

#[test]
fn function_equation() {
    let parser = EquationParser::default();

    let state = parser.create_parser_state();

    let result = parser.parse(&state, "sqrt(2)".as_bytes());
    println!("{:?}", result);
    assert!(result.is_ok());

    let result = parser.parse(&state, "sqrt(sqrt(2))".as_bytes());
    println!("{:?}", result);
    assert!(result.is_ok());
}

/// A tool that can search the web
pub struct CalculatorTool;

impl Tool for CalculatorTool {
    type Input = String;

    fn input_parser(
        &self,
    ) -> impl CreateParserState<Output = Self::Input, PartialState: Send + Sync + 'static>
           + Send
           + Sync
           + 'static {
        EquationParser::default()
    }

    fn name(&self) -> String {
        "Calculator".to_string()
    }

    fn input_prompt(&self) -> String {
        "Numerical expression to calculate: ".to_string()
    }

    fn description(&self) -> String {
        let input_prompt = self.input_prompt();
        format!("Evaluate a mathematical expression (made only of numbers and one of the prebuilt math functions). Available functions: sqrt, abs, exp, ln, sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, floor, ceil, round, signum, pi, e\nUse tool with:\nAction: Calculator\nAction Input: the expression\nExample:\nQuestion: What is 2 + 2?\nThought: I should calculate 2 + 2.\nAction: Calculator\n{input_prompt}2 + 2\nObservation: 4\nThought: I now know that 2 + 2 is 4.\nFinal Answer: 4")
    }

    async fn run<'a>(&'a mut self, expr: &'a Self::Input) -> String {
        match meval::eval_str(expr){
                Ok(result) => result.to_string(),
                Err(e) => format!("Input was invalid, try again making sure to only use numbers and one of the prebuilt math functions. {e}"),
        }
    }
}