Reorganized code, added basic error handling

pull/2/head
Mark 2023-03-21 19:37:02 -07:00
parent b942e9dcf9
commit 2d9eeffb39
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
6 changed files with 117 additions and 79 deletions

View File

@ -4,6 +4,8 @@ use std::io::Write;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use termcolor::{ use termcolor::{
Color, Color,
ColorChoice, ColorChoice,
@ -13,6 +15,9 @@ use termcolor::{
}; };
mod parser; mod parser;
use crate::parser::Token;
//use crate::parser::ParserError;
use crate::parser::LineLocation;
const PROMPT_PREFIX: &str = "==> "; const PROMPT_PREFIX: &str = "==> ";
@ -77,23 +82,26 @@ fn main() -> Result<(), std::io::Error> {
continue; continue;
} }
// Tokenize input. // Parse input.
// Fail if we encounter invalid characters. // Fail if we encounter invalid characters.
let mut g = match parser::tokenize::tokenize(&input) { let g: Token = match parser::parse(&input) {
Ok(v) => v, Ok(g) => g,
Err(_) => { Err((l, e)) => {
let LineLocation{pos, len} = l;
let s = " ";
let m = "^";
println!("{}{} {:?}", s.repeat(pos + 4), m.repeat(len), e);
stdout.flush()?;
continue; continue;
} }
}; };
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
write!(stdout, "\n => ")?; write!(stdout, "\n => ")?;
stdout.reset()?; stdout.reset()?;
write!(stdout, "Got {input}\n\n\n")?; write!(stdout, "Got {input}\n\n\n")?;
parser::parse(&mut g).expect("Could not parse");
writeln!(stdout, "Tokenized: {g:#?}")?; writeln!(stdout, "Tokenized: {g:#?}")?;
} }

View File

@ -1,18 +1,67 @@
pub mod tokenize; mod tokenize;
mod replace_pre; mod replace_pre;
mod fold_operators; mod fold_operators;
mod unwrap_groups; mod unwrap_groups;
use crate::parser::tokenize::Token; use crate::parser::tokenize::tokenize;
use crate::parser::replace_pre::replace_pre; use crate::parser::replace_pre::replace_pre;
use crate::parser::fold_operators::fold_operators; use crate::parser::fold_operators::fold_operators;
use crate::parser::unwrap_groups::unwrap_groups; use crate::parser::unwrap_groups::unwrap_groups;
use std::collections::VecDeque;
pub fn parse(g: &mut Token) -> Result<(), ()> {
replace_pre(g)?;
fold_operators(g)?;
unwrap_groups(g)?;
return Ok(()); #[derive(Debug)]
pub enum Token {
// Used only while tokenizing.
// All of these are replaced with one of the tokens below.
//
// If parsing is successful,
// - all PreGroups will vanish
// - all PreOperators will become Operators
// - all PreNumbers will become Numbers
PreGroup(LineLocation, VecDeque<Token>),
PreOperator(LineLocation, String),
PreNumber(LineLocation, String),
PreWord(LineLocation, String),
Number(f64),
// Operators
Multiply(VecDeque<Token>),
Divide(VecDeque<Token>),
Add(VecDeque<Token>),
Subtract(VecDeque<Token>),
Factorial(VecDeque<Token>),
Negative(VecDeque<Token>),
Power(VecDeque<Token>),
Modulo(VecDeque<Token>),
}
#[derive(Debug)]
#[derive(Copy, Clone)]
pub struct LineLocation {
pub pos: usize,
pub len: usize
}
#[derive(Debug)]
pub enum ParserError {
InvalidChar,
MissingCloseParen,
Syntax,
BadNumber // Cannot parse a number
}
pub fn parse(s: &String) -> Result<Token, (LineLocation, ParserError)> {
let mut g: Token = tokenize(s)?;
replace_pre(&mut g)?;
fold_operators(&mut g)?;
unwrap_groups(&mut g)?;
return Ok(g);
} }

View File

