From fea2b6c313d5f3105a4da8e2f75a69999f6e5e40 Mon Sep 17 00:00:00 2001 From: ada Date: Fri, 23 Oct 2020 17:36:21 +0200 Subject: [PATCH] added lisp files --- lisp/Cargo.toml | 15 +++ lisp/core.rs | 319 +++++++++++++++++++++++++++++++++++++++++++++ lisp/env.rs | 85 ++++++++++++ lisp/jubjub.lisp | 46 +++++++ lisp/new.lisp | 2 + lisp/printer.rs | 61 +++++++++ lisp/racket/jj.rkt | 113 ++++++++++++++++ lisp/racket/zk.rkt | 141 ++++++++++++++++++++ lisp/reader.rs | 156 ++++++++++++++++++++++ lisp/types.rs | 240 ++++++++++++++++++++++++++++++++++ 10 files changed, 1178 insertions(+) create mode 100644 lisp/Cargo.toml create mode 100644 lisp/core.rs create mode 100644 lisp/env.rs create mode 100644 lisp/jubjub.lisp create mode 100644 lisp/new.lisp create mode 100644 lisp/printer.rs create mode 100644 lisp/racket/jj.rkt create mode 100644 lisp/racket/zk.rkt create mode 100644 lisp/reader.rs create mode 100644 lisp/types.rs diff --git a/lisp/Cargo.toml b/lisp/Cargo.toml new file mode 100644 index 000000000..c4aca5fe1 --- /dev/null +++ b/lisp/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mal" +version = "0.1.0" +authors = ["mileschet"] + +[dependencies] +lazy_static = "1.4.0" + +regex = "1.3.1" +itertools = "0.8.0" +fnv = "1.0.6" + +[[bin]] +name = "lisp" +path = "lisp.rs" diff --git a/lisp/core.rs b/lisp/core.rs new file mode 100644 index 000000000..547f578d1 --- /dev/null +++ b/lisp/core.rs @@ -0,0 +1,319 @@ +use std::fs::File; +use std::io::Read; +use std::rc::Rc; +use std::sync::Mutex; +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::printer::pr_seq; +use crate::reader::read_str; +use crate::types::MalErr::ErrMalVal; +use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector}; +use crate::types::{MalArgs, MalRet, MalVal, _assoc, _dissoc, atom, error, func, hash_map}; + +macro_rules! fn_t_int_int { + ($ret:ident, $fn:expr) => {{ + |a: MalArgs| match (a[0].clone(), a[1].clone()) { + (Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))), + _ => error("expecting (int,int) args"), + } + }}; +} + +macro_rules! fn_is_type { + ($($ps:pat),*) => {{ + |a:MalArgs| { Ok(Bool(match a[0] { $($ps => true,)* _ => false})) } + }}; + ($p:pat if $e:expr) => {{ + |a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, _ => false})) } + }}; + ($p:pat if $e:expr,$($ps:pat),*) => {{ + |a:MalArgs| { Ok(Bool(match a[0] { $p if $e => true, $($ps => true,)* _ => false})) } + }}; +} + +macro_rules! fn_str { + ($fn:expr) => {{ + |a: MalArgs| match a[0].clone() { + Str(a0) => $fn(a0), + _ => error("expecting (str) arg"), + } + }}; +} + +fn symbol(a: MalArgs) -> MalRet { + match a[0] { + Str(ref s) => Ok(Sym(s.to_string())), + _ => error("illegal symbol call"), + } +} + + +fn slurp(f: String) -> MalRet { + let mut s = String::new(); + match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) { + Ok(_) => Ok(Str(s)), + Err(e) => error(&e.to_string()), + } +} + +fn time_ms(_a: MalArgs) -> MalRet { + let ms_e = match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(d) => d, + Err(e) => return error(&format!("{:?}", e)), + }; + Ok(Int( + ms_e.as_secs() as i64 * 1000 + ms_e.subsec_nanos() as i64 / 1_000_000 + )) +} + +fn get(a: MalArgs) -> MalRet { + match (a[0].clone(), a[1].clone()) { + (Nil, _) => Ok(Nil), + (Hash(ref hm, _), Str(ref s)) => match hm.get(s) { + Some(mv) => Ok(mv.clone()), + None => Ok(Nil), + }, + _ => error("illegal get args"), + } +} + +fn assoc(a: MalArgs) -> MalRet { + match a[0] { + Hash(ref hm, _) => _assoc((**hm).clone(), a[1..].to_vec()), + _ => error("assoc on non-Hash Map"), + } +} + +fn dissoc(a: MalArgs) -> MalRet { + match a[0] { + Hash(ref hm, _) => _dissoc((**hm).clone(), a[1..].to_vec()), + _ => error("dissoc on non-Hash Map"), + } +} + +fn contains_q(a: MalArgs) -> MalRet { + match (a[0].clone(), a[1].clone()) { + (Hash(ref hm, _), Str(ref s)) => Ok(Bool(hm.contains_key(s))), + _ => error("illegal get args"), + } +} + +fn keys(a: MalArgs) -> MalRet { + match a[0] { + Hash(ref hm, _) => Ok(list!(hm.keys().map(|k| { Str(k.to_string()) }).collect())), + _ => error("keys requires Hash Map"), + } +} + +fn vals(a: MalArgs) -> MalRet { + match a[0] { + Hash(ref hm, _) => Ok(list!(hm.values().map(|v| { v.clone() }).collect())), + _ => error("keys requires Hash Map"), + } +} + +fn vec(a: MalArgs) -> MalRet { + match a[0] { + List(ref v, _) | Vector(ref v, _) => Ok(vector!(v.to_vec())), + _ => error("non-seq passed to vec"), + } +} + +fn cons(a: MalArgs) -> MalRet { + match a[1].clone() { + List(v, _) | Vector(v, _) => { + let mut new_v = vec![a[0].clone()]; + new_v.extend_from_slice(&v); + Ok(list!(new_v.to_vec())) + } + _ => error("cons expects seq as second arg"), + } +} + +fn concat(a: MalArgs) -> MalRet { + let mut new_v = vec![]; + for seq in a.iter() { + match seq { + List(v, _) | Vector(v, _) => new_v.extend_from_slice(v), + _ => return error("non-seq passed to concat"), + } + } + Ok(list!(new_v.to_vec())) +} + +fn nth(a: MalArgs) -> MalRet { + match (a[0].clone(), a[1].clone()) { + (List(seq, _), Int(idx)) | (Vector(seq, _), Int(idx)) => { + if seq.len() <= idx as usize { + return error("nth: index out of range"); + } + Ok(seq[idx as usize].clone()) + } + _ => error("invalid args to nth"), + } +} + +fn first(a: MalArgs) -> MalRet { + match a[0].clone() { + List(ref seq, _) | Vector(ref seq, _) if seq.len() == 0 => Ok(Nil), + List(ref seq, _) | Vector(ref seq, _) => Ok(seq[0].clone()), + Nil => Ok(Nil), + _ => error("invalid args to first"), + } +} + +fn rest(a: MalArgs) -> MalRet { + match a[0].clone() { + List(ref seq, _) | Vector(ref seq, _) => { + if seq.len() > 1 { + Ok(list!(seq[1..].to_vec())) + } else { + Ok(list![]) + } + } + Nil => Ok(list![]), + _ => error("invalid args to first"), + } +} + +fn apply(a: MalArgs) -> MalRet { + match a[a.len() - 1] { + List(ref v, _) | Vector(ref v, _) => { + let f = &a[0]; + let mut fargs = a[1..a.len() - 1].to_vec(); + fargs.extend_from_slice(&v); + f.apply(fargs) + } + _ => error("apply called with non-seq"), + } +} + +fn map(a: MalArgs) -> MalRet { + match a[1] { + List(ref v, _) | Vector(ref v, _) => { + let mut res = vec![]; + for mv in v.iter() { + res.push(a[0].apply(vec![mv.clone()])?) + } + Ok(list!(res)) + } + _ => error("map called with non-seq"), + } +} + +fn conj(a: MalArgs) -> MalRet { + match a[0] { + List(ref v, _) => { + let sl = a[1..] + .iter() + .rev() + .map(|a| a.clone()) + .collect::>(); + Ok(list!([&sl[..], v].concat())) + } + Vector(ref v, _) => Ok(vector!([v, &a[1..]].concat())), + _ => error("conj: called with non-seq"), + } +} + +fn seq(a: MalArgs) -> MalRet { + match a[0] { + List(ref v, _) | Vector(ref v, _) if v.len() == 0 => Ok(Nil), + List(ref v, _) | Vector(ref v, _) => Ok(list!(v.to_vec())), + Str(ref s) if s.len() == 0 => Ok(Nil), + Str(ref s) if !a[0].keyword_q() => { + Ok(list!(s.chars().map(|c| { Str(c.to_string()) }).collect())) + } + Nil => Ok(Nil), + _ => error("seq: called with non-seq"), + } +} + +pub fn ns() -> Vec<(&'static str, MalVal)> { + vec![ + ("=", func(|a| Ok(Bool(a[0] == a[1])))), + ("throw", func(|a| Err(ErrMalVal(a[0].clone())))), + ("nil?", func(fn_is_type!(Nil))), + ("true?", func(fn_is_type!(Bool(true)))), + ("false?", func(fn_is_type!(Bool(false)))), + ("symbol", func(symbol)), + ("symbol?", func(fn_is_type!(Sym(_)))), + ( + "string?", + func(fn_is_type!(Str(ref s) if !s.starts_with("\u{29e}"))), + ), + ("keyword", func(|a| a[0].keyword())), + ( + "keyword?", + func(fn_is_type!(Str(ref s) if s.starts_with("\u{29e}"))), + ), + ("number?", func(fn_is_type!(Int(_)))), + ( + "fn?", + func(fn_is_type!(MalFunc{is_macro,..} if !is_macro,Func(_,_))), + ), + ( + "macro?", + func(fn_is_type!(MalFunc{is_macro,..} if is_macro)), + ), + ("pr-str", func(|a| Ok(Str(pr_seq(&a, true, "", "", " "))))), + ("str", func(|a| Ok(Str(pr_seq(&a, false, "", "", ""))))), + ( + "prn", + func(|a| { + println!("{}", pr_seq(&a, true, "", "", " ")); + Ok(Nil) + }), + ), + ( + "println", + func(|a| { + println!("{}", pr_seq(&a, false, "", "", " ")); + Ok(Nil) + }), + ), + ("read-string", func(fn_str!(|s| { read_str(s) }))), + ("slurp", func(fn_str!(|f| { slurp(f) }))), + ("<", func(fn_t_int_int!(Bool, |i, j| { i < j }))), + ("<=", func(fn_t_int_int!(Bool, |i, j| { i <= j }))), + (">", func(fn_t_int_int!(Bool, |i, j| { i > j }))), + (">=", func(fn_t_int_int!(Bool, |i, j| { i >= j }))), + ("+", func(fn_t_int_int!(Int, |i, j| { i + j }))), + ("-", func(fn_t_int_int!(Int, |i, j| { i - j }))), + ("*", func(fn_t_int_int!(Int, |i, j| { i * j }))), + ("/", func(fn_t_int_int!(Int, |i, j| { i / j }))), + ("time-ms", func(time_ms)), + ("sequential?", func(fn_is_type!(List(_, _), Vector(_, _)))), + ("list", func(|a| Ok(list!(a)))), + ("list?", func(fn_is_type!(List(_, _)))), + ("vector", func(|a| Ok(vector!(a)))), + ("vector?", func(fn_is_type!(Vector(_, _)))), + ("hash-map", func(|a| hash_map(a))), + ("map?", func(fn_is_type!(Hash(_, _)))), + ("assoc", func(assoc)), + ("dissoc", func(dissoc)), + ("get", func(get)), + ("contains?", func(contains_q)), + ("keys", func(keys)), + ("vals", func(vals)), + ("vec", func(vec)), + ("cons", func(cons)), + ("concat", func(concat)), + ("empty?", func(|a| a[0].empty_q())), + ("nth", func(nth)), + ("first", func(first)), + ("rest", func(rest)), + ("count", func(|a| a[0].count())), + ("apply", func(apply)), + ("map", func(map)), + ("conj", func(conj)), + ("seq", func(seq)), + ("meta", func(|a| a[0].get_meta())), + ("with-meta", func(|a| a[0].clone().with_meta(&a[1]))), + ("atom", func(|a| Ok(atom(&a[0])))), + ("atom?", func(fn_is_type!(Atom(_)))), + ("deref", func(|a| a[0].deref())), + ("reset!", func(|a| a[0].reset_bang(&a[1]))), + ("swap!", func(|a| a[0].swap_bang(&a[1..].to_vec()))), + ] +} diff --git a/lisp/env.rs b/lisp/env.rs new file mode 100644 index 000000000..27e77b424 --- /dev/null +++ b/lisp/env.rs @@ -0,0 +1,85 @@ +use std::cell::RefCell; +use std::rc::Rc; +//use std::collections::HashMap; +use fnv::FnvHashMap; + +use crate::types::MalErr::ErrString; +use crate::types::MalVal::{List, Nil, Sym, Vector}; +use crate::types::{error, MalErr, MalRet, MalVal}; + +#[derive(Debug)] +pub struct EnvStruct { + data: RefCell>, + pub outer: Option, +} + +pub type Env = Rc; + +// TODO: it would be nice to use impl here but it doesn't work on +// a deftype (i.e. Env) + +pub fn env_new(outer: Option) -> Env { + Rc::new(EnvStruct { + data: RefCell::new(FnvHashMap::default()), + outer: outer, + }) +} + +// TODO: mbinds and exprs as & types +pub fn env_bind(outer: Option, mbinds: MalVal, exprs: Vec) -> Result { + let env = env_new(outer); + match mbinds { + List(binds, _) | Vector(binds, _) => { + for (i, b) in binds.iter().enumerate() { + match b { + Sym(s) if s == "&" => { + env_set(&env, binds[i + 1].clone(), list!(exprs[i..].to_vec()))?; + break; + } + _ => { + env_set(&env, b.clone(), exprs[i].clone())?; + } + } + } + Ok(env) + } + _ => Err(ErrString("env_bind binds not List/Vector".to_string())), + } +} + +pub fn env_find(env: &Env, key: &str) -> Option { + match (env.data.borrow().contains_key(key), env.outer.clone()) { + (true, _) => Some(env.clone()), + (false, Some(o)) => env_find(&o, key), + _ => None, + } +} + +pub fn env_get(env: &Env, key: &MalVal) -> MalRet { + match key { + Sym(ref s) => match env_find(env, s) { + Some(e) => Ok(e + .data + .borrow() + .get(s) + .ok_or(ErrString(format!("'{}' not found", s)))? + .clone()), + _ => error(&format!("'{}' not found", s)), + }, + _ => error("Env.get called with non-Str"), + } +} + +pub fn env_set(env: &Env, key: MalVal, val: MalVal) -> MalRet { + match key { + Sym(ref s) => { + env.data.borrow_mut().insert(s.to_string(), val.clone()); + Ok(val) + } + _ => error("Env.set called with non-Str"), + } +} + +pub fn env_sets(env: &Env, key: &str, val: MalVal) { + env.data.borrow_mut().insert(key.to_string(), val); +} diff --git a/lisp/jubjub.lisp b/lisp/jubjub.lisp new file mode 100644 index 000000000..65e55e056 --- /dev/null +++ b/lisp/jubjub.lisp @@ -0,0 +1,46 @@ + (def C_D (const "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")) + (def C_ONE (const "0x0000000000000000000000000000000000000000000000000000000000000001")) + (def jjadd (fn x1 y1 x2 y2) ( + (def U (mul (add (x1 y1)) (add (x2 y2)))) + (enforce + (add_lc0 (x1 y1)) + (add_lc1 (x2 y2)) + (add_lc2 U)) + (def A (mul (x2 x1))) + (def B (mul (x2 y1))) + (def C (mul (C_ONE A))) + (enforce + (add_coeff_lc0 (C_D A)) + (add_lc1 B) + (add_lc2 C)) + (def Px + (div (add A B) + (add C_ONE C))) + (enforce + (add_one_lc0) + (sub_lc0 C) + (add_lc1 Px) + (add_lc2 (A B))) + + (def Py + (div (sub U A B) + (sub C_ONE C))) + (enforce + (add_one_lc0) + (sub_lc0 C) + (add_lc1 Py) + (add_lc2 (U A B))) + (Px Py) + ) + (def input_spend (contract x1 y1 x2 y2) ( + (def P (jjadd (x1 y1 x2 y2))) + (enforce + (add_lc0 Px) + (add_lc1_one) + (add_lc2 Px)) + (enforce + (add_lc0 Py) + (add_lc1_one) + (add_lc2 Py)) + ) + diff --git a/lisp/new.lisp b/lisp/new.lisp new file mode 100644 index 000000000..a8162d468 --- /dev/null +++ b/lisp/new.lisp @@ -0,0 +1,2 @@ +(def! C_D "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1") +(println C_D) diff --git a/lisp/printer.rs b/lisp/printer.rs new file mode 100644 index 000000000..6cd4bc8b4 --- /dev/null +++ b/lisp/printer.rs @@ -0,0 +1,61 @@ +use crate::types::MalVal; +use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector}; + +fn escape_str(s: &str) -> String { + s.chars() + .map(|c| match c { + '"' => "\\\"".to_string(), + '\n' => "\\n".to_string(), + '\\' => "\\\\".to_string(), + _ => c.to_string(), + }) + .collect::>() + .join("") +} + +impl MalVal { + pub fn pr_str(&self, print_readably: bool) -> String { + match self { + Nil => String::from("nil"), + Bool(true) => String::from("true"), + Bool(false) => String::from("false"), + Int(i) => format!("{}", i), + //Float(f) => format!("{}", f), + Str(s) => { + if s.starts_with("\u{29e}") { + format!(":{}", &s[2..]) + } else if print_readably { + format!("\"{}\"", escape_str(s)) + } else { + s.clone() + } + } + Sym(s) => s.clone(), + List(l, _) => pr_seq(&**l, print_readably, "(", ")", " "), + Vector(l, _) => pr_seq(&**l, print_readably, "[", "]", " "), + Hash(hm, _) => { + let l: Vec = hm + .iter() + .flat_map(|(k, v)| vec![Str(k.to_string()), v.clone()]) + .collect(); + pr_seq(&l, print_readably, "{", "}", " ") + } + Func(f, _) => format!("#", f), + MalFunc { + ast: a, params: p, .. + } => format!("(fn* {} {})", p.pr_str(true), a.pr_str(true)), + Atom(a) => format!("(atom {})", a.borrow().pr_str(true)), + } + } +} + +pub fn pr_seq( + seq: &Vec, + print_readably: bool, + start: &str, + end: &str, + join: &str, +) -> String { + let strs: Vec = seq.iter().map(|x| x.pr_str(print_readably)).collect(); + format!("{}{}{}", start, strs.join(join), end) +} diff --git a/lisp/racket/jj.rkt b/lisp/racket/jj.rkt new file mode 100644 index 000000000..5e79f16a0 --- /dev/null +++ b/lisp/racket/jj.rkt @@ -0,0 +1,113 @@ +#lang racket + +(require "zk.rkt") + +(struct jj_point + (u v) +) + +(define (create_jj_param_point name) + (jj_point + (zk_param (string-append name "_u")) + (zk_param (string-append name "_v")) + ) +) +(define (create_jj_public_point name) + (jj_point + (zk_public (string-append name "_u")) + (zk_public (string-append name "_v")) + ) +) + +(define (zk_jj_add namespace result a b) + (zk_comment "call jj_add()") + (let* ([namespace (append namespace (list "_jj_add"))] + [U (zk_private namespace 'U)] + [A (zk_private namespace 'A)] + [B (zk_private namespace 'B)] + [C (zk_private namespace 'C)] + [tmp (zk_local namespace 'tmp)]) + (zk_comment "Compute U = (x1 + y1) * (y2 - EDWARDS_A*x2)") + (zk_comment " = (x1 + y1) * (x2 + y2)") + (zk_set U (jj_point-u a)) + (zk_add U (jj_point-v a)) + + (zk_set tmp (jj_point-u b)) + (zk_add tmp (jj_point-v b)) + + (zk_mul U tmp) + + (zk_comment "assert (x1 + y1) * (x2 + y2) == U") + (zk_lc0_add (jj_point-u a)) + (zk_lc0_add (jj_point-v a)) + (zk_lc1_add (jj_point-u b)) + (zk_lc1_add (jj_point-v b)) + (zk_lc2_add U) + (zk_enforce) + + (zk_comment "Compute A = y2 * x1") + (zk_set A (jj_point-v b)) + (zk_mul A (jj_point-u a)) + (zk_comment "Compute B = x2 * y1") + (zk_set B (jj_point-u b)) + (zk_mul B (jj_point-v a)) + (zk_comment "Compute C = d*A*B") + (zk_load C const_d) + (zk_mul C A) + (zk_mul C B) + + (zk_comment "assert (d * A) * (B) == C") + (zk_lc0_add_coeff const_d A) + (zk_lc1_add B) + (zk_lc2_add C) + (zk_enforce) + + (zk_comment "Compute P.x = (A + B) / (1 + C)") + (zk_set (jj_point-u result) A) + (zk_add (jj_point-u result) B) + ; Re-use the tmp variable from earlier here + (zk_load tmp const_one) + (zk_add tmp C) + (zk_divide (jj_point-u result) tmp) + + (zk_lc0_add_one) + (zk_lc0_add C) + (zk_lc1_add (jj_point-u result)) + (zk_lc2_add A) + (zk_lc2_add B) + (zk_enforce) + + (zk_comment "Compute P.y = (U - A - B) / (1 - C)") + (zk_set (jj_point-v result) U) + (zk_sub (jj_point-v result) A) + (zk_sub (jj_point-v result) B) + ; Re-use the tmp variable from earlier here + (zk_load tmp const_one) + (zk_sub tmp C) + (zk_divide (jj_point-v result) tmp) + + (zk_lc0_add_one) + (zk_lc0_sub C) + (zk_lc1_add (jj_point-v result)) + (zk_lc2_add U) + (zk_lc2_sub A) + (zk_lc2_sub B) + (zk_enforce) + ) +) + +(create_zk_output "jj.psm") + +(define const_d (zk_constant + "d" "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")) +(define const_one (zk_constant + "one" "0x0000000000000000000000000000000000000000000000000000000000000001")) + +(zk_contract_begin "foo") +(define namespace (list "_")) +(define a (create_jj_param_point "a")) +(define b (create_jj_param_point "b")) +(define result (create_jj_public_point "result")) +(zk_jj_add namespace result a b) +(zk_contract_end) + diff --git a/lisp/racket/zk.rkt b/lisp/racket/zk.rkt new file mode 100644 index 000000000..1c89ba1c4 --- /dev/null +++ b/lisp/racket/zk.rkt @@ -0,0 +1,141 @@ +#lang racket + +(provide zk_variable) +(provide zk_constant) +(provide zk_param) +(provide zk_public) +(provide zk_local) +(provide zk_private) +(provide zk_comment) +(provide zk_set) +(provide zk_add) +(provide zk_sub) +(provide zk_mul) +(provide zk_divide) +(provide zk_load) +(provide zk_lc0_add) +(provide zk_lc1_add) +(provide zk_lc2_add) +(provide zk_lc0_sub) +(provide zk_lc1_sub) +(provide zk_lc2_sub) +(provide zk_lc0_add_coeff) +(provide zk_lc1_add_coeff) +(provide zk_lc2_add_coeff) +(provide zk_lc0_add_one) +(provide zk_lc1_add_one) +(provide zk_lc2_add_one) +(provide zk_enforce) + +(provide create_zk_output) +(provide zk_contract_begin) +(provide zk_contract_end) + +(define out '0) +(define (create_zk_output filename) + (set! out (open-output-file "jj.psm" #:exists 'truncate)) +) + +(struct zk_variable + (name type) +) + +(define (zk_constant name hex_value) + (fprintf out "constant ~a ~a\n" name hex_value) + name +) + +(define (zk_contract_begin contract_name) + (fprintf out "contract ~a\n" contract_name) +) +(define (zk_contract_end) + (fprintf out "end\n") +) + +(define (zk_param name) + (fprintf out "param ~a\n" name) + (zk_variable name 'param) +) + +(define (zk_public name) + (fprintf out "public ~a\n" name) + (zk_variable name 'public) +) + +(define (strings->string sts) + (apply string-append sts)) + +(define (apply_ns namespace name) + (strings->string + (append namespace + (list "__" (symbol->string name)) + ))) + +(define (zk_local namespace name) + (let ([name (apply_ns namespace name)]) + (fprintf out "local ~a\n" name) + (zk_variable name 'local) + ) +) + +(define (zk_private namespace name) + (let ([name (apply_ns namespace name)]) + (fprintf out "private ~a\n" name) + (zk_variable name 'private) + ) +) + +(define (zk_comment str) + (fprintf out "# ~a\n" str) +) + +(define (zk_set self other) + (fprintf out "set ~a ~a\n" (zk_variable-name self) (zk_variable-name other)) +) +(define (zk_add self other) + (fprintf out "add ~a ~a\n" (zk_variable-name self) (zk_variable-name other)) +) +(define (zk_sub self other) + (fprintf out "sub ~a ~a\n" (zk_variable-name self) (zk_variable-name other)) +) +(define (zk_mul self other) + (fprintf out "mul ~a ~a\n" (zk_variable-name self) (zk_variable-name other)) +) +(define (zk_divide self other) + (fprintf out "divide ~a ~a\n" + (zk_variable-name self) (zk_variable-name other)) +) + +(define (zk_load self constant) + (fprintf out "load ~a ~a\n" (zk_variable-name self) constant) +) + +(define (zk_lc0_add self) + (fprintf out "lc0_add ~a\n" (zk_variable-name self))) +(define (zk_lc1_add self) + (fprintf out "lc1_add ~a\n" (zk_variable-name self))) +(define (zk_lc2_add self) + (fprintf out "lc2_add ~a\n" (zk_variable-name self))) +(define (zk_lc0_sub self) + (fprintf out "lc0_sub ~a\n" (zk_variable-name self))) +(define (zk_lc1_sub self) + (fprintf out "lc1_sub ~a\n" (zk_variable-name self))) +(define (zk_lc2_sub self) + (fprintf out "lc2_sub ~a\n" (zk_variable-name self))) +(define (zk_lc0_add_coeff constant self) + (fprintf out "lc0_add_coeff ~a ~a\n" constant (zk_variable-name self))) +(define (zk_lc1_add_coeff constant self) + (fprintf out "lc1_add_coeff ~a ~a\n" constant (zk_variable-name self))) +(define (zk_lc2_add_coeff constant self) + (fprintf out "lc2_add_coeff ~a ~a\n" constant (zk_variable-name self))) +(define (zk_lc0_add_one) + (fprintf out "lc0_add_one\n")) +(define (zk_lc1_add_one) + (fprintf out "lc1_add_one\n")) +(define (zk_lc2_add_one) + (fprintf out "lc2_add_one\n")) +(define (zk_enforce) + (fprintf out "enforce\n") +) + + diff --git a/lisp/reader.rs b/lisp/reader.rs new file mode 100644 index 000000000..4355ff568 --- /dev/null +++ b/lisp/reader.rs @@ -0,0 +1,156 @@ +use regex::{Captures, Regex}; +use std::rc::Rc; + +use crate::types::MalErr::ErrString; +use crate::types::MalVal::{Bool, Int, List, Nil, Str, Sym, Vector}; +use crate::types::{error, hash_map, MalErr, MalRet, MalVal}; + +#[derive(Debug, Clone)] +struct Reader { + tokens: Vec, + pos: usize, +} + +impl Reader { + fn next(&mut self) -> Result { + self.pos = self.pos + 1; + Ok(self + .tokens + .get(self.pos - 1) + .ok_or(ErrString("underflow".to_string()))? + .to_string()) + } + fn peek(&self) -> Result { + Ok(self + .tokens + .get(self.pos) + .ok_or(ErrString("underflow".to_string()))? + .to_string()) + } +} + +fn tokenize(str: &str) -> Vec { + lazy_static! { + static ref RE: Regex = Regex::new( + r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"### + ) + .unwrap(); + } + + let mut res = vec![]; + for cap in RE.captures_iter(str) { + if cap[1].starts_with(";") { + continue; + } + res.push(String::from(&cap[1])); + } + res +} + +fn unescape_str(s: &str) -> String { + lazy_static! { + static ref RE: Regex = Regex::new(r#"\\(.)"#).unwrap(); + } + RE.replace_all(&s, |caps: &Captures| { + format!("{}", if &caps[1] == "n" { "\n" } else { &caps[1] }) + }) + .to_string() +} + +fn read_atom(rdr: &mut Reader) -> MalRet { + lazy_static! { + static ref INT_RE: Regex = Regex::new(r"^-?[0-9]+$").unwrap(); + static ref STR_RE: Regex = Regex::new(r#""(?:\\.|[^\\"])*""#).unwrap(); + } + let token = rdr.next()?; + match &token[..] { + "nil" => Ok(Nil), + "false" => Ok(Bool(false)), + "true" => Ok(Bool(true)), + _ => { + if INT_RE.is_match(&token) { + Ok(Int(token.parse().unwrap())) + } else if STR_RE.is_match(&token) { + Ok(Str(unescape_str(&token[1..token.len() - 1]))) + } else if token.starts_with("\"") { + error("expected '\"', got EOF") + } else if token.starts_with(":") { + Ok(Str(format!("\u{29e}{}", &token[1..]))) + } else { + Ok(Sym(token.to_string())) + } + } + } +} + +fn read_seq(rdr: &mut Reader, end: &str) -> MalRet { + let mut seq: Vec = vec![]; + rdr.next()?; + loop { + let token = match rdr.peek() { + Ok(t) => t, + Err(_) => return error(&format!("expected '{}', got EOF", end)), + }; + if token == end { + break; + } + seq.push(read_form(rdr)?) + } + let _ = rdr.next(); + match end { + ")" => Ok(list!(seq)), + "]" => Ok(vector!(seq)), + "}" => hash_map(seq), + _ => error("read_seq unknown end value"), + } +} + +fn read_form(rdr: &mut Reader) -> MalRet { + let token = rdr.peek()?; + match &token[..] { + "'" => { + let _ = rdr.next(); + Ok(list![Sym("quote".to_string()), read_form(rdr)?]) + } + "`" => { + let _ = rdr.next(); + Ok(list![Sym("quasiquote".to_string()), read_form(rdr)?]) + } + "~" => { + let _ = rdr.next(); + Ok(list![Sym("unquote".to_string()), read_form(rdr)?]) + } + "~@" => { + let _ = rdr.next(); + Ok(list![Sym("splice-unquote".to_string()), read_form(rdr)?]) + } + "^" => { + let _ = rdr.next(); + let meta = read_form(rdr)?; + Ok(list![Sym("with-meta".to_string()), read_form(rdr)?, meta]) + } + "@" => { + let _ = rdr.next(); + Ok(list![Sym("deref".to_string()), read_form(rdr)?]) + } + ")" => error("unexpected ')'"), + "(" => read_seq(rdr, ")"), + "]" => error("unexpected ']'"), + "[" => read_seq(rdr, "]"), + "}" => error("unexpected '}'"), + "{" => read_seq(rdr, "}"), + _ => read_atom(rdr), + } +} + +pub fn read_str(str: String) -> MalRet { + let tokens = tokenize(&str); + //println!("tokens: {:?}", tokens); + if tokens.len() == 0 { + return error("no input"); + } + read_form(&mut Reader { + pos: 0, + tokens: tokens, + }) +} diff --git a/lisp/types.rs b/lisp/types.rs new file mode 100644 index 000000000..108a646cf --- /dev/null +++ b/lisp/types.rs @@ -0,0 +1,240 @@ +use std::cell::RefCell; +use std::rc::Rc; +//use std::collections::HashMap; +use fnv::FnvHashMap; +use itertools::Itertools; + +use crate::env::{env_bind, Env}; +use crate::types::MalErr::{ErrMalVal, ErrString}; +use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector}; + +#[derive(Debug, Clone)] +pub enum MalVal { + Nil, + Bool(bool), + Int(i64), + //Float(f64), + Str(String), + Sym(String), + List(Rc>, Rc), + Vector(Rc>, Rc), + Hash(Rc>, Rc), + Func(fn(MalArgs) -> MalRet, Rc), + MalFunc { + eval: fn(ast: MalVal, env: Env) -> MalRet, + ast: Rc, + env: Env, + params: Rc, + is_macro: bool, + meta: Rc, + }, + Atom(Rc>), +} + +#[derive(Debug)] +pub enum MalErr { + ErrString(String), + ErrMalVal(MalVal), +} + +pub type MalArgs = Vec; +pub type MalRet = Result; + +// type utility macros + +macro_rules! list { + ($seq:expr) => {{ + List(Rc::new($seq),Rc::new(Nil)) + }}; + [$($args:expr),*] => {{ + let v: Vec = vec![$($args),*]; + List(Rc::new(v),Rc::new(Nil)) + }} +} + +macro_rules! vector { + ($seq:expr) => {{ + Vector(Rc::new($seq),Rc::new(Nil)) + }}; + [$($args:expr),*] => {{ + let v: Vec = vec![$($args),*]; + Vector(Rc::new(v),Rc::new(Nil)) + }} +} + +// type utility functions + +pub fn error(s: &str) -> MalRet { + Err(ErrString(s.to_string())) +} + +pub fn format_error(e: MalErr) -> String { + match e { + ErrString(s) => s.clone(), + ErrMalVal(mv) => mv.pr_str(true), + } +} + +pub fn atom(mv: &MalVal) -> MalVal { + Atom(Rc::new(RefCell::new(mv.clone()))) +} + +impl MalVal { + pub fn keyword(&self) -> MalRet { + match self { + Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())), + Str(s) => Ok(Str(format!("\u{29e}{}", s))), + _ => error("invalid type for keyword"), + } + } + + pub fn empty_q(&self) -> MalRet { + match self { + List(l, _) | Vector(l, _) => Ok(Bool(l.len() == 0)), + Nil => Ok(Bool(true)), + _ => error("invalid type for empty?"), + } + } + + pub fn count(&self) -> MalRet { + match self { + List(l, _) | Vector(l, _) => Ok(Int(l.len() as i64)), + Nil => Ok(Int(0)), + _ => error("invalid type for count"), + } + } + + pub fn apply(&self, args: MalArgs) -> MalRet { + match *self { + Func(f, _) => f(args), + MalFunc { + eval, + ref ast, + ref env, + ref params, + .. + } => { + let a = &**ast; + let p = &**params; + let fn_env = env_bind(Some(env.clone()), p.clone(), args)?; + Ok(eval(a.clone(), fn_env)?) + } + _ => error("attempt to call non-function"), + } + } + + pub fn keyword_q(&self) -> bool { + match self { + Str(s) if s.starts_with("\u{29e}") => true, + _ => false, + } + } + + pub fn deref(&self) -> MalRet { + match self { + Atom(a) => Ok(a.borrow().clone()), + _ => error("attempt to deref a non-Atom"), + } + } + + pub fn reset_bang(&self, new: &MalVal) -> MalRet { + match self { + Atom(a) => { + *a.borrow_mut() = new.clone(); + Ok(new.clone()) + } + _ => error("attempt to reset! a non-Atom"), + } + } + + pub fn swap_bang(&self, args: &MalArgs) -> MalRet { + match self { + Atom(a) => { + let f = &args[0]; + let mut fargs = args[1..].to_vec(); + fargs.insert(0, a.borrow().clone()); + *a.borrow_mut() = f.apply(fargs)?; + Ok(a.borrow().clone()) + } + _ => error("attempt to swap! a non-Atom"), + } + } + + pub fn get_meta(&self) -> MalRet { + match self { + List(_, meta) | Vector(_, meta) | Hash(_, meta) => Ok((&**meta).clone()), + Func(_, meta) => Ok((&**meta).clone()), + MalFunc { meta, .. } => Ok((&**meta).clone()), + _ => error("meta not supported by type"), + } + } + + pub fn with_meta(&mut self, new_meta: &MalVal) -> MalRet { + match self { + List(_, ref mut meta) + | Vector(_, ref mut meta) + | Hash(_, ref mut meta) + | Func(_, ref mut meta) + | MalFunc { ref mut meta, .. } => { + *meta = Rc::new((&*new_meta).clone()); + } + _ => return error("with-meta not supported by type"), + }; + Ok(self.clone()) + } +} + +impl PartialEq for MalVal { + fn eq(&self, other: &MalVal) -> bool { + match (self, other) { + (Nil, Nil) => true, + (Bool(ref a), Bool(ref b)) => a == b, + (Int(ref a), Int(ref b)) => a == b, + (Str(ref a), Str(ref b)) => a == b, + (Sym(ref a), Sym(ref b)) => a == b, + (List(ref a, _), List(ref b, _)) + | (Vector(ref a, _), Vector(ref b, _)) + | (List(ref a, _), Vector(ref b, _)) + | (Vector(ref a, _), List(ref b, _)) => a == b, + (Hash(ref a, _), Hash(ref b, _)) => a == b, + (MalFunc { .. }, MalFunc { .. }) => false, + _ => false, + } + } +} + +pub fn func(f: fn(MalArgs) -> MalRet) -> MalVal { + Func(f, Rc::new(Nil)) +} + +pub fn _assoc(mut hm: FnvHashMap, kvs: MalArgs) -> MalRet { + if kvs.len() % 2 != 0 { + return error("odd number of elements"); + } + for (k, v) in kvs.iter().tuples() { + match k { + Str(s) => { + hm.insert(s.to_string(), v.clone()); + } + _ => return error("key is not string"), + } + } + Ok(Hash(Rc::new(hm), Rc::new(Nil))) +} + +pub fn _dissoc(mut hm: FnvHashMap, ks: MalArgs) -> MalRet { + for k in ks.iter() { + match k { + Str(ref s) => { + hm.remove(s); + } + _ => return error("key is not string"), + } + } + Ok(Hash(Rc::new(hm), Rc::new(Nil))) +} + +pub fn hash_map(kvs: MalArgs) -> MalRet { + let hm: FnvHashMap = FnvHashMap::default(); + _assoc(hm, kvs) +}