diff --git a/pkg/rudus.d.ts b/pkg/rudus.d.ts index 363ba34..23ca68f 100644 --- a/pkg/rudus.d.ts +++ b/pkg/rudus.d.ts @@ -14,8 +14,8 @@ export interface InitOutput { readonly __wbindgen_malloc: (a: number, b: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_export_6: WebAssembly.Table; - readonly closure304_externref_shim: (a: number, b: number, c: any) => void; - readonly closure328_externref_shim: (a: number, b: number, c: any, d: any) => void; + readonly closure303_externref_shim: (a: number, b: number, c: any) => void; + readonly closure327_externref_shim: (a: number, b: number, c: any, d: any) => void; readonly __wbindgen_start: () => void; } diff --git a/pkg/rudus.js b/pkg/rudus.js index 483c42b..77903a2 100644 --- a/pkg/rudus.js +++ b/pkg/rudus.js @@ -240,13 +240,13 @@ function _assertNum(n) { function __wbg_adapter_20(arg0, arg1, arg2) { _assertNum(arg0); _assertNum(arg1); - wasm.closure304_externref_shim(arg0, arg1, arg2); + wasm.closure303_externref_shim(arg0, arg1, arg2); } function __wbg_adapter_50(arg0, arg1, arg2, arg3) { _assertNum(arg0); _assertNum(arg1); - wasm.closure328_externref_shim(arg0, arg1, arg2, arg3); + wasm.closure327_externref_shim(arg0, arg1, arg2, arg3); } async function __wbg_load(module, imports) { @@ -425,8 +425,8 @@ function __wbg_get_imports() { _assertBoolean(ret); return ret; }; - imports.wbg.__wbindgen_closure_wrapper7890 = function() { return logError(function (arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 305, __wbg_adapter_20); + imports.wbg.__wbindgen_closure_wrapper7980 = function() { return logError(function (arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 304, __wbg_adapter_20); return ret; }, arguments) }; imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { diff --git a/pkg/rudus_bg.wasm b/pkg/rudus_bg.wasm index d7317fa..391ce7f 100644 Binary files a/pkg/rudus_bg.wasm and b/pkg/rudus_bg.wasm differ diff --git a/pkg/rudus_bg.wasm.d.ts b/pkg/rudus_bg.wasm.d.ts index 29db78a..2755b90 100644 --- a/pkg/rudus_bg.wasm.d.ts +++ b/pkg/rudus_bg.wasm.d.ts @@ -9,6 +9,6 @@ export const __wbindgen_free: (a: number, b: number, c: number) => void; export const __wbindgen_malloc: (a: number, b: number) => number; export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; export const __wbindgen_export_6: WebAssembly.Table; -export const closure304_externref_shim: (a: number, b: number, c: any) => void; -export const closure328_externref_shim: (a: number, b: number, c: any, d: any) => void; +export const closure303_externref_shim: (a: number, b: number, c: any) => void; +export const closure327_externref_shim: (a: number, b: number, c: any, d: any) => void; export const __wbindgen_start: () => void; diff --git a/src/errors.rs b/src/errors.rs index 086ced4..2d2b4f3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,7 @@ // use crate::process::{LErr, Trace}; use crate::lexer::Token; use crate::validator::VErr; +use chumsky::error::RichPattern; use chumsky::prelude::*; const SEPARATOR: &str = "\n\n"; @@ -42,10 +43,50 @@ pub fn validation(errs: Vec) -> String { msgs.join(SEPARATOR) } -pub fn parsing(errs: Vec>) -> String { - let mut msg = "Syntax errors".to_string(); +pub fn parsing(errs: Vec>, src: &'static str, input: &'static str) -> String { + let mut msgs = vec!["Ludus found some errors.".to_string()]; for err in errs { - msg = format!("{msg}\n{:#?}", err); + let mut msg = vec![]; + let line_number = line_number(src, *err.span()); + let line = get_line(src, line_number); + let details = parsing_message(err); + msg.push(format!("Syntax error: {}", details)); + msg.push(format!(" on line {} in {}", line_number + 1, input)); + msg.push(format!(" >>> {line}")); + msgs.push(msg.join("\n")) } - msg + msgs.join(SEPARATOR) +} + +fn parsing_message(err: Rich<'static, Token>) -> String { + let found = match err.found() { + Some(token) => token.show(), + None => "end of input".to_string(), + }; + let expected = err.expected(); + let mut expecteds = vec![]; + for pattern in expected { + let shown = match pattern { + RichPattern::Token(t) => t.show(), + RichPattern::Label(s) => s.to_string(), + RichPattern::Identifier(s) => s.clone(), + RichPattern::Any => "any".to_string(), + RichPattern::SomethingElse => "something else".to_string(), + RichPattern::EndOfInput => "eof".to_string(), + }; + expecteds.push(shown); + } + let expecteds = if expecteds.iter().any(|e| e == &"else".to_string()) { + vec!["else".to_string()] + } else { + expecteds + }; + let expecteds = if expecteds.iter().any(|e| e == &"then".to_string()) { + vec!["then".to_string()] + } else { + expecteds + }; + + let expecteds = expecteds.join(" | "); + format!("Ludus did not expect to see: {found}\n expected: {expecteds}") } diff --git a/src/lexer.rs b/src/lexer.rs index 3711436..c9090d6 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -2,7 +2,7 @@ use crate::spans::*; use chumsky::prelude::*; use std::fmt; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub enum Token { Nil, Number(f64), @@ -32,6 +32,24 @@ impl fmt::Display for Token { } } +impl Token { + pub fn show(&self) -> String { + match self { + Token::Number(n) => format!("{n}"), + Token::Boolean(b) => format!("{b}"), + Token::Keyword(k) => format!(":{k}"), + Token::Method(m) => format!("::{m}"), + Token::Nil => "nil".to_string(), + Token::String(s) => format!("\"{s}\""), + Token::Reserved(s) | Token::Word(s) => s.to_string(), + Token::Punctuation(s) => { + let out = if *s == "\n" { "newline" } else { s }; + out.to_string() + } + } + } +} + pub fn lexer( ) -> impl Parser<'static, &'static str, Vec<(Token, Span)>, extra::Err>> { let number = just('-') @@ -71,14 +89,14 @@ pub fn lexer( let escape = just('\\') .then(choice(( just('\\').to('\\'), - just('"').to('"'), just('n').to('\n'), just('t').to('\t'), just('r').to('\r'), + just('"').to('"'), // TODO: figure out why this isn't working ))) .ignored(); - let string = none_of("\\\"") + let string = none_of('"') .ignored() .or(escape) .repeated() @@ -147,6 +165,6 @@ mod tests { .0 .unwrap(); let (token, _) = spanned_toks[0].clone(); - assert_eq!(token, Token::String("foo \\\"bar baz")); + assert_eq!(token, Token::String("foo \"bar baz")); } } diff --git a/src/lib.rs b/src/lib.rs index ab8bf89..875aacd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,7 +135,7 @@ pub async fn ludus(src: String) { .parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s))) .into_output_errors(); if !parse_errors.is_empty() { - send_err_to_ludus_console(parsing(parse_errors)).await; + send_err_to_ludus_console(parsing(parse_errors, src, "user script")).await; return; } diff --git a/src/parser.rs b/src/parser.rs index c7628c5..0d355cd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -141,13 +141,15 @@ where just(Token::Punctuation(",")) .or(just(Token::Punctuation("\n"))) .then(separators.clone().repeated()) - }); + }) + .labelled("separator"); let terminators = recursive(|terminators| { just(Token::Punctuation(";")) .or(just(Token::Punctuation("\n"))) .then(terminators.clone().repeated()) - }); + }) + .labelled("terminator"); let placeholder_pattern = select! {Token::Punctuation("_") => PlaceholderPattern}.map_with(|p, e| (p, e.span())); @@ -207,7 +209,8 @@ where .allow_trailing() .collect() .delimited_by(just(Token::Punctuation("[")), just(Token::Punctuation("]"))) - .map_with(|list, e| (ListPattern(list), e.span())); + .map_with(|list, e| (ListPattern(list), e.span())) + .labelled("list pattern"); let key_pair_pattern = select! {Token::Keyword(k) => k} .then(pattern.clone()) @@ -228,6 +231,7 @@ where .or(shorthand_pattern) .or(str_pair_pattern) .or(splattern.clone()) + .labelled("pair pattern") .separated_by(separators.clone()) .allow_leading() .allow_trailing() @@ -238,11 +242,14 @@ where ) .map_with(|dict, e| (DictPattern(dict), e.span())); - let keyword = select! {Token::Keyword(k) => Keyword(k)}.map_with(|k, e| (k, e.span())); + let keyword = select! {Token::Keyword(k) => Keyword(k)} + .map_with(|k, e| (k, e.span())) + .labelled("keyword"); let as_pattern = select! {Token::Word(w) => w} .then_ignore(just(Token::Reserved("as"))) .then(select! {Token::Keyword(k) => k}) + .labelled("keyword") .map_with(|(w, t), e| (AsPattern(w, t), e.span())); pattern.define( @@ -289,7 +296,8 @@ where .allow_trailing() .collect() .delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")"))) - .map_with(|tuple, e| (Tuple(tuple), e.span())); + .map_with(|tuple, e| (Tuple(tuple), e.span())) + .labelled("tuple"); let args = simple .clone() @@ -299,7 +307,8 @@ where .allow_trailing() .collect() .delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")"))) - .map_with(|args, e| (Arguments(args), e.span())); + .map_with(|args, e| (Arguments(args), e.span())) + .labelled("args"); let or = just(Token::Reserved("or")).map_with(|_, e| (Or, e.span())); @@ -307,7 +316,8 @@ where let method = select!(Token::Method(m) => m) .then(tuple.clone()) - .map_with(|(m, t), e| (Ast::Method(m, Box::new(t)), e.span())); + .map_with(|(m, t), e| (Ast::Method(m, Box::new(t)), e.span())) + .labelled("method"); let synth_root = or.or(and).or(word).or(keyword); @@ -327,7 +337,8 @@ where Splat(if let Word(w) = w { w } else { unreachable!() }), e.span(), ) - }); + }) + .labelled("..."); let list = simple .clone()