1
0
Fork 0
mirror of synced 2025-09-23 20:28:36 +00:00

First basic implemntation for code to r1cs

This commit is contained in:
Dennis Kuhnert 2017-01-27 23:45:24 +01:00
parent cabe205949
commit 840ba4b57b
4 changed files with 460 additions and 0 deletions

7
Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "code_to_r1cs"
version = "0.1.0"
authors = ["Dennis Kuhnert <mail@kyroy.com>"]
[dependencies]
regex = "0.2"

12
examples/example.code Normal file
View file

@ -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

67
src/ast.rs Normal file
View file

@ -0,0 +1,67 @@
use std::fmt;
pub struct Prog {
pub id: String,
pub args: Vec<Parameter>,
pub defs: Vec<Definition>,
}
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::<Vec<_>>().join(","), self.defs.iter().map(|x| format!("\t{}", x)).collect::<Vec<_>>().join("\n"))
}
}
pub enum Definition {
Return(Expression),
Definition(String, Expression),
#[allow(dead_code)]
IfElse(Expression, Expression, Option<Expression>),
}
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<Expression>, Box<Expression>),
Sub(Box<Expression>, Box<Expression>),
Mult(Box<Expression>, Box<Expression>),
Div(Box<Expression>, Box<Expression>),
Pow(Box<Expression>, Box<Expression>),
#[allow(dead_code)]
IfElse(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
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),
}
}
}

374
src/main.rs Normal file
View file

@ -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<lhs>[[:alnum:]]+)(?P<op>(\*\*|[\+\-\*/]))(?P<rhs>.*)$").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::<i32>().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::<i32>().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<id>\D[a-zA-Z0-9]+)\(\s*([a-z]+)(,\s*[a-z]+)*\s*\):$").unwrap();
let args_regex = Regex::new(r"\(\s*(?P<args>[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::<Vec<_>>();
},
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<lhs>[a-zA-Z]+)\s*=\s*(?P<rhs>[a-zA-Z0-9\s\+\-\*/]+)\s*$").unwrap();
let return_regex = Regex::new(r"^return\s*(?P<rhs>[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<Definition>, 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<String>, 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<String> = Vec::new();
variables.push("~one".to_string());
let mut a: Vec<Vec<(usize, i32)>> = Vec::new();
let mut b: Vec<Vec<(usize, i32)>> = Vec::new();
let mut c: Vec<Vec<(usize, i32)>> = 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);
}