Skip to content

Instantly share code, notes, and snippets.

@NickAger
Last active February 14, 2022 20:03
Show Gist options
  • Select an option

  • Save NickAger/d58f3120388fe899cad1022a01c162b9 to your computer and use it in GitHub Desktop.

Select an option

Save NickAger/d58f3120388fe899cad1022a01c162b9 to your computer and use it in GitHub Desktop.
Attempt to simplify parsing code shown in https://github.com/fivemoreminix/rsc but didn't work
use std::fmt;
use rsc::{tokenize, parse, Interpreter, TokenizeError, ParseError, InterpretError};
// error handling based on:
// * https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/wrap_error.html
// Unfortunately this didn't work in the context of `fn evaluate` as:
// 1) `cannot return value referencing local variable `tokens``
// 2) `cannot return value referencing local variable `expr``
// `ParseError` contains a reference to a Token rather than copy of it
// I tried to change `ParseErrorCode::UnexpectedToken` to contain a copy of a `Token`
// rather than a reference to a `Token` but `Token` contains a range which is uncopyable.
// https://users.rust-lang.org/t/why-doesnt-range-t-copy-implement-copy/51224
#[derive(Debug)]
enum EquationError<'input_string> {
Tokenize(TokenizeError<'input_string>),
Parse(ParseError<'input_string, f64>),
Interpret(InterpretError<'input_string>)
}
impl<'input_string> From<TokenizeError<'input_string>> for EquationError<'input_string> {
fn from(err: TokenizeError<'input_string>) -> EquationError<'input_string> {
EquationError::Tokenize(err)
}
}
impl<'input_string> From<ParseError<'input_string, f64>> for EquationError<'input_string> {
fn from(err: ParseError<'input_string, f64>) -> EquationError<'input_string> {
EquationError::Parse(err)
}
}
impl<'input_string> From<InterpretError<'input_string>> for EquationError<'input_string> {
fn from(err: InterpretError<'input_string>) -> EquationError<'input_string> {
EquationError::Interpret(err)
}
}
impl fmt::Display for EquationError<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EquationError::Tokenize(token_error) =>
write!(f, "Tokenize Error: {:?}", token_error),
EquationError::Parse(parse_error) =>
write!(f, "Parse Error: {:?}", parse_error),
EquationError::Interpret(interpret_error) =>
write!(f, "Interpret Error: {:?}", interpret_error)
}
}
}
/*
original evaluate
fn evaluate(input: &str, interpreter: &mut Interpreter<f64>) -> Result<f64, ()> {
// You have to call each function in the pipeline, but this gives you the most
// control over error handling and performance.
match tokenize(input) { // Step 1: splits input into symbols, words, and numbers
Ok(tokens) => match parse(&tokens) { // Step 2: builds an Expr using tokens
Ok(expr) => match interpreter.eval(&expr) { // Step 3: interprets the Expr
Ok(result) => println!("{}", result),
Err(interpret_error) => eprintln!("{:?}", interpret_error),
},
Err(parse_error) => eprintln!("{:?}", parse_error),
},
Err(tokenize_error) => eprintln!("{:?}", tokenize_error),
}
}
*/
type EvaluateResult<'input> = std::result::Result<f64, EquationError<'input>>;
fn evaluate<'input>(input: &'input str, interpreter: &mut Interpreter<f64>) -> EvaluateResult<'input> {
let tokens = tokenize(&input)?;
let expr = parse(&tokens)?;
let result = interpreter.eval(&expr)?;
return Ok(result);
}
fn main() {
// Constructs an f64 interpreter with included variables
let mut interpreter = Interpreter::default();
evaluate("5^2", &mut interpreter); // prints "25"
evaluate("x = 3", &mut interpreter); // prints "3"
evaluate("x(3) + 1", &mut interpreter); // prints "10"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment