2025-07-04 18:10:03 +00:00
|
|
|
use crate::js::*;
|
2025-07-03 03:47:02 +00:00
|
|
|
use crate::lexer::Token;
|
2025-07-04 05:23:16 +00:00
|
|
|
use crate::panic::{Panic, PanicMsg};
|
2025-07-04 18:10:03 +00:00
|
|
|
use crate::validator::VErr;
|
2025-07-04 05:23:16 +00:00
|
|
|
use crate::vm::CallFrame;
|
2025-07-04 03:23:14 +00:00
|
|
|
use chumsky::error::RichPattern;
|
2025-07-03 03:47:02 +00:00
|
|
|
use chumsky::prelude::*;
|
2024-12-13 00:01:51 +00:00
|
|
|
|
2025-07-04 00:45:55 +00:00
|
|
|
const SEPARATOR: &str = "\n\n";
|
2024-12-13 00:01:51 +00:00
|
|
|
|
2025-07-03 03:47:02 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2025-07-04 00:45:55 +00:00
|
|
|
pub fn lexing(errs: Vec<Rich<'static, char>>, src: &'static str, input: &'static str) -> String {
|
|
|
|
let mut msgs = vec!["Ludus found some errors.".to_string()];
|
2025-07-03 03:47:02 +00:00
|
|
|
for err in errs {
|
2025-07-04 00:45:55 +00:00
|
|
|
let mut msg = vec![];
|
|
|
|
let line_number = line_number(src, *err.span());
|
|
|
|
let line = get_line(src, line_number);
|
|
|
|
let char = src.chars().nth(err.span().start).unwrap();
|
|
|
|
msg.push(format!("Syntax error: unexpected {char}"));
|
|
|
|
msg.push(format!(" on line {} in {}", line_number + 1, input));
|
|
|
|
msg.push(format!(" >>> {line}"));
|
|
|
|
msgs.push(msg.join("\n"));
|
2025-07-03 03:47:02 +00:00
|
|
|
}
|
2025-07-04 00:45:55 +00:00
|
|
|
msgs.join(SEPARATOR)
|
2025-07-03 03:47:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn validation(errs: Vec<VErr>) -> String {
|
2025-07-04 00:45:55 +00:00
|
|
|
let mut msgs = vec!["Ludus found some errors.".to_string()];
|
2025-07-03 03:47:02 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2025-07-04 03:23:14 +00:00
|
|
|
pub fn parsing(errs: Vec<Rich<'static, Token>>, src: &'static str, input: &'static str) -> String {
|
|
|
|
let mut msgs = vec!["Ludus found some errors.".to_string()];
|
2024-12-13 00:01:51 +00:00
|
|
|
for err in errs {
|
2025-07-04 03:23:14 +00:00
|
|
|
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"))
|
2024-12-13 00:01:51 +00:00
|
|
|
}
|
2025-07-04 03:23:14 +00:00
|
|
|
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}")
|
2024-12-13 00:01:51 +00:00
|
|
|
}
|
2025-07-04 05:23:16 +00:00
|
|
|
|
2025-07-04 18:10:03 +00:00
|
|
|
pub fn panic(panic: Panic) -> String {
|
|
|
|
// console_log!("Ludus panicked!: {panic}");
|
|
|
|
// panic.call_stack.last().unwrap().chunk().dissasemble();
|
|
|
|
// console_log!("{:?}", panic.call_stack.last().unwrap().chunk().spans);
|
|
|
|
let mut msgs = vec!["Ludus panicked!".to_string()];
|
2025-07-04 05:23:16 +00:00
|
|
|
let msg = match panic.msg {
|
2025-07-04 18:10:03 +00:00
|
|
|
PanicMsg::Generic(ref s) => s,
|
|
|
|
_ => &"no match".to_string(),
|
|
|
|
};
|
|
|
|
msgs.push(msg.clone());
|
|
|
|
msgs.push(traceback(&panic));
|
2025-07-04 05:23:16 +00:00
|
|
|
|
2025-07-04 18:10:03 +00:00
|
|
|
msgs.join("\n")
|
2025-07-04 05:23:16 +00:00
|
|
|
}
|
|
|
|
|
2025-07-04 18:10:03 +00:00
|
|
|
fn traceback(panic: &Panic) -> String {
|
|
|
|
let mut traceback = vec![];
|
|
|
|
for frame in panic.call_stack.iter().rev() {
|
|
|
|
traceback.push(frame_info(frame));
|
|
|
|
}
|
|
|
|
traceback.join("\n")
|
2025-07-04 05:23:16 +00:00
|
|
|
}
|
|
|
|
|
2025-07-04 18:10:03 +00:00
|
|
|
fn frame_info(frame: &CallFrame) -> String {
|
|
|
|
let span = frame.chunk().spans[if frame.ip == 0 {
|
|
|
|
frame.ip
|
|
|
|
} else {
|
|
|
|
frame.ip - 1
|
|
|
|
}];
|
|
|
|
let line_number = line_number(frame.chunk().src, span);
|
|
|
|
let line = get_line(frame.chunk().src, line_number);
|
|
|
|
let line = line.trim_start();
|
|
|
|
let name = frame.function.as_fn().name();
|
|
|
|
let input = frame.chunk().input;
|
|
|
|
format!(
|
|
|
|
" in {name} on line {} in {input}\n >>> {line}",
|
|
|
|
line_number + 1
|
|
|
|
)
|
2025-07-04 05:23:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/////// Some thoughts
|
|
|
|
// We're putting the information we need on the function and the chunk.
|
|
|
|
// In the compiler, on functions, build up a vec of strings that are the patterns the function can match against
|
|
|
|
// The pattern asts have a `show` method.
|
|
|
|
// And with the additional members on Chunk, we should have everything we need for a pretty fn no match message
|
|
|
|
// Let no match is no problem, either. We should have no concerns pulling the line with the span start and string
|
|
|
|
// We don't need to reproduce the pattern, since it will be right there in the code
|
|
|
|
// As for match forms, we'll just use "no match" and print the value
|