187 lines
4.6 KiB
Rust
187 lines
4.6 KiB
Rust
use chumsky::{input::Stream, prelude::*};
|
|
use imbl::HashMap;
|
|
use wasm_bindgen::prelude::*;
|
|
use std::rc::Rc;
|
|
use std::cell::RefCell;
|
|
|
|
|
|
const DEBUG_SCRIPT_COMPILE: bool = false;
|
|
const DEBUG_SCRIPT_RUN: bool = false;
|
|
const DEBUG_PRELUDE_COMPILE: bool = false;
|
|
const DEBUG_PRELUDE_RUN: bool = false;
|
|
|
|
// #[cfg(target_family = "wasm")]
|
|
// #[global_allocator]
|
|
// static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
|
|
|
|
mod io;
|
|
|
|
mod ast;
|
|
use crate::ast::Ast;
|
|
|
|
mod base;
|
|
|
|
mod world;
|
|
use crate::world::{World, Zoo};
|
|
|
|
mod spans;
|
|
use crate::spans::Spanned;
|
|
|
|
mod lexer;
|
|
use crate::lexer::lexer;
|
|
|
|
mod parser;
|
|
use crate::parser::parser;
|
|
|
|
mod validator;
|
|
use crate::validator::Validator;
|
|
|
|
mod errors;
|
|
use crate::errors::report_invalidation;
|
|
|
|
mod chunk;
|
|
mod op;
|
|
|
|
mod compiler;
|
|
use crate::compiler::Compiler;
|
|
|
|
mod value;
|
|
use value::Value;
|
|
|
|
mod vm;
|
|
use vm::Creature;
|
|
|
|
const PRELUDE: &str = include_str!("../assets/test_prelude.ld");
|
|
|
|
fn prelude() -> HashMap<&'static str, Value> {
|
|
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() {
|
|
log("ERROR PARSING PRELUDE:");
|
|
log(format!("{:?}", parse_errors).as_str());
|
|
panic!("parsing errors in prelude");
|
|
}
|
|
|
|
let parsed = parsed.unwrap();
|
|
let (ast, span) = &parsed;
|
|
|
|
let base = base::make_base();
|
|
let mut base_env = imbl::HashMap::new();
|
|
base_env.insert("base", base.clone());
|
|
|
|
let mut validator = Validator::new(ast, span, "prelude", PRELUDE, base_env);
|
|
|
|
validator.validate();
|
|
|
|
if !validator.errors.is_empty() {
|
|
log("VALIDATION ERRORS IN PRLUDE:");
|
|
// report_invalidation(validator.errors);
|
|
log(format!("{:?}", validator.errors).as_str());
|
|
panic!("validator errors in prelude");
|
|
}
|
|
|
|
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;
|
|
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();
|
|
match prelude {
|
|
Value::Dict(hashmap) => *hashmap,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
#[wasm_bindgen(js_namespace = console)]
|
|
fn log(s: &str);
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub async fn ludus(src: String) -> String {
|
|
console_error_panic_hook::set_once();
|
|
let src = src.to_string().leak();
|
|
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
|
if !lex_errs.is_empty() {
|
|
return format!("{:?}", lex_errs);
|
|
}
|
|
|
|
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() {
|
|
return format!("{:?}", parse_errors);
|
|
}
|
|
|
|
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
|
|
|
|
let prelude = prelude();
|
|
// 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() {
|
|
report_invalidation(validator.errors);
|
|
return "Ludus found some validation errors.".to_string();
|
|
}
|
|
|
|
let mut compiler = Compiler::new(
|
|
parsed,
|
|
"ludus script",
|
|
src,
|
|
0,
|
|
prelude.clone(),
|
|
DEBUG_SCRIPT_COMPILE,
|
|
);
|
|
|
|
compiler.compile();
|
|
|
|
if DEBUG_SCRIPT_COMPILE {
|
|
println!("=== source code ===");
|
|
println!("{src}");
|
|
compiler.disassemble();
|
|
println!("\n\n")
|
|
}
|
|
|
|
if DEBUG_SCRIPT_RUN {
|
|
println!("=== vm run ===");
|
|
}
|
|
|
|
let vm_chunk = compiler.chunk;
|
|
|
|
let mut world = World::new(vm_chunk, prelude.clone(), DEBUG_SCRIPT_RUN);
|
|
world.run().await;
|
|
let result = world.result.clone();
|
|
|
|
let output = 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();
|
|
}
|
|
|
|
output
|
|
}
|