From 558b914e5f25dcf375ab3dd915b3b13dcb34069a Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 4 Aug 2023 22:40:27 -0700 Subject: [PATCH] Moved functions to main.rs --- src/entry/unix/unix.rs | 312 ++--------------------------------------- src/main.rs | 267 ++++++++++++++++++++++++++++++++++- 2 files changed, 277 insertions(+), 302 deletions(-) diff --git a/src/entry/unix/unix.rs b/src/entry/unix/unix.rs index eaa8dad..b6e5d43 100644 --- a/src/entry/unix/unix.rs +++ b/src/entry/unix/unix.rs @@ -6,277 +6,23 @@ use std::env; use termion::{ event::Key, input::TermRead, - raw::IntoRawMode, - raw::RawTerminal, - color, - style, + raw::IntoRawMode }; use super::promptbuffer::PromptBuffer; -use crate::errors::DaisyError; -use crate::formattedtext::FormattedText; -use crate::parser; use crate::command; -use crate::evaluate; use crate::context::Context; -use crate::parser::LineLocation; -use crate::parser::substitute; - -#[inline(always)] -fn do_expression( - stdout: &mut RawTerminal, - s: &String, - context: &mut Context -) -> Result { - - // Parse string +/* +Make this a macro: #[cfg(debug_assertions)] RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g = parser::parse(&s, context)?; + + code + #[cfg(debug_assertions)] RawTerminal::activate_raw_mode(&stdout).unwrap(); - - // Evaluate expression - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g_evaluated = evaluate::evaluate(&g, context)?; - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - // Display parsed string - write!( - stdout, " {}{}=>{}{} {}\r\n", - style::Bold, color::Fg(color::Magenta), - style::Reset, color::Fg(color::Reset), - g.to_string() - ).unwrap(); - - // Display result - write!( - stdout, "\n {}{}={} {}{}\r\n\n", - style::Bold, - color::Fg(color::Green), - style::Reset, - g_evaluated.to_string_outer(), - color::Fg(color::Reset) - ).unwrap(); - - return Ok(g_evaluated); -} - - -#[inline(always)] -fn do_assignment( - stdout: &mut RawTerminal, - s: &String, - context: &mut Context -) -> Result<(), (LineLocation, DaisyError)> { - - let parts = s.split("=").collect::>(); - if parts.len() != 2 { - return Err(( - LineLocation::new_zero(), - DaisyError::Syntax - )); - } - - // Index of first non-whitespace character in left - // (relative to whole prompt) - let starting_left = parts[0] - .char_indices() - .find(|(_, ch)| !(ch.is_whitespace() && *ch != '\n')) - .map(|(i, _)| i) - .unwrap_or_else(|| parts[0].len()); - - // Index of first non-whitespace character in right - // (relative to whole prompt) - // +1 accounts for equals sign - let starting_right = parts[0].chars().count() + 1 + - parts[1] - .char_indices() - .find(|(_, ch)| !(ch.is_whitespace() && *ch != '\n')) - .map(|(i, _)| i) - .unwrap_or_else(|| parts[0].len()); - - let left = parts[0].trim().to_string(); - let right = parts[1].trim().to_string(); - let right = substitute(&right, &context); - let left = substitute(&left, &context); - - let is_function = left.contains("("); - - - - if is_function { - let mut mode = 0; - let mut name = String::new(); - let mut args = String::new(); - for c in left.chars() { - match mode { - - // Mode 0: reading function name - 0 => { - if c == '(' { - mode = 1; continue; - } else { name.push(c); } - }, - - // Mode 1: reading arguments - 1 => { - if c == ')' { - mode = 2; continue; - } else { args.push(c); } - }, - - // Mode 2: we should be done by now. - // That close paren should've been the last character. - 2 => { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::Syntax - )); - }, - - _ => unreachable!() - } - } - - - let args = args - .split(",").collect::>() - .iter().map(|x| x.trim().to_string()).collect::>(); - - if name.len() == 0 { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::Syntax - )); - }; - - if !context.valid_function(&name) { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::BadFunction - )); - }; - - if args.iter().find(|x| &x[..] == "").is_some() { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::Syntax - )); - }; - - for a in &args { - if !context.valid_varible(a) { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::BadVariable - )); - } - } - - // Parse right hand side - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g = parser::parse(&right, context); - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - let Ok(g) = g else { - let Err((l, e)) = g else { unreachable!() }; - return Err(( - LineLocation{ pos: l.pos + starting_right, len: l.len}, - e - )); - }; - - // Display parsed string - write!( - stdout, " {}{}=>{}{} {left} = {}\r\n\n", - style::Bold, color::Fg(color::Magenta), - style::Reset, color::Fg(color::Reset), - g.to_string() - ).unwrap(); - - - for a in &args { - context.add_shadow(a.to_string(), None); - } - - // Evaluate expression - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g_evaluated = evaluate::evaluate(&g, context); - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - context.clear_shadow(); - - let Ok(g_evaluated) = g_evaluated else { - let Err((l, e)) = g_evaluated else { unreachable!() }; - return Err(( - LineLocation{ pos: l.pos + starting_right, len: l.len}, - e - )); - }; - - context.push_function(name, args, g_evaluated).unwrap(); - - } else { - - if !context.valid_varible(&left) { - return Err(( - LineLocation{ pos: starting_left, len: left.chars().count() }, - DaisyError::BadVariable - )); - } - - // Parse right hand side - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g = parser::parse(&right, context); - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - let Ok(g) = g else { - let Err((l, e)) = g else { unreachable!() }; - return Err(( - LineLocation{ pos: l.pos + starting_right, len: l.len}, - e - )); - }; - - // Display parsed string - write!( - stdout, " {}{}=>{}{} {left} = {}\r\n\n", - style::Bold, color::Fg(color::Magenta), - style::Reset, color::Fg(color::Reset), - g.to_string() - ).unwrap(); - - // Evaluate expression - #[cfg(debug_assertions)] - RawTerminal::suspend_raw_mode(&stdout).unwrap(); - let g_evaluated = evaluate::evaluate(&g, context); - #[cfg(debug_assertions)] - RawTerminal::activate_raw_mode(&stdout).unwrap(); - - let Ok(g_evaluated) = g_evaluated else { - let Err((l, e)) = g_evaluated else { unreachable!() }; - return Err(( - LineLocation{ pos: l.pos + starting_right, len: l.len}, - e - )); - }; - - context.push_variable(left.to_string(), g_evaluated).unwrap(); - } - - return Ok(()); - -} +*/ #[inline(always)] @@ -321,47 +67,13 @@ pub fn main() -> Result<(), std::io::Error> { if in_str.trim() == "quit" { break 'outer; - } else if command::is_command(&in_str) { - let t = command::do_command(&in_str, &mut context); - t.write(&mut stdout)?; - } else if in_str.contains("=") { - let r = do_assignment(&mut stdout, &in_str, &mut context); - if let Err((l, e)) = r { - - let t = FormattedText::new( - format!( - concat!( - "{}[e]{}[n]\n", - " {}\n" - ), - " ".repeat(l.pos + 4), - "^".repeat(l.len), - e.text().to_string(), - ) - ); - - t.write(&mut stdout).unwrap(); - } } else { - let r = do_expression(&mut stdout, &in_str, &mut context); - if let Ok(t) = r { - context.push_hist(t); - } else { - let Err((l, e)) = r else { unreachable!() }; + let r = crate::do_string(&in_str, &mut context); - let t = FormattedText::new( - format!( - concat!( - "{}[e]{}[n]\n", - " {}\n" - ), - " ".repeat(l.pos + 4), - "^".repeat(l.len), - e.text().to_string(), - ) - ); - - t.write(&mut stdout).unwrap(); + match r { + Ok(t) | Err(t) => { + t.write(&mut stdout).unwrap(); + } } } diff --git a/src/main.rs b/src/main.rs index 1ccd622..d0ac3a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,10 +9,273 @@ pub mod formattedtext; mod entry; use crate::entry::main_e; +use crate::parser::substitute; +use crate::errors::DaisyError; +use crate::formattedtext::FormattedText; +use crate::context::Context; +use crate::parser::LineLocation; + +#[cfg(test)] +mod tests; + fn main() -> Result<(), std::io::Error> { return main_e(); } +#[inline(always)] +pub fn do_string( + s: &String, + mut context: &mut Context +) -> Result { + + let r: (LineLocation, DaisyError); + if command::is_command(s) { + return Ok(command::do_command(s, &mut context)); + } else if s.contains("=") { + let x = do_assignment(s, &mut context); + match x { + Ok(t) => { return Ok(t) }, + Err(t) => { r = t } + }; + } else { + let x = do_expression(s, &mut context); + match x { + Ok((t, e)) => { context.push_hist(e); return Ok(t) }, + Err(t) => { r = t } + }; + } + + let (l, e) = r; + let t = FormattedText::new( + format!( + concat!( + "{}[e]{}[n]\n", + " {}\n" + ), + " ".repeat(l.pos + 4), + "^".repeat(l.len), + e.text().to_string(), + ) + ); + + return Err(t); +} + +// Handle a simple evaluation string. +// Returns a FormattedText with output that should be printed. +#[inline(always)] +fn do_expression( + s: &String, + context: &mut Context +) -> Result<(FormattedText, parser::Expression), (LineLocation, DaisyError)> { + + let mut output = FormattedText::new("".to_string()); + + let g = parser::parse(&s, context)?; + let g_evaluated = evaluate::evaluate(&g, context)?; + + // Display parsed string + output.push(&format!( + " [s]=>[n] {}\n\n", + g.to_string() + )); + + // Display result + output.push(&format!( + " [r]=[n] {}\n\n", + g_evaluated.to_string_outer(), + )); + + return Ok((output, g_evaluated)); +} + + +// Handle a variable or function definition string. +// Returns a FormattedText with output that should be printed. +#[inline(always)] +fn do_assignment( + s: &String, + context: &mut Context +) -> Result { + + let mut output = FormattedText::new("".to_string()); + + let parts = s.split("=").collect::>(); + if parts.len() != 2 { + return Err(( + LineLocation::new_zero(), + DaisyError::Syntax + )); + } + + // Index of first non-whitespace character in left + // (relative to whole prompt) + let starting_left = parts[0] + .char_indices() + .find(|(_, ch)| !(ch.is_whitespace() && *ch != '\n')) + .map(|(i, _)| i) + .unwrap_or_else(|| parts[0].len()); + + // Index of first non-whitespace character in right + // (relative to whole prompt) + // +1 accounts for equals sign + let starting_right = parts[0].chars().count() + 1 + + parts[1] + .char_indices() + .find(|(_, ch)| !(ch.is_whitespace() && *ch != '\n')) + .map(|(i, _)| i) + .unwrap_or_else(|| parts[0].len()); + + + let left = substitute(&parts[0].trim().to_string(), &context); + let right = substitute(&parts[1].trim().to_string(), &context); + let is_function = left.contains("("); + + // The order of methods below is a bit odd. + // This is intentional, since we want to check a definition's + // variable name before even attempting to parse its content. + if is_function { + let mut mode = 0; + let mut name = String::new(); + let mut args = String::new(); + for c in left.chars() { + match mode { + + // Mode 0: reading function name + 0 => { + if c == '(' { + mode = 1; continue; + } else { name.push(c); } + }, + + // Mode 1: reading arguments + 1 => { + if c == ')' { + mode = 2; continue; + } else { args.push(c); } + }, + + // Mode 2: we should be done by now. + // That close paren should've been the last character. + 2 => { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::Syntax + )); + }, + + _ => unreachable!() + } + } + + + let args = args + .split(",").collect::>() + .iter().map(|x| x.trim().to_string()).collect::>(); + + if name.len() == 0 { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::Syntax + )); + }; + + if !context.valid_function(&name) { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::BadFunction + )); + }; + + if args.iter().find(|x| &x[..] == "").is_some() { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::Syntax + )); + }; + + for a in &args { + if !context.valid_varible(a) { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::BadVariable + )); + } + } + + // Parse right hand side + let g = parser::parse(&right, context); + let Ok(g) = g else { + let Err((l, e)) = g else { unreachable!() }; + return Err(( + LineLocation{ pos: l.pos + starting_right, len: l.len}, + e + )); + }; + + // Display parsed string + output.push(&format!( + " [s]=>[n] {left} = {}\n\n", + g.to_string() + )); + + // Evaluate expression with shadow variables + for a in &args { context.add_shadow(a.to_string(), None);} + let g_evaluated = evaluate::evaluate(&g, context); + context.clear_shadow(); + let Ok(_g_evaluated) = g_evaluated else { + let Err((l, e)) = g_evaluated else { unreachable!() }; + return Err(( + LineLocation{ pos: l.pos + starting_right, len: l.len}, + e + )); + }; + + // We could push g_evaluated instead, but an un-evaluated string + // makes the 'vars' command prettier. + // + // We still need to evaluate g above, though, to make sure it works. + context.push_function(name, args, g).unwrap(); + } else { + + if !context.valid_varible(&left) { + return Err(( + LineLocation{ pos: starting_left, len: left.chars().count() }, + DaisyError::BadVariable + )); + } + + // Parse right hand side + let g = parser::parse(&right, context); + let Ok(g) = g else { + let Err((l, e)) = g else { unreachable!() }; + return Err(( + LineLocation{ pos: l.pos + starting_right, len: l.len}, + e + )); + }; + + // Display parsed string + output.push(&format!( + " [t]=>[n] {left} = {}\n\n", + g.to_string() + )); + + // Evaluate expression + let g_evaluated = evaluate::evaluate(&g, context); + let Ok(g_evaluated) = g_evaluated else { + let Err((l, e)) = g_evaluated else { unreachable!() }; + return Err(( + LineLocation{ pos: l.pos + starting_right, len: l.len}, + e + )); + }; + + context.push_variable(left.to_string(), g_evaluated).unwrap(); + } + + return Ok(output); + +} + -#[cfg(test)] -mod tests; \ No newline at end of file