From b1756c7c210e9447c5bda07ad67cafe0cb6a6889 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 9 Apr 2022 11:48:25 +0200 Subject: [PATCH] something works at least --- Cargo.lock | 16 ++++ parser/Cargo.toml | 1 + parser/src/lexer.rs | 48 ++++++++++ parser/src/lib.rs | 88 +++++++++++++++++-- parser/src/parser.rs | 83 +++++++++-------- ...__parser__tests__nested_function2.snap.new | 14 --- .../parser__parser__tests__var_decl.snap | 48 ++++++++++ 7 files changed, 234 insertions(+), 64 deletions(-) delete mode 100644 parser/src/snapshots/parser__parser__tests__nested_function2.snap.new create mode 100644 parser/src/snapshots/parser__parser__tests__var_decl.snap diff --git a/Cargo.lock b/Cargo.lock index b6e6d2d..5eaaebe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "const-random", ] +[[package]] +name = "ariadne" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c" +dependencies = [ + "yansi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -190,6 +199,7 @@ checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" name = "parser" version = "0.1.0" dependencies = [ + "ariadne", "chumsky", "insta", "logos", @@ -365,3 +375,9 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index d1861db..d56c217 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ariadne = "0.1.5" chumsky = "0.8.0" logos = "0.12.0" diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 5f90832..f4e1c41 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,3 +1,5 @@ +use std::fmt::{Debug, Display, Formatter}; + use logos::Logos; #[derive(Logos, Debug, Clone, Hash, PartialEq, Eq)] @@ -91,6 +93,52 @@ pub enum Token<'a> { Error, } +impl<'a> Display for Token<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Token::Comment => f.write_str("comment"), + Token::BraceO => f.write_str("{"), + Token::BraceC => f.write_str("}"), + Token::BracketO => f.write_str("["), + Token::BracketC => f.write_str("]"), + Token::ParenO => f.write_str("("), + Token::ParenC => f.write_str(")"), + Token::Dot => f.write_str("."), + Token::Comma => f.write_str(","), + Token::Semi => f.write_str(";"), + Token::Eq => f.write_str("="), + Token::EqEq => f.write_str("=="), + Token::Bang => f.write_str("!"), + Token::BangEq => f.write_str("!="), + Token::Greater => f.write_str(">"), + Token::Less => f.write_str("<"), + Token::GreaterEq => f.write_str(">="), + Token::LessEq => f.write_str("<="), + Token::Asterisk => f.write_str("*"), + Token::Slash => f.write_str("/"), + Token::Plus => f.write_str("+"), + Token::Minus => f.write_str("-"), + Token::Or => f.write_str("|"), + Token::And => f.write_str("&"), + Token::OrOr => f.write_str("||"), + Token::AndAnd => f.write_str("&&"), + Token::Caret => f.write_str("^"), + Token::Arrow => f.write_str("->"), + Token::Colon => f.write_str(":"), + Token::Struct => f.write_str("struct"), + Token::Fn => f.write_str("fn"), + Token::If => f.write_str("if"), + Token::Else => f.write_str("else"), + Token::While => f.write_str("while"), + Token::Loop => f.write_str("loop"), + Token::Ident(ident) => write!(f, "identifier `{ident}`"), + Token::String(str) => write!(f, "\"{str}\""), + Token::Integer(int) => write!(f, "{int}"), + Token::Error => f.write_str("error"), + } + } +} + pub fn lex<'src>(code: &'src str) -> logos::Lexer<'_, Token<'src>> { Token::lexer(code) } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 16616fa..5625ffd 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; use logos::Logos; use crate::lexer::Token; @@ -16,16 +17,89 @@ pub fn parse(_str: &str, _file_name: PathBuf) -> Result { } pub fn test() { - let lexer = Token::lexer( - " + let src = " fn main() { - if 1 { 5 + 5; } + // if 1 { 5 + 5; } + u64 hello = 5; } -", - ); +"; + + let lexer = Token::lexer(src); let len = lexer.source().len(); - let r = parser::parse(lexer.spanned(), len, "test_file".into()); + let (file, errors) = parser::parse(lexer.spanned(), len, "test_file".into()); - println!("{r:#?}"); + if let Some(file) = file { + println!("AST: {file:#?}"); + } + + errors + .into_iter() + .map(|e| e.map(|c| c.to_string())) + .for_each(|e| { + let report = Report::build(ReportKind::Error, (), e.span().start); + + let report = match e.reason() { + chumsky::error::SimpleReason::Unclosed { span, delimiter } => report + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_label( + Label::new(span.clone()) + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_color(Color::Yellow), + ) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Must be closed before this {}", + e.found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + chumsky::error::SimpleReason::Unexpected => report + .with_message(format!( + "{}, expected {}", + if e.found().is_some() { + "Unexpected token in input" + } else { + "Unexpected end of input" + }, + if e.expected().len() == 0 { + "something else".to_string() + } else { + e.expected() + .map(|expected| match expected { + Some(expected) => expected.to_string(), + None => "end of input".to_string(), + }) + .collect::>() + .join(", ") + } + )) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Unexpected token {}", + e.found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label( + Label::new(e.span()) + .with_message(format!("{}", msg.fg(Color::Red))) + .with_color(Color::Red), + ), + }; + + report.finish().print(Source::from(&src)).unwrap(); + }); } diff --git a/parser/src/parser.rs b/parser/src/parser.rs index e9ce98d..e1803e1 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -156,35 +156,38 @@ fn statement_parser<'src>() -> impl Parser, Stmt, Error = Error<'src }) }); - let if_stmt = recursive(|if_stmt| { - just(Token::If) - .ignore_then(expr_parser()) - .then( - stmt.clone() - .repeated() - .delimited_by(just(Token::BraceO), just(Token::BraceC)), - ) - .then( - just(Token::Else).ignore_then( - if_stmt - .map(|if_stmt| ElsePart::ElseIf(Box::new(if_stmt))) - .or(stmt - .clone() - .repeated() - .delimited_by(just(Token::BraceO), just(Token::BraceC)) - .map_with_span(ElsePart::Else)) - .or_not(), - ), - ) - .map_with_span(|((cond, body), else_part), span| IfStmt { - cond, - body, - else_part, - span, - }) - }) - .map(Stmt::IfStmt); - choice((var_decl, assignment, if_stmt, expr_parser().map(Stmt::Expr))) + // let if_stmt = recursive(|if_stmt| { + // just(Token::If) + // .ignore_then(expr_parser()) + // .then( + // stmt.clone() + // .repeated() + // .delimited_by(just(Token::BraceO), just(Token::BraceC)), + // ) + // .then( + // just(Token::Else).ignore_then( + // if_stmt + // .map(|if_stmt| ElsePart::ElseIf(Box::new(if_stmt))) + // .or(stmt + // .clone() + // .repeated() + // .delimited_by(just(Token::BraceO), just(Token::BraceC)) + // .map_with_span(ElsePart::Else)) + // .or_not(), + // ), + // ) + // .map_with_span(|((cond, body), else_part), span| IfStmt { + // cond, + // body, + // else_part, + // span, + // }) + // }) + // .map(Stmt::IfStmt); + + var_decl + .or(assignment) + .or(expr_parser().map(Stmt::Expr)) .then_ignore(just(Token::Semi)) }) .labelled("statement") @@ -257,10 +260,10 @@ fn file_parser<'src>( file_name: PathBuf, ) -> impl Parser, File, Error = Error<'src>> + Clone { item_parser() - .repeated() + // .repeated() .map(move |items| File { name: file_name.clone(), - items, + items: vec![items], }) .labelled("file") } @@ -301,30 +304,24 @@ mod tests { #[test] fn expression() { let r = parse("fn main() { (4 / hallo()) + 5; }"); - insta::assert_debug_snapshot!(r) + insta::assert_debug_snapshot!(r); } #[test] fn function() { let r = parse("fn foo() -> u64 { 1 + 5; }"); - insta::assert_debug_snapshot!(r) + insta::assert_debug_snapshot!(r); } - //#[test] - //fn nested_function() { - // let r = parse("fn foo() { fn foo2() {} fn foo3() {} }"); - // insta::assert_debug_snapshot!(r) - //} - #[test] - fn nested_function2() { - let r = parse("fn foo() { fn foo2() {} 1 + 5; }"); - insta::assert_debug_snapshot!(r) + fn var_decl() { + let r = parse("fn foo() -> u64 { u64 hello = 5; }"); + insta::assert_debug_snapshot!(r); } #[test] fn struct_() { let r = parse("struct X { y: u64, x: u64 }"); - insta::assert_debug_snapshot!(r) + insta::assert_debug_snapshot!(r); } } diff --git a/parser/src/snapshots/parser__parser__tests__nested_function2.snap.new b/parser/src/snapshots/parser__parser__tests__nested_function2.snap.new deleted file mode 100644 index 8eebe1c..0000000 --- a/parser/src/snapshots/parser__parser__tests__nested_function2.snap.new +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: parser/src/parser.rs -assertion_line: 330 -expression: r ---- -( - Some( - File { - name: "parser__parser__tests", - items: [], - }, - ), - [], -) diff --git a/parser/src/snapshots/parser__parser__tests__var_decl.snap b/parser/src/snapshots/parser__parser__tests__var_decl.snap new file mode 100644 index 0000000..44dafde --- /dev/null +++ b/parser/src/snapshots/parser__parser__tests__var_decl.snap @@ -0,0 +1,48 @@ +--- +source: parser/src/parser.rs +assertion_line: 319 +expression: r +--- +( + Some( + File { + name: "parser__parser__tests", + items: [ + FnDecl( + FnDecl { + name: "foo", + params: [], + ret_ty: Some( + Ty { + span: 12..15, + kind: U64, + }, + ), + span: 0..2, + body: [ + VarDecl( + VarDecl { + name: "hello", + ty: Ty { + span: 18..21, + kind: U64, + }, + rhs: Some( + Literal( + Integer( + 5, + 30..31, + ), + ), + ), + span: 0..0, + }, + ), + ], + }, + ), + ], + }, + ), + [], +)