diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..0ed00b4f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "code_to_r1cs" +version = "0.1.0" +authors = ["Dennis Kuhnert "] + +[dependencies] +regex = "0.2" diff --git a/examples/example.code b/examples/example.code new file mode 100644 index 00000000..f2e981ba --- /dev/null +++ b/examples/example.code @@ -0,0 +1,12 @@ +// comment + +// comment + +def qeval(x): + // comment in function + y = x**3 + //b = x**5 + //z = x + 3 - y + 7 + //a = z * y + 3 * y + return y + x + 5 +// comment after function diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 00000000..8638a095 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,67 @@ +use std::fmt; + +pub struct Prog { + pub id: String, + pub args: Vec, + pub defs: Vec, +} +impl fmt::Display for Prog { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}({}):\n{}", self.id, self.args.iter().map(|x| format!("{}", x)).collect::>().join(","), self.defs.iter().map(|x| format!("\t{}", x)).collect::>().join("\n")) + } +} + +pub enum Definition { + Return(Expression), + Definition(String, Expression), + #[allow(dead_code)] + IfElse(Expression, Expression, Option), +} +impl fmt::Display for Definition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Definition::Return(ref expr) => write!(f, "return {}", expr), + Definition::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs), + Definition::IfElse(_, _, _) => write!(f, "IfElse not implemented yet!"), + } + } +} +impl fmt::Debug for Definition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } +} + +pub struct Parameter { pub id: String } +impl fmt::Display for Parameter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.id) + } +} + +#[derive(Clone)] +pub enum Expression { + Add(Box, Box), + Sub(Box, Box), + Mult(Box, Box), + Div(Box, Box), + Pow(Box, Box), + #[allow(dead_code)] + IfElse(Box, Box, Option>), + NumberLiteral(i32), + VariableReference(String), +} +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Expression::Add(ref lhs, ref rhs) => write!(f, "{} + {}", lhs, rhs), + Expression::Sub(ref lhs, ref rhs) => write!(f, "{} - {}", lhs, rhs), + Expression::Mult(ref lhs, ref rhs) => write!(f, "{} * {}", lhs, rhs), + Expression::Div(ref lhs, ref rhs) => write!(f, "{} / {}", lhs, rhs), + Expression::Pow(ref lhs, ref rhs) => write!(f, "{}**{}", lhs, rhs), + Expression::IfElse(_, _, _) => write!(f, "ifelse"), + Expression::NumberLiteral(ref i) => write!(f, "{}", i), + Expression::VariableReference(ref var) => write!(f, "{}", var), + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..9e78c323 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,374 @@ +#![feature(box_patterns, box_syntax)] + +mod ast; +extern crate regex; + +use ast::*; +use regex::Regex; +use std::path::Path; +use std::fs::File; +use std::io::BufReader; +use std::io::prelude::*; + +fn parse_expression_rhs(text: String) -> Expression { + let op_regex = Regex::new(r"^(?P[[:alnum:]]+)(?P(\*\*|[\+\-\*/]))(?P.*)$").unwrap(); + let variable_regex = Regex::new(r"^[[:alpha:]][[:alnum:]]*$").unwrap(); + let number_regex = Regex::new(r"^[[:digit:]]+$").unwrap(); + let line = text.replace(" ", "").replace("\t", ""); + match op_regex.captures(&line) { + Some(x) => { + let lhs = if variable_regex.is_match(&x["lhs"]) { + Box::new(Expression::VariableReference(x["lhs"].to_string())) + } else if number_regex.is_match(&x["lhs"]) { + Box::new(Expression::NumberLiteral(x["lhs"].parse::().unwrap())) + } else { + panic!("Could not read lhs: {:?}", &x["lhs"]) + }; + let rhs = if variable_regex.is_match(&x["rhs"]) { + Box::new(Expression::VariableReference(x["rhs"].to_string())) + } else if number_regex.is_match(&x["rhs"]) { + Box::new(Expression::NumberLiteral(x["rhs"].parse::().unwrap())) + } else { + Box::new(parse_expression_rhs(x["rhs"].to_string())) + }; + match &x["op"] { + "+" => Expression::Add(lhs, rhs), + "-" => Expression::Sub(lhs, rhs), + "*" => Expression::Mult(lhs, rhs), + "/" => Expression::Div(lhs, rhs), + "**" if number_regex.is_match(&x["rhs"]) => Expression::Pow(lhs, rhs), + _ => panic!("Not implemented yet") + } + }, + None => panic!("Could not parse rhs of expression: {:?}", text), + } +} + +fn parse_program(file: File) -> Prog { + let reader = BufReader::new(file); + let mut lines = reader.lines(); + + let id; + let args; + let def_regex = Regex::new(r"^def\s(?P\D[a-zA-Z0-9]+)\(\s*([a-z]+)(,\s*[a-z]+)*\s*\):$").unwrap(); + let args_regex = Regex::new(r"\(\s*(?P[a-z]+(,\s*[a-z]+)*)\s*\)").unwrap(); + loop { // search and make Prog + match lines.next() { + Some(Ok(ref x)) if x.starts_with("def") => { + // assert!(def_regex.is_match(x)); + match def_regex.captures(x) { + Some(x) => { + // println!("def_regex {:?}", x); + id = x["id"].to_string(); + }, + None => panic!("Wrong definition of function"), + } + match args_regex.captures(x) { + Some(x) => { + // println!("args_regex {:?}", x); + args = x["args"].replace(" ", "").replace("\t", "").split(",") + .map(|p| Parameter { id: p.to_string() }) + .collect::>(); + }, + None => panic!("Wrong argument definition in function: {}", id), + } + break; + }, + Some(Ok(ref x)) if x.starts_with("//") || x == "" => {}, + None => panic!("End of file reached without function def"), + Some(x) => panic!("Found '{:?}' outside of function", x), + } + }; + + let mut defs = Vec::new(); + let definition_regex = Regex::new(r"(?P[a-zA-Z]+)\s*=\s*(?P[a-zA-Z0-9\s\+\-\*/]+)\s*$").unwrap(); + let return_regex = Regex::new(r"^return\s*(?P[a-zA-Z0-9\s\+\-\*/]+)\s*$").unwrap(); + loop { // make list of Definition + match lines.next() { + Some(Ok(ref x)) if x.trim().starts_with("//") || x == "" => {}, + Some(Ok(ref line)) if line.trim().starts_with("return") => { + match return_regex.captures(line.trim()) { + Some(x) => defs.push(Definition::Return(parse_expression_rhs(x["rhs"].to_string()))), + None => panic!("Wrong return definition in function: {}", id), + } + }, + Some(Ok(ref line)) => { + match definition_regex.captures(line.trim()) { + Some(x) => defs.push(Definition::Definition(x["lhs"].to_string(), parse_expression_rhs(x["rhs"].to_string()))), + None => panic!("Wrong expression in function '{}':\n{}", id, line), + } + }, + None => { break }, + Some(Err(e)) => panic!("Error while reading Definitions: {}", e), + } + } + + match defs.last() { + Some(&Definition::Return(_)) => {}, + Some(x) => panic!("Last definition not Return: {}", x), + None => panic!("Error while checking last definition"), + } + Prog { id: id, args: args, defs: defs } +} + +fn flatten_rhs(defs_flattened: &mut Vec, num_variables: &mut i32, rhs: Expression) -> Expression { + match rhs { + Expression::Pow(base, exponent) => { + match exponent { + box Expression::NumberLiteral(x) if x > 1 => { + match base { + box Expression::VariableReference(ref var) => { + let id = if x > 2 { + let tmp_expression = flatten_rhs( + defs_flattened, + num_variables, + Expression::Pow( + box Expression::VariableReference(var.to_string()), + box Expression::NumberLiteral(x - 1) + ) + ); + let new_name = format!("sym_{}", num_variables); + *num_variables += 1; + defs_flattened.push(Definition::Definition(new_name.to_string(), tmp_expression)); + new_name + } else { + var.to_string() + }; + Expression::Mult( + box Expression::VariableReference(id.to_string()), + box Expression::VariableReference(var.to_string()) + ) + }, + box Expression::NumberLiteral(var) => Expression::Mult( + box Expression::NumberLiteral(var), + box Expression::NumberLiteral(var) + ), + _ => panic!("Pow only allowed for numbers and variables") + } + } + _ => panic!("No Number as exponent"), + } + }, + Expression::Add(left, right) => { + // TODO currently assuming that left is always Number of Variable + let new_right = match right { + box Expression::NumberLiteral(x) => Expression::NumberLiteral(x), + box Expression::VariableReference(ref x) => Expression::VariableReference(x.to_string()), + box expr => { + let tmp_expression = flatten_rhs( + defs_flattened, + num_variables, + expr + ); + let new_name = format!("sym_{}", num_variables); + *num_variables += 1; + defs_flattened.push(Definition::Definition(new_name.to_string(), tmp_expression)); + Expression::VariableReference(new_name) + }, + }; + Expression::Add(left, Box::new(new_right)) + }, + Expression::Sub(left, right) => { + // TODO currently assuming that left is always Number of Variable + let new_right = match right { + box Expression::NumberLiteral(x) => Expression::NumberLiteral(x), + box Expression::VariableReference(ref x) => Expression::VariableReference(x.to_string()), + box expr => { + let tmp_expression = flatten_rhs( + defs_flattened, + num_variables, + expr + ); + let new_name = format!("sym_{}", num_variables); + *num_variables += 1; + defs_flattened.push(Definition::Definition(new_name.to_string(), tmp_expression)); + Expression::VariableReference(new_name) + }, + }; + Expression::Sub(left, Box::new(new_right)) + }, + Expression::Mult(left, right) => { + // TODO currently assuming that left is always Number of Variable + let new_right = match right { + box Expression::NumberLiteral(x) => Expression::NumberLiteral(x), + box Expression::VariableReference(ref x) => Expression::VariableReference(x.to_string()), + box expr => { + let tmp_expression = flatten_rhs( + defs_flattened, + num_variables, + expr + ); + let new_name = format!("sym_{}", num_variables); + *num_variables += 1; + defs_flattened.push(Definition::Definition(new_name.to_string(), tmp_expression)); + Expression::VariableReference(new_name) + }, + }; + Expression::Mult(left, Box::new(new_right)) + }, + Expression::Div(left, right) => { + // TODO currently assuming that left is always Number of Variable + let new_right = match right { + box Expression::NumberLiteral(x) => Expression::NumberLiteral(x), + box Expression::VariableReference(ref x) => Expression::VariableReference(x.to_string()), + box expr => { + let tmp_expression = flatten_rhs( + defs_flattened, + num_variables, + expr + ); + let new_name = format!("sym_{}", num_variables); + *num_variables += 1; + defs_flattened.push(Definition::Definition(new_name.to_string(), tmp_expression)); + Expression::VariableReference(new_name) + }, + }; + Expression::Div(left, Box::new(new_right)) + }, + _ => Expression::NumberLiteral(0), + } +} + +fn flatten_program(prog: Prog) -> Prog { + let mut defs_flattened = Vec::new(); + let mut num_variables: i32 = 0; + for def in prog.defs { + match def { + Definition::Return(expr) => { + let rhs = flatten_rhs(&mut defs_flattened, &mut num_variables, expr); + defs_flattened.push(Definition::Return(rhs)); + }, + Definition::Definition(id, expr) => { + let rhs = flatten_rhs(&mut defs_flattened, &mut num_variables, expr); + defs_flattened.push(Definition::Definition(id, rhs)); + }, + Definition::IfElse(_, _, _) => panic!("Not implemented yet"), + } + } + Prog { id: prog.id, args: prog.args, defs: defs_flattened } +} + +fn r1cs_expression(idx: usize, expr: Expression, variables: &mut Vec, a_row: &mut Vec<(usize, i32)>, b_row: &mut Vec<(usize, i32)>, c_row: &mut Vec<(usize, i32)>) { + match expr { + Expression::Add(lhs, rhs) => { + c_row.push((idx, 1)); + match (lhs, rhs) { + (box Expression::VariableReference(ref x1), box Expression::VariableReference(ref x2)) if x1 == x2 => { + a_row.push((variables.iter().position(|r| r == x1).unwrap(), 2)); + b_row.push((0, 1)); + }, + (box Expression::VariableReference(ref x1), box Expression::VariableReference(ref x2)) /*if x1 != x2*/ => { + a_row.push((variables.iter().position(|r| r == x1).unwrap(), 1)); + a_row.push((variables.iter().position(|r| r == x2).unwrap(), 1)); + b_row.push((0, 1)); + }, + (box Expression::NumberLiteral(num), box Expression::VariableReference(ref x)) | + (box Expression::VariableReference(ref x), box Expression::NumberLiteral(num)) => { + a_row.push((0, num)); + a_row.push((variables.iter().position(|r| r == x).unwrap(), 1)); + b_row.push((0, 1)); + } + _ => panic!("Not flattened!"), + } + }, + Expression::Sub(lhs, rhs) => { // a - b = c --> c + b = a + a_row.push((idx, 1)); + match lhs { + box Expression::NumberLiteral(x) => c_row.push((0, x)), + box Expression::VariableReference(x) => c_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + match rhs { + box Expression::NumberLiteral(x) => b_row.push((0, x)), + box Expression::VariableReference(x) => b_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + }, + Expression::Mult(lhs, rhs) => { + c_row.push((idx, 1)); + match lhs { + box Expression::NumberLiteral(x) => a_row.push((0, x)), + box Expression::VariableReference(x) => a_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + match rhs { + box Expression::NumberLiteral(x) => b_row.push((0, x)), + box Expression::VariableReference(x) => b_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + }, + Expression::Div(lhs, rhs) => { // a / b = c --> c * b = a + a_row.push((idx, 1)); + match lhs { + box Expression::NumberLiteral(x) => c_row.push((0, x)), + box Expression::VariableReference(x) => c_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + match rhs { + box Expression::NumberLiteral(x) => b_row.push((0, x)), + box Expression::VariableReference(x) => b_row.push((variables.iter().position(|r| r == &x).unwrap(), 1)), + _ => panic!("Not flattened!"), + }; + }, + _ => panic!("Not implemented yet or forbidden!"), + } +} + +fn r1cs_program(prog: Prog) { + let mut variables: Vec = Vec::new(); + variables.push("~one".to_string()); + let mut a: Vec> = Vec::new(); + let mut b: Vec> = Vec::new(); + let mut c: Vec> = Vec::new(); + variables.extend(prog.args.iter().map(|x| format!("{}", x))); + for def in prog.defs { + let mut a_row: Vec<(usize, i32)> = Vec::new(); + let mut b_row: Vec<(usize, i32)> = Vec::new(); + let mut c_row: Vec<(usize, i32)> = Vec::new(); + let idx = variables.len(); + match def { // get all variables + Definition::Return(expr) => { + variables.push("~out".to_string()); + r1cs_expression(idx, expr, &mut variables, &mut a_row, &mut b_row, &mut c_row); + }, + Definition::Definition(id, expr) => { + variables.push(id); + r1cs_expression(idx, expr, &mut variables, &mut a_row, &mut b_row, &mut c_row); + }, + Definition::IfElse(_, _, _) => panic!("Not implemented yet"), + } + a.push(a_row); + b.push(b_row); + c.push(c_row); + } + println!("variables {:?}", variables); + println!("A"); + for x in a { + println!("{:?}", x); + } + println!("B"); + for x in b { + println!("{:?}", x); + } + println!("C"); + for x in c { + println!("{:?}", x); + } +} + +fn main() { + let args: Vec<_> = std::env::args().collect(); + assert!(args.len() == 2); + + let path = Path::new(&args[1]); + let file = match File::open(&path) { + Ok(file) => file, + Err(why) => panic!("couldn't open {}: {}", path.display(), why), + }; + + let program_ast = parse_program(file); + println!("program:\n{}", program_ast); + let program_flattened = flatten_program(program_ast); + println!("flattened:\n{}", program_flattened); + r1cs_program(program_flattened); +}