diff --git a/src/main.rs b/src/main.rs index 11bfe63..f1db5b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,11 @@ -//! Tests the lexer -use doughlang::{ - ast::{Anno, Pat}, - parser::pat::Prec as PPrec, -}; -#[allow(unused_imports)] +//! Tests the lexer\ use doughlang::{ ast::{ - Expr, + Anno, Annotation, Bind, Expr, Literal, Pat, Path, Use, macro_matcher::{Match, Subst}, }, lexer::{EOF, LexError, Lexer}, - parser::{ParseError, Parser}, + parser::{Parse, ParseError, Parser}, span::Span, token::{TKind, Token}, }; @@ -18,125 +13,116 @@ 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) } - "lex" => { - lex()?; - Ok(Response::Deny) - } - "expr" => { - exprs()?; - Ok(Response::Deny) - } - "pat" => { - pats()?; - Ok(Response::Deny) - } "macro" => { if let Err(e) = subst() { println!("\x1b[31m{e}\x1b[0m"); } - Ok(Response::Deny) + 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") => { - parse(line); + parsing.with()(line, verbose); Ok(Response::Accept) } _ => Ok(Response::Continue), })?; } else { let doc = std::io::read_to_string(stdin())?; - // lex(&doc); - parse(&doc); + parsing.with()(&doc, verbose); } Ok(()) } -fn lex() -> Result<(), Box> { - clear(); - read_and("\x1b[93m", " >", "?>", |line| { - let mut lexer = Lexer::new(line); - if line.trim().is_empty() { - return Ok(Response::Break); - } - loop { - match lexer.scan() { - Err(LexError { res: EOF, .. }) => { - break Ok(Response::Accept); - } - Err(e) => { - println!("\x1b[31m{e}\x1b[0m"); - break Ok(Response::Deny); - } - Ok(Token { lexeme, kind, span: Span { head, tail } }) => { - println!("{kind:?}\x1b[11G {head:<4} {tail:<4} {lexeme:?}") - } - } - } - })?; - Ok(()) -} - -fn exprs() -> Result<(), Box> { - clear(); - read_and("\x1b[93m", ".>", " >", |line| { - let mut parser = Parser::new(Lexer::new(line)); - if line.trim().is_empty() { - return Ok(Response::Break); - } - for idx in 0.. { - match parser.parse::>(0) { - Err(ParseError::FromLexer(LexError { res: EOF, .. })) => { - return Ok(Response::Accept); - } - Err(e) => { - println!("\x1b[31m{e}\x1b[0m"); - return Ok(Response::Deny); - } - Ok(v) => println!("{idx}: {v}\n{v:#?}"), - } - } - Ok(Response::Accept) - })?; - Ok(()) -} - -fn pats() -> Result<(), Box> { - clear(); - read_and("\x1b[94m", " >", "?>", |line| { - let mut parser = Parser::new(Lexer::new(line)); - if line.trim().is_empty() { - return Ok(Response::Break); - } - loop { - match parser.parse::(PPrec::Min) { - Err(ParseError::FromLexer(LexError { res: EOF, .. })) => { - break Ok(Response::Accept); - } - Err(e) => { - println!("\x1b[31m{e}\x1b[0m"); - break Ok(Response::Deny); - } - Ok(v) => println!("{v}\n{v:#?}"), - } - } - })?; - Ok(()) -} - fn subst() -> Result<(), Box> { let mut rl = repline::Repline::new("\x1b[35mexp", " >", "?>"); let exp = rl.read()?; @@ -191,15 +177,34 @@ fn plural(count: usize) -> &'static str { } } -fn parse(document: &str) { +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)); - let verbose = !matches!( - std::env::var("DOUGHLANG_VERBOSE").as_deref(), - Ok("false" | "0" | "no") - ); for idx in 0.. { - match parser.parse::>(0) { - Err(e @ ParseError::EOF(s)) if s.tail == document.len() as _ => { + 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(), @@ -208,7 +213,7 @@ fn parse(document: &str) { ); break; } - Err(e @ ParseError::EOF(_)) => { + (Err(e @ ParseError::EOF(_)), _) => { println!( "\x1b[93m{e} (total {} byte{}, {idx} expression{})\x1b[0m", document.len(), @@ -217,13 +222,16 @@ fn parse(document: &str) { ); break; } - Err(e) => { + (Err(e), _) => { println!("\x1b[91m{e}\x1b[0m"); break; } - Ok(Anno(expr, span)) if verbose => { + (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); + } _ => {} } } diff --git a/test.sh b/test.sh index bee2495..fb661e1 100755 --- a/test.sh +++ b/test.sh @@ -1,9 +1,12 @@ #!/usr/bin/env bash -export DOUGHLANG_VERBOSE="${DOUGHLANG_VERBOSE:-0}" +export DO_VERBOSE="${DO_VERBOSE:-0}" +export DO_PARSING="${DO_PARSING:-expr}" + +cargo build --release for file in $(find "$@" -type f); do echo $file; - cat $file | cargo run -q $CARGO_FLAGS; + cat $file | cargo run --release -q $CARGO_FLAGS; echo; done