@@ -24,6 +24,8 @@ We use a C implementation for our Nock interpreter. But building a Nock interpre
2424
2525- [Ruby](#ruby)
2626
27+- [Rust](#rust)
28+
2729- [Scala](#scala)
2830
2931- [Scheme](#scheme)
@@ -1757,6 +1759,561 @@ def nock(arr)
17571759end
17581760```
17591761
1762+## Rust
1763+
1764+From [Peter McEvoy](https://github.com/mcevoypeter/nock/tree/main/src):
1765+
1766+```rust
1767+#![allow(unused_parens)]
1768+
1769+use std::{error, fmt};
1770+
1771+#[derive(Debug)]
1772+enum Noun {
1773+ Atom(Atom),
1774+ Cell(Cell),
1775+}
1776+
1777+impl Clone for Noun {
1778+ fn clone(&self) -> Self {
1779+ match self {
1780+ Noun::Atom(a) => Noun::Atom(Atom(a.0)),
1781+ Noun::Cell(c) => Noun::Cell(Cell {
1782+ h: c.h.clone(),
1783+ t: c.t.clone(),
1784+ }),
1785+ }
1786+ }
1787+}
1788+
1789+impl PartialEq for Noun {
1790+ fn eq(&self, other: &Self) -> bool {
1791+ if let (Noun::Atom(lh), Noun::Atom(rh)) = (&*self, &*other) {
1792+ lh == rh
1793+ } else if let (Noun::Cell(lh), Noun::Cell(rh)) = (&*self, &*other) {
1794+ lh == rh
1795+ } else {
1796+ false
1797+ }
1798+ }
1799+}
1800+
1801+impl fmt::Display for Noun {
1802+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1803+ match self {
1804+ Noun::Atom(a) => {
1805+ write!(f, "{}", a.0)
1806+ }
1807+ Noun::Cell(ref c) => {
1808+ write!(f, "[{} {}]", c.h, c.t)
1809+ }
1810+ }
1811+ }
1812+}
1813+
1814+impl Noun {
1815+ fn from_loobean(loob: Loobean) -> Self {
1816+ match loob {
1817+ Loobean::Yes => Noun::Atom(Atom(0)),
1818+ Loobean::No => Noun::Atom(Atom(1)),
1819+ }
1820+ }
1821+
1822+ fn into_box(self) -> Box<Self> {
1823+ Box::new(self)
1824+ }
1825+}
1826+
1827+#[derive(Clone, Debug, PartialEq)]
1828+struct Atom(u64);
1829+
1830+#[derive(Debug)]
1831+pub struct Cell {
1832+ h: Box<Noun>,
1833+ t: Box<Noun>,
1834+}
1835+
1836+impl Clone for Cell {
1837+ fn clone(&self) -> Self {
1838+ Cell {
1839+ h: self.h.clone(),
1840+ t: self.t.clone(),
1841+ }
1842+ }
1843+}
1844+
1845+impl PartialEq for Cell {
1846+ fn eq(&self, other: &Self) -> bool {
1847+ if let (Noun::Atom(lh_h), Noun::Atom(rh_h)) = (&*self.h, &*other.h) {
1848+ lh_h == rh_h && *self.t == *other.t
1849+ } else if let (Noun::Cell(lh_h), Noun::Cell(rh_h)) = (&*self.h, &*other.h) {
1850+ Self::eq(lh_h, rh_h) && *self.t == *other.t
1851+ } else {
1852+ false
1853+ }
1854+ }
1855+}
1856+
1857+#[derive(Debug, PartialEq)]
1858+enum Loobean {
1859+ Yes,
1860+ No,
1861+}
1862+
1863+impl Loobean {
1864+ fn from_boolean(b: bool) -> Self {
1865+ if b {
1866+ Loobean::Yes
1867+ } else {
1868+ Loobean::No
1869+ }
1870+ }
1871+}
1872+
1873+#[derive(Debug)]
1874+struct Error {
1875+ msg: String,
1876+}
1877+
1878+impl fmt::Display for Error {
1879+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1880+ write!(f, "{}", self.msg)
1881+ }
1882+}
1883+
1884+impl error::Error for Error {}
1885+
1886+trait Wut {
1887+ fn wut(&self) -> Loobean;
1888+}
1889+
1890+impl Wut for Atom {
1891+ fn wut(&self) -> Loobean {
1892+ Loobean::No
1893+ }
1894+}
1895+
1896+impl Wut for Cell {
1897+ fn wut(&self) -> Loobean {
1898+ Loobean::Yes
1899+ }
1900+}
1901+
1902+trait Lus {
1903+ fn lus(self) -> Atom;
1904+}
1905+
1906+impl Lus for Atom {
1907+ fn lus(self) -> Atom {
1908+ Atom(1 + self.0)
1909+ }
1910+}
1911+
1912+trait Tis {
1913+ fn tis(&self) -> Loobean;
1914+}
1915+
1916+impl Tis for Cell {
1917+ fn tis(&self) -> Loobean {
1918+ Loobean::from_boolean(self.h == self.t)
1919+ }
1920+}
1921+
1922+trait Fas {
1923+ fn fas(self) -> Result<Noun, Error>;
1924+}
1925+
1926+impl Fas for Cell {
1927+ fn fas(self) -> Result<Noun, Error> {
1928+ let mut s = self;
1929+ loop {
1930+ match *s.h {
1931+ Noun::Atom(Atom(0)) => {
1932+ break Err(Error {
1933+ msg: "/[0 a] cannot be evaluated".to_string(),
1934+ })
1935+ }
1936+ Noun::Atom(Atom(1)) => break Ok(*s.t),
1937+ Noun::Atom(Atom(2)) => {
1938+ break {
1939+ if let Noun::Cell(t) = *s.t {
1940+ Ok(*t.h)
1941+ } else {
1942+ Err(Error {
1943+ msg: "/[2 a] cannot be evaluated when a is an atom".to_string(),
1944+ })
1945+ }
1946+ }
1947+ }
1948+ Noun::Atom(Atom(3)) => {
1949+ break {
1950+ if let Noun::Cell(t) = *s.t {
1951+ Ok(*t.t)
1952+ } else {
1953+ Err(Error {
1954+ msg: "/[3 a] cannot be evaluated when a is an atom".to_string(),
1955+ })
1956+ }
1957+ }
1958+ }
1959+ Noun::Atom(Atom(n)) => {
1960+ s = Cell {
1961+ h: Noun::Atom(Atom(2 + n % 2)).into_box(),
1962+ t: Cell {
1963+ h: Noun::Atom(Atom(n / 2)).into_box(),
1964+ t: s.t,
1965+ }
1966+ .fas()?
1967+ .into_box(),
1968+ }
1969+ }
1970+ Noun::Cell(_) => {
1971+ break Err(Error {
1972+ msg: "/[a b] cannot be evaluated when a is a cell".to_string(),
1973+ })
1974+ }
1975+ }
1976+ }
1977+ }
1978+}
1979+
1980+trait Hax {
1981+ fn hax(self) -> Result<Noun, Error>;
1982+}
1983+
1984+impl Hax for Cell {
1985+ fn hax(self) -> Result<Noun, Error> {
1986+ let mut s = self;
1987+ loop {
1988+ if let (Noun::Atom(h), Noun::Cell(t)) = (*s.h, *s.t) {
1989+ match h {
1990+ Atom(0) => {
1991+ break Err(Error {
1992+ msg: "#[0 a b] cannot be evaluated".to_string(),
1993+ })
1994+ }
1995+ Atom(1) => break Ok(*t.h),
1996+ Atom(n) if 0 == n % 2 => {
1997+ s = Cell {
1998+ h: Noun::Atom(Atom(n / 2)).into_box(),
1999+ t: Noun::Cell(Cell {
2000+ h: Noun::Cell(Cell {
2001+ h: t.h,
2002+ t: Cell {
2003+ h: Noun::Atom(Atom(n + 1)).into_box(),
2004+ t: t.t.clone(),
2005+ }
2006+ .fas()?
2007+ .into_box(),
2008+ })
2009+ .into_box(),
2010+ t: t.t,
2011+ })
2012+ .into_box(),
2013+ }
2014+ }
2015+ Atom(n) => {
2016+ s = Cell {
2017+ h: Noun::Atom(Atom(n / 2)).into_box(),
2018+ t: Noun::Cell(Cell {
2019+ h: Noun::Cell(Cell {
2020+ h: Cell {
2021+ h: Noun::Atom(Atom(n - 1)).into_box(),
2022+ t: t.t.clone(),
2023+ }
2024+ .fas()?
2025+ .into_box(),
2026+ t: t.h,
2027+ })
2028+ .into_box(),
2029+ t: t.t,
2030+ })
2031+ .into_box(),
2032+ }
2033+ }
2034+ }
2035+ } else {
2036+ break Err(Error {
2037+ msg: "#[a b] cannot be evaluated when a is cell and/or b is an atom"
2038+ .to_string(),
2039+ });
2040+ }
2041+ }
2042+ }
2043+}
2044+
2045+trait Tar {
2046+ fn tar(self) -> Result<Noun, Error>;
2047+}
2048+
2049+impl Tar for Cell {
2050+ fn tar(self) -> Result<Noun, Error> {
2051+ let mut s = self;
2052+ loop {
2053+ if let Noun::Cell(t) = *s.t {
2054+ match *t.h {
2055+ Noun::Atom(Atom(0)) => break Cell { h: t.t, t: s.h }.fas(),
2056+ Noun::Atom(Atom(1)) => break Ok(*t.t),
2057+ Noun::Atom(Atom(2)) => {
2058+ if let Noun::Cell(tt) = *t.t {
2059+ s = Cell {
2060+ h: Cell {
2061+ h: s.h.clone(),
2062+ t: tt.h,
2063+ }
2064+ .tar()?
2065+ .into_box(),
2066+ t: Cell { h: s.h, t: tt.t }.tar()?.into_box(),
2067+ }
2068+ } else {
2069+ break Err(Error {
2070+ msg: "*[a 2 b] cannot be evaluated when b is an atom".to_string(),
2071+ });
2072+ }
2073+ }
2074+ Noun::Atom(Atom(3)) => {
2075+ break {
2076+ match (Cell { h: s.h, t: t.t }.tar()?) {
2077+ Noun::Atom(a) => Ok(Noun::from_loobean(a.wut())),
2078+ Noun::Cell(c) => Ok(Noun::from_loobean(c.wut())),
2079+ }
2080+ }
2081+ }
2082+ Noun::Atom(Atom(4)) => {
2083+ break {
2084+ if let Noun::Atom(a) = (Cell { h: s.h, t: t.t }.tar()?) {
2085+ Ok(Noun::Atom(a.lus()))
2086+ } else {
2087+ Err(Error {
2088+ msg: "Cannot apply the + operator to a cell".to_string(),
2089+ })
2090+ }
2091+ }
2092+ }
2093+ Noun::Atom(Atom(5)) => {
2094+ break {
2095+ if let Noun::Cell(tt) = *t.t {
2096+ Ok(Noun::from_loobean(
2097+ Cell {
2098+ h: Cell {
2099+ h: s.h.clone(),
2100+ t: tt.h,
2101+ }
2102+ .tar()?
2103+ .into_box(),
2104+ t: Cell { h: s.h, t: tt.t }.tar()?.into_box(),
2105+ }
2106+ .tis(),
2107+ ))
2108+ } else {
2109+ Err(Error {
2110+ msg: "*[a 5 b] cannot be evaluated when b is an atom"
2111+ .to_string(),
2112+ })
2113+ }
2114+ }
2115+ }
2116+ Noun::Atom(Atom(6)) => {
2117+ if let Noun::Cell(tt) = *t.t {
2118+ if let Noun::Cell(ttt) = *tt.t {
2119+ s = Cell {
2120+ h: s.h.clone(),
2121+ t: Cell {
2122+ h: Noun::Cell(Cell { h: ttt.h, t: ttt.t }).into_box(),
2123+ t: Noun::Cell(Cell {
2124+ h: Noun::Atom(Atom(0)).into_box(),
2125+ t: Cell {
2126+ h: Noun::Cell(Cell {
2127+ h: Noun::Atom(Atom(2)).into_box(),
2128+ t: Noun::Atom(Atom(3)).into_box(),
2129+ })
2130+ .into_box(),
2131+ t: Noun::Cell(Cell {
2132+ h: Noun::Atom(Atom(0)).into_box(),
2133+ t: Cell {
2134+ h: s.h,
2135+ t: Noun::Cell(Cell {
2136+ h: Noun::Atom(Atom(4)).into_box(),
2137+ t: Noun::Cell(Cell {
2138+ h: Noun::Atom(Atom(4)).into_box(),
2139+ t: tt.h,
2140+ })
2141+ .into_box(),
2142+ })
2143+ .into_box(),
2144+ }
2145+ .tar()?
2146+ .into_box(),
2147+ })
2148+ .into_box(),
2149+ }
2150+ .tar()?
2151+ .into_box(),
2152+ })
2153+ .into_box(),
2154+ }
2155+ .tar()?
2156+ .into_box(),
2157+ }
2158+ } else {
2159+ break Err(Error {
2160+ msg: "*[a 6 b c] cannot be evaluated when c is an atom"
2161+ .to_string(),
2162+ });
2163+ }
2164+ } else {
2165+ break Err(Error {
2166+ msg: "*[a 6 b] cannot be evaluated when b is an atom".to_string(),
2167+ });
2168+ }
2169+ }
2170+ Noun::Atom(Atom(7)) => {
2171+ if let Noun::Cell(tt) = *t.t {
2172+ s = Cell {
2173+ h: Cell { h: s.h, t: tt.h }.tar()?.into_box(),
2174+ t: tt.t,
2175+ }
2176+ } else {
2177+ break Err(Error {
2178+ msg: "*[a 7 b] cannot be evaluated when b is an atom".to_string(),
2179+ });
2180+ }
2181+ }
2182+ Noun::Atom(Atom(8)) => {
2183+ if let Noun::Cell(tt) = *t.t {
2184+ s = Cell {
2185+ h: Noun::Cell(Cell {
2186+ h: Cell {
2187+ h: s.h.clone(),
2188+ t: tt.h,
2189+ }
2190+ .tar()?
2191+ .into_box(),
2192+ t: s.h,
2193+ })
2194+ .into_box(),
2195+ t: tt.t,
2196+ }
2197+ } else {
2198+ break Err(Error {
2199+ msg: "*[a 8 b] cannot be evaluated when b is an atom".to_string(),
2200+ });
2201+ }
2202+ }
2203+ Noun::Atom(Atom(9)) => {
2204+ if let Noun::Cell(tt) = *t.t {
2205+ s = Cell {
2206+ h: Cell { h: s.h, t: tt.t }.tar()?.into_box(),
2207+ t: Noun::Cell(Cell {
2208+ h: Noun::Atom(Atom(2)).into_box(),
2209+ t: Noun::Cell(Cell {
2210+ h: Noun::Cell(Cell {
2211+ h: Noun::Atom(Atom(0)).into_box(),
2212+ t: Noun::Atom(Atom(1)).into_box(),
2213+ })
2214+ .into_box(),
2215+ t: Noun::Cell(Cell {
2216+ h: Noun::Atom(Atom(0)).into_box(),
2217+ t: tt.h,
2218+ })
2219+ .into_box(),
2220+ })
2221+ .into_box(),
2222+ })
2223+ .into_box(),
2224+ }
2225+ } else {
2226+ break Err(Error {
2227+ msg: "*[a 9 b] cannot be evaluated when b is an atom".to_string(),
2228+ });
2229+ }
2230+ }
2231+ Noun::Atom(Atom(10)) => {
2232+ break if let Noun::Cell(tt) = *t.t {
2233+ if let Noun::Cell(tth) = *tt.h {
2234+ Cell {
2235+ h: tth.h,
2236+ t: Noun::Cell(Cell {
2237+ h: Cell {
2238+ h: s.h.clone(),
2239+ t: tth.t,
2240+ }
2241+ .tar()?
2242+ .into_box(),
2243+ t: Cell { h: s.h, t: tt.t }.tar()?.into_box(),
2244+ })
2245+ .into_box(),
2246+ }
2247+ .hax()
2248+ } else {
2249+ Err(Error {
2250+ msg: "*[a 10 b c] cannot be evaluated when b is an atom"
2251+ .to_string(),
2252+ })
2253+ }
2254+ } else {
2255+ Err(Error {
2256+ msg: "*[a 10 b] cannot be evaluated when b is an atom".to_string(),
2257+ })
2258+ }
2259+ }
2260+ Noun::Atom(Atom(11)) => {
2261+ if let Noun::Cell(tt) = *t.t {
2262+ match *tt.h {
2263+ Noun::Atom(_) => break Cell { h: s.h, t: tt.t }.tar(),
2264+ Noun::Cell(c) => {
2265+ s = Cell {
2266+ h: Noun::Cell(Cell {
2267+ h: Cell {
2268+ h: s.h.clone(),
2269+ t: c.t,
2270+ }
2271+ .tar()?
2272+ .into_box(),
2273+ t: Cell { h: s.h, t: tt.t }.tar()?.into_box(),
2274+ })
2275+ .into_box(),
2276+ t: Noun::Cell(Cell {
2277+ h: Noun::Atom(Atom(0)).into_box(),
2278+ t: Noun::Atom(Atom(3)).into_box(),
2279+ })
2280+ .into_box(),
2281+ }
2282+ }
2283+ }
2284+ } else {
2285+ break Err(Error {
2286+ msg: "*[a 11 b] cannot be evaluated when b is an atom".to_string(),
2287+ });
2288+ }
2289+ }
2290+ Noun::Atom(Atom(_)) => {
2291+ break Err(Error {
2292+ msg: "unsupported opcode".to_string(),
2293+ })
2294+ }
2295+ Noun::Cell(th) => {
2296+ break Ok(Noun::Cell(Cell {
2297+ h: Cell {
2298+ h: s.h.clone(),
2299+ t: Noun::Cell(th).into_box(),
2300+ }
2301+ .tar()?
2302+ .into_box(),
2303+ t: Cell { h: s.h, t: t.t }.tar()?.into_box(),
2304+ }))
2305+ }
2306+ }
2307+ } else {
2308+ break Err(Error {
2309+ msg: "*[a b] cannot be evaluated when b is an atom".to_string(),
2310+ });
2311+ }
2312+ }
2313+ }
2314+}
2315+```
2316+
17602317## Scala
17612318
17622319From [Steve Randy Waldman](https://github.com/swaldman/nnnock/blob/master/src/main/scala/com/mchange/sc/v1/nnnock/package.scala):