From 530deab19d843aec304eff17a40d08cf752d9727 Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Sat, 15 Jan 2022 17:28:31 -0800 Subject: [PATCH] Bring in the Rust implementation. --- implementations/Rust/.gitignore | 1 + implementations/Rust/Cargo.lock | 49 +++++++++++ implementations/Rust/Cargo.toml | 11 +++ implementations/Rust/src/main.rs | 86 +++++++++++++++++++ implementations/Rust/src/stack.rs | 133 ++++++++++++++++++++++++++++++ 5 files changed, 280 insertions(+) create mode 100644 implementations/Rust/.gitignore create mode 100644 implementations/Rust/Cargo.lock create mode 100644 implementations/Rust/Cargo.toml create mode 100644 implementations/Rust/src/main.rs create mode 100644 implementations/Rust/src/stack.rs diff --git a/implementations/Rust/.gitignore b/implementations/Rust/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/implementations/Rust/.gitignore @@ -0,0 +1 @@ +/target diff --git a/implementations/Rust/Cargo.lock b/implementations/Rust/Cargo.lock new file mode 100644 index 0000000..c9cd6a3 --- /dev/null +++ b/implementations/Rust/Cargo.lock @@ -0,0 +1,49 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "crust" +version = "0.1.0" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" diff --git a/implementations/Rust/Cargo.toml b/implementations/Rust/Cargo.toml new file mode 100644 index 0000000..8dad137 --- /dev/null +++ b/implementations/Rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "crust" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1" +lazy_static = "1" + diff --git a/implementations/Rust/src/main.rs b/implementations/Rust/src/main.rs new file mode 100644 index 0000000..968165f --- /dev/null +++ b/implementations/Rust/src/main.rs @@ -0,0 +1,86 @@ +mod stack; +use lazy_static::lazy_static; +use regex::Regex; +use std::io; + +#[derive(Debug)] +enum JoyVal { + True, + False, + List(stack::List), + Int(i64), + Sym(String), +} + +fn tokenize(str: &str) -> JoyVal { + lazy_static! { + static ref RE: Regex = Regex::new( + r"(?x) + (?Ptrue)\b + | (?Pfalse)\b + | (?P\[) + | (?P\]) + | (?P\d+) + | (?P[^\s+\[\]]+) " + ) + .unwrap(); + } + + let mut frame = vec![]; + let mut stack = vec![]; + for cap in RE.captures_iter(str) { + // Is this better than the if/elif/else chain below? + // More importantly, how would I go about figuring that out? + match cap.name("true") { + Some(_) => { + frame.push(JoyVal::True); + continue; + } + None => {} + } + // Is there a better way to do this? + //if cap.name("true") != None { + // println!("T"); + //} else + if cap.name("false") != None { + frame.push(JoyVal::False); + } else if cap.name("lbracket") != None { + stack.push(frame); + frame = vec![]; + } else if cap.name("rbracket") != None { + let v = frame; + frame = stack.pop().expect("Extra closing bracket."); + frame.push(JoyVal::List(stack::List::vec_to_list(v))); + } else if cap.name("int") != None { + frame.push(JoyVal::Int(cap[0].parse().unwrap())); + } else if cap.name("atom") != None { + frame.push(JoyVal::Sym(String::from(&cap[0]))); + } else { + println!("wtf"); + } + } + JoyVal::List(stack::List::vec_to_list(frame)) +} + +fn joy(mut s: stack::List, mut e: stack::List) -> stack::List { + while !e.empty() { + let term = e.head().expect("How!?"); + e = e.tail(); + match term { + JoyVal::True => { s = s.push(JoyVal::True) } + JoyVal::False => { s = s.push(JoyVal::False) } + JoyVal::List(_) => { s = s.push(term) } + JoyVal::Int(_) => { s = s.push(term) } + JoyVal::Sym(_) => { s = s.push(term) } + } + } + s +} + +fn main() { + println!("Enter a number:"); + let mut n = String::new(); + let m = io::stdin().read_line(&mut n).expect("WTF?"); + println!("{:?}", tokenize(&n)); + println!("Entered: {} {}", m, n); +} diff --git a/implementations/Rust/src/stack.rs b/implementations/Rust/src/stack.rs new file mode 100644 index 0000000..75fd9ef --- /dev/null +++ b/implementations/Rust/src/stack.rs @@ -0,0 +1,133 @@ +// https://rust-unofficial.github.io/too-many-lists/third.html +use std::rc::Rc; + +#[derive(Debug)] +pub struct List { + head: Link, +} + +type Link = Option>>; + +#[derive(Debug)] +struct Node { + elem: T, + next: Link, +} + +impl List { + pub fn new() -> Self { + List { head: None } + } + pub fn push(&self, elem: T) -> List { + List { + head: Some(Rc::new(Node { + elem: elem, + next: self.head.clone(), + })), + } + } + pub fn tail(&self) -> List { + List { + head: self.head.as_ref().and_then(|node| node.next.clone()), + } + } + pub fn head(&self) -> Option<&T> { + self.head.as_ref().map(|node| &node.elem) + } + pub fn iter(&self) -> Iter<'_, T> { + Iter { + next: self.head.as_deref(), + } + } + pub fn vec_to_list(v: Vec) -> List { + let mut list = List::new(); + for t in v.into_iter().rev() { + list = list.push(t); + } + list + } + pub fn empty(&self) -> bool { + match self.head { + Some(_) => false, + None => true + } + } +} + +impl Drop for List { + fn drop(&mut self) { + let mut head = self.head.take(); + while let Some(node) = head { + if let Ok(mut node) = Rc::try_unwrap(node) { + head = node.next.take(); + } else { + break; + } + } + } +} + +pub struct Iter<'a, T> { + next: Option<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_deref(); + &node.elem + }) + } +} + +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn basics() { + let list = List::new(); + assert_eq!(list.head(), None); + assert_eq!(list.empty(), true); + + let list = list.push(1).push(2).push(3); + assert_eq!(list.empty(), false); + assert_eq!(list.head(), Some(&3)); + + let list = list.tail(); + assert_eq!(list.head(), Some(&2)); + + let list = list.tail(); + assert_eq!(list.head(), Some(&1)); + + let list = list.tail(); + assert_eq!(list.head(), None); + + // Make sure empty tail works + let list = list.tail(); + assert_eq!(list.head(), None); + } + + #[test] + fn iter() { + let list = List::new().push(1).push(2).push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&1)); + } + + #[test] + fn v2l() { + let v = vec![1, 2, 3]; + let list = List::vec_to_list(v); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + } +}