//! Tests the lexer\ use doughlang::{ ast::{ Anno, Annotation, Bind, Expr, Literal, Pat, Path, Use, macro_matcher::{Match, Subst}, }, lexer::{EOF, LexError, Lexer}, parser::{Parse, ParseError, Parser}, span::Span, token::{TKind, Token}, }; use repline::prebaked::*; use std::{ error::Error, io::{IsTerminal, stdin}, marker::PhantomData, }; fn clear() { print!("\x1b[H\x1b[2J\x1b[3J"); } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] enum Verbosity { #[default] Pretty, Debug, Quiet, } impl From<&str> for Verbosity { fn from(value: &str) -> Self { match value { "quiet" | "false" | "0" | "no" => Verbosity::Quiet, "debug" | "d" => Verbosity::Debug, "pretty" => Verbosity::Pretty, _ => Default::default(), } } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] enum ParseMode { #[default] Expr, Pat, Bind, Path, Literal, Use, Tokens, } impl From<&str> for ParseMode { fn from(value: &str) -> Self { match value { "expr" => Self::Expr, "pat" => Self::Pat, "bind" => Self::Bind, "path" => Self::Path, "literal" => Self::Literal, "use" => Self::Use, "tokens" => Self::Tokens, _ => Default::default(), } } } impl ParseMode { fn with<'a>(&self) -> fn(&'a str, Verbosity) { match self { Self::Expr => parse::<'a, Expr>, Self::Pat => parse::<'a, Pat>, Self::Bind => parse::<'a, Bind>, Self::Path => parse::<'a, Path>, Self::Literal => parse::<'a, Literal>, Self::Use => parse::<'a, Use>, Self::Tokens => tokens::<'a, dyn Parse<'a, Prec = ()>>, } } } fn main() -> Result<(), Box> { let mut verbose = Verbosity::from(std::env::var("DO_VERBOSE").as_deref().unwrap_or_default()); let mut parsing = ParseMode::from(std::env::var("DO_PARSING").as_deref().unwrap_or_default()); if stdin().is_terminal() { read_and("\x1b[32m", ".>", " >", |line| match line.trim_end() { "" => Ok(Response::Continue), "exit" => Ok(Response::Break), "help" => { println!("Parsing: {parsing:?} (expr, pat, bind, path, literal, use, tokens)"); println!("Verbose: {verbose:?} (pretty, debug, quiet)"); Ok(Response::Deny) } "clear" => { clear(); Ok(Response::Deny) } "macro" => { if let Err(e) = subst() { println!("\x1b[31m{e}\x1b[0m"); } Ok(Response::Accept) } line @ ("tokens" | "expr" | "pat" | "bind" | "path" | "literal" | "use") => { parsing = ParseMode::from(line); println!("Parse mode set to '{parsing:?}'"); Ok(Response::Accept) } line @ ("quiet" | "debug" | "pretty") => { verbose = Verbosity::from(line); println!("Verbosity set to '{verbose:?}'"); Ok(Response::Accept) } _ if line.ends_with("\n\n") => { parsing.with()(line, verbose); Ok(Response::Accept) } _ => Ok(Response::Continue), })?; } else { let doc = std::io::read_to_string(stdin())?; parsing.with()(&doc, verbose); } Ok(()) } fn subst() -> Result<(), Box> { let mut rl = repline::Repline::new("\x1b[35mexp", " >", "?>"); let exp = rl.read()?; let mut exp: Expr = Parser::new(Lexer::new(&exp)).parse(0)?; println!("\x1b[G\x1b[J{exp}"); rl.accept(); loop { rl.set_color("\x1b[36mpat"); let pat = rl.read()?; rl.accept(); print!("\x1b[G\x1b[J"); let mut p = Parser::new(Lexer::new(&pat)); let Ok(pat) = p.parse::(0) else { println!("{exp}"); continue; }; if p.next_if(TKind::Arrow).is_err() { let Some(Subst { exp, pat }) = exp.construct(&pat) else { println!("Match failed: {exp} <- {pat}"); continue; }; let mut pats: Vec<_> = pat.into_iter().collect(); pats.sort_by(|(a, _), (b, _)| a.cmp(b)); for (name, pat) in pats { println!("{name}: {pat}") } let mut exprs: Vec<_> = exp.into_iter().collect(); exprs.sort_by(|(a, _), (b, _)| a.cmp(b)); for (name, expr) in exprs.iter() { println!("{name}: {expr}") } continue; } let sub: Expr = p.parse(0)?; if exp.apply_rule(&pat, &sub) { println!("{exp}"); } else { println!("No match: {pat} in {exp}\n") } } } fn plural(count: usize) -> &'static str { match count { 1 => "", _ => "s", } } fn tokens<'t, T: Parse<'t> + ?Sized>(document: &'t str, verbose: Verbosity) { let _: PhantomData; // for lifetime variance let mut lexer = Lexer::new(document); loop { match (lexer.scan(), verbose) { (Err(LexError { res: EOF, .. }), _) => { break; } (Err(e), _) => { println!("\x1b[31m{e}\x1b[0m"); break; } (Ok(Token { lexeme, kind, span: Span { head, tail } }), Verbosity::Pretty) => { println!("{kind:?}\x1b[11G {head:<4} {tail:<4} {lexeme:?}") } (Ok(token), Verbosity::Debug) => { println!("{token:?}") } _ => {} } } } fn parse<'t, T: Parse<'t> + Annotation>(document: &'t str, verbose: Verbosity) { let mut parser = Parser::new(Lexer::new(document)); for idx in 0.. { match (parser.parse::>(T::Prec::default()), verbose) { (Err(e @ ParseError::EOF(s)), _) if s.tail == document.len() as _ => { println!( "\x1b[92m{e} (total {} byte{}, {idx} expression{})\x1b[0m", document.len(), plural(document.len()), plural(idx), ); break; } (Err(e @ ParseError::EOF(_)), _) => { println!( "\x1b[93m{e} (total {} byte{}, {idx} expression{})\x1b[0m", document.len(), plural(document.len()), plural(idx), ); break; } (Err(e), _) => { println!("\x1b[91m{e}\x1b[0m"); break; } (Ok(Anno(expr, span)), Verbosity::Pretty) => { println!("\x1b[{}m{span}:\n{expr}", (idx + 5) % 6 + 31); } (Ok(Anno(expr, span)), Verbosity::Debug) => { println!("\x1b[{}m{span}:\n{expr:#?}", (idx + 5) % 6 + 31); } _ => {} } } }