// 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 // * [ ] add stack traces and code locations to panics // * [ ] 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` // * [ ] string patterns // * [x] string interpolation // * [x] docstrings // * [~] 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; use std::collections::HashMap; 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 context; use crate::context::*; #[derive(Embed)] #[folder = "assets/"] struct Asset; pub fn prelude<'src>() -> Context<'src> { let prelude = Asset::get("test_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 p_ast = Box::leak(Box::new(p_ast.unwrap().0)); let base_pkg = base(); let mut base_ctx = Context::<'src> { locals: vec![], ast: p_ast, prelude: base_pkg, // prelude_ast: &Ast::Nil, }; 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!(); } }; Context { locals: vec![], ast: &Ast::Nil, prelude: p_ctx, // prelude_ast: p_ast, } } 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 (ast, span) = parse_result.unwrap(); let dummy_prelude = vec![]; let mut v6or = validator::Validator::new(&ast, span, &dummy_prelude); v6or.validate(); dbg!(v6or); let mut ctx = prelude(); ctx.ast = * let result = ctx.eval(); match result { Ok(result) => println!("{}", result), Err(LErr { msg, .. }) => println!("Errors!\n{}", msg), } } pub fn main() { let src = " let bar = :bar match :foo with { :foo -> bar } "; run(src); // struct_scalpel::print_dissection_info::() // struct_scalpel::print_dissection_info::(); // println!("{}", std::mem::size_of::()) }