diff --git a/pkg/rudus.d.ts b/pkg/rudus.d.ts index 329ab57..3f771fc 100644 --- a/pkg/rudus.d.ts +++ b/pkg/rudus.d.ts @@ -1,6 +1,6 @@ /* tslint:disable */ /* eslint-disable */ -export function ludus(src: string): Promise; +export function ludus(src: string): Promise; export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; diff --git a/pkg/rudus.js b/pkg/rudus.js index c4921c7..4ce4f8a 100644 --- a/pkg/rudus.js +++ b/pkg/rudus.js @@ -225,7 +225,7 @@ function debugString(val) { } /** * @param {string} src - * @returns {Promise} + * @returns {Promise} */ export function ludus(src) { const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -237,13 +237,13 @@ export function ludus(src) { function _assertNum(n) { if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(n)}`); } -function __wbg_adapter_22(arg0, arg1, arg2) { +function __wbg_adapter_20(arg0, arg1, arg2) { _assertNum(arg0); _assertNum(arg1); wasm.closure346_externref_shim(arg0, arg1, arg2); } -function __wbg_adapter_52(arg0, arg1, arg2, arg3) { +function __wbg_adapter_50(arg0, arg1, arg2, arg3) { _assertNum(arg0); _assertNum(arg1); wasm.closure370_externref_shim(arg0, arg1, arg2, arg3); @@ -346,7 +346,7 @@ function __wbg_get_imports() { const a = state0.a; state0.a = 0; try { - return __wbg_adapter_52(a, state0.b, arg0, arg1); + return __wbg_adapter_50(a, state0.b, arg0, arg1); } finally { state0.a = a; } @@ -425,8 +425,8 @@ function __wbg_get_imports() { _assertBoolean(ret); return ret; }; - imports.wbg.__wbindgen_closure_wrapper7787 = function() { return logError(function (arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_22); + imports.wbg.__wbindgen_closure_wrapper7819 = function() { return logError(function (arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_20); return ret; }, arguments) }; imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { @@ -464,10 +464,6 @@ function __wbg_get_imports() { getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); }; - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return ret; - }; imports.wbg.__wbindgen_throw = function(arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; diff --git a/pkg/rudus_bg.wasm b/pkg/rudus_bg.wasm index e6e1cfd..fbaf4d6 100644 Binary files a/pkg/rudus_bg.wasm and b/pkg/rudus_bg.wasm differ diff --git a/src/errors.rs b/src/errors.rs index 59c9e96..777bc45 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,52 +1,44 @@ // use crate::process::{LErr, Trace}; +use crate::lexer::Token; use crate::validator::VErr; +use chumsky::prelude::*; -// pub fn report_panic(err: LErr) { -// let mut srcs = HashSet::new(); -// let mut stack = vec![]; -// let mut order = 1; -// for entry in err.trace.iter().rev() { -// let Trace { -// callee, -// caller, -// function, -// arguments, -// input, -// src, -// } = entry; -// let (_, first_span) = callee; -// let (_, second_span) = caller; -// let Value::Fn(f) = function else { -// unreachable!() -// }; -// let fn_name = f.borrow().name.clone(); -// let i = first_span.start; -// let j = second_span.end; -// let label = Label::new((entry.input, i..j)) -// .with_color(Color::Yellow) -// .with_message(format!("({order}) calling `{fn_name}` with `{arguments}`")); -// order += 1; -// stack.push(label); -// srcs.insert((*input, *src)); -// } -// Report::build(ReportKind::Error, (err.input, err.span.into_range())) -// .with_message(format!("Ludus panicked! {}", err.msg)) -// .with_label(Label::new((err.input, err.span.into_range())).with_color(Color::Red)) -// .with_labels(stack) -// .with_note(err.extra) -// .finish() -// .print(sources(srcs.iter().copied())) -// .unwrap(); -// } +const SEPARATOR: &str = "\n\n***\n"; -pub fn report_invalidation(errs: Vec) { - for err in errs { - // Report::build(ReportKind::Error, (err.input, err.span.into_range())) - // .with_message(err.msg.to_string()) - // .with_label(Label::new((err.input, err.span.into_range())).with_color(Color::Cyan)) - // .finish() - // .print(sources(vec![(err.input, err.src)])) - // .unwrap(); - println!("{}", err.msg); - } +fn line_number(src: &'static str, span: SimpleSpan) -> usize { + src.chars().take(span.start).filter(|c| *c == '\n').count() +} + +fn get_line(src: &'static str, line: usize) -> String { + src.split("\n").nth(line).unwrap().to_string() +} + +pub fn lexing(errs: Vec>) -> String { + let mut msg = "Syntax errors".to_string(); + for err in errs { + msg = format!("{msg}\n{:#?}", err); + } + msg +} + +pub fn validation(errs: Vec) -> String { + let mut msgs = vec![]; + for err in errs { + let mut msg = vec![]; + let line_number = line_number(err.src, *err.span); + let line = get_line(err.src, line_number); + msg.push(format!("Validation error: {}", err.msg)); + msg.push(format!(" on line {} in {}", line_number + 1, err.input)); + msg.push(format!(" >>> {line}")); + msgs.push(msg.join("\n")); + } + msgs.join(SEPARATOR) +} + +pub fn parsing(errs: Vec>) -> String { + let mut msg = "Syntax errors".to_string(); + for err in errs { + msg = format!("{msg}\n{:#?}", err); + } + msg } diff --git a/src/io.rs b/src/io.rs index 474de81..c6107b8 100644 --- a/src/io.rs +++ b/src/io.rs @@ -18,7 +18,6 @@ extern "C" { fn log(s: String); } -type Lines = Value; // expect a list of values type Commands = Value; // expect a list of values type Url = Value; // expect a string representing a URL type FinalValue = Result; @@ -29,7 +28,7 @@ fn make_json_payload(verb: &'static str, data: String) -> String { #[derive(Debug, Clone, PartialEq)] pub enum MsgOut { - Console(Lines), + Console(Value), Commands(Commands), Fetch(Url), Complete(FinalValue), @@ -65,7 +64,7 @@ impl MsgOut { } MsgOut::Console(lines) => { let lines = lines.as_list(); - let json_lines = lines.iter().map(|line| line.stringify()).collect::>().join("\\n"); + let json_lines = lines.iter().map(|line| line.to_json().unwrap()).collect::>().join("\\n"); let json_lines = format!("\"{json_lines}\""); make_json_payload("Console", json_lines) } @@ -123,6 +122,17 @@ impl MsgIn { } } +pub async fn send_err_to_ludus_console(msg: String) { + log(msg.clone()); + let console_msg = Value::string(msg); + let mut console_vector = Vector::new(); + console_vector.push_back(console_msg); + let console_list = Value::list(console_vector); + let console = MsgOut::Console(console_list); + let completion = MsgOut::Complete(Err(Panic::Str(""))); + do_io(vec![MsgOut::Ready, console, completion]).await; +} + pub async fn do_io (msgs: Vec) -> Vec { let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::>().join(",")); let inbox = io (outbox).await; diff --git a/src/lexer.rs b/src/lexer.rs index 8b6d566..3711436 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -70,8 +70,8 @@ pub fn lexer( let escape = just('\\') .then(choice(( - just('\\'), - just('"'), + just('\\').to('\\'), + just('"').to('"'), just('n').to('\n'), just('t').to('\t'), just('r').to('\r'), diff --git a/src/lib.rs b/src/lib.rs index 7ef08c4..310c9fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ const DEBUG_PRELUDE_RUN: bool = false; // static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() }; mod io; +use io::send_err_to_ludus_console; mod ast; use crate::ast::Ast; @@ -37,7 +38,7 @@ mod validator; use crate::validator::Validator; mod errors; -use crate::errors::report_invalidation; +use crate::errors::{lexing, parsing, validation}; mod chunk; mod op; @@ -113,13 +114,19 @@ extern "C" { fn log(s: &str); } + #[wasm_bindgen] -pub async fn ludus(src: String) -> String { +pub async fn ludus(src: String) { + // instrument wasm to report rust panics console_error_panic_hook::set_once(); + // leak the source so it lives FOREVER let src = src.to_string().leak(); + + // lex the source let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { - return format!("{:?}", lex_errs); + send_err_to_ludus_console(lexing(lex_errs)).await; + return; } let tokens = tokens.unwrap(); @@ -128,7 +135,8 @@ pub async fn ludus(src: String) -> String { .parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s))) .into_output_errors(); if !parse_errors.is_empty() { - return format!("{:?}", parse_errors); + send_err_to_ludus_console(parsing(parse_errors)).await; + return; } let parsed: &'static Spanned = Box::leak(Box::new(parse_result.unwrap())); @@ -141,8 +149,8 @@ pub async fn ludus(src: String) -> String { // TODO: validator should generate a string, not print to the console if !validator.errors.is_empty() { - report_invalidation(validator.errors); - return "Ludus found some validation errors.".to_string(); + send_err_to_ludus_console(validation(validator.errors)).await; + return; } let mut compiler = Compiler::new( @@ -173,7 +181,8 @@ pub async fn ludus(src: String) -> String { world.run().await; let result = world.result.clone(); - let output = match result { + // TODO: actually do something useful on a panic + match result { Some(Ok(val)) => val.show(), Some(Err(panic)) => format!("Ludus panicked! {panic}"), None => "Ludus run terminated by user".to_string() @@ -182,5 +191,4 @@ pub async fn ludus(src: String) -> String { // vm.print_stack(); } - output } diff --git a/src/value.rs b/src/value.rs index f63b35c..086fff4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -260,8 +260,8 @@ impl Value { use Value::*; match self { True | False | Number(..) => Some(self.show()), - String(string) => Some(serde_json::to_string(string.as_ref()).unwrap()), - Interned(str) => Some(serde_json::to_string(str).unwrap()), + String(string) => Some(string.escape_default().to_string()), + Interned(str) => Some(str.escape_default().to_string()), Keyword(str) => Some(format!("\"{str}\"")), List(members) => { let mut joined = "".to_string();