From 616a540513f271a067983088a8a18e9a510f952d Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:46:22 +0200
Subject: [PATCH 001/119] prototyping lisp v0
---
Cargo.toml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Cargo.toml b/Cargo.toml
index 1843add9a..0b711de28 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,9 @@ clap = "3.0.0-beta.1"
failure = "0.1.8"
failure_derive = "0.1.8"
+regex = "1"
+fancy-regex = "0.4.0"
+
[[bin]]
name = "sha256"
path = "src/sha256.rs"
@@ -107,3 +110,6 @@ path = "src/bin/mint.rs"
name = "jubjub"
path = "src/bin/jubjub.rs"
+[[bin]]
+name = "lisp"
+path = "lisp/lisp.rs"
From 2e6c6857e98c9ed621b4a5ce4ce21e961b2ec8de Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:46:31 +0200
Subject: [PATCH 002/119] prototyping lisp v0
---
lisp/lisp.rs | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 421 insertions(+)
create mode 100644 lisp/lisp.rs
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
new file mode 100644
index 000000000..74702f66a
--- /dev/null
+++ b/lisp/lisp.rs
@@ -0,0 +1,421 @@
+use std::collections::HashMap;
+
+use fancy_regex::Regex;
+use std::fmt;
+use std::fs::File;
+use std::io;
+use std::io::{BufRead, BufReader};
+use std::num::ParseFloatError;
+use std::rc::Rc;
+/*
+ Types
+*/
+
+#[derive(Clone)]
+enum RispExp {
+ Bool(bool),
+ Symbol(String),
+ Number(f64),
+ List(Vec),
+ Func(fn(&[RispExp]) -> Result),
+ Lambda(RispLambda),
+}
+
+#[derive(Clone)]
+struct RispLambda {
+ params_exp: Rc,
+ body_exp: Rc,
+}
+
+impl fmt::Display for RispExp {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let str = match self {
+ RispExp::Bool(a) => a.to_string(),
+ RispExp::Symbol(s) => s.clone(),
+ RispExp::Number(n) => n.to_string(),
+ RispExp::List(list) => {
+ let xs: Vec = list.iter().map(|x| x.to_string()).collect();
+ format!("({})", xs.join(","))
+ }
+ RispExp::Func(_) => "Function {}".to_string(),
+ RispExp::Lambda(_) => "Lambda {}".to_string(),
+ };
+
+ write!(f, "{}", str)
+ }
+}
+
+#[derive(Debug)]
+enum RispErr {
+ Reason(String),
+}
+
+#[derive(Clone)]
+struct RispEnv<'a> {
+ data: HashMap,
+ outer: Option<&'a RispEnv<'a>>,
+}
+
+/*
+ InPort
+*/
+// TODO change to symbol(eof)
+
+struct InPort {
+ line: String,
+ lines: Vec,
+ cur_line: usize
+}
+
+impl InPort {
+
+ fn init(filename: String) -> InPort {
+ let file = File::open(filename).unwrap();
+ let reader = io::BufReader::new(file).lines().map(|s| s.ok().unwrap().to_string()).collect();
+ InPort {
+ line : String::new(),
+ lines: reader,
+ cur_line: 0
+ }
+ }
+
+ pub fn next_token(&mut self) -> String {
+ loop {
+ if self.line.is_empty() || self.line == "#".to_string() {
+ // TODO add match
+ self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
+ self.cur_line = self.cur_line + 1;
+ }
+ if self.line.is_empty() {
+ self.cur_line = self.cur_line + 1;
+ return "#".to_string();
+ }
+ let (token, rest) = InPort::tokenize(self.line.clone());
+ self.line = rest;
+ self.cur_line = self.cur_line + 1;
+ if !token.is_empty() {
+ return token;
+ }
+ }
+ }
+
+ fn tokenize(expr: String) -> (String, String) {
+ let re =
+ Regex::new(r#"\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"#).unwrap();
+ let result = re.captures(&expr);
+ let captures = result
+ .expect("Error running regex")
+ .expect("No match found");
+// println!("{}", captures.get(0).unwrap().as_str().to_string());
+ (
+ captures.get(1).unwrap().as_str().to_string(),
+ captures.get(2).unwrap().as_str().to_string(),
+ )
+ }
+}
+
+fn parse_atom(token: &str) -> RispExp {
+ match token.as_ref() {
+ "true" => RispExp::Bool(true),
+ "false" => RispExp::Bool(false),
+ _ => {
+ let potential_float: Result = token.parse();
+ match potential_float {
+ Ok(v) => RispExp::Number(v),
+ Err(_) => RispExp::Symbol(token.to_string().clone()),
+ }
+ }
+ }
+}
+
+/*
+ Env
+*/
+
+macro_rules! ensure_tonicity {
+ ($check_fn:expr) => {{
+ |args: &[RispExp]| -> Result {
+ let floats = parse_list_of_floats(args)?;
+ let first = floats
+ .first()
+ .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
+ let rest = &floats[1..];
+ fn f(prev: &f64, xs: &[f64]) -> bool {
+ match xs.first() {
+ Some(x) => $check_fn(prev, x) && f(x, &xs[1..]),
+ None => true,
+ }
+ };
+ Ok(RispExp::Bool(f(first, rest)))
+ }
+ }};
+}
+
+fn default_env<'a>() -> RispEnv<'a> {
+ let mut data: HashMap = HashMap::new();
+ data.insert(
+ "+".to_string(),
+ RispExp::Func(|args: &[RispExp]| -> Result {
+ let sum = parse_list_of_floats(args)?
+ .iter()
+ .fold(0.0, |sum, a| sum + a);
+
+ Ok(RispExp::Number(sum))
+ }),
+ );
+ data.insert(
+ "-".to_string(),
+ RispExp::Func(|args: &[RispExp]| -> Result {
+ let floats = parse_list_of_floats(args)?;
+ let first = *floats
+ .first()
+ .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
+ let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
+
+ Ok(RispExp::Number(first - sum_of_rest))
+ }),
+ );
+ data.insert(
+ "=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a == b)),
+ );
+ data.insert(
+ ">".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a > b)),
+ );
+ data.insert(
+ ">=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a >= b)),
+ );
+ data.insert(
+ "<".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a < b)),
+ );
+ data.insert(
+ "<=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a <= b)),
+ );
+
+ RispEnv { data, outer: None }
+}
+
+fn parse_list_of_floats(args: &[RispExp]) -> Result, RispErr> {
+ args.iter().map(|x| parse_single_float(x)).collect()
+}
+
+fn parse_single_float(exp: &RispExp) -> Result {
+ match exp {
+ RispExp::Number(num) => Ok(*num),
+ _ => Err(RispErr::Reason("expected a number".to_string())),
+ }
+}
+
+/*
+ Eval
+*/
+
+fn eval_if_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
+ let test_form = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected test form".to_string()))?;
+ let test_eval = eval(test_form, env)?;
+ match test_eval {
+ RispExp::Bool(b) => {
+ let form_idx = if b { 1 } else { 2 };
+ let res_form = arg_forms
+ .get(form_idx)
+ .ok_or(RispErr::Reason(format!("expected form idx={}", form_idx)))?;
+ let res_eval = eval(res_form, env);
+
+ res_eval
+ }
+ _ => Err(RispErr::Reason(format!(
+ "unexpected test form='{}'",
+ test_form.to_string()
+ ))),
+ }
+}
+
+fn eval_def_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
+ let first_form = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected first form".to_string()))?;
+ let first_str = match first_form {
+ RispExp::Symbol(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected first form to be a symbol".to_string(),
+ )),
+ }?;
+ let second_form = arg_forms
+ .get(1)
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
+ if arg_forms.len() > 2 {
+ return Err(RispErr::Reason("def can only have two forms ".to_string()));
+ }
+ let second_eval = eval(second_form, env)?;
+ env.data.insert(first_str, second_eval);
+
+ Ok(first_form.clone())
+}
+
+fn eval_lambda_args(arg_forms: &[RispExp]) -> Result {
+ let params_exp = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected args form".to_string()))?;
+ let body_exp = arg_forms
+ .get(1)
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
+ if arg_forms.len() > 2 {
+ return Err(RispErr::Reason(
+ "fn definition can only have two forms ".to_string(),
+ ));
+ }
+
+ Ok(RispExp::Lambda(RispLambda {
+ body_exp: Rc::new(body_exp.clone()),
+ params_exp: Rc::new(params_exp.clone()),
+ }))
+}
+
+fn eval_built_in_form(
+ exp: &RispExp,
+ arg_forms: &[RispExp],
+ env: &mut RispEnv,
+) -> Option> {
+ match exp {
+ RispExp::Symbol(s) => match s.as_ref() {
+ "if" => Some(eval_if_args(arg_forms, env)),
+ "def" => Some(eval_def_args(arg_forms, env)),
+ "fn" => Some(eval_lambda_args(arg_forms)),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+fn env_get(k: &str, env: &RispEnv) -> Option {
+ match env.data.get(k) {
+ Some(exp) => Some(exp.clone()),
+ None => match &env.outer {
+ Some(outer_env) => env_get(k, &outer_env),
+ None => None,
+ },
+ }
+}
+
+fn parse_list_of_symbol_strings(form: Rc) -> Result, RispErr> {
+ let list = match form.as_ref() {
+ RispExp::List(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected args form to be a list".to_string(),
+ )),
+ }?;
+ list.iter()
+ .map(|x| match x {
+ RispExp::Symbol(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected symbols in the argument list".to_string(),
+ )),
+ })
+ .collect()
+}
+
+fn env_for_lambda<'a>(
+ params: Rc,
+ arg_forms: &[RispExp],
+ outer_env: &'a mut RispEnv,
+) -> Result, RispErr> {
+ let ks = parse_list_of_symbol_strings(params)?;
+ if ks.len() != arg_forms.len() {
+ return Err(RispErr::Reason(format!(
+ "expected {} arguments, got {}",
+ ks.len(),
+ arg_forms.len()
+ )));
+ }
+ let vs = eval_forms(arg_forms, outer_env)?;
+ let mut data: HashMap = HashMap::new();
+ for (k, v) in ks.iter().zip(vs.iter()) {
+ data.insert(k.clone(), v.clone());
+ }
+ Ok(RispEnv {
+ data,
+ outer: Some(outer_env),
+ })
+}
+
+fn eval_forms(arg_forms: &[RispExp], env: &mut RispEnv) -> Result, RispErr> {
+ arg_forms.iter().map(|x| eval(x, env)).collect()
+}
+
+fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
+ match exp {
+ RispExp::Symbol(k) => {
+ env_get(k, env).ok_or(RispErr::Reason(format!("unexpected symbol k='{}'", k)))
+ }
+ RispExp::Bool(_a) => Ok(exp.clone()),
+ RispExp::Number(_a) => Ok(exp.clone()),
+
+ RispExp::List(list) => {
+ let first_form = list
+ .first()
+ .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?;
+ let arg_forms = &list[1..];
+ match eval_built_in_form(first_form, arg_forms, env) {
+ Some(res) => res,
+ None => {
+ let first_eval = eval(first_form, env)?;
+ match first_eval {
+ RispExp::Func(f) => f(&eval_forms(arg_forms, env)?),
+ RispExp::Lambda(lambda) => {
+ let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?;
+ eval(&lambda.body_exp, new_env)
+ }
+ _ => Err(RispErr::Reason("first form must be a function".to_string())),
+ }
+ }
+ }
+ }
+ RispExp::Func(_) => Err(RispErr::Reason("unexpected form".to_string())),
+ RispExp::Lambda(_) => Err(RispErr::Reason("unexpected form".to_string())),
+ }
+}
+
+
+fn main() {
+ let env = &mut default_env();
+ let mut reader = InPort::init("new.lisp".to_string());
+ let mut token = String::new();
+ loop {
+ if token == "#".to_string() {
+ println!("EOF");
+ break;
+ }
+
+ token = reader.next_token().as_str().to_string();
+ println!("{}", token);
+ }
+ // read from file
+// let file = File::open("new.lisp").unwrap();
+// let lines = io::BufReader::new(file).lines();
+// let mut cur_line = String::new();
+// let mut token = String::new();
+// for line in lines {
+// let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
+// println!("token {:?}", token);
+// println!("line {:?}", cur_line);
+ // }
+
+ /*
+ loop {
+ println!("risp >");
+ let expr = slurp_expr();
+ match parse_eval(expr, env) {
+ Ok(res) => println!("// 🔥 => {}", res),
+ Err(e) => match e {
+ RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
+ },
+ }
+ }
+ */
+}
From 7068ca4ab09a766f0e533cc59e1231548c84bff1 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:54:24 +0200
Subject: [PATCH 003/119] prototyping lisp v0
---
lisp/lisp.rs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 74702f66a..197bfe4aa 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -81,18 +81,15 @@ impl InPort {
pub fn next_token(&mut self) -> String {
loop {
- if self.line.is_empty() || self.line == "#".to_string() {
- // TODO add match
+ if self.line.is_empty() {
self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
self.cur_line = self.cur_line + 1;
}
if self.line.is_empty() {
- self.cur_line = self.cur_line + 1;
return "#".to_string();
}
let (token, rest) = InPort::tokenize(self.line.clone());
self.line = rest;
- self.cur_line = self.cur_line + 1;
if !token.is_empty() {
return token;
}
From ae3399a826dad61afe89907ec602df3d1ac916be Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 20:22:40 +0200
Subject: [PATCH 004/119] prototyping lisp v0
---
lisp/lisp.rs | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 197bfe4aa..b8df722a1 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -111,6 +111,30 @@ impl InPort {
}
}
+// TODO change return type to Symbol
+fn read(inport: InPort) -> String {
+ fn read_ahead(token: String) {
+ match token {
+ "(" => {
+ let L = Vec::new();
+ loop {
+ let token = inport.next_token();
+ if token == ")" {
+ return L;
+ } else {
+ L.append(read_ahead(token));
+ }
+ }
+ },
+ _ => { println!("{}", token); }
+ }
+ }
+ let token1 = inport.next_token();
+ if token1 == "#".to_string() {
+ return "#".to_string();
+ }
+}
+
fn parse_atom(token: &str) -> RispExp {
match token.as_ref() {
"true" => RispExp::Bool(true),
@@ -383,15 +407,7 @@ fn main() {
let env = &mut default_env();
let mut reader = InPort::init("new.lisp".to_string());
let mut token = String::new();
- loop {
- if token == "#".to_string() {
- println!("EOF");
- break;
- }
-
- token = reader.next_token().as_str().to_string();
- println!("{}", token);
- }
+ read(reader);
// read from file
// let file = File::open("new.lisp").unwrap();
// let lines = io::BufReader::new(file).lines();
From 9e52cb7304d67565d039d5f0d3b026df33cc6865 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 21:18:22 +0200
Subject: [PATCH 005/119] exploring read function
---
lisp/lisp.rs | 118 +++++++++++++++++++++++++++++++--------------------
1 file changed, 72 insertions(+), 46 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index b8df722a1..7786e8276 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -63,36 +63,42 @@ struct RispEnv<'a> {
struct InPort {
line: String,
- lines: Vec,
- cur_line: usize
+ lines: Vec,
+ cur_line: usize,
}
impl InPort {
-
fn init(filename: String) -> InPort {
let file = File::open(filename).unwrap();
- let reader = io::BufReader::new(file).lines().map(|s| s.ok().unwrap().to_string()).collect();
- InPort {
- line : String::new(),
+ let reader = io::BufReader::new(file)
+ .lines()
+ .map(|s| s.ok().unwrap().to_string())
+ .collect();
+ InPort {
+ line: String::new(),
lines: reader,
- cur_line: 0
+ cur_line: 0,
}
}
pub fn next_token(&mut self) -> String {
loop {
- if self.line.is_empty() {
- self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
- self.cur_line = self.cur_line + 1;
- }
- if self.line.is_empty() {
- return "#".to_string();
- }
- let (token, rest) = InPort::tokenize(self.line.clone());
- self.line = rest;
- if !token.is_empty() {
- return token;
- }
+ if self.line.is_empty() {
+ self.line = self
+ .lines
+ .get(self.cur_line)
+ .unwrap_or(&"#".to_string())
+ .to_string();
+ self.cur_line = self.cur_line + 1;
+ }
+ if self.line.is_empty() {
+ return "#".to_string();
+ }
+ let (token, rest) = InPort::tokenize(self.line.clone());
+ self.line = rest;
+ if !token.is_empty() {
+ return token;
+ }
}
}
@@ -103,7 +109,7 @@ impl InPort {
let captures = result
.expect("Error running regex")
.expect("No match found");
-// println!("{}", captures.get(0).unwrap().as_str().to_string());
+ // println!("{}", captures.get(0).unwrap().as_str().to_string());
(
captures.get(1).unwrap().as_str().to_string(),
captures.get(2).unwrap().as_str().to_string(),
@@ -111,27 +117,48 @@ impl InPort {
}
}
-// TODO change return type to Symbol
-fn read(inport: InPort) -> String {
- fn read_ahead(token: String) {
- match token {
- "(" => {
- let L = Vec::new();
- loop {
- let token = inport.next_token();
- if token == ")" {
- return L;
+fn read_ahead(inport: &InPort, token: String) -> String {
+ match token.as_str() {
+ "(" => {
+ let mut L = Vec::new();
+ loop {
+ let t = &inport.next_token();
+ if t.to_string() == ")" {
+ return L.into_iter().collect();
} else {
- L.append(read_ahead(token));
+ L.push(read_ahead(&inport, t.to_string()));
}
}
- },
- _ => { println!("{}", token); }
+ }
+ _ => {
+ println!("{}", token);
+ return token;
}
}
- let token1 = inport.next_token();
- if token1 == "#".to_string() {
+}
+
+fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
+ let mut res: Vec = vec![];
+ let mut xs = tokens;
+ loop {
+ let (next_token, rest) = xs
+ .split_first()
+ .ok_or(RispErr::Reason("could not find closing `)`".to_string()))?;
+ if next_token == ")" {
+ return Ok((RispExp::List(res), rest)); // skip `)`, head to the token after
+ }
+ let (exp, new_xs) = parse(&xs)?;
+ res.push(exp);
+ xs = new_xs;
+ }
+}
+// TODO change return type to Symbol
+fn read(inport: &InPort) -> String {
+ let token1 = &inport.next_token().as_str();
+ if token1.to_string() == "#".to_string() {
return "#".to_string();
+ } else {
+ return read_ahead(&inport, token1.to_string());
}
}
@@ -402,22 +429,21 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
}
}
-
fn main() {
let env = &mut default_env();
let mut reader = InPort::init("new.lisp".to_string());
let mut token = String::new();
- read(reader);
+ read(&reader);
// read from file
-// let file = File::open("new.lisp").unwrap();
-// let lines = io::BufReader::new(file).lines();
-// let mut cur_line = String::new();
-// let mut token = String::new();
-// for line in lines {
-// let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
-// println!("token {:?}", token);
-// println!("line {:?}", cur_line);
- // }
+ // let file = File::open("new.lisp").unwrap();
+ // let lines = io::BufReader::new(file).lines();
+ // let mut cur_line = String::new();
+ // let mut token = String::new();
+ // for line in lines {
+ // let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
+ // println!("token {:?}", token);
+ // println!("line {:?}", cur_line);
+ // }
/*
loop {
From a1d5f0cc389572be1f204eaffdce916515fb1935 Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 17:30:53 +0200
Subject: [PATCH 006/119] added mal lisp and jubjub unfinished sample
---
Cargo.toml | 1 -
lisp/jj.rkt | 113 --------
lisp/lisp.rs | 740 ++++++++++++++++++++++-----------------------------
lisp/zk.rkt | 141 ----------
4 files changed, 317 insertions(+), 678 deletions(-)
delete mode 100644 lisp/jj.rkt
delete mode 100644 lisp/zk.rkt
diff --git a/Cargo.toml b/Cargo.toml
index 0b711de28..9ff6de47b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,7 +36,6 @@ failure = "0.1.8"
failure_derive = "0.1.8"
regex = "1"
-fancy-regex = "0.4.0"
[[bin]]
name = "sha256"
diff --git a/lisp/jj.rkt b/lisp/jj.rkt
deleted file mode 100644
index 5e79f16a0..000000000
--- a/lisp/jj.rkt
+++ /dev/null
@@ -1,113 +0,0 @@
-#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/lisp.rs b/lisp/lisp.rs
index 7786e8276..a3204f0fa 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -1,460 +1,354 @@
-use std::collections::HashMap;
+#![allow(non_snake_case)]
-use fancy_regex::Regex;
-use std::fmt;
-use std::fs::File;
-use std::io;
-use std::io::{BufRead, BufReader};
-use std::num::ParseFloatError;
use std::rc::Rc;
-/*
- Types
-*/
+//use std::collections::HashMap;
+use fnv::FnvHashMap;
+use itertools::Itertools;
-#[derive(Clone)]
-enum RispExp {
- Bool(bool),
- Symbol(String),
- Number(f64),
- List(Vec),
- Func(fn(&[RispExp]) -> Result),
- Lambda(RispLambda),
+#[macro_use]
+extern crate lazy_static;
+extern crate fnv;
+extern crate itertools;
+extern crate regex;
+
+#[macro_use]
+mod types;
+use crate::types::MalErr::{ErrMalVal, ErrString};
+use crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
+use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
+mod env;
+mod printer;
+mod reader;
+use crate::env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
+#[macro_use]
+mod core;
+
+// read
+fn read(str: &str) -> MalRet {
+ reader::read_str(str.to_string())
}
-#[derive(Clone)]
-struct RispLambda {
- params_exp: Rc,
- body_exp: Rc,
-}
+// eval
-impl fmt::Display for RispExp {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let str = match self {
- RispExp::Bool(a) => a.to_string(),
- RispExp::Symbol(s) => s.clone(),
- RispExp::Number(n) => n.to_string(),
- RispExp::List(list) => {
- let xs: Vec = list.iter().map(|x| x.to_string()).collect();
- format!("({})", xs.join(","))
- }
- RispExp::Func(_) => "Function {}".to_string(),
- RispExp::Lambda(_) => "Lambda {}".to_string(),
- };
-
- write!(f, "{}", str)
- }
-}
-
-#[derive(Debug)]
-enum RispErr {
- Reason(String),
-}
-
-#[derive(Clone)]
-struct RispEnv<'a> {
- data: HashMap,
- outer: Option<&'a RispEnv<'a>>,
-}
-
-/*
- InPort
-*/
-// TODO change to symbol(eof)
-
-struct InPort {
- line: String,
- lines: Vec,
- cur_line: usize,
-}
-
-impl InPort {
- fn init(filename: String) -> InPort {
- let file = File::open(filename).unwrap();
- let reader = io::BufReader::new(file)
- .lines()
- .map(|s| s.ok().unwrap().to_string())
- .collect();
- InPort {
- line: String::new(),
- lines: reader,
- cur_line: 0,
- }
- }
-
- pub fn next_token(&mut self) -> String {
- loop {
- if self.line.is_empty() {
- self.line = self
- .lines
- .get(self.cur_line)
- .unwrap_or(&"#".to_string())
- .to_string();
- self.cur_line = self.cur_line + 1;
- }
- if self.line.is_empty() {
- return "#".to_string();
- }
- let (token, rest) = InPort::tokenize(self.line.clone());
- self.line = rest;
- if !token.is_empty() {
- return token;
- }
- }
- }
-
- fn tokenize(expr: String) -> (String, String) {
- let re =
- Regex::new(r#"\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"#).unwrap();
- let result = re.captures(&expr);
- let captures = result
- .expect("Error running regex")
- .expect("No match found");
- // println!("{}", captures.get(0).unwrap().as_str().to_string());
- (
- captures.get(1).unwrap().as_str().to_string(),
- captures.get(2).unwrap().as_str().to_string(),
- )
- }
-}
-
-fn read_ahead(inport: &InPort, token: String) -> String {
- match token.as_str() {
- "(" => {
- let mut L = Vec::new();
- loop {
- let t = &inport.next_token();
- if t.to_string() == ")" {
- return L.into_iter().collect();
- } else {
- L.push(read_ahead(&inport, t.to_string()));
+fn qq_iter(elts: &MalArgs) -> MalVal {
+ let mut acc = list![];
+ for elt in elts.iter().rev() {
+ if let List(v, _) = elt {
+ if v.len() == 2 {
+ if let Sym(ref s) = v[0] {
+ if s == "splice-unquote" {
+ acc = list![Sym("concat".to_string()), v[1].clone(), acc];
+ continue;
}
}
}
- _ => {
- println!("{}", token);
- return token;
}
+ acc = list![Sym("cons".to_string()), quasiquote(&elt), acc];
}
+ return acc;
}
-fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
- let mut res: Vec = vec![];
- let mut xs = tokens;
- loop {
- let (next_token, rest) = xs
- .split_first()
- .ok_or(RispErr::Reason("could not find closing `)`".to_string()))?;
- if next_token == ")" {
- return Ok((RispExp::List(res), rest)); // skip `)`, head to the token after
- }
- let (exp, new_xs) = parse(&xs)?;
- res.push(exp);
- xs = new_xs;
- }
-}
-// TODO change return type to Symbol
-fn read(inport: &InPort) -> String {
- let token1 = &inport.next_token().as_str();
- if token1.to_string() == "#".to_string() {
- return "#".to_string();
- } else {
- return read_ahead(&inport, token1.to_string());
- }
-}
-
-fn parse_atom(token: &str) -> RispExp {
- match token.as_ref() {
- "true" => RispExp::Bool(true),
- "false" => RispExp::Bool(false),
- _ => {
- let potential_float: Result = token.parse();
- match potential_float {
- Ok(v) => RispExp::Number(v),
- Err(_) => RispExp::Symbol(token.to_string().clone()),
- }
- }
- }
-}
-
-/*
- Env
-*/
-
-macro_rules! ensure_tonicity {
- ($check_fn:expr) => {{
- |args: &[RispExp]| -> Result {
- let floats = parse_list_of_floats(args)?;
- let first = floats
- .first()
- .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
- let rest = &floats[1..];
- fn f(prev: &f64, xs: &[f64]) -> bool {
- match xs.first() {
- Some(x) => $check_fn(prev, x) && f(x, &xs[1..]),
- None => true,
+fn quasiquote(ast: &MalVal) -> MalVal {
+ match ast {
+ List(v, _) => {
+ if v.len() == 2 {
+ if let Sym(ref s) = v[0] {
+ if s == "unquote" {
+ return v[1].clone();
+ }
}
- };
- Ok(RispExp::Bool(f(first, rest)))
- }
- }};
-}
-
-fn default_env<'a>() -> RispEnv<'a> {
- let mut data: HashMap = HashMap::new();
- data.insert(
- "+".to_string(),
- RispExp::Func(|args: &[RispExp]| -> Result {
- let sum = parse_list_of_floats(args)?
- .iter()
- .fold(0.0, |sum, a| sum + a);
-
- Ok(RispExp::Number(sum))
- }),
- );
- data.insert(
- "-".to_string(),
- RispExp::Func(|args: &[RispExp]| -> Result {
- let floats = parse_list_of_floats(args)?;
- let first = *floats
- .first()
- .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
- let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
-
- Ok(RispExp::Number(first - sum_of_rest))
- }),
- );
- data.insert(
- "=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a == b)),
- );
- data.insert(
- ">".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a > b)),
- );
- data.insert(
- ">=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a >= b)),
- );
- data.insert(
- "<".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a < b)),
- );
- data.insert(
- "<=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a <= b)),
- );
-
- RispEnv { data, outer: None }
-}
-
-fn parse_list_of_floats(args: &[RispExp]) -> Result, RispErr> {
- args.iter().map(|x| parse_single_float(x)).collect()
-}
-
-fn parse_single_float(exp: &RispExp) -> Result {
- match exp {
- RispExp::Number(num) => Ok(*num),
- _ => Err(RispErr::Reason("expected a number".to_string())),
+ }
+ return qq_iter(&v);
+ },
+ Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)],
+ Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()],
+ _ => ast.clone(),
}
}
-/*
- Eval
-*/
-
-fn eval_if_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
- let test_form = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected test form".to_string()))?;
- let test_eval = eval(test_form, env)?;
- match test_eval {
- RispExp::Bool(b) => {
- let form_idx = if b { 1 } else { 2 };
- let res_form = arg_forms
- .get(form_idx)
- .ok_or(RispErr::Reason(format!("expected form idx={}", form_idx)))?;
- let res_eval = eval(res_form, env);
-
- res_eval
- }
- _ => Err(RispErr::Reason(format!(
- "unexpected test form='{}'",
- test_form.to_string()
- ))),
- }
-}
-
-fn eval_def_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
- let first_form = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected first form".to_string()))?;
- let first_str = match first_form {
- RispExp::Symbol(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected first form to be a symbol".to_string(),
- )),
- }?;
- let second_form = arg_forms
- .get(1)
- .ok_or(RispErr::Reason("expected second form".to_string()))?;
- if arg_forms.len() > 2 {
- return Err(RispErr::Reason("def can only have two forms ".to_string()));
- }
- let second_eval = eval(second_form, env)?;
- env.data.insert(first_str, second_eval);
-
- Ok(first_form.clone())
-}
-
-fn eval_lambda_args(arg_forms: &[RispExp]) -> Result {
- let params_exp = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected args form".to_string()))?;
- let body_exp = arg_forms
- .get(1)
- .ok_or(RispErr::Reason("expected second form".to_string()))?;
- if arg_forms.len() > 2 {
- return Err(RispErr::Reason(
- "fn definition can only have two forms ".to_string(),
- ));
- }
-
- Ok(RispExp::Lambda(RispLambda {
- body_exp: Rc::new(body_exp.clone()),
- params_exp: Rc::new(params_exp.clone()),
- }))
-}
-
-fn eval_built_in_form(
- exp: &RispExp,
- arg_forms: &[RispExp],
- env: &mut RispEnv,
-) -> Option> {
- match exp {
- RispExp::Symbol(s) => match s.as_ref() {
- "if" => Some(eval_if_args(arg_forms, env)),
- "def" => Some(eval_def_args(arg_forms, env)),
- "fn" => Some(eval_lambda_args(arg_forms)),
+fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
+ match ast {
+ List(v, _) => match v[0] {
+ Sym(ref s) => match env_find(env, s) {
+ Some(e) => match env_get(&e, &v[0]) {
+ Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
+ _ => None,
+ },
+ _ => None,
+ },
_ => None,
},
_ => None,
}
}
-fn env_get(k: &str, env: &RispEnv) -> Option {
- match env.data.get(k) {
- Some(exp) => Some(exp.clone()),
- None => match &env.outer {
- Some(outer_env) => env_get(k, &outer_env),
- None => None,
- },
+fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
+ let mut was_expanded = false;
+ while let Some((mf, args)) = is_macro_call(&ast, env) {
+ //println!("macroexpand 1: {:?}", ast);
+ ast = match mf.apply(args) {
+ Err(e) => return (false, Err(e)),
+ Ok(a) => a,
+ };
+ //println!("macroexpand 2: {:?}", ast);
+ was_expanded = true;
}
+ ((was_expanded, Ok(ast)))
}
-fn parse_list_of_symbol_strings(form: Rc) -> Result, RispErr> {
- let list = match form.as_ref() {
- RispExp::List(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected args form to be a list".to_string(),
- )),
- }?;
- list.iter()
- .map(|x| match x {
- RispExp::Symbol(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected symbols in the argument list".to_string(),
- )),
- })
- .collect()
-}
-
-fn env_for_lambda<'a>(
- params: Rc,
- arg_forms: &[RispExp],
- outer_env: &'a mut RispEnv,
-) -> Result, RispErr> {
- let ks = parse_list_of_symbol_strings(params)?;
- if ks.len() != arg_forms.len() {
- return Err(RispErr::Reason(format!(
- "expected {} arguments, got {}",
- ks.len(),
- arg_forms.len()
- )));
- }
- let vs = eval_forms(arg_forms, outer_env)?;
- let mut data: HashMap = HashMap::new();
- for (k, v) in ks.iter().zip(vs.iter()) {
- data.insert(k.clone(), v.clone());
- }
- Ok(RispEnv {
- data,
- outer: Some(outer_env),
- })
-}
-
-fn eval_forms(arg_forms: &[RispExp], env: &mut RispEnv) -> Result, RispErr> {
- arg_forms.iter().map(|x| eval(x, env)).collect()
-}
-
-fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
- match exp {
- RispExp::Symbol(k) => {
- env_get(k, env).ok_or(RispErr::Reason(format!("unexpected symbol k='{}'", k)))
+fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
+ match ast {
+ Sym(_) => Ok(env_get(&env, &ast)?),
+ List(v, _) => {
+ let mut lst: MalArgs = vec![];
+ for a in v.iter() {
+ lst.push(eval(a.clone(), env.clone())?)
+ }
+ Ok(list!(lst))
}
- RispExp::Bool(_a) => Ok(exp.clone()),
- RispExp::Number(_a) => Ok(exp.clone()),
+ Vector(v, _) => {
+ let mut lst: MalArgs = vec![];
+ for a in v.iter() {
+ lst.push(eval(a.clone(), env.clone())?)
+ }
+ Ok(vector!(lst))
+ }
+ Hash(hm, _) => {
+ let mut new_hm: FnvHashMap = FnvHashMap::default();
+ for (k, v) in hm.iter() {
+ new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
+ }
+ Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))
+ }
+ _ => Ok(ast.clone()),
+ }
+}
- RispExp::List(list) => {
- let first_form = list
- .first()
- .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?;
- let arg_forms = &list[1..];
- match eval_built_in_form(first_form, arg_forms, env) {
- Some(res) => res,
- None => {
- let first_eval = eval(first_form, env)?;
- match first_eval {
- RispExp::Func(f) => f(&eval_forms(arg_forms, env)?),
- RispExp::Lambda(lambda) => {
- let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?;
- eval(&lambda.body_exp, new_env)
- }
- _ => Err(RispErr::Reason("first form must be a function".to_string())),
+fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
+ let ret: MalRet;
+
+ 'tco: loop {
+ ret = match ast.clone() {
+ List(l, _) => {
+ if l.len() == 0 {
+ return Ok(ast);
+ }
+ match macroexpand(ast.clone(), &env) {
+ (true, Ok(new_ast)) => {
+ ast = new_ast;
+ continue 'tco;
}
+ (_, Err(e)) => return Err(e),
+ _ => (),
+ }
+
+ if l.len() == 0 {
+ return Ok(ast);
+ }
+ let a0 = &l[0];
+ match a0 {
+ Sym(ref a0sym) if a0sym == "def!" => {
+ env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
+ }
+ Sym(ref a0sym) if a0sym == "let*" => {
+ env = env_new(Some(env.clone()));
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ match a1 {
+ List(ref binds, _) | Vector(ref binds, _) => {
+ for (b, e) in binds.iter().tuples() {
+ match b {
+ Sym(_) => {
+ let _ = env_set(
+ &env,
+ b.clone(),
+ eval(e.clone(), env.clone())?,
+ );
+ }
+ _ => {
+ return error("let* with non-Sym binding");
+ }
+ }
+ }
+ }
+ _ => {
+ return error("let* with non-List bindings");
+ }
+ };
+ ast = a2;
+ continue 'tco;
+ }
+ Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
+ Sym(ref a0sym) if a0sym == "quasiquoteexpand" => Ok(quasiquote(&l[1])),
+ Sym(ref a0sym) if a0sym == "quasiquote" => {
+ ast = quasiquote(&l[1]);
+ continue 'tco;
+ }
+ Sym(ref a0sym) if a0sym == "defmacro!" => {
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ let r = eval(a2, env.clone())?;
+ match r {
+ MalFunc {
+ eval,
+ ast,
+ env,
+ params,
+ ..
+ } => Ok(env_set(
+ &env,
+ a1.clone(),
+ MalFunc {
+ eval: eval,
+ ast: ast.clone(),
+ env: env.clone(),
+ params: params.clone(),
+ is_macro: true,
+ meta: Rc::new(Nil),
+ },
+ )?),
+ _ => error("set_macro on non-function"),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "macroexpand" => {
+ match macroexpand(l[1].clone(), &env) {
+ (_, Ok(new_ast)) => Ok(new_ast),
+ (_, e) => return e,
+ }
+ }
+ Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) {
+ Err(ref e) if l.len() >= 3 => {
+ let exc = match e {
+ ErrMalVal(mv) => mv.clone(),
+ ErrString(s) => Str(s.to_string()),
+ };
+ match l[2].clone() {
+ List(c, _) => {
+ let catch_env = env_bind(
+ Some(env.clone()),
+ list!(vec![c[1].clone()]),
+ vec![exc],
+ )?;
+ eval(c[2].clone(), catch_env)
+ }
+ _ => error("invalid catch block"),
+ }
+ }
+ res => res,
+ },
+ Sym(ref a0sym) if a0sym == "do" => {
+ match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
+ List(_, _) => {
+ ast = l.last().unwrap_or(&Nil).clone();
+ continue 'tco;
+ }
+ _ => error("invalid do form"),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "if" => {
+ let cond = eval(l[1].clone(), env.clone())?;
+ match cond {
+ Bool(false) | Nil if l.len() >= 4 => {
+ ast = l[3].clone();
+ continue 'tco;
+ }
+ Bool(false) | Nil => Ok(Nil),
+ _ if l.len() >= 3 => {
+ ast = l[2].clone();
+ continue 'tco;
+ }
+ _ => Ok(Nil),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "fn*" => {
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ Ok(MalFunc {
+ eval: eval,
+ ast: Rc::new(a2),
+ env: env,
+ params: Rc::new(a1),
+ is_macro: false,
+ meta: Rc::new(Nil),
+ })
+ }
+ Sym(ref a0sym) if a0sym == "eval" => {
+ ast = eval(l[1].clone(), env.clone())?;
+ while let Some(ref e) = env.clone().outer {
+ env = e.clone();
+ }
+ continue 'tco;
+ }
+ _ => match eval_ast(&ast, &env)? {
+ List(ref el, _) => {
+ let ref f = el[0].clone();
+ let args = el[1..].to_vec();
+ match f {
+ Func(_, _) => f.apply(args),
+ MalFunc {
+ ast: mast,
+ env: menv,
+ params,
+ ..
+ } => {
+ let a = &**mast;
+ let p = &**params;
+ env = env_bind(Some(menv.clone()), p.clone(), args)?;
+ ast = a.clone();
+ continue 'tco;
+ }
+ _ => error("attempt to call non-function"),
+ }
+ }
+ _ => error("expected a list"),
+ },
}
}
- }
- RispExp::Func(_) => Err(RispErr::Reason("unexpected form".to_string())),
- RispExp::Lambda(_) => Err(RispErr::Reason("unexpected form".to_string())),
- }
+ _ => eval_ast(&ast, &env),
+ };
+
+ break;
+ } // end 'tco loop
+
+ ret
+}
+
+// print
+fn print(ast: &MalVal) -> String {
+ ast.pr_str(true)
+}
+
+fn rep(str: &str, env: &Env) -> Result {
+ let ast = read(str)?;
+ let exp = eval(ast, env.clone())?;
+ Ok(print(&exp))
}
fn main() {
- let env = &mut default_env();
- let mut reader = InPort::init("new.lisp".to_string());
- let mut token = String::new();
- read(&reader);
- // read from file
- // let file = File::open("new.lisp").unwrap();
- // let lines = io::BufReader::new(file).lines();
- // let mut cur_line = String::new();
- // let mut token = String::new();
- // for line in lines {
- // let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
- // println!("token {:?}", token);
- // println!("line {:?}", cur_line);
- // }
+ let mut args = std::env::args();
+ let arg1 = args.nth(1);
- /*
- loop {
- println!("risp >");
- let expr = slurp_expr();
- match parse_eval(expr, env) {
- Ok(res) => println!("// 🔥 => {}", res),
- Err(e) => match e {
- RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
- },
- }
+ // core.rs: defined using rust
+ let repl_env = env_new(None);
+ for (k, v) in core::ns() {
+ env_sets(&repl_env, k, v);
+ }
+ env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
+
+ // core.mal: defined using the language itself
+ let _ = rep("(def! *host-language* \"rust\")", &repl_env);
+ let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
+ let _ = rep(
+ "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
+ &repl_env,
+ );
+ let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
+
+ // Invoked with arguments
+ if let Some(f) = arg1 {
+ match rep(&format!("(load-file \"{}\")", f), &repl_env) {
+ Ok(_) => std::process::exit(0),
+ Err(e) => {
+ println!("Error: {}", format_error(e));
+ std::process::exit(1);
+ }
+ }
}
- */
}
diff --git a/lisp/zk.rkt b/lisp/zk.rkt
deleted file mode 100644
index 1c89ba1c4..000000000
--- a/lisp/zk.rkt
+++ /dev/null
@@ -1,141 +0,0 @@
-#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")
-)
-
-
From a8ca6b0c810fa3d637df6aafb4b9e9f4adc94602 Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 17:36:21 +0200
Subject: [PATCH 007/119] 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)
+}
From 10b140bf91c5bb09225ff616983260998918635b Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 19:01:50 +0200
Subject: [PATCH 008/119] upgrading jubjub to mal language
---
lisp/jubjub.lisp | 91 ++++++++++++++++++++++++------------------------
1 file changed, 46 insertions(+), 45 deletions(-)
diff --git a/lisp/jubjub.lisp b/lisp/jubjub.lisp
index 65e55e056..4058a193b 100644
--- a/lisp/jubjub.lisp
+++ b/lisp/jubjub.lisp
@@ -1,46 +1,47 @@
- (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))
- )
+ (def! D "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")
+ (def! ONE "0x0000000000000000000000000000000000000000000000000000000000000001")
+ (def! jj-add (fn* [x1 y1 x2 y2] (println x1 y1 x2 y2 D ONE)))
+ (jj-add 1 2 3 4)
+;; (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))
+;; )
From 1c0cc4721b3094d6737cd75f6f88b9f071271a57 Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 20:55:16 +0200
Subject: [PATCH 009/119] refactoring lisp code
---
lisp/jubjub.lisp | 52 +++++++++++++++---------------------------------
1 file changed, 16 insertions(+), 36 deletions(-)
diff --git a/lisp/jubjub.lisp b/lisp/jubjub.lisp
index 4058a193b..f06d59fdd 100644
--- a/lisp/jubjub.lisp
+++ b/lisp/jubjub.lisp
@@ -1,38 +1,19 @@
- (def! D "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")
- (def! ONE "0x0000000000000000000000000000000000000000000000000000000000000001")
- (def! jj-add (fn* [x1 y1 x2 y2] (println x1 y1 x2 y2 D ONE)))
- (jj-add 1 2 3 4)
-;; (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! D "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")
+(def! ONE "0x0000000000000000000000000000000000000000000000000000000000000001")
+(def! jj-add (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 (ONE A)))
+ (enforce (add-coeff-lc0 (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
@@ -44,4 +25,3 @@
;; (add_lc1_one)
;; (add_lc2 Py))
;; )
-
From eec38d5c3fb6aa85698e1932874031d8720d5012 Mon Sep 17 00:00:00 2001
From: ada
Date: Sat, 24 Oct 2020 20:56:42 +0200
Subject: [PATCH 010/119] added lib.rs and bits.lisp
---
lisp/Cargo.toml | 5 +++--
lisp/bits.lisp | 9 +++++++++
lisp/core.rs | 15 +++++++++++++++
lisp/lib.rs | 12 ++++++++++++
4 files changed, 39 insertions(+), 2 deletions(-)
create mode 100644 lisp/bits.lisp
create mode 100644 lisp/lib.rs
diff --git a/lisp/Cargo.toml b/lisp/Cargo.toml
index c4aca5fe1..c7685a7e3 100644
--- a/lisp/Cargo.toml
+++ b/lisp/Cargo.toml
@@ -1,14 +1,15 @@
[package]
-name = "mal"
+name = "zklisp"
version = "0.1.0"
authors = ["mileschet"]
[dependencies]
lazy_static = "1.4.0"
-
regex = "1.3.1"
itertools = "0.8.0"
fnv = "1.0.6"
+bellman = { version = "0.8", default-features = false, features = ["groth16"] }
+sapvi = { path = "../" }
[[bin]]
name = "lisp"
diff --git a/lisp/bits.lisp b/lisp/bits.lisp
new file mode 100644
index 000000000..5738d7013
--- /dev/null
+++ b/lisp/bits.lisp
@@ -0,0 +1,9 @@
+(def! bit-dec (fn* [x] (
+ (def! bits (unpack-bits x 256))
+ (def! enforce-step-1 (fn* [b] (enforce (add-one-lc0 (sub-lc0 b) (add-lc1 b))))
+ (map enforce-step-1 bits)
+ ;; this can be improved like declaring the function before using like the example above
+ (map (fn* [b] ((add-lc0 x) double-coeff-lc) bits)
+ (enforce reset-coeff-lc sub-lc0 add-one-lc1)
+ )))))
+
diff --git a/lisp/core.rs b/lisp/core.rs
index 547f578d1..f49b93836 100644
--- a/lisp/core.rs
+++ b/lisp/core.rs
@@ -153,6 +153,20 @@ fn nth(a: MalArgs) -> MalRet {
}
}
+// (unpack-bits x 256 it produces a Vec
+fn unpack_bits(a: MalArgs) -> MalRet {
+ // Scalar::from_string(
+ 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),
@@ -315,5 +329,6 @@ pub fn ns() -> Vec<(&'static str, MalVal)> {
("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()))),
+ ("unpack_bits", func(unpack_bits)),
]
}
diff --git a/lisp/lib.rs b/lisp/lib.rs
new file mode 100644
index 000000000..01a5b6fdb
--- /dev/null
+++ b/lisp/lib.rs
@@ -0,0 +1,12 @@
+use bellman::groth16;
+use bls12_381::{Bls12, Scalar};
+use std::collections::{HashMap, HashSet};
+
+pub use crate::bls_extensions::BlsStringConversion;
+pub use crate::error::{Error, Result};
+pub use crate::serial::{Decodable, Encodable};
+pub use crate::vm::{
+ AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef, ZKVMCircuit,
+ ZKVirtualMachine,
+};
+
From be8378d44d24ff1acb4af4badbb05ece1652ab49 Mon Sep 17 00:00:00 2001
From: ada
Date: Sun, 25 Oct 2020 21:15:44 +0100
Subject: [PATCH 011/119] update zklisp with cs methods
---
lisp/Cargo.toml | 16 ----------
lisp/bits.lisp | 16 +++++-----
lisp/core.rs | 80 +++++++++++++++++++++++++++++++++++++++++++------
lisp/lib.rs | 12 --------
lisp/new.lisp | 12 ++++++--
5 files changed, 89 insertions(+), 47 deletions(-)
delete mode 100644 lisp/Cargo.toml
delete mode 100644 lisp/lib.rs
diff --git a/lisp/Cargo.toml b/lisp/Cargo.toml
deleted file mode 100644
index c7685a7e3..000000000
--- a/lisp/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "zklisp"
-version = "0.1.0"
-authors = ["mileschet"]
-
-[dependencies]
-lazy_static = "1.4.0"
-regex = "1.3.1"
-itertools = "0.8.0"
-fnv = "1.0.6"
-bellman = { version = "0.8", default-features = false, features = ["groth16"] }
-sapvi = { path = "../" }
-
-[[bin]]
-name = "lisp"
-path = "lisp.rs"
diff --git a/lisp/bits.lisp b/lisp/bits.lisp
index 5738d7013..eb43aed21 100644
--- a/lisp/bits.lisp
+++ b/lisp/bits.lisp
@@ -1,9 +1,9 @@
-(def! bit-dec (fn* [x] (
- (def! bits (unpack-bits x 256))
- (def! enforce-step-1 (fn* [b] (enforce (add-one-lc0 (sub-lc0 b) (add-lc1 b))))
- (map enforce-step-1 bits)
- ;; this can be improved like declaring the function before using like the example above
- (map (fn* [b] ((add-lc0 x) double-coeff-lc) bits)
- (enforce reset-coeff-lc sub-lc0 add-one-lc1)
- )))))
+(def! bit-dec
+ (fn* [x] (
+ (def! bits (unpack-bits x 256))
+ (def! enforce-step-1 (fn* [b] (enforce (add-one-lc0 (sub-lc0 b) (add-lc1 b))))
+ (map enforce-step-1 bits)
+ (map (fn* [b] ((add-lc0 b) double-coeff-lc) bits)
+ (enforce reset-coeff-lc sub-lc0 add-one-lc1)
+ )))))
diff --git a/lisp/core.rs b/lisp/core.rs
index f49b93836..3f7a4ffb0 100644
--- a/lisp/core.rs
+++ b/lisp/core.rs
@@ -4,6 +4,25 @@ use std::rc::Rc;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
+use sapvi::bls_extensions::BlsStringConversion;
+use sapvi::error::{Error, Result};
+use sapvi::serial::{Decodable, Encodable};
+use sapvi::vm::{
+ AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef, ZKVMCircuit,
+ ZKVirtualMachine,
+};
+use bellman::{
+ gadgets::{
+ Assignment,
+ },
+ groth16, Circuit, ConstraintSystem, SynthesisError,
+};
+use bls12_381::Bls12;
+use bls12_381::Scalar;
+use ff::{Field, PrimeField};
+use rand::rngs::OsRng;
+use std::ops::{AddAssign, MulAssign, SubAssign};
+use std::time::Instant;
use crate::printer::pr_seq;
use crate::reader::read_str;
use crate::types::MalErr::ErrMalVal;
@@ -153,17 +172,20 @@ fn nth(a: MalArgs) -> MalRet {
}
}
-// (unpack-bits x 256 it produces a Vec
fn unpack_bits(a: MalArgs) -> MalRet {
- // Scalar::from_string(
+ let mut result = vec![];
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())
+ (Str(ref s), Int(size)) => {
+ let value = Scalar::from_string(s);
+ for (_, bit) in value.to_le_bits().into_iter().cloned().enumerate() {
+ match bit {
+ true => result.push(Scalar::one()),
+ false => result.push(Scalar::zero()),
+ }
+ }
+ Ok(list!(result.iter().map(|a| Str(std::string::ToString::to_string(&a))).collect::>()))
}
- _ => error("invalid args to nth"),
+ _ => error("invalid args to unpack-bits"),
}
}
@@ -243,6 +265,22 @@ fn seq(a: MalArgs) -> MalRet {
}
}
+fn add_one_lc0(a: MalArgs) -> MalRet {
+ Ok(Sym("add-one-lc0".to_string()))
+}
+
+fn enforce(a: MalArgs) -> MalRet {
+ Ok(Sym("enforce".to_string()))
+}
+
+fn double_coeff_lc(a: MalArgs) -> MalRet {
+ Ok(Sym("double-coeff-lc".to_string()))
+}
+
+fn reset_coeff_lc(a: MalArgs) -> MalRet {
+ Ok(Sym("reset-coeff-lc".to_string()))
+}
+
pub fn ns() -> Vec<(&'static str, MalVal)> {
vec![
("=", func(|a| Ok(Bool(a[0] == a[1])))),
@@ -329,6 +367,30 @@ pub fn ns() -> Vec<(&'static str, MalVal)> {
("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()))),
- ("unpack_bits", func(unpack_bits)),
+ ("unpack-bits", func(unpack_bits)),
+ ("add-lc0",
+ func(|a| {
+ println!("add-lc0 {}", pr_seq(&a, false, "", "", " "));
+ Ok(Nil)
+ })),
+ ("sub-lc0",
+ func(|a| {
+ println!("sub-lc0 {}", pr_seq(&a, false, "", "", " "));
+ Ok(Nil)
+ })),
+ ("enforce", func(enforce)),
+ ("double-coeff-lc", func(double_coeff_lc)),
+ ("reset-coeff-lc", func(double_coeff_lc)),
+ ("sub-lc0",
+ func(|a| {
+ println!("sub-lc0 {}", pr_seq(&a, false, "", "", " "));
+ Ok(Nil)
+ })),
+ ("add-one-lc0", func(add_one_lc0)),
+ ("add-lc1",
+ func(|a| {
+ println!("add-lc1 {}", pr_seq(&a, false, "", "", " "));
+ Ok(Nil)
+ })),
]
}
diff --git a/lisp/lib.rs b/lisp/lib.rs
deleted file mode 100644
index 01a5b6fdb..000000000
--- a/lisp/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use bellman::groth16;
-use bls12_381::{Bls12, Scalar};
-use std::collections::{HashMap, HashSet};
-
-pub use crate::bls_extensions::BlsStringConversion;
-pub use crate::error::{Error, Result};
-pub use crate::serial::{Decodable, Encodable};
-pub use crate::vm::{
- AllocType, ConstraintInstruction, CryptoOperation, VariableIndex, VariableRef, ZKVMCircuit,
- ZKVirtualMachine,
-};
-
diff --git a/lisp/new.lisp b/lisp/new.lisp
index a8162d468..e9e714f7c 100644
--- a/lisp/new.lisp
+++ b/lisp/new.lisp
@@ -1,2 +1,10 @@
-(def! C_D "0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1")
-(println C_D)
+
+(def! dec "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")
+(def! bits (unpack-bits dec 256))
+(println (count bits))
+(map (fn* [b] (println (add-one-lc0))) bits)
+
+;; (map (fn* [b] ((add-lc0 b) (double-coeff-lc))) bits)
+;; (reset-coeff-lc)
+;; (sub-lc0 x)
+;; (add-one-lc1)
From c304c68e3e465ab319ec207fdafb8f65e4270b6e Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 18:22:11 +0100
Subject: [PATCH 012/119] added zn* function definition
---
Cargo.toml | 4 ++++
lisp/core.rs | 40 ----------------------------------------
lisp/lisp.rs | 17 +++++++++++++++++
lisp/new.lisp | 26 +++++++++++++++++---------
lisp/printer.rs | 3 ++-
lisp/types.rs | 20 ++++++++++++++++++++
src/vm.rs | 2 +-
7 files changed, 61 insertions(+), 51 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 9ff6de47b..d4de5aaba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,6 +37,10 @@ failure_derive = "0.1.8"
regex = "1"
+lazy_static = "1.4.0"
+itertools = "0.8.0"
+fnv = "1.0.6"
+
[[bin]]
name = "sha256"
path = "src/sha256.rs"
diff --git a/lisp/core.rs b/lisp/core.rs
index 3f7a4ffb0..7674c81ec 100644
--- a/lisp/core.rs
+++ b/lisp/core.rs
@@ -265,22 +265,6 @@ fn seq(a: MalArgs) -> MalRet {
}
}
-fn add_one_lc0(a: MalArgs) -> MalRet {
- Ok(Sym("add-one-lc0".to_string()))
-}
-
-fn enforce(a: MalArgs) -> MalRet {
- Ok(Sym("enforce".to_string()))
-}
-
-fn double_coeff_lc(a: MalArgs) -> MalRet {
- Ok(Sym("double-coeff-lc".to_string()))
-}
-
-fn reset_coeff_lc(a: MalArgs) -> MalRet {
- Ok(Sym("reset-coeff-lc".to_string()))
-}
-
pub fn ns() -> Vec<(&'static str, MalVal)> {
vec![
("=", func(|a| Ok(Bool(a[0] == a[1])))),
@@ -368,29 +352,5 @@ pub fn ns() -> Vec<(&'static str, MalVal)> {
("reset!", func(|a| a[0].reset_bang(&a[1]))),
("swap!", func(|a| a[0].swap_bang(&a[1..].to_vec()))),
("unpack-bits", func(unpack_bits)),
- ("add-lc0",
- func(|a| {
- println!("add-lc0 {}", pr_seq(&a, false, "", "", " "));
- Ok(Nil)
- })),
- ("sub-lc0",
- func(|a| {
- println!("sub-lc0 {}", pr_seq(&a, false, "", "", " "));
- Ok(Nil)
- })),
- ("enforce", func(enforce)),
- ("double-coeff-lc", func(double_coeff_lc)),
- ("reset-coeff-lc", func(double_coeff_lc)),
- ("sub-lc0",
- func(|a| {
- println!("sub-lc0 {}", pr_seq(&a, false, "", "", " "));
- Ok(Nil)
- })),
- ("add-one-lc0", func(add_one_lc0)),
- ("add-lc1",
- func(|a| {
- println!("add-lc1 {}", pr_seq(&a, false, "", "", " "));
- Ok(Nil)
- })),
]
}
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index a3204f0fa..8dd766bca 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -23,6 +23,11 @@ use crate::env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
+// zk circuit
+fn zkcircuit_load(val: &MalVal) -> MalRet {
+ Ok(val.clone())
+}
+
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
@@ -258,6 +263,18 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
_ => Ok(Nil),
}
}
+ Sym(ref a0sym) if a0sym == "zk*" => {
+ // TODO create zk circuit and evaluate the rest
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ Ok(MalFunc {
+ eval: eval,
+ ast: Rc::new(a2),
+ env: env,
+ params: Rc::new(a1),
+ is_macro: false,
+ meta: Rc::new(Nil),
+ })
+ }
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
diff --git a/lisp/new.lisp b/lisp/new.lisp
index e9e714f7c..e255a2fe0 100644
--- a/lisp/new.lisp
+++ b/lisp/new.lisp
@@ -1,10 +1,18 @@
-
+(def! circuit (zk* [x] (
+ (def! bits (unpack-bits x 256))
+ (map (fn* [b] (println
+ '(add lc0 one)
+ '(sub b)
+ '(add lc1 x)
+ 'enforce)
+ ) bits)
+ (map (fn* [b] (println '(add lc0 b)
+ 'double-coeff-lc)
+ ) bits)
+ (println 'reset-coeff-lc
+ '(sub lc0 x)
+ '(add lc1 one)
+ 'enforce)
+)))
(def! dec "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")
-(def! bits (unpack-bits dec 256))
-(println (count bits))
-(map (fn* [b] (println (add-one-lc0))) bits)
-
-;; (map (fn* [b] ((add-lc0 b) (double-coeff-lc))) bits)
-;; (reset-coeff-lc)
-;; (sub-lc0 x)
-;; (add-one-lc1)
+(circuit dec)
diff --git a/lisp/printer.rs b/lisp/printer.rs
index 6cd4bc8b4..1b1fe33ff 100644
--- a/lisp/printer.rs
+++ b/lisp/printer.rs
@@ -1,5 +1,5 @@
use crate::types::MalVal;
-use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
+use crate::types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector, Zk};
fn escape_str(s: &str) -> String {
s.chars()
@@ -45,6 +45,7 @@ impl MalVal {
ast: a, params: p, ..
} => format!("(fn* {} {})", p.pr_str(true), a.pr_str(true)),
Atom(a) => format!("(atom {})", a.borrow().pr_str(true)),
+ Zk(a) => format!("ZKCircuit")
}
}
}
diff --git a/lisp/types.rs b/lisp/types.rs
index 108a646cf..07e8968bf 100644
--- a/lisp/types.rs
+++ b/lisp/types.rs
@@ -8,6 +8,17 @@ 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};
+use sapvi::{BlsStringConversion, Decodable, Encodable, ZKContract, ZKProof, ZKVMCircuit, ConstraintInstruction};
+use bls12_381::Scalar;
+
+use bellman::{
+ gadgets::{
+ boolean::{AllocatedBit, Boolean},
+ multipack, num, Assignment,
+ },
+ groth16, Circuit, ConstraintSystem, SynthesisError,
+};
+
#[derive(Debug, Clone)]
pub enum MalVal {
Nil,
@@ -29,6 +40,15 @@ pub enum MalVal {
meta: Rc,
},
Atom(Rc>),
+ Zk(ZKCircuit),
+}
+
+#[derive(Debug, Clone)]
+pub struct ZKCircuit {
+ name: String,
+ constraints: Vec,
+ private: Vec,
+ public: Vec,
}
#[derive(Debug)]
diff --git a/src/vm.rs b/src/vm.rs
index d8edcb417..ca134489d 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -57,7 +57,7 @@ pub enum AllocType {
Public,
}
-#[derive(Clone)]
+#[derive(Debug, Clone)]
pub enum ConstraintInstruction {
Lc0Add(VariableIndex),
Lc1Add(VariableIndex),
From 1154104ae5e212de422bd77d40cf7282479d5860 Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 18:28:23 +0100
Subject: [PATCH 013/119] added zn* function definition
---
lisp/lisp.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 8dd766bca..59c70eced 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -311,7 +311,8 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
ast = a.clone();
continue 'tco;
}
- _ => error("attempt to call non-function"),
+ _ => { println!("{:?}", el);
+ error("attempt to call non-function")},
}
}
_ => error("expected a list"),
From 4e620be9f132d7ec78861214c4bfa72d5d6eda10 Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 20:45:34 +0100
Subject: [PATCH 014/119] fn* for zk circuit
---
lisp/lisp.rs | 18 ++++--------------
lisp/new.lisp | 18 +++++++++---------
2 files changed, 13 insertions(+), 23 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 59c70eced..c48074d3a 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -263,18 +263,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
_ => Ok(Nil),
}
}
- Sym(ref a0sym) if a0sym == "zk*" => {
- // TODO create zk circuit and evaluate the rest
- let (a1, a2) = (l[1].clone(), l[2].clone());
- Ok(MalFunc {
- eval: eval,
- ast: Rc::new(a2),
- env: env,
- params: Rc::new(a1),
- is_macro: false,
- meta: Rc::new(Nil),
- })
- }
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
@@ -311,8 +299,10 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
ast = a.clone();
continue 'tco;
}
- _ => { println!("{:?}", el);
- error("attempt to call non-function")},
+ _ => {
+ Ok(Nil)
+ //error("attempt to call non-function")
+ },
}
}
_ => error("expected a list"),
diff --git a/lisp/new.lisp b/lisp/new.lisp
index e255a2fe0..0beb4f41c 100644
--- a/lisp/new.lisp
+++ b/lisp/new.lisp
@@ -1,18 +1,18 @@
-(def! circuit (zk* [x] (
+(def! circuit (fn* [x] (
(def! bits (unpack-bits x 256))
- (map (fn* [b] (println
+ (map (fn* [b] (println b
'(add lc0 one)
'(sub b)
'(add lc1 x)
'enforce)
) bits)
- (map (fn* [b] (println '(add lc0 b)
- 'double-coeff-lc)
- ) bits)
- (println 'reset-coeff-lc
- '(sub lc0 x)
- '(add lc1 one)
- 'enforce)
+ (map (fn* [b] (println b '(add lc0 b)
+ 'double-coeff-lc)
+ ) bits)
+ (println 'reset-coeff-lc
+ '(sub lc0 x)
+ '(add lc1 one)
+ 'enforce)
)))
(def! dec "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000")
(circuit dec)
From 53aef82501e177589600ca5d627987b934924da3 Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 21:07:08 +0100
Subject: [PATCH 015/119] added clap info
---
lisp/lisp.rs | 58 +++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 44 insertions(+), 14 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index c48074d3a..91ba1565d 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -1,10 +1,16 @@
#![allow(non_snake_case)]
-
+use simplelog::*;
+use std::fs;
+use std::fs::File;
+use std::time::Instant;
+use bls12_381::Scalar;
use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
use itertools::Itertools;
+#[macro_use]
+extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate fnv;
@@ -301,7 +307,6 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
}
_ => {
Ok(Nil)
- //error("attempt to call non-function")
},
}
}
@@ -329,18 +334,46 @@ fn rep(str: &str, env: &Env) -> Result {
Ok(print(&exp))
}
-fn main() {
- let mut args = std::env::args();
- let arg1 = args.nth(1);
+fn main() -> Result<(), ()> {
+
+ let matches = clap_app!(zklisp =>
+ (version: "0.1.0")
+ (author: "Roberto Santacroce Martins ")
+ (about: "A Lisp Interpreter for Zero Knowledge Virtual Machine")
+ (@subcommand load =>
+ (about: "Load the file into the interpreter")
+ (@arg FILE: +required "Lisp Contract filename")
+ )
+ )
+ .get_matches();
- // core.rs: defined using rust
+ CombinedLogger::init(vec![TermLogger::new(
+ LevelFilter::Debug,
+ Config::default(),
+ TerminalMode::Mixed,
+ )
+ .unwrap()])
+ .unwrap();
+
+ match matches.subcommand() {
+ Some(("load", matches)) => {
+ let file : String = matches.value_of("FILE").unwrap().parse().unwrap();
+ repl_load(file);
+ }
+ _ => {
+ eprintln!("error: Invalid subcommand invoked");
+ std::process::exit(-1);
+ }
+ }
+
+ Ok(())
+}
+
+fn repl_load(file: String) -> Result<(), ()> {
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
- env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
-
- // core.mal: defined using the language itself
let _ = rep("(def! *host-language* \"rust\")", &repl_env);
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
@@ -348,15 +381,12 @@ fn main() {
&repl_env,
);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
-
- // Invoked with arguments
- if let Some(f) = arg1 {
- match rep(&format!("(load-file \"{}\")", f), &repl_env) {
+ match rep(&format!("(load-file \"{}\")", file), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
- }
+ Ok(())
}
From 33210d7fe64f00169f38df4c34a45e7ebf0290fb Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 21:07:51 +0100
Subject: [PATCH 016/119] added TODO.md for lisp
---
lisp/TODO.md | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 lisp/TODO.md
diff --git a/lisp/TODO.md b/lisp/TODO.md
new file mode 100644
index 000000000..a3e614602
--- /dev/null
+++ b/lisp/TODO.md
@@ -0,0 +1,7 @@
+## TODO
+
+[] - Check for recursive
+[] - Disable functions not utilized
+[] - Document the language
+[] - Improve output for debug, log and error with ast and env dump
+[] - Integrate with zkvm command line
From 0c3a18a42292ed779c8623ce86bdf00c9b0c85d9 Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 22:34:03 +0100
Subject: [PATCH 017/119] added zk* function type to initiate the zkcircuit
---
lisp/lisp.rs | 13 +++++++++++++
lisp/new.lisp | 2 +-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 91ba1565d..ee5c4e9be 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -269,6 +269,19 @@ fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
_ => Ok(Nil),
}
}
+ Sym(ref a0sym) if a0sym == "zk*" => {
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ println!("---> {:?} {:?}", a1, a2);
+ // TODO add alloc name and nested eval to zkcircuit
+ Ok(MalFunc {
+ eval: eval,
+ ast: Rc::new(a2),
+ env: env,
+ params: Rc::new(a1),
+ is_macro: false,
+ meta: Rc::new(Nil),
+ })
+ }
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
diff --git a/lisp/new.lisp b/lisp/new.lisp
index 0beb4f41c..836245648 100644
--- a/lisp/new.lisp
+++ b/lisp/new.lisp
@@ -1,4 +1,4 @@
-(def! circuit (fn* [x] (
+(def! circuit (zk* [x] (
(def! bits (unpack-bits x 256))
(map (fn* [b] (println b
'(add lc0 one)
From ca399e318f7a14f132bf3cc8c40761b00dc906f6 Mon Sep 17 00:00:00 2001
From: ada
Date: Tue, 27 Oct 2020 22:38:38 +0100
Subject: [PATCH 018/119] added readme
---
lisp/TODO.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/lisp/TODO.md b/lisp/TODO.md
index a3e614602..dd1f665a9 100644
--- a/lisp/TODO.md
+++ b/lisp/TODO.md
@@ -5,3 +5,4 @@
[] - Document the language
[] - Improve output for debug, log and error with ast and env dump
[] - Integrate with zkvm command line
+[] - Integrate with ZKVMCircuit: allocs and constraints
From 13366f74c1a58c7bb3b4a8cc04230eeeffcb2df7 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:46:22 +0200
Subject: [PATCH 019/119] prototyping lisp v0
---
Cargo.toml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Cargo.toml b/Cargo.toml
index 25acfdeee..6e3f5b2ee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,9 @@ clap = "3.0.0-beta.1"
failure = "0.1.8"
failure_derive = "0.1.8"
+regex = "1"
+fancy-regex = "0.4.0"
+
[[bin]]
name = "sha256"
path = "src/sha256.rs"
@@ -107,3 +110,6 @@ path = "src/bin/mint.rs"
name = "jubjub"
path = "src/bin/jubjub.rs"
+[[bin]]
+name = "lisp"
+path = "lisp/lisp.rs"
From 2499b47560869c941a30118654dc1ecec163c7ae Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:46:31 +0200
Subject: [PATCH 020/119] prototyping lisp v0
---
lisp/lisp.rs | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 421 insertions(+)
create mode 100644 lisp/lisp.rs
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
new file mode 100644
index 000000000..74702f66a
--- /dev/null
+++ b/lisp/lisp.rs
@@ -0,0 +1,421 @@
+use std::collections::HashMap;
+
+use fancy_regex::Regex;
+use std::fmt;
+use std::fs::File;
+use std::io;
+use std::io::{BufRead, BufReader};
+use std::num::ParseFloatError;
+use std::rc::Rc;
+/*
+ Types
+*/
+
+#[derive(Clone)]
+enum RispExp {
+ Bool(bool),
+ Symbol(String),
+ Number(f64),
+ List(Vec),
+ Func(fn(&[RispExp]) -> Result),
+ Lambda(RispLambda),
+}
+
+#[derive(Clone)]
+struct RispLambda {
+ params_exp: Rc,
+ body_exp: Rc,
+}
+
+impl fmt::Display for RispExp {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let str = match self {
+ RispExp::Bool(a) => a.to_string(),
+ RispExp::Symbol(s) => s.clone(),
+ RispExp::Number(n) => n.to_string(),
+ RispExp::List(list) => {
+ let xs: Vec = list.iter().map(|x| x.to_string()).collect();
+ format!("({})", xs.join(","))
+ }
+ RispExp::Func(_) => "Function {}".to_string(),
+ RispExp::Lambda(_) => "Lambda {}".to_string(),
+ };
+
+ write!(f, "{}", str)
+ }
+}
+
+#[derive(Debug)]
+enum RispErr {
+ Reason(String),
+}
+
+#[derive(Clone)]
+struct RispEnv<'a> {
+ data: HashMap,
+ outer: Option<&'a RispEnv<'a>>,
+}
+
+/*
+ InPort
+*/
+// TODO change to symbol(eof)
+
+struct InPort {
+ line: String,
+ lines: Vec,
+ cur_line: usize
+}
+
+impl InPort {
+
+ fn init(filename: String) -> InPort {
+ let file = File::open(filename).unwrap();
+ let reader = io::BufReader::new(file).lines().map(|s| s.ok().unwrap().to_string()).collect();
+ InPort {
+ line : String::new(),
+ lines: reader,
+ cur_line: 0
+ }
+ }
+
+ pub fn next_token(&mut self) -> String {
+ loop {
+ if self.line.is_empty() || self.line == "#".to_string() {
+ // TODO add match
+ self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
+ self.cur_line = self.cur_line + 1;
+ }
+ if self.line.is_empty() {
+ self.cur_line = self.cur_line + 1;
+ return "#".to_string();
+ }
+ let (token, rest) = InPort::tokenize(self.line.clone());
+ self.line = rest;
+ self.cur_line = self.cur_line + 1;
+ if !token.is_empty() {
+ return token;
+ }
+ }
+ }
+
+ fn tokenize(expr: String) -> (String, String) {
+ let re =
+ Regex::new(r#"\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"#).unwrap();
+ let result = re.captures(&expr);
+ let captures = result
+ .expect("Error running regex")
+ .expect("No match found");
+// println!("{}", captures.get(0).unwrap().as_str().to_string());
+ (
+ captures.get(1).unwrap().as_str().to_string(),
+ captures.get(2).unwrap().as_str().to_string(),
+ )
+ }
+}
+
+fn parse_atom(token: &str) -> RispExp {
+ match token.as_ref() {
+ "true" => RispExp::Bool(true),
+ "false" => RispExp::Bool(false),
+ _ => {
+ let potential_float: Result = token.parse();
+ match potential_float {
+ Ok(v) => RispExp::Number(v),
+ Err(_) => RispExp::Symbol(token.to_string().clone()),
+ }
+ }
+ }
+}
+
+/*
+ Env
+*/
+
+macro_rules! ensure_tonicity {
+ ($check_fn:expr) => {{
+ |args: &[RispExp]| -> Result {
+ let floats = parse_list_of_floats(args)?;
+ let first = floats
+ .first()
+ .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
+ let rest = &floats[1..];
+ fn f(prev: &f64, xs: &[f64]) -> bool {
+ match xs.first() {
+ Some(x) => $check_fn(prev, x) && f(x, &xs[1..]),
+ None => true,
+ }
+ };
+ Ok(RispExp::Bool(f(first, rest)))
+ }
+ }};
+}
+
+fn default_env<'a>() -> RispEnv<'a> {
+ let mut data: HashMap = HashMap::new();
+ data.insert(
+ "+".to_string(),
+ RispExp::Func(|args: &[RispExp]| -> Result {
+ let sum = parse_list_of_floats(args)?
+ .iter()
+ .fold(0.0, |sum, a| sum + a);
+
+ Ok(RispExp::Number(sum))
+ }),
+ );
+ data.insert(
+ "-".to_string(),
+ RispExp::Func(|args: &[RispExp]| -> Result {
+ let floats = parse_list_of_floats(args)?;
+ let first = *floats
+ .first()
+ .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
+ let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
+
+ Ok(RispExp::Number(first - sum_of_rest))
+ }),
+ );
+ data.insert(
+ "=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a == b)),
+ );
+ data.insert(
+ ">".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a > b)),
+ );
+ data.insert(
+ ">=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a >= b)),
+ );
+ data.insert(
+ "<".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a < b)),
+ );
+ data.insert(
+ "<=".to_string(),
+ RispExp::Func(ensure_tonicity!(|a, b| a <= b)),
+ );
+
+ RispEnv { data, outer: None }
+}
+
+fn parse_list_of_floats(args: &[RispExp]) -> Result, RispErr> {
+ args.iter().map(|x| parse_single_float(x)).collect()
+}
+
+fn parse_single_float(exp: &RispExp) -> Result {
+ match exp {
+ RispExp::Number(num) => Ok(*num),
+ _ => Err(RispErr::Reason("expected a number".to_string())),
+ }
+}
+
+/*
+ Eval
+*/
+
+fn eval_if_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
+ let test_form = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected test form".to_string()))?;
+ let test_eval = eval(test_form, env)?;
+ match test_eval {
+ RispExp::Bool(b) => {
+ let form_idx = if b { 1 } else { 2 };
+ let res_form = arg_forms
+ .get(form_idx)
+ .ok_or(RispErr::Reason(format!("expected form idx={}", form_idx)))?;
+ let res_eval = eval(res_form, env);
+
+ res_eval
+ }
+ _ => Err(RispErr::Reason(format!(
+ "unexpected test form='{}'",
+ test_form.to_string()
+ ))),
+ }
+}
+
+fn eval_def_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
+ let first_form = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected first form".to_string()))?;
+ let first_str = match first_form {
+ RispExp::Symbol(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected first form to be a symbol".to_string(),
+ )),
+ }?;
+ let second_form = arg_forms
+ .get(1)
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
+ if arg_forms.len() > 2 {
+ return Err(RispErr::Reason("def can only have two forms ".to_string()));
+ }
+ let second_eval = eval(second_form, env)?;
+ env.data.insert(first_str, second_eval);
+
+ Ok(first_form.clone())
+}
+
+fn eval_lambda_args(arg_forms: &[RispExp]) -> Result {
+ let params_exp = arg_forms
+ .first()
+ .ok_or(RispErr::Reason("expected args form".to_string()))?;
+ let body_exp = arg_forms
+ .get(1)
+ .ok_or(RispErr::Reason("expected second form".to_string()))?;
+ if arg_forms.len() > 2 {
+ return Err(RispErr::Reason(
+ "fn definition can only have two forms ".to_string(),
+ ));
+ }
+
+ Ok(RispExp::Lambda(RispLambda {
+ body_exp: Rc::new(body_exp.clone()),
+ params_exp: Rc::new(params_exp.clone()),
+ }))
+}
+
+fn eval_built_in_form(
+ exp: &RispExp,
+ arg_forms: &[RispExp],
+ env: &mut RispEnv,
+) -> Option> {
+ match exp {
+ RispExp::Symbol(s) => match s.as_ref() {
+ "if" => Some(eval_if_args(arg_forms, env)),
+ "def" => Some(eval_def_args(arg_forms, env)),
+ "fn" => Some(eval_lambda_args(arg_forms)),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+fn env_get(k: &str, env: &RispEnv) -> Option {
+ match env.data.get(k) {
+ Some(exp) => Some(exp.clone()),
+ None => match &env.outer {
+ Some(outer_env) => env_get(k, &outer_env),
+ None => None,
+ },
+ }
+}
+
+fn parse_list_of_symbol_strings(form: Rc) -> Result, RispErr> {
+ let list = match form.as_ref() {
+ RispExp::List(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected args form to be a list".to_string(),
+ )),
+ }?;
+ list.iter()
+ .map(|x| match x {
+ RispExp::Symbol(s) => Ok(s.clone()),
+ _ => Err(RispErr::Reason(
+ "expected symbols in the argument list".to_string(),
+ )),
+ })
+ .collect()
+}
+
+fn env_for_lambda<'a>(
+ params: Rc,
+ arg_forms: &[RispExp],
+ outer_env: &'a mut RispEnv,
+) -> Result, RispErr> {
+ let ks = parse_list_of_symbol_strings(params)?;
+ if ks.len() != arg_forms.len() {
+ return Err(RispErr::Reason(format!(
+ "expected {} arguments, got {}",
+ ks.len(),
+ arg_forms.len()
+ )));
+ }
+ let vs = eval_forms(arg_forms, outer_env)?;
+ let mut data: HashMap = HashMap::new();
+ for (k, v) in ks.iter().zip(vs.iter()) {
+ data.insert(k.clone(), v.clone());
+ }
+ Ok(RispEnv {
+ data,
+ outer: Some(outer_env),
+ })
+}
+
+fn eval_forms(arg_forms: &[RispExp], env: &mut RispEnv) -> Result, RispErr> {
+ arg_forms.iter().map(|x| eval(x, env)).collect()
+}
+
+fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
+ match exp {
+ RispExp::Symbol(k) => {
+ env_get(k, env).ok_or(RispErr::Reason(format!("unexpected symbol k='{}'", k)))
+ }
+ RispExp::Bool(_a) => Ok(exp.clone()),
+ RispExp::Number(_a) => Ok(exp.clone()),
+
+ RispExp::List(list) => {
+ let first_form = list
+ .first()
+ .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?;
+ let arg_forms = &list[1..];
+ match eval_built_in_form(first_form, arg_forms, env) {
+ Some(res) => res,
+ None => {
+ let first_eval = eval(first_form, env)?;
+ match first_eval {
+ RispExp::Func(f) => f(&eval_forms(arg_forms, env)?),
+ RispExp::Lambda(lambda) => {
+ let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?;
+ eval(&lambda.body_exp, new_env)
+ }
+ _ => Err(RispErr::Reason("first form must be a function".to_string())),
+ }
+ }
+ }
+ }
+ RispExp::Func(_) => Err(RispErr::Reason("unexpected form".to_string())),
+ RispExp::Lambda(_) => Err(RispErr::Reason("unexpected form".to_string())),
+ }
+}
+
+
+fn main() {
+ let env = &mut default_env();
+ let mut reader = InPort::init("new.lisp".to_string());
+ let mut token = String::new();
+ loop {
+ if token == "#".to_string() {
+ println!("EOF");
+ break;
+ }
+
+ token = reader.next_token().as_str().to_string();
+ println!("{}", token);
+ }
+ // read from file
+// let file = File::open("new.lisp").unwrap();
+// let lines = io::BufReader::new(file).lines();
+// let mut cur_line = String::new();
+// let mut token = String::new();
+// for line in lines {
+// let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
+// println!("token {:?}", token);
+// println!("line {:?}", cur_line);
+ // }
+
+ /*
+ loop {
+ println!("risp >");
+ let expr = slurp_expr();
+ match parse_eval(expr, env) {
+ Ok(res) => println!("// 🔥 => {}", res),
+ Err(e) => match e {
+ RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
+ },
+ }
+ }
+ */
+}
From 8ad7001664a388161b0311dca6c22897a0c74d71 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 19:54:24 +0200
Subject: [PATCH 021/119] prototyping lisp v0
---
lisp/lisp.rs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 74702f66a..197bfe4aa 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -81,18 +81,15 @@ impl InPort {
pub fn next_token(&mut self) -> String {
loop {
- if self.line.is_empty() || self.line == "#".to_string() {
- // TODO add match
+ if self.line.is_empty() {
self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
self.cur_line = self.cur_line + 1;
}
if self.line.is_empty() {
- self.cur_line = self.cur_line + 1;
return "#".to_string();
}
let (token, rest) = InPort::tokenize(self.line.clone());
self.line = rest;
- self.cur_line = self.cur_line + 1;
if !token.is_empty() {
return token;
}
From 2a198b8a75bcd664c052c965bd1ec0e61ef39e37 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 20:22:40 +0200
Subject: [PATCH 022/119] prototyping lisp v0
---
lisp/lisp.rs | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index 197bfe4aa..b8df722a1 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -111,6 +111,30 @@ impl InPort {
}
}
+// TODO change return type to Symbol
+fn read(inport: InPort) -> String {
+ fn read_ahead(token: String) {
+ match token {
+ "(" => {
+ let L = Vec::new();
+ loop {
+ let token = inport.next_token();
+ if token == ")" {
+ return L;
+ } else {
+ L.append(read_ahead(token));
+ }
+ }
+ },
+ _ => { println!("{}", token); }
+ }
+ }
+ let token1 = inport.next_token();
+ if token1 == "#".to_string() {
+ return "#".to_string();
+ }
+}
+
fn parse_atom(token: &str) -> RispExp {
match token.as_ref() {
"true" => RispExp::Bool(true),
@@ -383,15 +407,7 @@ fn main() {
let env = &mut default_env();
let mut reader = InPort::init("new.lisp".to_string());
let mut token = String::new();
- loop {
- if token == "#".to_string() {
- println!("EOF");
- break;
- }
-
- token = reader.next_token().as_str().to_string();
- println!("{}", token);
- }
+ read(reader);
// read from file
// let file = File::open("new.lisp").unwrap();
// let lines = io::BufReader::new(file).lines();
From 5153df63c1675f8c6190f76854ff5cee24b4efd4 Mon Sep 17 00:00:00 2001
From: ada
Date: Thu, 22 Oct 2020 21:18:22 +0200
Subject: [PATCH 023/119] exploring read function
---
lisp/lisp.rs | 118 +++++++++++++++++++++++++++++++--------------------
1 file changed, 72 insertions(+), 46 deletions(-)
diff --git a/lisp/lisp.rs b/lisp/lisp.rs
index b8df722a1..7786e8276 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -63,36 +63,42 @@ struct RispEnv<'a> {
struct InPort {
line: String,
- lines: Vec,
- cur_line: usize
+ lines: Vec,
+ cur_line: usize,
}
impl InPort {
-
fn init(filename: String) -> InPort {
let file = File::open(filename).unwrap();
- let reader = io::BufReader::new(file).lines().map(|s| s.ok().unwrap().to_string()).collect();
- InPort {
- line : String::new(),
+ let reader = io::BufReader::new(file)
+ .lines()
+ .map(|s| s.ok().unwrap().to_string())
+ .collect();
+ InPort {
+ line: String::new(),
lines: reader,
- cur_line: 0
+ cur_line: 0,
}
}
pub fn next_token(&mut self) -> String {
loop {
- if self.line.is_empty() {
- self.line = self.lines.get(self.cur_line).unwrap_or(&"#".to_string()).to_string();
- self.cur_line = self.cur_line + 1;
- }
- if self.line.is_empty() {
- return "#".to_string();
- }
- let (token, rest) = InPort::tokenize(self.line.clone());
- self.line = rest;
- if !token.is_empty() {
- return token;
- }
+ if self.line.is_empty() {
+ self.line = self
+ .lines
+ .get(self.cur_line)
+ .unwrap_or(&"#".to_string())
+ .to_string();
+ self.cur_line = self.cur_line + 1;
+ }
+ if self.line.is_empty() {
+ return "#".to_string();
+ }
+ let (token, rest) = InPort::tokenize(self.line.clone());
+ self.line = rest;
+ if !token.is_empty() {
+ return token;
+ }
}
}
@@ -103,7 +109,7 @@ impl InPort {
let captures = result
.expect("Error running regex")
.expect("No match found");
-// println!("{}", captures.get(0).unwrap().as_str().to_string());
+ // println!("{}", captures.get(0).unwrap().as_str().to_string());
(
captures.get(1).unwrap().as_str().to_string(),
captures.get(2).unwrap().as_str().to_string(),
@@ -111,27 +117,48 @@ impl InPort {
}
}
-// TODO change return type to Symbol
-fn read(inport: InPort) -> String {
- fn read_ahead(token: String) {
- match token {
- "(" => {
- let L = Vec::new();
- loop {
- let token = inport.next_token();
- if token == ")" {
- return L;
+fn read_ahead(inport: &InPort, token: String) -> String {
+ match token.as_str() {
+ "(" => {
+ let mut L = Vec::new();
+ loop {
+ let t = &inport.next_token();
+ if t.to_string() == ")" {
+ return L.into_iter().collect();
} else {
- L.append(read_ahead(token));
+ L.push(read_ahead(&inport, t.to_string()));
}
}
- },
- _ => { println!("{}", token); }
+ }
+ _ => {
+ println!("{}", token);
+ return token;
}
}
- let token1 = inport.next_token();
- if token1 == "#".to_string() {
+}
+
+fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
+ let mut res: Vec = vec![];
+ let mut xs = tokens;
+ loop {
+ let (next_token, rest) = xs
+ .split_first()
+ .ok_or(RispErr::Reason("could not find closing `)`".to_string()))?;
+ if next_token == ")" {
+ return Ok((RispExp::List(res), rest)); // skip `)`, head to the token after
+ }
+ let (exp, new_xs) = parse(&xs)?;
+ res.push(exp);
+ xs = new_xs;
+ }
+}
+// TODO change return type to Symbol
+fn read(inport: &InPort) -> String {
+ let token1 = &inport.next_token().as_str();
+ if token1.to_string() == "#".to_string() {
return "#".to_string();
+ } else {
+ return read_ahead(&inport, token1.to_string());
}
}
@@ -402,22 +429,21 @@ fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
}
}
-
fn main() {
let env = &mut default_env();
let mut reader = InPort::init("new.lisp".to_string());
let mut token = String::new();
- read(reader);
+ read(&reader);
// read from file
-// let file = File::open("new.lisp").unwrap();
-// let lines = io::BufReader::new(file).lines();
-// let mut cur_line = String::new();
-// let mut token = String::new();
-// for line in lines {
-// let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
-// println!("token {:?}", token);
-// println!("line {:?}", cur_line);
- // }
+ // let file = File::open("new.lisp").unwrap();
+ // let lines = io::BufReader::new(file).lines();
+ // let mut cur_line = String::new();
+ // let mut token = String::new();
+ // for line in lines {
+ // let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
+ // println!("token {:?}", token);
+ // println!("line {:?}", cur_line);
+ // }
/*
loop {
From e27ac3d452ba360762997263cdd949b239d7250f Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 17:30:53 +0200
Subject: [PATCH 024/119] added mal lisp and jubjub unfinished sample
---
Cargo.toml | 1 -
lisp/jj.rkt | 113 --------
lisp/lisp.rs | 740 ++++++++++++++++++++++-----------------------------
lisp/zk.rkt | 141 ----------
4 files changed, 317 insertions(+), 678 deletions(-)
delete mode 100644 lisp/jj.rkt
delete mode 100644 lisp/zk.rkt
diff --git a/Cargo.toml b/Cargo.toml
index 6e3f5b2ee..b0840ec96 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,7 +36,6 @@ failure = "0.1.8"
failure_derive = "0.1.8"
regex = "1"
-fancy-regex = "0.4.0"
[[bin]]
name = "sha256"
diff --git a/lisp/jj.rkt b/lisp/jj.rkt
deleted file mode 100644
index 5e79f16a0..000000000
--- a/lisp/jj.rkt
+++ /dev/null
@@ -1,113 +0,0 @@
-#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/lisp.rs b/lisp/lisp.rs
index 7786e8276..a3204f0fa 100644
--- a/lisp/lisp.rs
+++ b/lisp/lisp.rs
@@ -1,460 +1,354 @@
-use std::collections::HashMap;
+#![allow(non_snake_case)]
-use fancy_regex::Regex;
-use std::fmt;
-use std::fs::File;
-use std::io;
-use std::io::{BufRead, BufReader};
-use std::num::ParseFloatError;
use std::rc::Rc;
-/*
- Types
-*/
+//use std::collections::HashMap;
+use fnv::FnvHashMap;
+use itertools::Itertools;
-#[derive(Clone)]
-enum RispExp {
- Bool(bool),
- Symbol(String),
- Number(f64),
- List(Vec),
- Func(fn(&[RispExp]) -> Result),
- Lambda(RispLambda),
+#[macro_use]
+extern crate lazy_static;
+extern crate fnv;
+extern crate itertools;
+extern crate regex;
+
+#[macro_use]
+mod types;
+use crate::types::MalErr::{ErrMalVal, ErrString};
+use crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
+use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
+mod env;
+mod printer;
+mod reader;
+use crate::env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
+#[macro_use]
+mod core;
+
+// read
+fn read(str: &str) -> MalRet {
+ reader::read_str(str.to_string())
}
-#[derive(Clone)]
-struct RispLambda {
- params_exp: Rc,
- body_exp: Rc,
-}
+// eval
-impl fmt::Display for RispExp {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let str = match self {
- RispExp::Bool(a) => a.to_string(),
- RispExp::Symbol(s) => s.clone(),
- RispExp::Number(n) => n.to_string(),
- RispExp::List(list) => {
- let xs: Vec = list.iter().map(|x| x.to_string()).collect();
- format!("({})", xs.join(","))
- }
- RispExp::Func(_) => "Function {}".to_string(),
- RispExp::Lambda(_) => "Lambda {}".to_string(),
- };
-
- write!(f, "{}", str)
- }
-}
-
-#[derive(Debug)]
-enum RispErr {
- Reason(String),
-}
-
-#[derive(Clone)]
-struct RispEnv<'a> {
- data: HashMap,
- outer: Option<&'a RispEnv<'a>>,
-}
-
-/*
- InPort
-*/
-// TODO change to symbol(eof)
-
-struct InPort {
- line: String,
- lines: Vec,
- cur_line: usize,
-}
-
-impl InPort {
- fn init(filename: String) -> InPort {
- let file = File::open(filename).unwrap();
- let reader = io::BufReader::new(file)
- .lines()
- .map(|s| s.ok().unwrap().to_string())
- .collect();
- InPort {
- line: String::new(),
- lines: reader,
- cur_line: 0,
- }
- }
-
- pub fn next_token(&mut self) -> String {
- loop {
- if self.line.is_empty() {
- self.line = self
- .lines
- .get(self.cur_line)
- .unwrap_or(&"#".to_string())
- .to_string();
- self.cur_line = self.cur_line + 1;
- }
- if self.line.is_empty() {
- return "#".to_string();
- }
- let (token, rest) = InPort::tokenize(self.line.clone());
- self.line = rest;
- if !token.is_empty() {
- return token;
- }
- }
- }
-
- fn tokenize(expr: String) -> (String, String) {
- let re =
- Regex::new(r#"\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"#).unwrap();
- let result = re.captures(&expr);
- let captures = result
- .expect("Error running regex")
- .expect("No match found");
- // println!("{}", captures.get(0).unwrap().as_str().to_string());
- (
- captures.get(1).unwrap().as_str().to_string(),
- captures.get(2).unwrap().as_str().to_string(),
- )
- }
-}
-
-fn read_ahead(inport: &InPort, token: String) -> String {
- match token.as_str() {
- "(" => {
- let mut L = Vec::new();
- loop {
- let t = &inport.next_token();
- if t.to_string() == ")" {
- return L.into_iter().collect();
- } else {
- L.push(read_ahead(&inport, t.to_string()));
+fn qq_iter(elts: &MalArgs) -> MalVal {
+ let mut acc = list![];
+ for elt in elts.iter().rev() {
+ if let List(v, _) = elt {
+ if v.len() == 2 {
+ if let Sym(ref s) = v[0] {
+ if s == "splice-unquote" {
+ acc = list![Sym("concat".to_string()), v[1].clone(), acc];
+ continue;
}
}
}
- _ => {
- println!("{}", token);
- return token;
}
+ acc = list![Sym("cons".to_string()), quasiquote(&elt), acc];
}
+ return acc;
}
-fn read_seq<'a>(tokens: &'a [String]) -> Result<(RispExp, &'a [String]), RispErr> {
- let mut res: Vec = vec![];
- let mut xs = tokens;
- loop {
- let (next_token, rest) = xs
- .split_first()
- .ok_or(RispErr::Reason("could not find closing `)`".to_string()))?;
- if next_token == ")" {
- return Ok((RispExp::List(res), rest)); // skip `)`, head to the token after
- }
- let (exp, new_xs) = parse(&xs)?;
- res.push(exp);
- xs = new_xs;
- }
-}
-// TODO change return type to Symbol
-fn read(inport: &InPort) -> String {
- let token1 = &inport.next_token().as_str();
- if token1.to_string() == "#".to_string() {
- return "#".to_string();
- } else {
- return read_ahead(&inport, token1.to_string());
- }
-}
-
-fn parse_atom(token: &str) -> RispExp {
- match token.as_ref() {
- "true" => RispExp::Bool(true),
- "false" => RispExp::Bool(false),
- _ => {
- let potential_float: Result = token.parse();
- match potential_float {
- Ok(v) => RispExp::Number(v),
- Err(_) => RispExp::Symbol(token.to_string().clone()),
- }
- }
- }
-}
-
-/*
- Env
-*/
-
-macro_rules! ensure_tonicity {
- ($check_fn:expr) => {{
- |args: &[RispExp]| -> Result {
- let floats = parse_list_of_floats(args)?;
- let first = floats
- .first()
- .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
- let rest = &floats[1..];
- fn f(prev: &f64, xs: &[f64]) -> bool {
- match xs.first() {
- Some(x) => $check_fn(prev, x) && f(x, &xs[1..]),
- None => true,
+fn quasiquote(ast: &MalVal) -> MalVal {
+ match ast {
+ List(v, _) => {
+ if v.len() == 2 {
+ if let Sym(ref s) = v[0] {
+ if s == "unquote" {
+ return v[1].clone();
+ }
}
- };
- Ok(RispExp::Bool(f(first, rest)))
- }
- }};
-}
-
-fn default_env<'a>() -> RispEnv<'a> {
- let mut data: HashMap = HashMap::new();
- data.insert(
- "+".to_string(),
- RispExp::Func(|args: &[RispExp]| -> Result {
- let sum = parse_list_of_floats(args)?
- .iter()
- .fold(0.0, |sum, a| sum + a);
-
- Ok(RispExp::Number(sum))
- }),
- );
- data.insert(
- "-".to_string(),
- RispExp::Func(|args: &[RispExp]| -> Result {
- let floats = parse_list_of_floats(args)?;
- let first = *floats
- .first()
- .ok_or(RispErr::Reason("expected at least one number".to_string()))?;
- let sum_of_rest = floats[1..].iter().fold(0.0, |sum, a| sum + a);
-
- Ok(RispExp::Number(first - sum_of_rest))
- }),
- );
- data.insert(
- "=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a == b)),
- );
- data.insert(
- ">".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a > b)),
- );
- data.insert(
- ">=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a >= b)),
- );
- data.insert(
- "<".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a < b)),
- );
- data.insert(
- "<=".to_string(),
- RispExp::Func(ensure_tonicity!(|a, b| a <= b)),
- );
-
- RispEnv { data, outer: None }
-}
-
-fn parse_list_of_floats(args: &[RispExp]) -> Result, RispErr> {
- args.iter().map(|x| parse_single_float(x)).collect()
-}
-
-fn parse_single_float(exp: &RispExp) -> Result {
- match exp {
- RispExp::Number(num) => Ok(*num),
- _ => Err(RispErr::Reason("expected a number".to_string())),
+ }
+ return qq_iter(&v);
+ },
+ Vector(v, _) => return list![Sym("vec".to_string()), qq_iter(&v)],
+ Hash(_, _) | Sym(_)=> return list![Sym("quote".to_string()), ast.clone()],
+ _ => ast.clone(),
}
}
-/*
- Eval
-*/
-
-fn eval_if_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
- let test_form = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected test form".to_string()))?;
- let test_eval = eval(test_form, env)?;
- match test_eval {
- RispExp::Bool(b) => {
- let form_idx = if b { 1 } else { 2 };
- let res_form = arg_forms
- .get(form_idx)
- .ok_or(RispErr::Reason(format!("expected form idx={}", form_idx)))?;
- let res_eval = eval(res_form, env);
-
- res_eval
- }
- _ => Err(RispErr::Reason(format!(
- "unexpected test form='{}'",
- test_form.to_string()
- ))),
- }
-}
-
-fn eval_def_args(arg_forms: &[RispExp], env: &mut RispEnv) -> Result {
- let first_form = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected first form".to_string()))?;
- let first_str = match first_form {
- RispExp::Symbol(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected first form to be a symbol".to_string(),
- )),
- }?;
- let second_form = arg_forms
- .get(1)
- .ok_or(RispErr::Reason("expected second form".to_string()))?;
- if arg_forms.len() > 2 {
- return Err(RispErr::Reason("def can only have two forms ".to_string()));
- }
- let second_eval = eval(second_form, env)?;
- env.data.insert(first_str, second_eval);
-
- Ok(first_form.clone())
-}
-
-fn eval_lambda_args(arg_forms: &[RispExp]) -> Result {
- let params_exp = arg_forms
- .first()
- .ok_or(RispErr::Reason("expected args form".to_string()))?;
- let body_exp = arg_forms
- .get(1)
- .ok_or(RispErr::Reason("expected second form".to_string()))?;
- if arg_forms.len() > 2 {
- return Err(RispErr::Reason(
- "fn definition can only have two forms ".to_string(),
- ));
- }
-
- Ok(RispExp::Lambda(RispLambda {
- body_exp: Rc::new(body_exp.clone()),
- params_exp: Rc::new(params_exp.clone()),
- }))
-}
-
-fn eval_built_in_form(
- exp: &RispExp,
- arg_forms: &[RispExp],
- env: &mut RispEnv,
-) -> Option> {
- match exp {
- RispExp::Symbol(s) => match s.as_ref() {
- "if" => Some(eval_if_args(arg_forms, env)),
- "def" => Some(eval_def_args(arg_forms, env)),
- "fn" => Some(eval_lambda_args(arg_forms)),
+fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
+ match ast {
+ List(v, _) => match v[0] {
+ Sym(ref s) => match env_find(env, s) {
+ Some(e) => match env_get(&e, &v[0]) {
+ Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
+ _ => None,
+ },
+ _ => None,
+ },
_ => None,
},
_ => None,
}
}
-fn env_get(k: &str, env: &RispEnv) -> Option {
- match env.data.get(k) {
- Some(exp) => Some(exp.clone()),
- None => match &env.outer {
- Some(outer_env) => env_get(k, &outer_env),
- None => None,
- },
+fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
+ let mut was_expanded = false;
+ while let Some((mf, args)) = is_macro_call(&ast, env) {
+ //println!("macroexpand 1: {:?}", ast);
+ ast = match mf.apply(args) {
+ Err(e) => return (false, Err(e)),
+ Ok(a) => a,
+ };
+ //println!("macroexpand 2: {:?}", ast);
+ was_expanded = true;
}
+ ((was_expanded, Ok(ast)))
}
-fn parse_list_of_symbol_strings(form: Rc) -> Result, RispErr> {
- let list = match form.as_ref() {
- RispExp::List(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected args form to be a list".to_string(),
- )),
- }?;
- list.iter()
- .map(|x| match x {
- RispExp::Symbol(s) => Ok(s.clone()),
- _ => Err(RispErr::Reason(
- "expected symbols in the argument list".to_string(),
- )),
- })
- .collect()
-}
-
-fn env_for_lambda<'a>(
- params: Rc,
- arg_forms: &[RispExp],
- outer_env: &'a mut RispEnv,
-) -> Result, RispErr> {
- let ks = parse_list_of_symbol_strings(params)?;
- if ks.len() != arg_forms.len() {
- return Err(RispErr::Reason(format!(
- "expected {} arguments, got {}",
- ks.len(),
- arg_forms.len()
- )));
- }
- let vs = eval_forms(arg_forms, outer_env)?;
- let mut data: HashMap = HashMap::new();
- for (k, v) in ks.iter().zip(vs.iter()) {
- data.insert(k.clone(), v.clone());
- }
- Ok(RispEnv {
- data,
- outer: Some(outer_env),
- })
-}
-
-fn eval_forms(arg_forms: &[RispExp], env: &mut RispEnv) -> Result, RispErr> {
- arg_forms.iter().map(|x| eval(x, env)).collect()
-}
-
-fn eval(exp: &RispExp, env: &mut RispEnv) -> Result {
- match exp {
- RispExp::Symbol(k) => {
- env_get(k, env).ok_or(RispErr::Reason(format!("unexpected symbol k='{}'", k)))
+fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
+ match ast {
+ Sym(_) => Ok(env_get(&env, &ast)?),
+ List(v, _) => {
+ let mut lst: MalArgs = vec![];
+ for a in v.iter() {
+ lst.push(eval(a.clone(), env.clone())?)
+ }
+ Ok(list!(lst))
}
- RispExp::Bool(_a) => Ok(exp.clone()),
- RispExp::Number(_a) => Ok(exp.clone()),
+ Vector(v, _) => {
+ let mut lst: MalArgs = vec![];
+ for a in v.iter() {
+ lst.push(eval(a.clone(), env.clone())?)
+ }
+ Ok(vector!(lst))
+ }
+ Hash(hm, _) => {
+ let mut new_hm: FnvHashMap = FnvHashMap::default();
+ for (k, v) in hm.iter() {
+ new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
+ }
+ Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))
+ }
+ _ => Ok(ast.clone()),
+ }
+}
- RispExp::List(list) => {
- let first_form = list
- .first()
- .ok_or(RispErr::Reason("expected a non-empty list".to_string()))?;
- let arg_forms = &list[1..];
- match eval_built_in_form(first_form, arg_forms, env) {
- Some(res) => res,
- None => {
- let first_eval = eval(first_form, env)?;
- match first_eval {
- RispExp::Func(f) => f(&eval_forms(arg_forms, env)?),
- RispExp::Lambda(lambda) => {
- let new_env = &mut env_for_lambda(lambda.params_exp, arg_forms, env)?;
- eval(&lambda.body_exp, new_env)
- }
- _ => Err(RispErr::Reason("first form must be a function".to_string())),
+fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
+ let ret: MalRet;
+
+ 'tco: loop {
+ ret = match ast.clone() {
+ List(l, _) => {
+ if l.len() == 0 {
+ return Ok(ast);
+ }
+ match macroexpand(ast.clone(), &env) {
+ (true, Ok(new_ast)) => {
+ ast = new_ast;
+ continue 'tco;
}
+ (_, Err(e)) => return Err(e),
+ _ => (),
+ }
+
+ if l.len() == 0 {
+ return Ok(ast);
+ }
+ let a0 = &l[0];
+ match a0 {
+ Sym(ref a0sym) if a0sym == "def!" => {
+ env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
+ }
+ Sym(ref a0sym) if a0sym == "let*" => {
+ env = env_new(Some(env.clone()));
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ match a1 {
+ List(ref binds, _) | Vector(ref binds, _) => {
+ for (b, e) in binds.iter().tuples() {
+ match b {
+ Sym(_) => {
+ let _ = env_set(
+ &env,
+ b.clone(),
+ eval(e.clone(), env.clone())?,
+ );
+ }
+ _ => {
+ return error("let* with non-Sym binding");
+ }
+ }
+ }
+ }
+ _ => {
+ return error("let* with non-List bindings");
+ }
+ };
+ ast = a2;
+ continue 'tco;
+ }
+ Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
+ Sym(ref a0sym) if a0sym == "quasiquoteexpand" => Ok(quasiquote(&l[1])),
+ Sym(ref a0sym) if a0sym == "quasiquote" => {
+ ast = quasiquote(&l[1]);
+ continue 'tco;
+ }
+ Sym(ref a0sym) if a0sym == "defmacro!" => {
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ let r = eval(a2, env.clone())?;
+ match r {
+ MalFunc {
+ eval,
+ ast,
+ env,
+ params,
+ ..
+ } => Ok(env_set(
+ &env,
+ a1.clone(),
+ MalFunc {
+ eval: eval,
+ ast: ast.clone(),
+ env: env.clone(),
+ params: params.clone(),
+ is_macro: true,
+ meta: Rc::new(Nil),
+ },
+ )?),
+ _ => error("set_macro on non-function"),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "macroexpand" => {
+ match macroexpand(l[1].clone(), &env) {
+ (_, Ok(new_ast)) => Ok(new_ast),
+ (_, e) => return e,
+ }
+ }
+ Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) {
+ Err(ref e) if l.len() >= 3 => {
+ let exc = match e {
+ ErrMalVal(mv) => mv.clone(),
+ ErrString(s) => Str(s.to_string()),
+ };
+ match l[2].clone() {
+ List(c, _) => {
+ let catch_env = env_bind(
+ Some(env.clone()),
+ list!(vec![c[1].clone()]),
+ vec![exc],
+ )?;
+ eval(c[2].clone(), catch_env)
+ }
+ _ => error("invalid catch block"),
+ }
+ }
+ res => res,
+ },
+ Sym(ref a0sym) if a0sym == "do" => {
+ match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
+ List(_, _) => {
+ ast = l.last().unwrap_or(&Nil).clone();
+ continue 'tco;
+ }
+ _ => error("invalid do form"),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "if" => {
+ let cond = eval(l[1].clone(), env.clone())?;
+ match cond {
+ Bool(false) | Nil if l.len() >= 4 => {
+ ast = l[3].clone();
+ continue 'tco;
+ }
+ Bool(false) | Nil => Ok(Nil),
+ _ if l.len() >= 3 => {
+ ast = l[2].clone();
+ continue 'tco;
+ }
+ _ => Ok(Nil),
+ }
+ }
+ Sym(ref a0sym) if a0sym == "fn*" => {
+ let (a1, a2) = (l[1].clone(), l[2].clone());
+ Ok(MalFunc {
+ eval: eval,
+ ast: Rc::new(a2),
+ env: env,
+ params: Rc::new(a1),
+ is_macro: false,
+ meta: Rc::new(Nil),
+ })
+ }
+ Sym(ref a0sym) if a0sym == "eval" => {
+ ast = eval(l[1].clone(), env.clone())?;
+ while let Some(ref e) = env.clone().outer {
+ env = e.clone();
+ }
+ continue 'tco;
+ }
+ _ => match eval_ast(&ast, &env)? {
+ List(ref el, _) => {
+ let ref f = el[0].clone();
+ let args = el[1..].to_vec();
+ match f {
+ Func(_, _) => f.apply(args),
+ MalFunc {
+ ast: mast,
+ env: menv,
+ params,
+ ..
+ } => {
+ let a = &**mast;
+ let p = &**params;
+ env = env_bind(Some(menv.clone()), p.clone(), args)?;
+ ast = a.clone();
+ continue 'tco;
+ }
+ _ => error("attempt to call non-function"),
+ }
+ }
+ _ => error("expected a list"),
+ },
}
}
- }
- RispExp::Func(_) => Err(RispErr::Reason("unexpected form".to_string())),
- RispExp::Lambda(_) => Err(RispErr::Reason("unexpected form".to_string())),
- }
+ _ => eval_ast(&ast, &env),
+ };
+
+ break;
+ } // end 'tco loop
+
+ ret
+}
+
+// print
+fn print(ast: &MalVal) -> String {
+ ast.pr_str(true)
+}
+
+fn rep(str: &str, env: &Env) -> Result {
+ let ast = read(str)?;
+ let exp = eval(ast, env.clone())?;
+ Ok(print(&exp))
}
fn main() {
- let env = &mut default_env();
- let mut reader = InPort::init("new.lisp".to_string());
- let mut token = String::new();
- read(&reader);
- // read from file
- // let file = File::open("new.lisp").unwrap();
- // let lines = io::BufReader::new(file).lines();
- // let mut cur_line = String::new();
- // let mut token = String::new();
- // for line in lines {
- // let (token, cur_line) = tokenize(line.ok().unwrap().as_str().to_string());
- // println!("token {:?}", token);
- // println!("line {:?}", cur_line);
- // }
+ let mut args = std::env::args();
+ let arg1 = args.nth(1);
- /*
- loop {
- println!("risp >");
- let expr = slurp_expr();
- match parse_eval(expr, env) {
- Ok(res) => println!("// 🔥 => {}", res),
- Err(e) => match e {
- RispErr::Reason(msg) => println!("// 🙀 => {}", msg),
- },
- }
+ // core.rs: defined using rust
+ let repl_env = env_new(None);
+ for (k, v) in core::ns() {
+ env_sets(&repl_env, k, v);
+ }
+ env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
+
+ // core.mal: defined using the language itself
+ let _ = rep("(def! *host-language* \"rust\")", &repl_env);
+ let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
+ let _ = rep(
+ "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
+ &repl_env,
+ );
+ let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
+
+ // Invoked with arguments
+ if let Some(f) = arg1 {
+ match rep(&format!("(load-file \"{}\")", f), &repl_env) {
+ Ok(_) => std::process::exit(0),
+ Err(e) => {
+ println!("Error: {}", format_error(e));
+ std::process::exit(1);
+ }
+ }
}
- */
}
diff --git a/lisp/zk.rkt b/lisp/zk.rkt
deleted file mode 100644
index 1c89ba1c4..000000000
--- a/lisp/zk.rkt
+++ /dev/null
@@ -1,141 +0,0 @@
-#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")
-)
-
-
From fea2b6c313d5f3105a4da8e2f75a69999f6e5e40 Mon Sep 17 00:00:00 2001
From: ada
Date: Fri, 23 Oct 2020 17:36:21 +0200
Subject: [PATCH 025/119] 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