string keys on dicts now fully work

This commit is contained in:
Scott Richmond 2025-07-03 15:30:51 -04:00
parent 659fdd3506
commit 9f9f59b33b
17 changed files with 237 additions and 139 deletions

View File

@ -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"

View File

@ -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 {

4
pkg/rudus.d.ts vendored
View File

@ -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;
}

View File

@ -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) {

Binary file not shown.

View File

@ -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;

View File

@ -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<Spanned<Self>>),
Repeat(Box<Spanned<Self>>, Box<Spanned<Self>>),
Splat(&'static str),
Pair(&'static str, Box<Spanned<Self>>),
StringPair(&'static str, Box<Spanned<Self>>),
KeywordPair(&'static str, Box<Spanned<Self>>),
Loop(Box<Spanned<Self>>, Vec<Spanned<Self>>),
Recur(Vec<Spanned<Self>>),
@ -80,7 +81,8 @@ pub enum Ast {
PlaceholderPattern,
TuplePattern(Vec<Spanned<Self>>),
ListPattern(Vec<Spanned<Self>>),
PairPattern(&'static str, Box<Spanned<Self>>),
StrPairPattern(&'static str, Box<Spanned<Self>>),
KeyPairPattern(&'static str, Box<Spanned<Self>>),
DictPattern(Vec<Spanned<Self>>),
}
@ -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::<Vec<_>>()
.join(", ")
),
PairPattern(key, value) => write!(f, ":{} {}", key, value.0),
InterpolatedPattern(strprts) => write!(
f,
"interpolated: \"{}\"",

View File

@ -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()

View File

@ -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<String>,
pub words: Vec<&'static str>,
pub re: Regex,
}
@ -16,7 +16,7 @@ pub struct Chunk {
pub bytecode: Vec<u8>,
pub keywords: Vec<&'static str>,
pub string_patterns: Vec<StrPattern>,
pub env: HashMap<&'static str, Value>,
pub env: HashMap<Key, Value>,
pub msgs: Vec<String>,
}

View File

@ -101,7 +101,7 @@ impl Compiler {
name: &'static str,
src: &'static str,
depth: usize,
env: imbl::HashMap<&'static str, Value>,
env: imbl::HashMap<Key, Value>,
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);

View File

@ -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<Key, 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)))
@ -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);

View File

@ -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::<Value>();
}

View File

@ -79,7 +79,7 @@ fn parse_string(s: &'static str, span: SimpleSpan) -> Result<Vec<Spanned<StringP
'}' => {
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()

View File

@ -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<Arity>, 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<Key, Value>,
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<Key, Value>,
) -> 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

View File

@ -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<HashMap<Key, Value>>),
Box(Rc<RefCell<Value>>),
Fn(Rc<LFn>),
BaseFn(BaseFn),
BaseFn(Box<BaseFn>),
Partial(Rc<Partial>),
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, _)

View File

@ -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)) => {

View File

@ -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<Key, Value>) -> 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<Key, Value>, 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);