work on errors

This commit is contained in:
Scott Richmond 2025-07-02 23:47:02 -04:00
parent 2ffff9edd9
commit d334e483a5
8 changed files with 79 additions and 73 deletions

2
pkg/rudus.d.ts vendored
View File

@ -1,6 +1,6 @@
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */ /* eslint-disable */
export function ludus(src: string): Promise<string>; export function ludus(src: string): Promise<void>;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

View File

@ -225,7 +225,7 @@ function debugString(val) {
} }
/** /**
* @param {string} src * @param {string} src
* @returns {Promise<string>} * @returns {Promise<void>}
*/ */
export function ludus(src) { export function ludus(src) {
const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const ptr0 = passStringToWasm0(src, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -237,13 +237,13 @@ export function ludus(src) {
function _assertNum(n) { function _assertNum(n) {
if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(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(arg0);
_assertNum(arg1); _assertNum(arg1);
wasm.closure346_externref_shim(arg0, arg1, arg2); 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(arg0);
_assertNum(arg1); _assertNum(arg1);
wasm.closure370_externref_shim(arg0, arg1, arg2, arg3); wasm.closure370_externref_shim(arg0, arg1, arg2, arg3);
@ -346,7 +346,7 @@ function __wbg_get_imports() {
const a = state0.a; const a = state0.a;
state0.a = 0; state0.a = 0;
try { try {
return __wbg_adapter_52(a, state0.b, arg0, arg1); return __wbg_adapter_50(a, state0.b, arg0, arg1);
} finally { } finally {
state0.a = a; state0.a = a;
} }
@ -425,8 +425,8 @@ function __wbg_get_imports() {
_assertBoolean(ret); _assertBoolean(ret);
return ret; return ret;
}; };
imports.wbg.__wbindgen_closure_wrapper7787 = function() { return logError(function (arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper7819 = function() { return logError(function (arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_22); const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_20);
return ret; return ret;
}, arguments) }; }, arguments) };
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 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 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, 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) { imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1)); throw new Error(getStringFromWasm0(arg0, arg1));
}; };

Binary file not shown.

View File

@ -1,52 +1,44 @@
// use crate::process::{LErr, Trace}; // use crate::process::{LErr, Trace};
use crate::lexer::Token;
use crate::validator::VErr; use crate::validator::VErr;
use chumsky::prelude::*;
// pub fn report_panic(err: LErr) { const SEPARATOR: &str = "\n\n***\n";
// 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();
// }
pub fn report_invalidation(errs: Vec<VErr>) { fn line_number(src: &'static str, span: SimpleSpan) -> usize {
for err in errs { src.chars().take(span.start).filter(|c| *c == '\n').count()
// 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)) fn get_line(src: &'static str, line: usize) -> String {
// .finish() src.split("\n").nth(line).unwrap().to_string()
// .print(sources(vec![(err.input, err.src)])) }
// .unwrap();
println!("{}", err.msg); pub fn lexing(errs: Vec<Rich<'static, char>>) -> String {
} let mut msg = "Syntax errors".to_string();
for err in errs {
msg = format!("{msg}\n{:#?}", err);
}
msg
}
pub fn validation(errs: Vec<VErr>) -> 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<Rich<'static, Token>>) -> String {
let mut msg = "Syntax errors".to_string();
for err in errs {
msg = format!("{msg}\n{:#?}", err);
}
msg
} }

View File

@ -18,7 +18,6 @@ extern "C" {
fn log(s: String); fn log(s: String);
} }
type Lines = Value; // expect a list of values
type Commands = Value; // expect a list of values type Commands = Value; // expect a list of values
type Url = Value; // expect a string representing a URL type Url = Value; // expect a string representing a URL
type FinalValue = Result<Value, Panic>; type FinalValue = Result<Value, Panic>;
@ -29,7 +28,7 @@ fn make_json_payload(verb: &'static str, data: String) -> String {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum MsgOut { pub enum MsgOut {
Console(Lines), Console(Value),
Commands(Commands), Commands(Commands),
Fetch(Url), Fetch(Url),
Complete(FinalValue), Complete(FinalValue),
@ -65,7 +64,7 @@ impl MsgOut {
} }
MsgOut::Console(lines) => { MsgOut::Console(lines) => {
let lines = lines.as_list(); let lines = lines.as_list();
let json_lines = lines.iter().map(|line| line.stringify()).collect::<Vec<_>>().join("\\n"); let json_lines = lines.iter().map(|line| line.to_json().unwrap()).collect::<Vec<_>>().join("\\n");
let json_lines = format!("\"{json_lines}\""); let json_lines = format!("\"{json_lines}\"");
make_json_payload("Console", 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<MsgOut>) -> Vec<MsgIn> { pub async fn do_io (msgs: Vec<MsgOut>) -> Vec<MsgIn> {
let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::<Vec<_>>().join(",")); let outbox = format!("[{}]", msgs.iter().map(|msg| msg.to_json()).collect::<Vec<_>>().join(","));
let inbox = io (outbox).await; let inbox = io (outbox).await;

View File

@ -70,8 +70,8 @@ pub fn lexer(
let escape = just('\\') let escape = just('\\')
.then(choice(( .then(choice((
just('\\'), just('\\').to('\\'),
just('"'), just('"').to('"'),
just('n').to('\n'), just('n').to('\n'),
just('t').to('\t'), just('t').to('\t'),
just('r').to('\r'), just('r').to('\r'),

View File

@ -15,6 +15,7 @@ const DEBUG_PRELUDE_RUN: bool = false;
// static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() }; // static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
mod io; mod io;
use io::send_err_to_ludus_console;
mod ast; mod ast;
use crate::ast::Ast; use crate::ast::Ast;
@ -37,7 +38,7 @@ mod validator;
use crate::validator::Validator; use crate::validator::Validator;
mod errors; mod errors;
use crate::errors::report_invalidation; use crate::errors::{lexing, parsing, validation};
mod chunk; mod chunk;
mod op; mod op;
@ -113,13 +114,19 @@ extern "C" {
fn log(s: &str); fn log(s: &str);
} }
#[wasm_bindgen] #[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(); console_error_panic_hook::set_once();
// leak the source so it lives FOREVER
let src = src.to_string().leak(); let src = src.to_string().leak();
// lex the source
let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if !lex_errs.is_empty() { if !lex_errs.is_empty() {
return format!("{:?}", lex_errs); send_err_to_ludus_console(lexing(lex_errs)).await;
return;
} }
let tokens = tokens.unwrap(); 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))) .parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
.into_output_errors(); .into_output_errors();
if !parse_errors.is_empty() { if !parse_errors.is_empty() {
return format!("{:?}", parse_errors); send_err_to_ludus_console(parsing(parse_errors)).await;
return;
} }
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap())); let parsed: &'static Spanned<Ast> = 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 // TODO: validator should generate a string, not print to the console
if !validator.errors.is_empty() { if !validator.errors.is_empty() {
report_invalidation(validator.errors); send_err_to_ludus_console(validation(validator.errors)).await;
return "Ludus found some validation errors.".to_string(); return;
} }
let mut compiler = Compiler::new( let mut compiler = Compiler::new(
@ -173,7 +181,8 @@ pub async fn ludus(src: String) -> String {
world.run().await; world.run().await;
let result = world.result.clone(); 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(Ok(val)) => val.show(),
Some(Err(panic)) => format!("Ludus panicked! {panic}"), Some(Err(panic)) => format!("Ludus panicked! {panic}"),
None => "Ludus run terminated by user".to_string() None => "Ludus run terminated by user".to_string()
@ -182,5 +191,4 @@ pub async fn ludus(src: String) -> String {
// vm.print_stack(); // vm.print_stack();
} }
output
} }

View File

@ -260,8 +260,8 @@ impl Value {
use Value::*; use Value::*;
match self { match self {
True | False | Number(..) => Some(self.show()), True | False | Number(..) => Some(self.show()),
String(string) => Some(serde_json::to_string(string.as_ref()).unwrap()), String(string) => Some(string.escape_default().to_string()),
Interned(str) => Some(serde_json::to_string(str).unwrap()), Interned(str) => Some(str.escape_default().to_string()),
Keyword(str) => Some(format!("\"{str}\"")), Keyword(str) => Some(format!("\"{str}\"")),
List(members) => { List(members) => {
let mut joined = "".to_string(); let mut joined = "".to_string();