ludus/src/lib.rs

187 lines
4.6 KiB
Rust
Raw Normal View History

2025-06-25 19:41:44 +00:00
use chumsky::{input::Stream, prelude::*};
use imbl::HashMap;
use wasm_bindgen::prelude::*;
2025-06-30 22:59:59 +00:00
use std::rc::Rc;
use std::cell::RefCell;
2025-06-25 19:41:44 +00:00
const DEBUG_SCRIPT_COMPILE: bool = false;
2025-07-06 16:08:57 +00:00
const DEBUG_SCRIPT_RUN: bool = false;
2025-06-25 19:41:44 +00:00
const DEBUG_PRELUDE_COMPILE: bool = false;
const DEBUG_PRELUDE_RUN: bool = false;
2025-06-30 16:48:50 +00:00
mod io;
2025-07-03 03:47:02 +00:00
use io::send_err_to_ludus_console;
2025-06-30 16:48:50 +00:00
2025-06-27 23:05:17 +00:00
mod ast;
use crate::ast::Ast;
2025-06-25 19:41:44 +00:00
mod base;
2025-06-26 20:11:35 +00:00
2025-06-26 05:28:33 +00:00
mod world;
2025-06-30 22:59:59 +00:00
use crate::world::{World, Zoo};
2025-06-25 19:41:44 +00:00
mod spans;
use crate::spans::Spanned;
mod lexer;
use crate::lexer::lexer;
mod parser;
2025-06-27 23:05:17 +00:00
use crate::parser::parser;
2025-06-25 19:41:44 +00:00
mod validator;
use crate::validator::Validator;
mod errors;
2025-07-03 03:47:02 +00:00
use crate::errors::{lexing, parsing, validation};
2025-06-25 19:41:44 +00:00
2025-07-04 05:23:16 +00:00
mod panic;
mod js;
use crate::js::*;
2025-06-25 19:41:44 +00:00
mod chunk;
mod op;
mod compiler;
use crate::compiler::Compiler;
2025-07-03 19:30:51 +00:00
pub mod value;
use value::{Value, Key};
2025-06-25 19:41:44 +00:00
mod vm;
2025-06-30 22:59:59 +00:00
use vm::Creature;
2025-06-25 19:41:44 +00:00
2025-07-07 04:12:01 +00:00
const PRELUDE: &str = include_str!("../assets/prelude.ld");
2025-06-25 19:41:44 +00:00
2025-07-03 19:30:51 +00:00
fn prelude() -> HashMap<Key, Value> {
2025-06-25 19:41:44 +00:00
let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap();
let (parsed, parse_errors) = parser()
.parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s)))
.into_output_errors();
if !parse_errors.is_empty() {
console_log!("ERROR PARSING PRELUDE:");
console_log!("{:?}", parse_errors);
2025-07-01 22:52:03 +00:00
panic!("parsing errors in prelude");
2025-06-25 19:41:44 +00:00
}
let parsed = parsed.unwrap();
let (ast, span) = &parsed;
let base = base::make_base();
let mut base_env = imbl::HashMap::new();
2025-07-03 19:30:51 +00:00
base_env.insert(Key::Keyword("base"), base.clone());
2025-06-25 19:41:44 +00:00
let mut validator = Validator::new(ast, span, "prelude", PRELUDE, base_env);
validator.validate();
if !validator.errors.is_empty() {
console_log!("VALIDATION ERRORS IN PRLUDE:");
2025-07-01 22:52:03 +00:00
// report_invalidation(validator.errors);
console_log!("{:?}", validator.errors);
2025-06-30 22:59:59 +00:00
panic!("validator errors in prelude");
2025-06-25 19:41:44 +00:00
}
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parsed));
let mut compiler = Compiler::new(
parsed,
"prelude",
PRELUDE,
0,
HashMap::new(),
DEBUG_PRELUDE_COMPILE,
);
compiler.emit_constant(base);
compiler.bind("base");
compiler.compile();
let chunk = compiler.chunk;
2025-06-30 22:59:59 +00:00
let stub_zoo = Rc::new(RefCell::new(Zoo::new()));
let mut prld_sync = Creature::new(chunk, stub_zoo, DEBUG_PRELUDE_RUN);
prld_sync.interpret();
let prelude = prld_sync.result.unwrap().unwrap();
2025-06-25 19:41:44 +00:00
match prelude {
Value::Dict(hashmap) => *hashmap,
_ => unreachable!(),
}
}
#[wasm_bindgen]
2025-07-03 03:47:02 +00:00
pub async fn ludus(src: String) {
// instrument wasm to report rust panics
2025-06-30 22:59:59 +00:00
console_error_panic_hook::set_once();
2025-07-03 03:47:02 +00:00
// leak the source so it lives FOREVER
2025-06-25 21:43:30 +00:00
let src = src.to_string().leak();
2025-07-03 03:47:02 +00:00
// lex the source
2025-06-25 19:41:44 +00:00
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if !lex_errs.is_empty() {
2025-07-04 00:45:55 +00:00
send_err_to_ludus_console(lexing(lex_errs, src, "user script")).await;
2025-07-03 03:47:02 +00:00
return;
2025-06-25 19:41:44 +00:00
}
let tokens = tokens.unwrap();
let (parse_result, parse_errors) = parser()
.parse(Stream::from_iter(tokens).map((0..src.len()).into(), |(t, s)| (t, s)))
.into_output_errors();
if !parse_errors.is_empty() {
2025-07-04 03:23:14 +00:00
send_err_to_ludus_console(parsing(parse_errors, src, "user script")).await;
2025-07-03 03:47:02 +00:00
return;
2025-06-25 19:41:44 +00:00
}
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
2025-06-30 22:59:59 +00:00
let prelude = prelude();
2025-06-25 19:41:44 +00:00
// let prelude = imbl::HashMap::new();
let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone());
validator.validate();
// TODO: validator should generate a string, not print to the console
if !validator.errors.is_empty() {
2025-07-03 03:47:02 +00:00
send_err_to_ludus_console(validation(validator.errors)).await;
return;
2025-06-25 19:41:44 +00:00
}
2025-06-30 16:48:50 +00:00
let mut compiler = Compiler::new(
parsed,
"user script",
2025-06-30 16:48:50 +00:00
src,
0,
prelude.clone(),
DEBUG_SCRIPT_COMPILE,
);
2025-06-25 19:41:44 +00:00
compiler.compile();
2025-07-01 16:54:11 +00:00
2025-06-25 19:41:44 +00:00
if DEBUG_SCRIPT_COMPILE {
2025-07-06 02:40:11 +00:00
console_log!("=== source code ===");
console_log!("{src}");
2025-06-25 19:41:44 +00:00
compiler.disassemble();
2025-07-06 02:40:11 +00:00
console_log!("\n\n")
2025-06-25 19:41:44 +00:00
}
if DEBUG_SCRIPT_RUN {
2025-07-06 02:40:11 +00:00
console_log!("=== vm run ===");
2025-06-25 19:41:44 +00:00
}
let vm_chunk = compiler.chunk;
2025-06-30 22:59:59 +00:00
let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN);
world.run().await;
2025-06-25 19:41:44 +00:00
2025-07-03 03:47:02 +00:00
// TODO: actually do something useful on a panic
2025-07-04 05:23:16 +00:00
// match result {
// Some(Ok(val)) => val.show(),
// Some(Err(panic)) => format!("Ludus panicked! {panic}"),
// None => "Ludus run terminated by user".to_string()
// };
// if DEBUG_SCRIPT_RUN {
// // vm.print_stack();
// }
2025-06-25 19:41:44 +00:00
}