// an implementation of Ludus // curently left undone (and not adding for a while yet): // * sets // * interpolated strings & string patterns // * pkgs, namespaces, imports, `use` forms // * with forms // * test forms // * ignored words // todo: // * [x] rewrite fn parser to use chumsky::Recursive::declare/define // - [x] do this to extract/simplify/DRY things like tuple patterns, fn clauses, etc. // * [x] Work around chumsky::Stream::from_iter().spanned disappearing in most recent version // * [x] investigate using labels (which is behind a compiler flag, somehow) // * [ ] write parsing errors // * [ ] wire up Ariadne parsing errors // * [x] add stack traces and code locations to panics // * [x] validation // * [x] break this out into multiple files // * [x] write a tree-walk VM // - [x] learn how to deal with lifetimes // - [x] with stack mechanics and refcounting // - [ ] with tail-call optimization (nb: this may not be possible w/ a TW-VM) // - [ ] with all the necessary forms for current Ludus // * [x] guards in match clauses // * [x] `as` patterns // * [x] splat patterns in tuples, lists, dicts // * [x] splats in list and dict literals // * [x] `loop` and `recur` // * [x] string patterns // * [x] string interpolation // * [x] docstrings // * [x] write `base` in Rust // * [ ] turn this into a library function // * [ ] compile this into WASM // * [ ] perf testing use chumsky::{input::Stream, prelude::*}; use rust_embed::Embed; mod spans; mod lexer; use crate::lexer::*; mod value; use crate::value::*; mod parser; use crate::parser::*; mod base; use crate::base::*; mod validator; use crate::validator::*; mod process; use crate::process::*; mod errors; use crate::errors::*; #[derive(Embed)] #[folder = "assets/"] struct Asset; pub fn prelude<'src>() -> ( Vec<(String, Value<'src>)>, std::collections::HashMap<*const Ast, FnInfo>, ) { let prelude = Asset::get("prelude.ld").unwrap().data.into_owned(); // we know for sure Prelude should live through the whole run of the program let leaked = Box::leak(Box::new(prelude)); let prelude = std::str::from_utf8(leaked).unwrap(); let (ptoks, perrs) = lexer().parse(prelude).into_output_errors(); if !perrs.is_empty() { println!("Errors lexing Prelude"); println!("{:?}", perrs); panic!(); } let ptoks = ptoks.unwrap(); let (p_ast, perrs) = parser() .parse(Stream::from_iter(ptoks).map((0..prelude.len()).into(), |(t, s)| (t, s))) .into_output_errors(); if !perrs.is_empty() { println!("Errors parsing Prelude"); println!("{:?}", perrs); panic!(); } let prelude_parsed = Box::leak(Box::new(p_ast.unwrap())); let base_pkg = base(); let mut v6or = Validator::new( &prelude_parsed.0, prelude_parsed.1, "prelude", prelude, &base_pkg, ); v6or.validate(); if !v6or.errors.is_empty() { report_invalidation(v6or.errors); panic!("interal Ludus error: invalid prelude") } let mut base_ctx = Process::<'src> { input: "prelude", src: prelude, locals: base_pkg.clone(), ast: &prelude_parsed.0, span: prelude_parsed.1, prelude: vec![], fn_info: v6or.fn_info, }; let prelude = base_ctx.eval(); let mut p_ctx = vec![]; match prelude { Ok(Value::Dict(p_dict)) => { for (key, value) in p_dict.iter() { p_ctx.push((key.to_string(), value.clone())) } } Ok(_) => { println!("Bad Prelude export"); panic!(); } Err(LErr { msg, .. }) => { println!("Error running Prelude"); println!("{:?}", msg); panic!(); } }; (p_ctx, base_ctx.fn_info) } pub fn run(src: &'static str) { let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { println!("{:?}", lex_errs); return; } let tokens = tokens.unwrap(); let to_parse = tokens.clone(); let (parse_result, parse_errors) = parser() .parse(Stream::from_iter(to_parse).map((0..src.len()).into(), |(t, s)| (t, s))) .into_output_errors(); if !parse_errors.is_empty() { println!("{:?}", parse_errors); return; } let parsed = parse_result.unwrap(); let (prelude_ctx, mut prelude_fn_info) = prelude(); let mut v6or = Validator::new(&parsed.0, parsed.1, "script", src, &prelude_ctx); v6or.validate(); if !v6or.errors.is_empty() { report_invalidation(v6or.errors); return; } prelude_fn_info.extend(&mut v6or.fn_info.into_iter()); let mut proc = Process { input: "script", src, locals: vec![], prelude: prelude_ctx, ast: &parsed.0, span: parsed.1, fn_info: prelude_fn_info, }; let result = proc.eval(); match result { Ok(result) => println!("{}", result), Err(err) => report_panic(err), } } pub fn main() { let src = " loop (10) with { (0) -> :done (n) -> { print! (n) recur (dec (n)) } } "; run(src); // struct_scalpel::print_dissection_info::() // struct_scalpel::print_dissection_info::(); // println!("{}", std::mem::size_of::()) }