@ -1,5 +1,10 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::parser::tokenize::Token;
use crate::parser::Token;
use crate::parser::LineLocation;
use crate::parser::ParserError;
enum OperatorType { enum OperatorType {
Binary, // A binary operator, like a + b Binary, // A binary operator, like a + b
@ -12,7 +17,7 @@ fn fold_operators_once(
op_type: &OperatorType, op_type: &OperatorType,
check: fn(&str) -> bool, check: fn(&str) -> bool,
new_token: fn(&str, VecDeque<Token>) -> Token, new_token: fn(&str, VecDeque<Token>) -> Token,
) -> Result<(), ()> { ) -> Result<(), (LineLocation, ParserError)> {
// Groups to process // Groups to process
let mut t_vec: VecDeque<&mut Token> = VecDeque::with_capacity(32); let mut t_vec: VecDeque<&mut Token> = VecDeque::with_capacity(32);
@ -122,7 +127,7 @@ fn fold_operators_once(
return Ok(()); return Ok(());
} }
pub fn fold_operators(exp: &mut Token) -> Result<(), ()> { pub fn fold_operators(exp: &mut Token) -> Result<(), (LineLocation, ParserError)> {
fold_operators_once( fold_operators_once(
exp, &OperatorType::UnaryLeft, exp, &OperatorType::UnaryLeft,
|s| s=="!", |s| s=="!",

View File

@ -1,6 +1,9 @@
use crate::parser::tokenize::Token; use crate::parser::Token;
use crate::parser::LineLocation;
use crate::parser::ParserError;
pub fn replace_pre(g: &mut Token) -> Result<(), ()> {
pub fn replace_pre(g: &mut Token) -> Result<(), (LineLocation, ParserError)> {
match g { match g {
Token::PreGroup(_, ref mut vec) => { Token::PreGroup(_, ref mut vec) => {
@ -8,10 +11,10 @@ pub fn replace_pre(g: &mut Token) -> Result<(), ()> {
replace_pre(i)?; replace_pre(i)?;
} }
}, },
Token::PreNumber(_, s) => { Token::PreNumber(l, s) => {
let n = match s.parse() { let n = match s.parse() {
Ok(n) => n, Ok(n) => n,
Err(_) => panic!() Err(_) => return Err((*l, ParserError::BadNumber))
}; };
*g = Token::Number(n); *g = Token::Number(n);
} }
@ -19,8 +22,7 @@ pub fn replace_pre(g: &mut Token) -> Result<(), ()> {
if s == "mod" { if s == "mod" {
*g = Token::PreOperator(*l, String::from("mod")); *g = Token::PreOperator(*l, String::from("mod"));
} else { } else {
return Err(()); return Err((*l, ParserError::Syntax));
//new.push_back(t);
} }
}, },
Token::PreOperator(_, _) => {}, Token::PreOperator(_, _) => {},

View File

@ -1,44 +1,8 @@
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Debug)] use crate::parser::Token;
#[derive(Copy, Clone)] use crate::parser::LineLocation;
pub struct LineLocation { use crate::parser::ParserError;
pos: usize,
len: usize
}
#[derive(Debug)]
pub enum Token {
// Only used after tokenizing
PreGroup(LineLocation, VecDeque<Token>),
PreOperator(LineLocation, String),
PreNumber(LineLocation, String),
PreWord(LineLocation, String),
// All PreGroups should vanish after operator folding
// All PreOperators should become Operators
// All PreNumbers should become Numbers
// All PreWords should become TODO.
// Only used in tree
Number(f64),
// Functions
// Operators
Multiply(VecDeque<Token>),
Divide(VecDeque<Token>),
Add(VecDeque<Token>),
Subtract(VecDeque<Token>),
Factorial(VecDeque<Token>),
Negative(VecDeque<Token>),
Power(VecDeque<Token>),
Modulo(VecDeque<Token>),
//Function(String, VecDeque<Token>),
}
#[inline(always)] #[inline(always)]
fn update_line_location(mut t: Token, stop_i: usize) -> Token { fn update_line_location(mut t: Token, stop_i: usize) -> Token {
@ -60,16 +24,8 @@ fn update_line_location(mut t: Token, stop_i: usize) -> Token {
} }
/// Turn a string into a set of tokens.
/// Does not check syntax. Fails if `input` contains an invalid character. pub fn tokenize(input: &String) -> Result<Token, (LineLocation, ParserError)> {
//
// # Arguments:
// `input`: A string like `(-3*2.2)/3`
//
// # Returns:
// * `Ok(Vec<token>)` if we were successful.
// * `Err(())` if we couldn't tokenize this string.
pub fn tokenize(input: &String) -> Result<Token, ()> {
let mut t: Option<Token> = None; // The current token we're reading let mut t: Option<Token> = None; // The current token we're reading
let mut g: Vec<Token> = Vec::with_capacity(8); // Vector of "grouping levels" let mut g: Vec<Token> = Vec::with_capacity(8); // Vector of "grouping levels"
g.push(Token::PreGroup(LineLocation{pos: 0, len: 0}, VecDeque::with_capacity(8))); g.push(Token::PreGroup(LineLocation{pos: 0, len: 0}, VecDeque::with_capacity(8)));
@ -190,7 +146,7 @@ pub fn tokenize(input: &String) -> Result<Token, ()> {
_ => panic!() _ => panic!()
}; };
g_now.push_back(update_line_location(new_group, i)); g_now.push_back(update_line_location(new_group, i+1));
}, },
// Space. Basic seperator. // Space. Basic seperator.
@ -199,7 +155,7 @@ pub fn tokenize(input: &String) -> Result<Token, ()> {
} }
// Invalid token // Invalid token
_ => { return Err(()); } _ => { return Err((LineLocation{pos: i, len: 1}, ParserError::InvalidChar)); }
}; };
} }
@ -210,5 +166,21 @@ pub fn tokenize(input: &String) -> Result<Token, ()> {
}; };
if t.is_some() { g_now.push_back(update_line_location(t.unwrap(), input.len())); } if t.is_some() { g_now.push_back(update_line_location(t.unwrap(), input.len())); }
if g.len() != 1 {
let q: LineLocation = match g.last_mut().unwrap() {
Token::PreGroup(l, _) => *l,
_ => panic!()
};
let LineLocation{pos:p, ..} = q;
return Err((
LineLocation{
pos: p,
len: input.len() - p
},
ParserError::MissingCloseParen
))
}
return Ok(g.pop().unwrap()); return Ok(g.pop().unwrap());
} }

View File

@ -1,12 +1,14 @@
use crate::parser::tokenize::Token; use crate::parser::Token;
use crate::parser::ParserError;
use crate::parser::LineLocation;
pub fn unwrap_groups(g: &mut Token) -> Result<(), ()> { pub fn unwrap_groups(g: &mut Token) -> Result<(), (LineLocation, ParserError)> {
match g { match g {
// If g is a PreGroup, unwrap it // If g is a PreGroup, unwrap it
Token::PreGroup(_, ref mut vec) => { Token::PreGroup(l, ref mut vec) => {
if vec.len() != 1 { if vec.len() != 1 {
panic!(); return Err((*l, ParserError::Syntax));
} }
let mut i = vec.pop_front().unwrap(); let mut i = vec.pop_front().unwrap();