diff --git a/Cargo.lock b/Cargo.lock index ee65321..a5c995c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,23 +20,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "ascii-canvas" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] @@ -58,12 +46,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - [[package]] name = "beef" version = "0.5.0" @@ -86,21 +68,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] -name = "blake2b_simd" -version = "0.5.11" +name = "bitflags" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "cfg-if" @@ -131,29 +102,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "crossbeam-utils" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" -dependencies = [ - "autocfg", - "cfg-if", - "lazy_static", -] - [[package]] name = "crunchy" version = "0.2.2" @@ -190,10 +144,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] -name = "dirs" -version = "1.0.5" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", @@ -262,9 +226,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" -version = "0.1.16" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", @@ -273,9 +237,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "heck" @@ -288,18 +252,18 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "indexmap" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", "hashbrown", @@ -322,9 +286,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -337,9 +301,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "lalrpop" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46962a8c71b91c3524b117dfdd70844d4265a173c4c9109f98171aebdcf1195f" +checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" dependencies = [ "ascii-canvas", "atty", @@ -360,9 +324,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a708007b751af124d09e9c5d97515257902bc6b486a56b40bcafd939e8ff467" +checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" dependencies = [ "regex", ] @@ -375,9 +339,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.94" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "linked-hash-map" @@ -475,9 +439,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "precomputed-hash" @@ -517,19 +481,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] [[package]] name = "redox_users" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", "redox_syscall", - "rust-argon2", ] [[package]] @@ -549,18 +515,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -570,6 +524,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + [[package]] name = "ryu" version = "1.0.5" @@ -722,12 +682,12 @@ dependencies = [ [[package]] name = "term" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "byteorder", - "dirs", + "dirs-next", + "rustversion", "winapi", ] @@ -752,18 +712,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote", @@ -817,9 +777,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/error.rs b/src/error.rs index 095fdef..333dc58 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::parser::{ ast::{Ast, AstType, Span}, lexer, parse_error, }; diff --git a/src/format_diagnostic.rs b/src/format_diagnostic.rs index 5fd8a71..d72c16a 100644 --- a/src/format_diagnostic.rs +++ b/src/format_diagnostic.rs @@ -1,42 +1,60 @@ use codespan_reporting::{diagnostic, files}; use simplexpr::dynval; -use crate::{ast::Span, error::AstError, parse_error}; use diagnostic::*; -fn span_to_label(span: Span) -> Label { - Label::primary(span.2, span.0..span.1) +use crate::error::AstError; + +use super::parser::{ast::Span, parse_error}; + +macro_rules! gen_diagnostic { + ( + $(msg = $msg:expr)? + $(, label = $span:expr $(=> $label:expr)?)? + $(, note = $note:expr)? $(,)? + ) => { + Diagnostic::error() + $(.with_message($msg))? + $(.with_labels(vec![ + Label::primary($span.2, $span.0..$span.1) + $(.with_message($label))? + ]))? + $(.with_notes(vec![$note]))? + }; + ($msg:expr $(, $span:expr $(,)?)?) => {{ + Diagnostic::error() + .with_message($msg) + $(.with_labels(vec![Label::primary($span.2, $span.0..$span.1)]))? + }}; } pub trait ToDiagnostic { - fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic; + fn to_diagnostic(&self) -> Diagnostic; } impl ToDiagnostic for AstError { - fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic { + fn to_diagnostic(&self) -> Diagnostic { let diag = Diagnostic::error(); if let Some(span) = self.get_span() { use lalrpop_util::ParseError::*; match self { AstError::InvalidDefinition(_) => todo!(), - AstError::MissingNode(_, expected) => diag - .with_message(format!("Missing {}", expected)) - .with_labels(vec![span_to_label(span).with_message(format!("Expected `{}` here", expected))]), + AstError::MissingNode(_, expected) => gen_diagnostic! { + msg = format!("Missing {}", expected), + label = span => format!("Expected `{}` here", expected), + }, - AstError::WrongExprType(_, expected, actual) => diag - .with_message("Wrong type of expression") - .with_notes(vec![format!("Expected: {}\nGot: {}", expected, actual)]) - .with_labels(vec![span_to_label(span).with_message(format!("Expected a `{}` here", expected))]), + AstError::WrongExprType(_, expected, actual) => gen_diagnostic! { + msg = "Wrong type of expression", + label = span => format!("Expected a `{}` here", expected), + note = format!("Expected: {}\nGot: {}", expected, actual), + }, - AstError::ParseError { file_id, source } => { - lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| match error { - parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, diag, span), - parse_error::ParseError::LexicalError(_) => diag - .with_message("Invalid token") - .with_labels(vec![span_to_label(span).with_message("Invalid token")]), - }) - } + AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, span, |error| match error { + parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, span), + parse_error::ParseError::LexicalError(_) => lexical_error_to_diagnostic(span), + }), } } else { diag.with_message(format!("{}", self)) @@ -46,36 +64,47 @@ impl ToDiagnostic for AstError { fn lalrpop_error_to_diagnostic( error: &lalrpop_util::ParseError, - diag: Diagnostic, span: Span, - handle_user_error: impl FnOnce(Diagnostic, &E) -> Diagnostic, + handle_user_error: impl FnOnce(&E) -> Diagnostic, ) -> Diagnostic { use lalrpop_util::ParseError::*; match error { - InvalidToken { location } => diag.with_message("Invalid token").with_labels(vec![span_to_label(span)]), - UnrecognizedEOF { location, expected } => diag - .with_message("Input ended unexpectedly. Check if you have any unclosed delimiters") - .with_labels(vec![span_to_label(span)]), - UnrecognizedToken { token, expected } => diag - .with_message(format!("Unexpected token `{}` encoutered", token.1)) - .with_labels(vec![span_to_label(span).with_message("Token unexpected")]), - - ExtraToken { token } => diag.with_message(format!("Extra token encountered: `{}`", token.1)), - User { error } => handle_user_error(diag, error), + InvalidToken { location } => gen_diagnostic! { msg = "Invalid token", label = span }, + UnrecognizedEOF { location, expected } => gen_diagnostic! { + msg = "Input ended unexpectedly. Check if you have any unclosed delimiters", + label = span + }, + UnrecognizedToken { token, expected } => gen_diagnostic! { + msg = format!("Unexpected token `{}` encountered", token.1), + label = span => "Token unexpected", + }, + ExtraToken { token } => gen_diagnostic!(format!("Extra token encountered: `{}`", token.1)), + User { error } => handle_user_error(error), } } -fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, diag: Diagnostic, span: Span) -> Diagnostic { +fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, span: Span) -> Diagnostic { + use simplexpr::error::Error::*; match error { - simplexpr::error::Error::ParseError { source } => lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| { - diag.with_message("Invalid token").with_labels(vec![span_to_label(span).with_message("Invalid token")]) - }), - simplexpr::error::Error::ConversionError(dynval::ConversionError { value, target_type, source }) => diag - .with_message(format!("{}", error)) - .with_labels(vec![span_to_label(span).with_message(format!("{} is not of type `{}`", value, target_type))]) - .with_notes(source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()), - simplexpr::error::Error::Spanned(..) => todo!(), - simplexpr::error::Error::Eval(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), - simplexpr::error::Error::Other(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]), + ParseError { source } => lalrpop_error_to_diagnostic(source, span, move |error| lexical_error_to_diagnostic(span)), + ConversionError(error) => conversion_error_to_diagnostic(error, span), + Eval(error) => gen_diagnostic!(format!("{}", error), span), + Other(error) => gen_diagnostic!(format!("{}", error), span), + Spanned(_, error) => gen_diagnostic!(format!("{}", error), span), + } +} + +fn conversion_error_to_diagnostic(error: &dynval::ConversionError, span: Span) -> Diagnostic { + let diag = gen_diagnostic! { + msg = format!("{}", error), + label = span => format!("{} is not of type `{}`", error.value, error.target_type), + }; + diag.with_notes(error.source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()) +} + +fn lexical_error_to_diagnostic(span: Span) -> Diagnostic { + gen_diagnostic! { + msg = "Invalid token", + label = span => "Invalid token" } } diff --git a/src/lib.rs b/src/lib.rs index ddae1d1..687c8f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,67 +2,8 @@ #![allow(unused)] #![feature(try_blocks)] -pub mod ast; pub mod config; pub mod error; pub mod format_diagnostic; -mod lexer; -mod parse_error; +pub mod parser; pub mod value; - -use ast::Ast; -use error::{AstError, AstResult}; - -use std::{fmt::Display, ops::Deref}; - -use itertools::Itertools; - -use lalrpop_util::lalrpop_mod; - -lalrpop_mod!( - #[allow(clippy::all)] - pub parser -); - -pub fn parse_string(file_id: usize, s: &str) -> AstResult { - let lexer = lexer::Lexer::new(file_id, s.to_string()); - let parser = parser::AstParser::new(); - parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) -} - -macro_rules! test_parser { - ($($text:literal),*) => {{ - let p = crate::parser::AstParser::new(); - use crate::lexer::Lexer; - - ::insta::with_settings!({sort_maps => true}, { - $( - ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text.to_string()))); - )* - }); - }} -} - -#[test] -fn test() { - test_parser!( - "1", - "(12)", - "1.2", - "-1.2", - "(1 2)", - "(1 :foo 1)", - "(:foo 1)", - "(:foo->: 1)", - "(foo 1)", - "(lol😄 1)", - r#"(test "hi")"#, - r#"(test "h\"i")"#, - r#"(test " hi ")"#, - "(+ (1 2 (* 2 5)))", - r#"; test"#, - r#"(f arg ; test - arg2)"#, - "\"h\\\"i\"" - ); -} diff --git a/src/ast.rs b/src/parser/ast.rs similarity index 90% rename from src/ast.rs rename to src/parser/ast.rs index a3cc70b..66ed23e 100644 --- a/src/ast.rs +++ b/src/parser/ast.rs @@ -2,9 +2,11 @@ use itertools::Itertools; use simplexpr::ast::SimplExpr; use std::collections::HashMap; -use crate::{config::FromAst, error::*}; use std::fmt::Display; +use super::element::FromAst; +use crate::error::{AstError, AstResult}; + #[derive(Eq, PartialEq, Clone, Copy)] pub struct Span(pub usize, pub usize, pub usize); @@ -124,15 +126,16 @@ impl std::fmt::Display for Ast { impl std::fmt::Debug for Ast { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use Ast::*; - match self { - List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), - Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), - Keyword(span, x) => write!(f, "Number<{}>({})", span, x), - Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), - Value(span, x) => write!(f, "Value<{}>({})", span, x), - SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), - Comment(span) => write!(f, "Comment<{}>", span), - } + write!(f, "{}", self) + // match self { + // List(span, x) => f.debug_tuple(&format!("List<{}>", span)).field(x).finish(), + // Array(span, x) => f.debug_tuple(&format!("Array<{}>", span)).field(x).finish(), + // Keyword(span, x) => write!(f, "Number<{}>({})", span, x), + // Symbol(span, x) => write!(f, "Symbol<{}>({})", span, x), + // Value(span, x) => write!(f, "Value<{}>({})", span, x), + // SimplExpr(span, x) => write!(f, "SimplExpr<{}>({})", span, x), + // Comment(span) => write!(f, "Comment<{}>", span), + //} } } diff --git a/src/config.rs b/src/parser/element.rs similarity index 84% rename from src/config.rs rename to src/parser/element.rs index 3e64b7a..728ac0b 100644 --- a/src/config.rs +++ b/src/parser/element.rs @@ -1,8 +1,5 @@ -use crate::{ - ast::{Ast, AstIterator, AstType, Span}, - error::*, - parser, spanned, -}; +use super::ast::{Ast, AstIterator, AstType, Span}; +use crate::{error::*, parser, spanned}; use itertools::Itertools; use std::{ collections::{HashMap, LinkedList}, @@ -49,13 +46,17 @@ impl FromAst for Element { #[cfg(test)] mod test { - use super::*; - use crate::lexer; + use super::super::{ + ast::Ast, + element::{Element, FromAst}, + lexer, + }; + use insta; #[test] fn test() { - let parser = parser::AstParser::new(); + let parser = super::parser::parser::AstParser::new(); insta::with_settings!({sort_maps => true}, { let lexer = lexer::Lexer::new(0, "(box :bar 12 :baz \"hi\" foo (bar))".to_string()); insta::assert_debug_snapshot!( diff --git a/src/lexer.rs b/src/parser/lexer.rs similarity index 99% rename from src/lexer.rs rename to src/parser/lexer.rs index ded1181..de184e1 100644 --- a/src/lexer.rs +++ b/src/parser/lexer.rs @@ -1,6 +1,6 @@ use regex::{Regex, RegexSet}; -use crate::{ast::Span, parse_error}; +use super::{ast::Span, parse_error}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { diff --git a/src/parser/mod.rs b/src/parser/mod.rs new file mode 100644 index 0000000..d00ad8a --- /dev/null +++ b/src/parser/mod.rs @@ -0,0 +1,62 @@ +use lalrpop_util::lalrpop_mod; + +use super::error::{AstError, AstResult}; +use ast::Ast; + +use std::{fmt::Display, ops::Deref}; + +use itertools::Itertools; + +pub mod ast; +pub mod element; +pub(crate) mod lexer; +pub(crate) mod parse_error; + +lalrpop_mod!( + #[allow(clippy::all)] + pub parser, + "/parser/parser.rs" +); + +pub fn parse_string(file_id: usize, s: &str) -> AstResult { + let lexer = lexer::Lexer::new(file_id, s.to_string()); + let parser = parser::AstParser::new(); + parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e)) +} + +macro_rules! test_parser { + ($($text:literal),*) => {{ + let p = parser::AstParser::new(); + use lexer::Lexer; + + ::insta::with_settings!({sort_maps => true}, { + $( + ::insta::assert_debug_snapshot!(p.parse(0, Lexer::new(0, $text.to_string()))); + )* + }); + }} +} + +#[test] +fn test() { + test_parser!( + "1", + "(12)", + "1.2", + "-1.2", + "(1 2)", + "(1 :foo 1)", + "(:foo 1)", + "(:foo->: 1)", + "(foo 1)", + "(lol😄 1)", + r#"(test "hi")"#, + r#"(test "h\"i")"#, + r#"(test " hi ")"#, + "(+ (1 2 (* 2 5)))", + r#"; test"#, + r#"(f arg ; test + arg2)"#, + "\"h\\\"i\"" + ); +} diff --git a/src/parse_error.rs b/src/parser/parse_error.rs similarity index 89% rename from src/parse_error.rs rename to src/parser/parse_error.rs index 972fb6c..9c2fe44 100644 --- a/src/parse_error.rs +++ b/src/parser/parse_error.rs @@ -1,4 +1,4 @@ -use crate::ast::Span; +use super::ast::Span; #[derive(Debug, thiserror::Error)] pub enum ParseError { diff --git a/src/parser.lalrpop b/src/parser/parser.lalrpop similarity index 90% rename from src/parser.lalrpop rename to src/parser/parser.lalrpop index 3ae49a8..817a57b 100644 --- a/src/parser.lalrpop +++ b/src/parser/parser.lalrpop @@ -1,6 +1,5 @@ use std::str::FromStr; -use crate::lexer::{Token}; -use crate::ast::{Ast, Span}; +use crate::parser::{lexer::{Token}, ast::{Ast, Span}, parse_error}; use simplexpr::ast::SimplExpr; use simplexpr; use lalrpop_util::ParseError; @@ -9,7 +8,7 @@ grammar(file_id: usize); extern { type Location = usize; - type Error = crate::parse_error::ParseError; + type Error = parse_error::ParseError; enum Token { "(" => Token::LPren, @@ -58,7 +57,7 @@ SimplExpr: SimplExpr = { let expr = x[1..x.len() - 1].to_string(); simplexpr::parse_string(&expr).map_err(|e| { let span = e.get_span().map(|simplexpr::Span(simpl_l, simpl_r)| Span(1 + l + simpl_l, 1 + l + simpl_r, file_id)); - ParseError::User { error: crate::parse_error::ParseError::SimplExpr(span, e) }}) + ParseError::User { error: parse_error::ParseError::SimplExpr(span, e) }}) } }