Skip to content

Commit

Permalink
Add hashmap words
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Aug 19, 2023
1 parent e115e67 commit d73892d
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 29 deletions.
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ pub use self::dictionary::{Dictionaries, Dictionary, DictionaryEntry};
pub use self::env::{Environment, SourceBlock};
pub use self::lexer::Lexer;
pub use self::stack::{
OwnedCellSlice, SharedBox, Stack, StackTuple, StackValue, StackValueType, WordList,
HashMapTreeKey, HashMapTreeNode, OwnedCellSlice, SharedBox, Stack, StackTuple, StackValue,
StackValueType, WordList,
};

pub mod cont;
Expand Down
34 changes: 27 additions & 7 deletions src/core/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl Stack {
}
}

pub fn push_opt_raw(&mut self, value: Option<Rc<dyn StackValue>>) -> Result<()> {
pub fn push_opt_raw<T: StackValue + 'static>(&mut self, value: Option<Rc<T>>) -> Result<()> {
match value {
None => self.push_null(),
Some(value) => self.push_raw(value),
Expand Down Expand Up @@ -217,6 +217,13 @@ impl Stack {
self.pop()?.into_cont()
}

pub fn pop_cont_owned(&mut self) -> Result<Cont> {
Ok(match Rc::try_unwrap(self.pop()?.into_cont()?) {
Ok(inner) => inner,
Err(rc) => rc.as_ref().clone(),
})
}

pub fn pop_word_list(&mut self) -> Result<Rc<WordList>> {
self.pop()?.into_word_list()
}
Expand All @@ -240,7 +247,7 @@ impl Stack {
self.pop()?.into_atom()
}

pub fn pop_hashmap_owned(&mut self) -> Result<Option<Rc<HashMapTreeNode>>> {
pub fn pop_hashmap(&mut self) -> Result<Option<Rc<HashMapTreeNode>>> {
let value = self.pop()?;
if value.is_null() {
Ok(None)
Expand Down Expand Up @@ -425,15 +432,15 @@ define_stack_value! {
if v.is_empty() {
return f.write_str("[]");
}
f.write_str("[")?;
f.write_str("[ ")?;
let mut first = true;
for item in v {
if !std::mem::take(&mut first) {
f.write_str(" ")?;
}
StackValue::fmt_dump(item.as_ref(), f)?;
}
f.write_str("]")
f.write_str(" ]")
},
as_tuple(v): &StackTuple = Ok(v),
into_tuple,
Expand Down Expand Up @@ -799,6 +806,12 @@ impl HashMapTreeNode {
self.into_iter()
}

pub fn owned_iter(self: Rc<Self>) -> HashMapTreeOwnedIter {
HashMapTreeOwnedIter {
stack: vec![(self, None)],
}
}

pub fn lookup<K>(root_opt: &Option<Rc<Self>>, key: K) -> Option<&'_ Rc<HashMapTreeNode>>
where
K: AsHashMapTreeKeyRef,
Expand Down Expand Up @@ -1079,12 +1092,12 @@ impl<'a> Iterator for HashMapTreeIter<'a> {
}
}

#[derive(Default)]
pub struct HashMapTreeIntoIter {
#[derive(Default, Clone)]
pub struct HashMapTreeOwnedIter {
stack: Vec<(Rc<HashMapTreeNode>, Option<bool>)>,
}

impl Iterator for HashMapTreeIntoIter {
impl Iterator for HashMapTreeOwnedIter {
type Item = Rc<HashMapTreeNode>;

fn next(&mut self) -> Option<Self::Item> {
Expand Down Expand Up @@ -1117,6 +1130,13 @@ pub trait AsHashMapTreeKeyRef {
fn as_equivalent(&self) -> HashMapTreeKeyRef<'_>;
}

impl<T: AsHashMapTreeKeyRef> AsHashMapTreeKeyRef for &T {
#[inline]
fn as_equivalent(&self) -> HashMapTreeKeyRef<'_> {
<T as AsHashMapTreeKeyRef>::as_equivalent(self)
}
}

#[derive(Clone)]
pub struct HashMapTreeKey {
pub hash: u64,
Expand Down
10 changes: 5 additions & 5 deletions src/modules/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Control {
#[cmd(name = "times", tail)]
fn interpret_execute_times(ctx: &mut Context) -> Result<Option<Cont>> {
let count = ctx.stack.pop_smallint_range(0, 1000000000)? as usize;
let body = ctx.stack.pop_cont()?.as_ref().clone();
let body = ctx.stack.pop_cont_owned()?;
Ok(match count {
0 => None,
1 => Some(body),
Expand Down Expand Up @@ -95,8 +95,8 @@ impl Control {

#[cmd(name = "while", tail)]
fn interpret_while(ctx: &mut Context) -> Result<Option<Cont>> {
let body = ctx.stack.pop_cont()?.as_ref().clone();
let cond = ctx.stack.pop_cont()?.as_ref().clone();
let body = ctx.stack.pop_cont_owned()?;
let cond = ctx.stack.pop_cont_owned()?;
ctx.next = Some(Rc::new(cont::WhileCont {
condition: Some(cond.clone()),
body: Some(body),
Expand All @@ -108,7 +108,7 @@ impl Control {

#[cmd(name = "until", tail)]
fn interpret_until(ctx: &mut Context) -> Result<Option<Cont>> {
let body = ctx.stack.pop_cont()?.as_ref().clone();
let body = ctx.stack.pop_cont_owned()?;
ctx.next = Some(Rc::new(cont::UntilCont {
body: Some(body.clone()),
after: ctx.next.take(),
Expand Down Expand Up @@ -226,7 +226,7 @@ impl Control {
}
};
let word = ctx.stack.pop_string_owned()?;
let cont = ctx.stack.pop_cont()?.as_ref().clone();
let cont = ctx.stack.pop_cont_owned()?;
define_word(&mut ctx.dicts.current, word, cont, mode)
}

Expand Down
8 changes: 4 additions & 4 deletions src/modules/dict_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl DictUtils {
#[cmd(name = "dictmapext", tail, args(ext = true, s = false))]
#[cmd(name = "idictmapext", tail, args(ext = true, s = true))]
fn interpret_dict_map(ctx: &mut Context, ext: bool, s: bool) -> Result<Option<Cont>> {
let func = ctx.stack.pop_cont()?.as_ref().clone();
let func = ctx.stack.pop_cont_owned()?;
let bits = ctx.stack.pop_smallint_range(0, MAX_KEY_BITS)? as u16;
let cell = pop_maybe_cell(&mut ctx.stack)?;
Ok(Some(Rc::new(LoopCont::new(
Expand All @@ -190,7 +190,7 @@ impl DictUtils {
#[cmd(name = "dictforeachrev", tail, args(r = true, s = false))]
#[cmd(name = "idictforeachrev", tail, args(r = true, s = true))]
fn interpret_dict_foreach(ctx: &mut Context, r: bool, s: bool) -> Result<Option<Cont>> {
let func = ctx.stack.pop_cont()?.as_ref().clone();
let func = ctx.stack.pop_cont_owned()?;
let bits = ctx.stack.pop_smallint_range(0, MAX_KEY_BITS)? as u16;
let cell = pop_maybe_cell(&mut ctx.stack)?;
Ok(Some(Rc::new(LoopCont::new(
Expand All @@ -206,7 +206,7 @@ impl DictUtils {

#[cmd(name = "dictmerge", tail)]
fn interpret_dict_merge(ctx: &mut Context) -> Result<Option<Cont>> {
let func = ctx.stack.pop_cont()?.as_ref().clone();
let func = ctx.stack.pop_cont_owned()?;
let bits = ctx.stack.pop_smallint_range(0, MAX_KEY_BITS)? as u16;
let right = pop_maybe_cell(&mut ctx.stack)?;
let left = pop_maybe_cell(&mut ctx.stack)?;
Expand All @@ -224,7 +224,7 @@ impl DictUtils {

#[cmd(name = "dictdiff", tail)]
fn interpret_dict_diff(ctx: &mut Context) -> Result<Option<Cont>> {
let func = ctx.stack.pop_cont()?.as_ref().clone();
let func = ctx.stack.pop_cont_owned()?;
let bits = ctx.stack.pop_smallint_range(0, MAX_KEY_BITS)? as u16;
let right = pop_maybe_cell(&mut ctx.stack)?;
let left = pop_maybe_cell(&mut ctx.stack)?;
Expand Down
131 changes: 131 additions & 0 deletions src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::iter::Peekable;
use std::rc::Rc;

use anyhow::{Context as _, Result};
Expand Down Expand Up @@ -250,6 +251,107 @@ impl FiftModule for BaseModule {
stack.push(tuple)
}

// === Hashmaps ===

#[cmd(name = "hmapnew", stack)]
fn interpret_hmap_new(stack: &mut Stack) -> Result<()> {
stack.push_null()
}

#[cmd(name = "hmap@", stack, args(chk = false))]
#[cmd(name = "hmap@?", stack, args(chk = true))]
fn interpret_hmap_fetch(stack: &mut Stack, chk: bool) -> Result<()> {
let map = stack.pop_hashmap()?;
let key = HashMapTreeKey::new(stack.pop()?)?;
let value = HashMapTreeNode::lookup(&map, key).map(|node| node.value.clone());

let found = value.is_some();
match value {
Some(value) => stack.push_raw(value)?,
None if !chk => stack.push_null()?,
_ => {}
}
if chk {
stack.push_bool(found)?;
}
Ok(())
}

#[cmd(name = "hmap-", stack, args(chk = false, read = false))]
#[cmd(name = "hmap-?", stack, args(chk = true, read = false))]
#[cmd(name = "hmap@-", stack, args(chk = false, read = true))]
fn interpret_hmap_delete(stack: &mut Stack, chk: bool, read: bool) -> Result<()> {
let mut map = stack.pop_hashmap()?;
let key = HashMapTreeKey::new(stack.pop()?)?;
let value = HashMapTreeNode::remove(&mut map, key);
stack.push_opt_raw(map)?;

let exists = value.is_some();
match value {
Some(value) if read => stack.push_raw(value)?,
None if read && !chk => stack.push_null()?,
_ => {}
}
if chk {
stack.push_bool(exists)?;
}
Ok(())
}

#[cmd(name = "hmap!", stack, args(add = false))]
#[cmd(name = "hmap!+", stack, args(add = true))]
fn interpret_hmap_store(stack: &mut Stack, add: bool) -> Result<()> {
let mut map = stack.pop_hashmap()?;
let key = HashMapTreeKey::new(stack.pop()?)?;
let value = stack.pop()?;

if add {
HashMapTreeNode::set(&mut map, &key, &value);
} else {
HashMapTreeNode::replace(&mut map, key, &value);
}
stack.push_opt_raw(map)
}

#[cmd(name = "hmapempty?", stack)]
fn interpret_hmap_is_empty(stack: &mut Stack) -> Result<()> {
let map = stack.pop_hashmap()?;
stack.push_bool(map.is_none())
}

#[cmd(name = "hmapunpack", stack)]
fn interpret_hmap_decompose(stack: &mut Stack) -> Result<()> {
let map = stack.pop_hashmap()?;
let not_empty = map.is_some();

if let Some(map) = map {
stack.push_raw(map.key.stack_value.clone())?;
stack.push_raw(map.value.clone())?;
stack.push_opt_raw(map.left.clone())?;
stack.push_opt_raw(map.right.clone())?;
}

stack.push_bool(not_empty)
}

#[cmd(name = "hmapforeach", tail)]
fn interpret_hmap_foreach(ctx: &mut Context) -> Result<Option<Cont>> {
let func = ctx.stack.pop_cont_owned()?;
let Some(map) = ctx.stack.pop_hashmap()? else {
return Ok(None);
};
Ok(Some(Rc::new(cont::LoopCont::new(
HmapIterCont {
iter: map.owned_iter().peekable(),
ok: true,
},
func,
ctx.next.take(),
))))
}

// === Environment ===

#[cmd(name = "now")]
fn interpret_now(ctx: &mut Context) -> Result<()> {
ctx.stack.push_int(ctx.env.now_ms() / 1000)
Expand Down Expand Up @@ -311,3 +413,32 @@ impl FiftModule for BaseModule {
ctx.stack.push_bool(exists)
}
}

#[derive(Clone)]
struct HmapIterCont {
iter: Peekable<stack::HashMapTreeOwnedIter>,
ok: bool,
}

impl cont::LoopContImpl for HmapIterCont {
fn pre_exec(&mut self, ctx: &mut Context) -> Result<bool> {
let entry = match self.iter.next() {
Some(entry) => entry,
None => return Ok(false),
};

ctx.stack.push_raw(entry.key.stack_value.clone())?;
ctx.stack.push_raw(entry.value.clone())?;
Ok(true)
}

fn post_exec(&mut self, ctx: &mut Context) -> Result<bool> {
self.ok = ctx.stack.pop_bool()?;
Ok(self.ok && self.iter.peek().is_some())
}

fn finalize(&mut self, ctx: &mut Context) -> Result<bool> {
ctx.stack.push_bool(self.ok)?;
Ok(true)
}
}

0 comments on commit d73892d

Please sign in to comment.