From eadd6780ce57bccd2a3b8193578a9b1af0d15268 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 4 Aug 2023 21:33:42 -0700 Subject: [PATCH] Added user function parsing --- src/command/mod.rs | 2 +- src/entry/unix/promptbuffer.rs | 10 ++++----- src/entry/unix/unix.rs | 10 ++++----- src/evaluate/operator.rs | 2 ++ src/parser/expression/operator.rs | 17 +++++++++++++--- src/parser/mod.rs | 13 ++++++------ src/parser/stage/groupify.rs | 24 +++++++++++++--------- src/parser/stage/tokenize.rs | 34 ++++++++++++++++++------------- src/parser/stage/treeify.rs | 22 ++++++++++---------- 9 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/command/mod.rs b/src/command/mod.rs index 4eac489..8b408cc 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -231,7 +231,7 @@ pub fn do_command( } let v = args[1].to_string(); - let v = substitute(&v); + let v = substitute(&v, context); let r = context.delete(&v); return match r { diff --git a/src/entry/unix/promptbuffer.rs b/src/entry/unix/promptbuffer.rs index 2be13b5..5a3ed83 100644 --- a/src/entry/unix/promptbuffer.rs +++ b/src/entry/unix/promptbuffer.rs @@ -4,7 +4,7 @@ use termion::raw::RawTerminal; use termion::color; use termion::style; use crate::parser::substitute_cursor; - +use crate::context::Context; #[derive(Debug)] pub struct PromptBuffer { @@ -37,9 +37,9 @@ impl PromptBuffer { } // Same as write_primpt, but pretends there is no cursor - pub fn write_prompt_nocursor(&mut self, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { + pub fn write_prompt_nocursor(&mut self, stdout: &mut RawTerminal, context: &Context) -> Result<(), std::io::Error> { // Draw prettyprinted expression - let (_, s) = substitute_cursor(&self.get_contents(), self.buffer.chars().count()); + let (_, s) = substitute_cursor(&self.get_contents(), self.buffer.chars().count(), context); write!( stdout, "\r{}{}==>{}{} {}", @@ -64,12 +64,12 @@ impl PromptBuffer { return Ok(()); } - pub fn write_prompt(&mut self, stdout: &mut RawTerminal) -> Result<(), std::io::Error> { + pub fn write_prompt(&mut self, stdout: &mut RawTerminal, context: &Context) -> Result<(), std::io::Error> { let l = self.buffer.chars().count(); let i = if l == 0 {0} else {l - self.cursor}; // Draw prettyprinted expression - let (display_cursor, s) = substitute_cursor(&self.get_contents(), i); + let (display_cursor, s) = substitute_cursor(&self.get_contents(), i, context); write!( stdout, "\r{}{}==>{}{} {}", diff --git a/src/entry/unix/unix.rs b/src/entry/unix/unix.rs index 87b86e4..eaa8dad 100644 --- a/src/entry/unix/unix.rs +++ b/src/entry/unix/unix.rs @@ -101,8 +101,8 @@ fn do_assignment( let left = parts[0].trim().to_string(); let right = parts[1].trim().to_string(); - let right = substitute(&right); - let left = substitute(&left); + let right = substitute(&right, &context); + let left = substitute(&left, &context); let is_function = left.contains("("); @@ -305,7 +305,7 @@ pub fn main() -> Result<(), std::io::Error> { 'outer: loop { - pb.write_prompt(&mut stdout)?; + pb.write_prompt(&mut stdout, &context)?; let stdin = stdin(); for c in stdin.keys() { @@ -314,7 +314,7 @@ pub fn main() -> Result<(), std::io::Error> { '\n' => { // Print again without cursor, in case we pressed enter // while inside a substitution - pb.write_prompt_nocursor(&mut stdout)?; + pb.write_prompt_nocursor(&mut stdout, &context)?; let in_str = pb.enter(); write!(stdout, "\r\n")?; if in_str == "" { break; } @@ -384,7 +384,7 @@ pub fn main() -> Result<(), std::io::Error> { }; }; - pb.write_prompt(&mut stdout)?; + pb.write_prompt(&mut stdout, &context)?; } } diff --git a/src/evaluate/operator.rs b/src/evaluate/operator.rs index c389139..2ee61f5 100644 --- a/src/evaluate/operator.rs +++ b/src/evaluate/operator.rs @@ -13,6 +13,8 @@ pub fn eval_operator(g: &Expression, _context: &mut Context) -> Result unreachable!("Functions are handled seperately."), + Operator::UserFunction(_) => unimplemented!(), + Operator::Tuple => { if args.len() != 2 { panic!() }; let a = &args[0]; diff --git a/src/parser/expression/operator.rs b/src/parser/expression/operator.rs index a6d16b6..86b4e17 100644 --- a/src/parser/expression/operator.rs +++ b/src/parser/expression/operator.rs @@ -1,6 +1,8 @@ use std::cmp::Ordering; use std::collections::VecDeque; +use crate::context::Context; + use super::Expression; use super::Function; @@ -8,7 +10,7 @@ use super::Function; /// Operator types, in order of increasing priority. #[derive(Debug)] #[derive(Clone)] -#[derive(Copy)] +//#[derive(Copy)] #[repr(usize)] pub enum Operator { // When adding operators, don't forget to update help command text. @@ -32,6 +34,7 @@ pub enum Operator { Factorial, Function(Function), + UserFunction(String) } impl PartialEq for Operator { @@ -61,13 +64,17 @@ impl Operator { } #[inline(always)] - pub fn from_string(s: &str) -> Option { + pub fn from_string(s: &str, context: &Context) -> Option { let f = Function::from_string(s); if let Some(f) = f { return Some(Operator::Function(f)); } + if context.is_function(s) { + return Some(Operator::UserFunction(s.to_string())); + } + return match s { "," => {Some( Operator::Tuple )}, "+" => {Some( Operator::Add )}, @@ -117,7 +124,7 @@ impl Operator { fn print_map(&self) -> Operator { match self { Operator::ImplicitMultiply => Operator::Multiply, - _ => *self + _ => self.clone() } } @@ -326,6 +333,10 @@ impl Operator { Operator::Function(s) => { return format!("{}({})", s.to_string(), args[0].to_string()); + }, + + Operator::UserFunction(s) => { + return format!("{}({})", s.to_string(), args[0].to_string()); } }; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5b49b39..6d2f792 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -21,9 +21,9 @@ pub fn parse( s: &String, context: &Context ) -> Result { - let expressions = stage::tokenize(s); + let expressions = stage::tokenize(s, context); let (_, expressions) = stage::find_subs(expressions); - let g = stage::groupify(expressions)?; + let g = stage::groupify(expressions, context)?; let g = stage::treeify(g, context)?; return Ok(g); @@ -33,14 +33,15 @@ pub fn parse_no_context(s: &String) -> Result String { - let (_, s) = substitute_cursor(s, s.chars().count()); +pub fn substitute(s: &String, context: &Context) -> String { + let (_, s) = substitute_cursor(s, s.chars().count(), context); return s; } pub fn substitute_cursor( s: &String, // The string to substitute - c: usize // Location of the cursor right now + c: usize, // Location of the cursor right now + context: &Context ) -> ( usize, // Location of cursor in substituted string String // String with substitutions @@ -49,7 +50,7 @@ pub fn substitute_cursor( let mut new_s = s.clone(); let l = s.chars().count(); - let expressions = stage::tokenize(s); + let expressions = stage::tokenize(s, context); let (mut subs, _) = stage::find_subs(expressions); let mut new_c = l - c; diff --git a/src/parser/stage/groupify.rs b/src/parser/stage/groupify.rs index f88c40d..8d87366 100644 --- a/src/parser/stage/groupify.rs +++ b/src/parser/stage/groupify.rs @@ -7,10 +7,12 @@ use super::super::{ }; use crate::errors::DaisyError; +use crate::context::Context; fn lookback_signs( - g: &mut VecDeque + g: &mut VecDeque, + context: &Context ) -> Result<(), (LineLocation, DaisyError)> { // Convert `-` operators to `neg` operators @@ -42,7 +44,7 @@ fn lookback_signs( (Token::Operator(_, sa), Token::Operator(l,sb)) => { if { - let o = Operator::from_string(sa); + let o = Operator::from_string(sa, context); o.is_some() && ( @@ -99,10 +101,11 @@ fn lookback_signs( // Inserts implicit operators fn lookback( - g: &mut VecDeque + g: &mut VecDeque, + context: &Context ) -> Result<(), (LineLocation, DaisyError)> { - lookback_signs(g)?; + lookback_signs(g, context)?; let mut i: usize = 0; while i < g.len() { @@ -139,7 +142,7 @@ fn lookback( => { let la = la.clone(); let lb = lb.clone(); - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); g.insert(i-1, b); if o.is_some() { @@ -161,7 +164,7 @@ fn lookback( => { let la = la.clone(); let lb = lb.clone(); - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); g.insert(i-1, b); if o.is_some() { @@ -193,7 +196,8 @@ fn lookback( pub fn groupify( - mut g: VecDeque + mut g: VecDeque, + context: &Context ) -> Result< Token, (LineLocation, DaisyError) @@ -228,7 +232,7 @@ pub fn groupify( let (_, mut v) = levels.pop().unwrap(); let (_, v_now) = levels.last_mut().unwrap(); - lookback(&mut v)?; + lookback(&mut v, context)?; v_now.push_back(Token::Group(l, v)); }, @@ -254,14 +258,14 @@ pub fn groupify( let (_, v_now) = levels.last_mut().unwrap(); if v.len() == 0 { return Err((l, DaisyError::EmptyGroup)) } - lookback(&mut v)?; + lookback(&mut v, context)?; v_now.push_back(Token::Group(l, v)); } let (_, mut v) = levels.pop().unwrap(); - lookback(&mut v)?; + lookback(&mut v, context)?; return Ok(Token::Group(LineLocation{pos:0, len:last_linelocation.pos + last_linelocation.len}, v)); } \ No newline at end of file diff --git a/src/parser/stage/tokenize.rs b/src/parser/stage/tokenize.rs index 47b81b8..a092cd4 100644 --- a/src/parser/stage/tokenize.rs +++ b/src/parser/stage/tokenize.rs @@ -1,4 +1,5 @@ use std::collections::VecDeque; +use crate::context::Context; use super::super::{ Token, @@ -8,7 +9,12 @@ use super::super::{ // Called whenever a token is finished. #[inline(always)] -fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { +fn push_token( + g: &mut VecDeque, + t: Option, + stop_i: usize, + context: &Context +) { if t.is_none() { return } let mut t = t.unwrap(); @@ -52,7 +58,7 @@ fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { // Some operators are written as words. if let Token::Word(l, s) = &t { - if Operator::from_string(s).is_some() { + if Operator::from_string(s, context).is_some() { t = Token::Operator(*l, s.clone()); } } @@ -61,7 +67,7 @@ fn push_token(g: &mut VecDeque, t: Option, stop_i: usize) { } /// Turns a string into Tokens. First stage of parsing. -pub fn tokenize(input: &String) -> VecDeque { +pub fn tokenize(input: &String, context: &Context) -> VecDeque { let mut t: Option = None; // The current token we're reading let mut g: VecDeque = VecDeque::with_capacity(32); @@ -80,7 +86,7 @@ pub fn tokenize(input: &String) -> VecDeque { // If we're not building a number, finalize // previous token and start one. _ => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Quantity(LineLocation{pos: i, len: 0}, String::from(c))); } }; @@ -94,7 +100,7 @@ pub fn tokenize(input: &String) -> VecDeque { Some(Token::Quantity(_, val)) => { val.push(c); }, _ => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Word(LineLocation{pos: i, len: 0}, String::from(c))); } }; @@ -114,7 +120,7 @@ pub fn tokenize(input: &String) -> VecDeque { } else { // Otherwise, end the number. // We probably have a subtraction. - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Operator( LineLocation{pos: i, len: 1}, String::from(c) @@ -126,7 +132,7 @@ pub fn tokenize(input: &String) -> VecDeque { // Multi-character operators with - and + are NOT supported! // (for example, we can't use -> for unit conversion) _ => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Operator( LineLocation{pos: i, len: 1}, String::from(c) @@ -136,7 +142,7 @@ pub fn tokenize(input: &String) -> VecDeque { }, ',' => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Operator( LineLocation{pos: i, len: 1}, String::from(c) @@ -152,7 +158,7 @@ pub fn tokenize(input: &String) -> VecDeque { match &mut t { Some(Token::Operator(_, val)) => { val.push(c); }, _ => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Operator(LineLocation{pos: i, len: 0}, String::from(c))); } }; @@ -160,17 +166,17 @@ pub fn tokenize(input: &String) -> VecDeque { // Group '(' => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::GroupStart(LineLocation{pos: i, len: 0})); }, ')' => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::GroupEnd(LineLocation{pos: i, len: 0})); }, // Space. Basic seperator. ' ' => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = None; } @@ -180,7 +186,7 @@ pub fn tokenize(input: &String) -> VecDeque { Some(Token::Word(_, val)) => { val.push(c); }, _ => { - push_token(&mut g, t, i); + push_token(&mut g, t, i, context); t = Some(Token::Word(LineLocation{pos: i, len: 0}, String::from(c))); } }; @@ -188,7 +194,7 @@ pub fn tokenize(input: &String) -> VecDeque { }; } - push_token(&mut g, t, input.chars().count()); + push_token(&mut g, t, input.chars().count(), context); return g; } \ No newline at end of file diff --git a/src/parser/stage/treeify.rs b/src/parser/stage/treeify.rs index 5e12955..e453bfc 100644 --- a/src/parser/stage/treeify.rs +++ b/src/parser/stage/treeify.rs @@ -56,7 +56,7 @@ fn treeify_binary( if let Token::Operator(l, s) = left { - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string let o = o.unwrap(); @@ -72,7 +72,7 @@ fn treeify_binary( } if let Token::Operator(l, s) = right { - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string let o = o.unwrap(); @@ -91,7 +91,7 @@ fn treeify_binary( // This operator let this_op = { let Token::Operator(l, s) = this else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // bad operator string o.unwrap() }; @@ -99,14 +99,14 @@ fn treeify_binary( // The operators contesting our arguments let left_op = if i > 1 { let Token::Operator(l, s) = &g_inner[i-2] else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad operator string Some(o.unwrap()) } else { None }; let right_op = if i < g_inner.len()-2 { let Token::Operator(l, s) = &g_inner[i+2] else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad operator string Some(o.unwrap()) } else { None }; @@ -137,7 +137,7 @@ fn treeify_binary( let (l, o) = { let Token::Operator(l, s) = this_pre else {panic!()}; - let o = Operator::from_string(&s); + let o = Operator::from_string(&s, context); if o.is_none() { panic!() } (l, o.unwrap()) }; @@ -218,7 +218,7 @@ fn treeify_unary( // This operator let this_op = { let Token::Operator(l, s) = this else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string o.unwrap() }; @@ -227,14 +227,14 @@ fn treeify_unary( let next_op = if left_associative { if i > 1 { let Token::Operator(l, s) = &g_inner[i-2] else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string Some(o.unwrap()) } else { None } } else { if i < g_inner.len()-2 { let Token::Operator(l, s) = &g_inner[i+2] else {panic!()}; - let o = Operator::from_string(s); + let o = Operator::from_string(s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } // Bad string Some(o.unwrap()) } else { None } @@ -258,7 +258,7 @@ fn treeify_unary( let (l, o) = { let Token::Operator(l, s) = this_pre else {panic!()}; - let o = Operator::from_string(&s); + let o = Operator::from_string(&s, context); if o.is_none() { panic!() } (l, o.unwrap()) }; @@ -314,7 +314,7 @@ pub fn treeify( // If not an operator, move on. let this_op = match &g_inner[i] { Token::Operator(l, s) => { - let o = Operator::from_string(&s); + let o = Operator::from_string(&s, context); if o.is_none() { return Err((*l, DaisyError::Syntax)); } o.unwrap() },