diff --git a/Cargo.toml b/Cargo.toml index 56f5760..efadb5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ wasm-bindgen-futures = "0.4.50" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" console_error_panic_hook = "0.1.7" +struct_scalpel = "0.1.1" diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 9813042..bceec72 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -879,6 +879,12 @@ fn get { nil -> default val -> val } + (k as :string) -> get (k, _) + (k as :string, d as :dict) -> base :get (d, k) + (k as :string, d as :dict, default) -> match base :get (d, k) with { + nil -> default + val -> val + } } fn update { diff --git a/pkg/rudus.d.ts b/pkg/rudus.d.ts index 3f771fc..ae53166 100644 --- a/pkg/rudus.d.ts +++ b/pkg/rudus.d.ts @@ -14,8 +14,8 @@ export interface InitOutput { readonly __wbindgen_malloc: (a: number, b: number) => number; readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; readonly __wbindgen_export_6: WebAssembly.Table; - readonly closure346_externref_shim: (a: number, b: number, c: any) => void; - readonly closure370_externref_shim: (a: number, b: number, c: any, d: any) => void; + readonly closure347_externref_shim: (a: number, b: number, c: any) => void; + readonly closure371_externref_shim: (a: number, b: number, c: any, d: any) => void; readonly __wbindgen_start: () => void; } diff --git a/pkg/rudus.js b/pkg/rudus.js index 4ce4f8a..65e5ee7 100644 --- a/pkg/rudus.js +++ b/pkg/rudus.js @@ -240,13 +240,13 @@ function _assertNum(n) { function __wbg_adapter_20(arg0, arg1, arg2) { _assertNum(arg0); _assertNum(arg1); - wasm.closure346_externref_shim(arg0, arg1, arg2); + wasm.closure347_externref_shim(arg0, arg1, arg2); } function __wbg_adapter_50(arg0, arg1, arg2, arg3) { _assertNum(arg0); _assertNum(arg1); - wasm.closure370_externref_shim(arg0, arg1, arg2, arg3); + wasm.closure371_externref_shim(arg0, arg1, arg2, arg3); } async function __wbg_load(module, imports) { @@ -425,8 +425,8 @@ function __wbg_get_imports() { _assertBoolean(ret); return ret; }; - imports.wbg.__wbindgen_closure_wrapper7819 = function() { return logError(function (arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 347, __wbg_adapter_20); + imports.wbg.__wbindgen_closure_wrapper7945 = function() { return logError(function (arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 348, __wbg_adapter_20); return ret; }, arguments) }; imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { diff --git a/pkg/rudus_bg.wasm b/pkg/rudus_bg.wasm index fbaf4d6..81f0fa6 100644 Binary files a/pkg/rudus_bg.wasm and b/pkg/rudus_bg.wasm differ diff --git a/pkg/rudus_bg.wasm.d.ts b/pkg/rudus_bg.wasm.d.ts index e177a90..9a87573 100644 --- a/pkg/rudus_bg.wasm.d.ts +++ b/pkg/rudus_bg.wasm.d.ts @@ -9,6 +9,6 @@ export const __wbindgen_free: (a: number, b: number, c: number) => void; export const __wbindgen_malloc: (a: number, b: number) => number; export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; export const __wbindgen_export_6: WebAssembly.Table; -export const closure346_externref_shim: (a: number, b: number, c: any) => void; -export const closure370_externref_shim: (a: number, b: number, c: any, d: any) => void; +export const closure347_externref_shim: (a: number, b: number, c: any) => void; +export const closure371_externref_shim: (a: number, b: number, c: any, d: any) => void; export const __wbindgen_start: () => void; diff --git a/src/ast.rs b/src/ast.rs index 30e28c8..0e0e680 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -4,7 +4,7 @@ use std::fmt; #[derive(Clone, Debug, PartialEq, Eq)] pub enum StringPart { Data(String), - Word(String), + Word(&'static str), Inline(String), } @@ -63,7 +63,8 @@ pub enum Ast { Do(Vec>), Repeat(Box>, Box>), Splat(&'static str), - Pair(&'static str, Box>), + StringPair(&'static str, Box>), + KeywordPair(&'static str, Box>), Loop(Box>, Vec>), Recur(Vec>), @@ -80,7 +81,8 @@ pub enum Ast { PlaceholderPattern, TuplePattern(Vec>), ListPattern(Vec>), - PairPattern(&'static str, Box>), + StrPairPattern(&'static str, Box>), + KeyPairPattern(&'static str, Box>), DictPattern(Vec>), } @@ -212,7 +214,12 @@ impl Ast { Splat(word) => format!("...{}", word), Splattern(pattern) => format!("...{}", pattern.0.show()), AsPattern(word, type_keyword) => format!("{word} as :{type_keyword}"), - Pair(key, value) | PairPattern(key, value) => format!(":{key} {}", value.0.show()), + KeywordPair(key, value) | KeyPairPattern(key, value) => { + format!(":{key} {}", value.0.show()) + } + StringPair(key, value) | StrPairPattern(key, value) => { + format!("\"{key}\" {}", value.0.show()) + } Loop(init, body) => format!( "loop {} with {{\n {}\n}}", init.0.show(), @@ -377,8 +384,11 @@ impl fmt::Display for Ast { Splat(word) => { write!(f, "splat: {}", word) } - Pair(k, v) => { - write!(f, "pair: {} {}", k, v.0) + KeywordPair(k, v) | KeyPairPattern(k, v) => { + write!(f, "key_pair: {} {}", k, v.0) + } + StringPair(k, v) | StrPairPattern(k, v) => { + write!(f, "str_pair: {k} {}", v.0) } Loop(init, body) => { write!( @@ -446,7 +456,6 @@ impl fmt::Display for Ast { .collect::>() .join(", ") ), - PairPattern(key, value) => write!(f, ":{} {}", key, value.0), InterpolatedPattern(strprts) => write!( f, "interpolated: \"{}\"", diff --git a/src/base.rs b/src/base.rs index 21e8083..65718b4 100644 --- a/src/base.rs +++ b/src/base.rs @@ -612,64 +612,133 @@ pub fn r#mod(x: &Value, y: &Value) -> Value { pub fn make_base() -> Value { let members = vec![ - ("add", Value::BaseFn(BaseFn::Binary("add", add))), - ("append", Value::BaseFn(BaseFn::Binary("append", append))), - ("assoc", Value::BaseFn(BaseFn::Ternary("assoc", assoc))), - ("at", Value::BaseFn(BaseFn::Binary("at", at))), - ("atan_2", Value::BaseFn(BaseFn::Binary("atan_2", atan_2))), - ("bool", Value::BaseFn(BaseFn::Unary("bool", r#bool))), - ("ceil", Value::BaseFn(BaseFn::Unary("ceil", ceil))), - ("chars", Value::BaseFn(BaseFn::Unary("chars", chars))), - ("concat", Value::BaseFn(BaseFn::Binary("concat", concat))), - ("cos", Value::BaseFn(BaseFn::Unary("cos", cos))), - ("count", Value::BaseFn(BaseFn::Unary("count", count))), - ("dec", Value::BaseFn(BaseFn::Unary("dec", dec))), - ("dissoc", Value::BaseFn(BaseFn::Binary("dissoc", dissoc))), - ("div", Value::BaseFn(BaseFn::Binary("div", div))), - ("doc!", Value::BaseFn(BaseFn::Unary("doc!", doc))), + ("add", Value::BaseFn(Box::new(BaseFn::Binary("add", add)))), + ( + "append", + Value::BaseFn(Box::new(BaseFn::Binary("append", append))), + ), + ( + "assoc", + Value::BaseFn(Box::new(BaseFn::Ternary("assoc", assoc))), + ), + ("at", Value::BaseFn(Box::new(BaseFn::Binary("at", at)))), + ( + "atan_2", + Value::BaseFn(Box::new(BaseFn::Binary("atan_2", atan_2))), + ), + ( + "bool", + Value::BaseFn(Box::new(BaseFn::Unary("bool", r#bool))), + ), + ("ceil", Value::BaseFn(Box::new(BaseFn::Unary("ceil", ceil)))), + ( + "chars", + Value::BaseFn(Box::new(BaseFn::Unary("chars", chars))), + ), + ( + "concat", + Value::BaseFn(Box::new(BaseFn::Binary("concat", concat))), + ), + ("cos", Value::BaseFn(Box::new(BaseFn::Unary("cos", cos)))), + ( + "count", + Value::BaseFn(Box::new(BaseFn::Unary("count", count))), + ), + ("dec", Value::BaseFn(Box::new(BaseFn::Unary("dec", dec)))), + ( + "dissoc", + Value::BaseFn(Box::new(BaseFn::Binary("dissoc", dissoc))), + ), + ("div", Value::BaseFn(Box::new(BaseFn::Binary("div", div)))), + ("doc!", Value::BaseFn(Box::new(BaseFn::Unary("doc!", doc)))), ( "downcase", - Value::BaseFn(BaseFn::Unary("downcase", downcase)), + Value::BaseFn(Box::new(BaseFn::Unary("downcase", downcase))), + ), + ("eq?", Value::BaseFn(Box::new(BaseFn::Binary("eq?", eq)))), + ( + "first", + Value::BaseFn(Box::new(BaseFn::Unary("first", first))), + ), + ( + "floor", + Value::BaseFn(Box::new(BaseFn::Unary("floor", floor))), + ), + ("get", Value::BaseFn(Box::new(BaseFn::Binary("get", get)))), + ("gt?", Value::BaseFn(Box::new(BaseFn::Binary("gt?", gt)))), + ("gte?", Value::BaseFn(Box::new(BaseFn::Binary("gte?", gte)))), + ("inc", Value::BaseFn(Box::new(BaseFn::Unary("inc", inc)))), + ("last", Value::BaseFn(Box::new(BaseFn::Unary("last", last)))), + ("list", Value::BaseFn(Box::new(BaseFn::Unary("list", list)))), + ("lt?", Value::BaseFn(Box::new(BaseFn::Binary("lt?", lt)))), + ("lte?", Value::BaseFn(Box::new(BaseFn::Binary("lte?", lte)))), + ("mod", Value::BaseFn(Box::new(BaseFn::Binary("mod", r#mod)))), + ( + "mult", + Value::BaseFn(Box::new(BaseFn::Binary("mult", mult))), + ), + ( + "number", + Value::BaseFn(Box::new(BaseFn::Unary("number", number))), ), - ("eq?", Value::BaseFn(BaseFn::Binary("eq?", eq))), - ("first", Value::BaseFn(BaseFn::Unary("first", first))), - ("floor", Value::BaseFn(BaseFn::Unary("floor", floor))), - ("get", Value::BaseFn(BaseFn::Binary("get", get))), - ("gt?", Value::BaseFn(BaseFn::Binary("gt?", gt))), - ("gte?", Value::BaseFn(BaseFn::Binary("gte?", gte))), - ("inc", Value::BaseFn(BaseFn::Unary("inc", inc))), - ("last", Value::BaseFn(BaseFn::Unary("last", last))), - ("list", Value::BaseFn(BaseFn::Unary("list", list))), - ("lt?", Value::BaseFn(BaseFn::Binary("lt?", lt))), - ("lte?", Value::BaseFn(BaseFn::Binary("lte?", lte))), - ("mod", Value::BaseFn(BaseFn::Binary("mod", r#mod))), - ("mult", Value::BaseFn(BaseFn::Binary("mult", mult))), - ("number", Value::BaseFn(BaseFn::Unary("number", number))), ("pi", Value::Number(std::f64::consts::PI)), - ("print!", Value::BaseFn(BaseFn::Unary("print!", print))), + ( + "print!", + Value::BaseFn(Box::new(BaseFn::Unary("print!", print))), + ), ("process", Value::Process), ( "random", - Value::BaseFn(BaseFn::Nullary("random", base_random)), + Value::BaseFn(Box::new(BaseFn::Nullary("random", base_random))), ), - ("range", Value::BaseFn(BaseFn::Binary("range", range))), - ("rest", Value::BaseFn(BaseFn::Unary("rest", rest))), - ("round", Value::BaseFn(BaseFn::Unary("round", round))), - ("show", Value::BaseFn(BaseFn::Unary("show", show))), - ("sin", Value::BaseFn(BaseFn::Unary("sin", sin))), - ("slice", Value::BaseFn(BaseFn::Ternary("slice", slice))), - ("split", Value::BaseFn(BaseFn::Binary("split", split))), - ("sqrt", Value::BaseFn(BaseFn::Unary("sqrt", sqrt))), + ( + "range", + Value::BaseFn(Box::new(BaseFn::Binary("range", range))), + ), + ("rest", Value::BaseFn(Box::new(BaseFn::Unary("rest", rest)))), + ( + "round", + Value::BaseFn(Box::new(BaseFn::Unary("round", round))), + ), + ("show", Value::BaseFn(Box::new(BaseFn::Unary("show", show)))), + ("sin", Value::BaseFn(Box::new(BaseFn::Unary("sin", sin)))), + ( + "slice", + Value::BaseFn(Box::new(BaseFn::Ternary("slice", slice))), + ), + ( + "split", + Value::BaseFn(Box::new(BaseFn::Binary("split", split))), + ), + ("sqrt", Value::BaseFn(Box::new(BaseFn::Unary("sqrt", sqrt)))), ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)), - ("store!", Value::BaseFn(BaseFn::Binary("store!", store))), - ("sub", Value::BaseFn(BaseFn::Binary("sub", sub))), - ("tan", Value::BaseFn(BaseFn::Unary("tan", tan))), - ("trim", Value::BaseFn(BaseFn::Unary("trim", trim))), - ("triml", Value::BaseFn(BaseFn::Unary("triml", triml))), - ("trimr", Value::BaseFn(BaseFn::Unary("trimr", trimr))), - ("type", Value::BaseFn(BaseFn::Unary("type", r#type))), - ("unbox", Value::BaseFn(BaseFn::Unary("unbox", unbox))), - ("upcase", Value::BaseFn(BaseFn::Unary("upcase", upcase))), + ( + "store!", + Value::BaseFn(Box::new(BaseFn::Binary("store!", store))), + ), + ("sub", Value::BaseFn(Box::new(BaseFn::Binary("sub", sub)))), + ("tan", Value::BaseFn(Box::new(BaseFn::Unary("tan", tan)))), + ("trim", Value::BaseFn(Box::new(BaseFn::Unary("trim", trim)))), + ( + "triml", + Value::BaseFn(Box::new(BaseFn::Unary("triml", triml))), + ), + ( + "trimr", + Value::BaseFn(Box::new(BaseFn::Unary("trimr", trimr))), + ), + ( + "type", + Value::BaseFn(Box::new(BaseFn::Unary("type", r#type))), + ), + ( + "unbox", + Value::BaseFn(Box::new(BaseFn::Unary("unbox", unbox))), + ), + ( + "upcase", + Value::BaseFn(Box::new(BaseFn::Unary("upcase", upcase))), + ), ]; let members = members .iter() diff --git a/src/chunk.rs b/src/chunk.rs index e86826d..445260e 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,12 +1,12 @@ use crate::op::Op; -use crate::value::Value; +use crate::value::{Key, Value}; use imbl::HashMap; use num_traits::FromPrimitive; use regex::Regex; #[derive(Clone, Debug)] pub struct StrPattern { - pub words: Vec, + pub words: Vec<&'static str>, pub re: Regex, } @@ -16,7 +16,7 @@ pub struct Chunk { pub bytecode: Vec, pub keywords: Vec<&'static str>, pub string_patterns: Vec, - pub env: HashMap<&'static str, Value>, + pub env: HashMap, pub msgs: Vec, } diff --git a/src/compiler.rs b/src/compiler.rs index 483b2a4..83a4803 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -101,7 +101,7 @@ impl Compiler { name: &'static str, src: &'static str, depth: usize, - env: imbl::HashMap<&'static str, Value>, + env: imbl::HashMap, debug: bool, ) -> Compiler { let chunk = Chunk { @@ -703,10 +703,12 @@ impl Compiler { let match_depth = self.match_depth; self.match_depth = 0; for pair in pairs.iter().take(pairs_len) { - let (PairPattern(key, pattern), _) = pair else { - unreachable!() + let (key, pattern) = match &pair.0 { + KeyPairPattern(key, pattern) => (Value::Keyword(key), pattern), + StrPairPattern(key, pattern) => (Value::Interned(key), pattern), + _ => unreachable!("expected key to be keyword or string"), }; - self.emit_constant(Value::Keyword(key)); + self.emit_constant(key); self.emit_op(Op::LoadDictValue); self.emit_byte(dict_stack_pos); self.visit(pattern); @@ -721,7 +723,7 @@ impl Compiler { self.stack_depth += 1; for pair in pairs.iter().take(pairs_len) { - let (PairPattern(key, _), _) = pair else { + let (KeyPairPattern(key, _), _) = pair else { unreachable!() }; self.emit_constant(Value::Keyword(key)); @@ -785,9 +787,8 @@ impl Compiler { self.emit_byte(pattern_idx); for word in moar_words { - let name: &'static str = std::string::String::leak(word); let binding = Binding { - name, + name: word, depth: self.scope_depth, stack_pos: self.stack_depth, }; @@ -797,7 +798,7 @@ impl Compiler { self.patch_jump(jnm_idx, self.len() - jnm_idx - 3); } - PairPattern(_, _) => unreachable!(), + KeyPairPattern(..) | StrPairPattern(..) => unreachable!(), Tuple(members) => { self.tail_pos = false; for member in members { @@ -842,7 +843,12 @@ impl Compiler { } } } - Pair(key, value) => { + StringPair(key, value) => { + self.tail_pos = false; + self.emit_constant(Value::Interned(key)); + self.visit(value); + } + KeywordPair(key, value) => { self.tail_pos = false; self.emit_constant(Value::Keyword(key)); self.visit(value); diff --git a/src/lib.rs b/src/lib.rs index 310c9fb..06a3e68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,15 +46,15 @@ mod op; mod compiler; use crate::compiler::Compiler; -mod value; -use value::Value; +pub mod value; +use value::{Value, Key}; mod vm; use vm::Creature; const PRELUDE: &str = include_str!("../assets/test_prelude.ld"); -fn prelude() -> HashMap<&'static str, Value> { +fn prelude() -> HashMap { 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))) @@ -71,7 +71,7 @@ fn prelude() -> HashMap<&'static str, Value> { let base = base::make_base(); let mut base_env = imbl::HashMap::new(); - base_env.insert("base", base.clone()); + base_env.insert(Key::Keyword("base"), base.clone()); let mut validator = Validator::new(ast, span, "prelude", PRELUDE, base_env); diff --git a/src/main.rs b/src/main.rs index 210846e..94dad4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,8 @@ -use rudus::ludus; +use rudus::value::Value; use std::env; -use std::fs; +use struct_scalpel::print_dissection_info; pub fn main() { env::set_var("RUST_BACKTRACE", "1"); - let src = fs::read_to_string("sandbox.ld").unwrap(); - let json = ludus(src); - // println!("{json}"); + print_dissection_info::(); } diff --git a/src/parser.rs b/src/parser.rs index 5ba3d1d..c7628c5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -79,7 +79,7 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result { if is_word { parts.push(( - StringPart::Word(current_part.clone()), + StringPart::Word(current_part.leak()), SimpleSpan::new(span.context(), start..start + i), )); current_part = String::new(); @@ -209,19 +209,24 @@ where .delimited_by(just(Token::Punctuation("[")), just(Token::Punctuation("]"))) .map_with(|list, e| (ListPattern(list), e.span())); - let pair_pattern = select! {Token::Keyword(k) => k} + let key_pair_pattern = select! {Token::Keyword(k) => k} .then(pattern.clone()) - .map_with(|(key, patt), e| (PairPattern(key, Box::new(patt)), e.span())); + .map_with(|(key, patt), e| (KeyPairPattern(key, Box::new(patt)), e.span())); let shorthand_pattern = select! {Token::Word(w) => w}.map_with(|w, e| { ( - PairPattern(w, Box::new((WordPattern(w), e.span()))), + KeyPairPattern(w, Box::new((WordPattern(w), e.span()))), e.span(), ) }); - let dict_pattern = pair_pattern + let str_pair_pattern = select! {Token::String(s) => s} + .then(pattern.clone()) + .map_with(|(key, patt), e| (StrPairPattern(key, Box::new(patt)), e.span())); + + let dict_pattern = key_pair_pattern .or(shorthand_pattern) + .or(str_pair_pattern) .or(splattern.clone()) .separated_by(separators.clone()) .allow_leading() @@ -334,15 +339,20 @@ where .delimited_by(just(Token::Punctuation("[")), just(Token::Punctuation("]"))) .map_with(|list, e| (List(list), e.span())); - let pair = select! {Token::Keyword(k) => k} + let key_pair = select! {Token::Keyword(k) => k} .then(simple.clone()) - .map_with(|(key, value), e| (Pair(key, Box::new(value)), e.span())); + .map_with(|(key, value), e| (KeywordPair(key, Box::new(value)), e.span())); let shorthand = select! {Token::Word(w) => w} - .map_with(|w, e| (Pair(w, Box::new((Word(w), e.span()))), e.span())); + .map_with(|w, e| (KeywordPair(w, Box::new((Word(w), e.span()))), e.span())); - let dict = pair + let str_pair = select! {Token::String(s) => s} + .then(simple.clone()) + .map_with(|(key, value), e| (StringPair(key, Box::new(value)), e.span())); + + let dict = key_pair .or(shorthand) + .or(str_pair) .or(splat.clone()) .separated_by(separators.clone()) .allow_leading() diff --git a/src/validator.rs b/src/validator.rs index 9174b79..6af0d09 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -6,7 +6,7 @@ use crate::ast::{Ast, StringPart}; use crate::spans::{Span, Spanned}; -use crate::value::Value; +use crate::value::{Key, Value}; use std::collections::{HashMap, HashSet}; #[derive(Clone, Debug, PartialEq)] @@ -61,7 +61,7 @@ fn match_arities(arities: &HashSet, num_args: u8) -> bool { #[derive(Debug, PartialEq)] pub struct Validator<'a> { pub locals: Vec<(String, &'a Span, FnInfo)>, - pub prelude: imbl::HashMap<&'static str, Value>, + pub prelude: imbl::HashMap, pub input: &'static str, pub src: &'static str, pub ast: &'a Ast, @@ -77,7 +77,7 @@ impl<'a> Validator<'a> { span: &'a Span, input: &'static str, src: &'static str, - prelude: imbl::HashMap<&'static str, Value>, + prelude: imbl::HashMap, ) -> Validator<'a> { Validator { input, @@ -113,9 +113,12 @@ impl<'a> Validator<'a> { self.locals[i] = new_binding; } - fn resolved(&self, name: &str) -> bool { + fn resolved(&self, name: &'static str) -> bool { self.locals.iter().any(|(bound, ..)| name == bound.as_str()) - || self.prelude.iter().any(|(bound, _)| name == *bound) + || self + .prelude + .iter() + .any(|(bound, _)| Key::Keyword(name) == *bound) } fn bound(&self, name: &str) -> Option<&(String, &Span, FnInfo)> { @@ -172,7 +175,7 @@ impl<'a> Validator<'a> { for part in parts { if let (StringPart::Word(name), span) = part { self.span = span; - if !self.resolved(name.as_str()) { + if !self.resolved(name) { self.err(format!("unbound name `{name}`")); } else { self.use_name(name.to_string()); @@ -267,7 +270,7 @@ impl<'a> Validator<'a> { self.status.tail_position = tailpos; } - Pair(_, value) => self.visit(value.as_ref()), + KeywordPair(_, value) | StringPair(_, value) => self.visit(value.as_ref()), Dict(dict) => { if dict.is_empty() { return; @@ -592,7 +595,7 @@ impl<'a> Validator<'a> { self.visit(last); self.status.last_term = false; } - PairPattern(_, patt) => self.visit(patt.as_ref()), + KeyPairPattern(_, patt) | StrPairPattern(_, patt) => self.visit(patt.as_ref()), // terminals can never be invalid Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) | And | Or | Method(..) => (), // terminal patterns can never be invalid diff --git a/src/value.rs b/src/value.rs index 0f8e5c8..8c4f66d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -3,6 +3,7 @@ use crate::chunk::Chunk; use imbl::{HashMap, Vector}; use std::cell::RefCell; use std::rc::Rc; +use struct_scalpel::Dissectible; #[derive(Clone, Debug)] pub enum LFn { @@ -141,14 +142,14 @@ impl Key { pub fn from_value(value: Value) -> Key { match value { Value::Keyword(s) => Key::Keyword(s), - Value::Interned(s) => Key::Keyword(s), + Value::Interned(s) => Key::Interned(s), Value::String(s) => Key::String(s.clone()), _ => unreachable!("dict keys must be keywords or strings"), } } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Dissectible)] pub enum Value { Nothing, Nil, @@ -163,7 +164,7 @@ pub enum Value { Dict(Box>), Box(Rc>), Fn(Rc), - BaseFn(BaseFn), + BaseFn(Box), Partial(Rc), Process, } @@ -233,7 +234,7 @@ impl std::fmt::Display for Value { Box(value) => write!(f, "box {{ {} }}", value.as_ref().borrow()), Fn(lfn) => write!(f, "fn {}", lfn.name()), BaseFn(inner) => { - let name = match inner { + let name = match **inner { crate::base::BaseFn::Nullary(name, _) | crate::base::BaseFn::Unary(name, _) | crate::base::BaseFn::Binary(name, _) diff --git a/src/vm.rs b/src/vm.rs index a3d6bd5..0c5cfb3 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -3,7 +3,7 @@ use crate::base::BaseFn; use crate::chunk::Chunk; use crate::op::Op; use crate::spans::Spanned; -use crate::value::{LFn, Value}; +use crate::value::{Key, LFn, Value}; use crate::world::Zoo; use imbl::{HashMap, Vector}; use num_traits::FromPrimitive; @@ -477,7 +477,7 @@ impl Creature { let Value::Keyword(name) = key else { unreachable!("internal Ludus error: expected key for global resolution") }; - let value = self.chunk().env.get(name).unwrap(); + let value = self.chunk().env.get(&Key::Keyword(name)).unwrap(); self.push(value.clone()); } Store => { @@ -700,9 +700,7 @@ impl Creature { } AppendDict => { let value = self.pop(); - let Value::Keyword(key) = self.pop() else { - unreachable!() - }; + let key = Key::from_value(self.pop()); let Value::Dict(mut dict) = self.pop() else { unreachable!() }; @@ -731,9 +729,7 @@ impl Creature { unreachable!("expected dict, got {value}") } }; - let Value::Keyword(key) = self.pop() else { - unreachable!("expected keyword, got something else") - }; + let key = Key::from_value(self.pop()); let value = dict.get(&key).unwrap_or(&Value::Nil); self.push(value.clone()); } @@ -756,13 +752,11 @@ impl Creature { } } DropDictEntry => { - let Value::Keyword(key_to_drop) = self.pop() else { - unreachable!() - }; + let key_to_drop = Key::from_value(self.pop()); let Value::Dict(mut dict) = self.pop() else { unreachable!() }; - dict.remove(key_to_drop); + dict.remove(&key_to_drop); self.push(Value::Dict(dict)); } PushBox => { @@ -770,13 +764,10 @@ impl Creature { self.push(Value::Box(Rc::new(RefCell::new(val)))); } GetKey => { - let key = self.pop(); - let Value::Keyword(idx) = key else { - unreachable!() - }; + let key = Key::from_value(self.pop()); let dict = self.pop(); let value = match dict { - Value::Dict(d) => d.as_ref().get(&idx).unwrap_or(&Value::Nil).clone(), + Value::Dict(d) => d.get(&key).unwrap_or(&Value::Nil).clone(), _ => Value::Nil, }; self.push(value); @@ -899,13 +890,17 @@ impl Creature { } Get => { let key = self.pop(); + if !matches!( + key, + Value::Keyword(_) | Value::String(_) | Value::Interned(_) + ) { + return self.panic("keys must be keywords"); + } + let key = Key::from_value(key); let dict = self.pop(); - let value = match (key, dict) { - (Value::Keyword(k), Value::Dict(d)) => { - d.as_ref().get(&k).unwrap_or(&Value::Nil).clone() - } - (Value::Keyword(_), _) => Value::Nil, - _ => return self.panic("keys must be keywords"), + let value = match dict { + Value::Dict(d) => d.get(&key).unwrap_or(&Value::Nil).clone(), + _ => Value::Nil.clone(), }; self.push(value); } @@ -1042,7 +1037,7 @@ impl Creature { self.ip = 0; } Value::BaseFn(base_fn) => { - let value = match (arity, base_fn) { + let value = match (arity, *base_fn) { (0, BaseFn::Nullary(_, f)) => f(), (1, BaseFn::Unary(_, f)) => f(&self.pop()), (2, BaseFn::Binary(_, f)) => { @@ -1149,7 +1144,7 @@ impl Creature { self.ip = 0; } Value::BaseFn(base_fn) => { - let value = match (arity, base_fn) { + let value = match (arity, *base_fn) { (0, BaseFn::Nullary(_, f)) => f(), (1, BaseFn::Unary(_, f)) => f(&self.pop()), (2, BaseFn::Binary(_, f)) => { diff --git a/src/world.rs b/src/world.rs index bb80762..988255e 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,5 +1,5 @@ use crate::chunk::Chunk; -use crate::value::Value; +use crate::value::{Value, Key}; use crate::vm::{Creature, Panic}; use crate::io::{MsgOut, MsgIn, do_io}; use std::cell::RefCell; @@ -260,13 +260,13 @@ pub struct Buffers { } impl Buffers { - pub fn new (prelude: imbl::HashMap<&'static str, Value>) -> Buffers { + pub fn new (prelude: imbl::HashMap) -> Buffers { Buffers { - console: prelude.get("console").unwrap().clone(), - commands: prelude.get("turtle_commands").unwrap().clone(), - fetch_out: prelude.get("fetch_outbox").unwrap().clone(), - fetch_in: prelude.get("fetch_inbox").unwrap().clone(), - input: prelude.get("input").unwrap().clone(), + console: prelude.get(&Key::Keyword("console")).unwrap().clone(), + commands: prelude.get(&Key::Keyword("turtle_commands")).unwrap().clone(), + fetch_out: prelude.get(&Key::Keyword("fetch_outbox")).unwrap().clone(), + fetch_in: prelude.get(&Key::Keyword("fetch_inbox")).unwrap().clone(), + input: prelude.get(&Key::Keyword("input")).unwrap().clone(), } } @@ -304,7 +304,7 @@ pub struct World { } impl World { - pub fn new(chunk: Chunk, prelude: imbl::HashMap<&'static str, Value>, debug: bool) -> World { + pub fn new(chunk: Chunk, prelude: imbl::HashMap, debug: bool) -> World { let zoo = Rc::new(RefCell::new(Zoo::new())); let main = Creature::new(chunk, zoo.clone(), debug); let id = zoo.borrow_mut().put(main);