Merge pull request #15 from Schaeff/explicit-private-inputs
Make private inputs explicit
This commit is contained in:
commit
706e22727e
8 changed files with 451 additions and 34 deletions
|
@ -1,4 +1,4 @@
|
|||
// a and b are factorization of c
|
||||
def main(c):
|
||||
def main(c, private a, private b):
|
||||
c == a * b
|
||||
return 1
|
||||
|
|
|
@ -22,7 +22,7 @@ def validateInput(x):
|
|||
return (x-1)*(x-2)*(x-3)*(x-4)
|
||||
|
||||
// variables naming: box'row''column'
|
||||
def main(a21,b11,b22,c11,c22,d21):
|
||||
def main(a21, b11, b22, c11, c22, d21, private a11, private a12, private a22, private b12, private b21, private c12, private c21, private d11, private d12, private d22):
|
||||
|
||||
// validate inputs
|
||||
0 == validateInput(a11)
|
||||
|
|
21
src/absy.rs
21
src/absy.rs
|
@ -7,10 +7,9 @@
|
|||
|
||||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{stdin, BufRead};
|
||||
use field::Field;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Prog<T: Field> {
|
||||
/// Functions of the program
|
||||
pub functions: Vec<Function<T>>,
|
||||
|
@ -56,7 +55,7 @@ impl<T: Field> fmt::Debug for Prog<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Function<T: Field> {
|
||||
/// Name of the program
|
||||
pub id: String,
|
||||
|
@ -195,11 +194,13 @@ impl<T: Field> fmt::Debug for Statement<T> {
|
|||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Parameter {
|
||||
pub id: String,
|
||||
pub private: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for Parameter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.id)
|
||||
let visibility = if self.private { "private " } else { "" };
|
||||
write!(f, "{}{}", visibility, self.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,19 +289,11 @@ impl<T: Field> Expression<T> {
|
|||
}
|
||||
assert_eq!(num, T::zero());
|
||||
} else {
|
||||
println!(
|
||||
"Could not calculate variable {:?}, inputs: {:?}",
|
||||
panic!(
|
||||
"Variable {:?} is undeclared in inputs: {:?}",
|
||||
var,
|
||||
inputs
|
||||
);
|
||||
println!("Please enter a value for {:?}:", var);
|
||||
let mut input = String::new();
|
||||
let stdin = stdin();
|
||||
stdin
|
||||
.lock()
|
||||
.read_line(&mut input)
|
||||
.expect("Did not enter a correct String");
|
||||
inputs.insert(var.to_string(), T::from(input.trim()));
|
||||
}
|
||||
}
|
||||
inputs[var].clone()
|
||||
|
|
|
@ -617,6 +617,7 @@ impl Flattener {
|
|||
for arg in funct.arguments {
|
||||
arguments_flattened.push(Parameter {
|
||||
id: arg.id.to_string(),
|
||||
private: arg.private
|
||||
});
|
||||
}
|
||||
// flatten statements in functions and apply substitution
|
||||
|
|
60
src/main.rs
60
src/main.rs
|
@ -18,6 +18,7 @@ extern crate regex;
|
|||
|
||||
mod absy;
|
||||
mod parser;
|
||||
mod semantics;
|
||||
mod flatten;
|
||||
mod r1cs;
|
||||
mod field;
|
||||
|
@ -26,13 +27,14 @@ mod libsnark;
|
|||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::io::{BufWriter, Write, BufReader, BufRead};
|
||||
use std::io::{BufWriter, Write, BufReader, BufRead, stdin};
|
||||
use std::collections::HashMap;
|
||||
use std::string::String;
|
||||
use std::io::prelude::*;
|
||||
use field::{Field, FieldPrime};
|
||||
use absy::Prog;
|
||||
use parser::parse_program;
|
||||
use semantics::Checker;
|
||||
use flatten::Flattener;
|
||||
use r1cs::r1cs_program;
|
||||
use clap::{App, AppSettings, Arg, SubCommand};
|
||||
|
@ -139,7 +141,7 @@ fn main() {
|
|||
.arg(Arg::with_name("input")
|
||||
.short("i")
|
||||
.long("input")
|
||||
.help("path of comiled code.")
|
||||
.help("path of compiled code.")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
|
@ -159,6 +161,10 @@ fn main() {
|
|||
.takes_value(true)
|
||||
.multiple(true) // allows multiple values
|
||||
.required(false)
|
||||
).arg(Arg::with_name("interactive")
|
||||
.long("interactive")
|
||||
.help("enter private inputs interactively.")
|
||||
.required(false)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("generate-proof")
|
||||
|
@ -209,6 +215,12 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
// check semantics
|
||||
match Checker::new().check_program(program_ast.clone()) {
|
||||
Ok(()) => (),
|
||||
Err(why) => panic!("Semantic analysis failed with: {}", why)
|
||||
};
|
||||
|
||||
// flatten input program
|
||||
let program_flattened =
|
||||
Flattener::new(FieldPrime::get_required_bits()).flatten_program(program_ast);
|
||||
|
@ -286,22 +298,54 @@ fn main() {
|
|||
println!("{}", main_flattened);
|
||||
|
||||
// validate #arguments
|
||||
let mut args: Vec<FieldPrime> = Vec::new();
|
||||
let mut cli_arguments: Vec<FieldPrime> = Vec::new();
|
||||
match sub_matches.values_of("arguments"){
|
||||
Some(p) => {
|
||||
let arg_strings: Vec<&str> = p.collect();
|
||||
args = arg_strings.into_iter().map(|x| FieldPrime::from(x)).collect();
|
||||
cli_arguments = arg_strings.into_iter().map(|x| FieldPrime::from(x)).collect();
|
||||
},
|
||||
None => {
|
||||
}
|
||||
}
|
||||
|
||||
if main_flattened.arguments.len() != args.len() {
|
||||
println!("Wrong number of arguments. Given: {}, Required: {}.", args.len(), main_flattened.arguments.len());
|
||||
// handle interactive and non-interactive modes
|
||||
let is_interactive = sub_matches.occurrences_of("interactive") > 0;
|
||||
|
||||
// in interactive mode, only public inputs are expected
|
||||
let expected_cli_args_count = main_flattened.arguments.iter().filter(|x| !(x.private && is_interactive)).count();
|
||||
|
||||
if cli_arguments.len() != expected_cli_args_count {
|
||||
println!("Wrong number of arguments. Given: {}, Required: {}.", cli_arguments.len(), expected_cli_args_count);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let witness_map = main_flattened.get_witness(args);
|
||||
let mut cli_arguments_iter = cli_arguments.into_iter();
|
||||
let arguments = main_flattened.arguments.clone().into_iter().map(|x| {
|
||||
match x.private && is_interactive {
|
||||
// private inputs are passed interactively when the flag is present
|
||||
true => {
|
||||
println!("Please enter a value for {:?}:", x.id);
|
||||
let mut input = String::new();
|
||||
let stdin = stdin();
|
||||
stdin
|
||||
.lock()
|
||||
.read_line(&mut input)
|
||||
.expect("Did not enter a correct String");
|
||||
FieldPrime::from(input.trim())
|
||||
}
|
||||
// otherwise, they are taken from the CLI arguments
|
||||
false => {
|
||||
match cli_arguments_iter.next() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let witness_map = main_flattened.get_witness(arguments);
|
||||
// let witness_map: HashMap<String, FieldPrime> = main_flattened.get_witness(args);
|
||||
println!("Witness: {:?}", witness_map);
|
||||
match witness_map.get("~out") {
|
||||
|
@ -381,7 +425,7 @@ fn main() {
|
|||
// run setup phase
|
||||
#[cfg(not(feature="nolibsnark"))]{
|
||||
// number of inputs in the zkSNARK sense, i.e., input variables + output variables
|
||||
let num_inputs = main_flattened.arguments.len() + 1; //currently exactly one output variable
|
||||
let num_inputs = main_flattened.arguments.iter().filter(|x| !x.private).count() + 1;
|
||||
println!("setup successful: {:?}", setup(variables, a, b, c, num_inputs, pk_path, vk_path));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ enum Token<T: Field> {
|
|||
Mult,
|
||||
Div,
|
||||
Pow,
|
||||
Private,
|
||||
Ide(String),
|
||||
Num(T),
|
||||
Unknown(String),
|
||||
|
@ -188,6 +189,7 @@ impl<T: Field> fmt::Display for Token<T> {
|
|||
Token::Mult => write!(f, "*"),
|
||||
Token::Div => write!(f, "/"),
|
||||
Token::Pow => write!(f, "**"),
|
||||
Token::Private => write!(f, "private"),
|
||||
Token::Ide(ref x) => write!(f, "{}", x),
|
||||
Token::Num(ref x) => write!(f, "{}", x),
|
||||
Token::Unknown(ref x) => write!(f, "{}", x),
|
||||
|
@ -502,6 +504,14 @@ fn next_token<T: Field>(input: &String, pos: &Position) -> (Token<T>, String, Po
|
|||
},
|
||||
)
|
||||
}
|
||||
Some(_) if input[offset..].starts_with("private ") => (
|
||||
Token::Private,
|
||||
input[offset + 8..].to_string(),
|
||||
Position {
|
||||
line: pos.line,
|
||||
col: pos.col + offset + 8,
|
||||
},
|
||||
),
|
||||
Some(x) => match x {
|
||||
'0'...'9' => parse_num(
|
||||
&input[offset..].to_string(),
|
||||
|
@ -1132,8 +1142,55 @@ fn parse_function<T: Field>(
|
|||
let mut p = p3;
|
||||
loop {
|
||||
match next_token(&s, &p) {
|
||||
(Token::Private, s4, p4) => {
|
||||
match next_token(&s4, &p4) {
|
||||
(Token::Ide(x), s5, p5) => {
|
||||
args.push(Parameter { id: x, private: true });
|
||||
match next_token(&s5, &p5) {
|
||||
(Token::Comma, s6, p6) => {
|
||||
s = s6;
|
||||
p = p6;
|
||||
}
|
||||
(Token::Close, s5, p5) => match next_token(&s5, &p5) {
|
||||
(Token::Colon, s6, p6) => match next_token(&s6, &p6) {
|
||||
(Token::InlineComment(_), _, _) => break,
|
||||
(Token::Unknown(ref x6), ..) if x6 == "" => break,
|
||||
(t6, _, p6) => {
|
||||
return Err(Error {
|
||||
expected: vec![Token::Unknown("".to_string())],
|
||||
got: t6,
|
||||
pos: p6,
|
||||
})
|
||||
}
|
||||
},
|
||||
(t6, _, p6) => {
|
||||
return Err(Error {
|
||||
expected: vec![Token::Colon],
|
||||
got: t6,
|
||||
pos: p6,
|
||||
})
|
||||
}
|
||||
},
|
||||
(t5, _, p5) => {
|
||||
return Err(Error {
|
||||
expected: vec![Token::Comma, Token::Close],
|
||||
got: t5,
|
||||
pos: p5,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
(t5, _, p5) => {
|
||||
return Err(Error {
|
||||
expected: vec![Token::Comma, Token::Close],
|
||||
got: t5,
|
||||
pos: p5,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
(Token::Ide(x), s4, p4) => {
|
||||
args.push(Parameter { id: x });
|
||||
args.push(Parameter { id: x, private: false });
|
||||
match next_token(&s4, &p4) {
|
||||
(Token::Comma, s5, p5) => {
|
||||
s = s5;
|
||||
|
|
12
src/r1cs.rs
12
src/r1cs.rs
|
@ -290,7 +290,8 @@ pub fn r1cs_program<T: Field>(
|
|||
.iter()
|
||||
.find(|x: &&Function<T>| x.id == "main".to_string())
|
||||
.unwrap();
|
||||
variables.extend(main.arguments.iter().map(|x| format!("{}", x)));
|
||||
variables.extend(main.arguments.iter().filter(|x| !x.private).map(|x| format!("{}", x)));
|
||||
|
||||
// ~out is added after main's arguments as we want variables (columns)
|
||||
// in the r1cs to be aligned like "public inputs | private inputs"
|
||||
variables.push("~out".to_string());
|
||||
|
@ -311,14 +312,7 @@ pub fn r1cs_program<T: Field>(
|
|||
&mut b_row,
|
||||
&mut c_row,
|
||||
),
|
||||
Statement::Definition(ref id, ref expr) => r1cs_expression(
|
||||
Identifier(id.to_string()),
|
||||
expr.clone(),
|
||||
&mut variables,
|
||||
&mut a_row,
|
||||
&mut b_row,
|
||||
&mut c_row,
|
||||
),
|
||||
Statement::Definition(_, _) => continue,
|
||||
Statement::Condition(ref expr1, ref expr2) => r1cs_expression(
|
||||
expr1.clone(),
|
||||
expr2.clone(),
|
||||
|
|
328
src/semantics.rs
Normal file
328
src/semantics.rs
Normal file
|
@ -0,0 +1,328 @@
|
|||
//! Module containing semantic analysis tools to run at compile time
|
||||
//! The goal is to detect semantic errors such as undefined variables
|
||||
//! A variable is undefined if it isn't present in the static scope
|
||||
//!
|
||||
//! @file semantics.rs
|
||||
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
|
||||
//! @date 2017
|
||||
|
||||
use std::collections::HashSet;
|
||||
use absy::*;
|
||||
use field::Field;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Symbol {
|
||||
id: String,
|
||||
level: usize
|
||||
}
|
||||
|
||||
// Checker, checks the semantics of a program.
|
||||
pub struct Checker {
|
||||
scope: HashSet<Symbol>,
|
||||
level: usize
|
||||
}
|
||||
|
||||
impl Checker {
|
||||
pub fn new() -> Checker {
|
||||
Checker {
|
||||
scope: HashSet::new(),
|
||||
level: 0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn new_with_args(scope: HashSet<Symbol>, level: usize) -> Checker {
|
||||
Checker {
|
||||
scope: scope,
|
||||
level: level
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_program<T: Field>(&mut self, prog: Prog<T>) -> Result<(), String> {
|
||||
for func in prog.functions {
|
||||
self.check_function(func)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_function<T: Field>(&mut self, funct: Function<T>) -> Result<(), String> {
|
||||
self.level += 1;
|
||||
for arg in funct.arguments {
|
||||
self.scope.insert(Symbol {
|
||||
id: arg.id.to_string(),
|
||||
level: self.level
|
||||
});
|
||||
}
|
||||
for stat in funct.statements {
|
||||
self.check_statement(stat)?;
|
||||
}
|
||||
let current_level = self.level.clone();
|
||||
let current_scope = self.scope.clone();
|
||||
let to_remove = current_scope.iter().filter(|symbol| symbol.level == current_level);
|
||||
for symbol in to_remove {
|
||||
self.scope.remove(symbol);
|
||||
}
|
||||
self.level -= 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_statement<T: Field>(&mut self, stat: Statement<T>) -> Result<(), String> {
|
||||
match stat {
|
||||
Statement::Return(expr) => {
|
||||
self.check_expression(expr)?;
|
||||
Ok(())
|
||||
}
|
||||
Statement::Definition(id, expr) | Statement::Compiler(id, expr) => {
|
||||
self.check_expression(expr)?;
|
||||
self.scope.insert(Symbol {
|
||||
id: id.to_string(),
|
||||
level: self.level
|
||||
});
|
||||
Ok(())
|
||||
|
||||
}
|
||||
Statement::Condition(lhs, rhs) => {
|
||||
self.check_expression(lhs)?;
|
||||
self.check_expression(rhs)?;
|
||||
Ok(())
|
||||
}
|
||||
Statement::For(id, _, _, statements) => {
|
||||
self.level += 1;
|
||||
let index = Symbol {
|
||||
id: id.to_string(),
|
||||
level: self.level
|
||||
};
|
||||
self.scope.insert(index.clone());
|
||||
for stat in statements {
|
||||
self.check_statement(stat)?;
|
||||
}
|
||||
self.scope.remove(&index);
|
||||
self.level -= 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expression<T: Field>(&mut self, expr: Expression<T>) -> Result<(), String> {
|
||||
match expr {
|
||||
Expression::Identifier(id) => {
|
||||
// check that `id` is defined in the scope
|
||||
match self.scope.iter().filter(|symbol| symbol.id == id.to_string()).count() {
|
||||
0 => Err(format!("{:?} is undefined", id.to_string())),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
Expression::Add(box e1, box e2) | Expression::Sub(box e1, box e2) | Expression::Mult(box e1, box e2) |
|
||||
Expression::Div(box e1, box e2) | Expression::Pow(box e1, box e2) => {
|
||||
self.check_expression(e1)?;
|
||||
self.check_expression(e2)?;
|
||||
Ok(())
|
||||
}
|
||||
Expression::IfElse(box condition, box consequence, box alternative) => {
|
||||
self.check_condition(condition)?;
|
||||
self.check_expression(consequence)?;
|
||||
self.check_expression(alternative)?;
|
||||
Ok(())
|
||||
}
|
||||
Expression::FunctionCall(_, param_expressions) => {
|
||||
for expr in param_expressions {
|
||||
self.check_expression(expr)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expression::Number(_) => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_condition<T: Field>(&mut self, cond: Condition<T>) -> Result<(), String> {
|
||||
match cond {
|
||||
Condition::Lt(e1, e2) |
|
||||
Condition::Le(e1, e2) |
|
||||
Condition::Eq(e1, e2) |
|
||||
Condition::Ge(e1, e2) |
|
||||
Condition::Gt(e1, e2) => {
|
||||
self.check_expression(e1)?;
|
||||
self.check_expression(e2)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use field::FieldPrime;
|
||||
|
||||
#[test]
|
||||
fn undefined_variable_in_statement() {
|
||||
// a = b
|
||||
// b undefined
|
||||
let statement: Statement<FieldPrime> = Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Identifier(String::from("b"))
|
||||
);
|
||||
let mut checker = Checker::new();
|
||||
assert_eq!(checker.check_statement(statement), Err("\"b\" is undefined".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defined_variable_in_statement() {
|
||||
// a = b
|
||||
// b defined
|
||||
let statement: Statement<FieldPrime> = Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Identifier(String::from("b"))
|
||||
);
|
||||
let mut scope = HashSet::new();
|
||||
scope.insert(Symbol {
|
||||
id: String::from("b"),
|
||||
level: 0
|
||||
});
|
||||
let mut checker = Checker::new_with_args(scope, 1);
|
||||
assert_eq!(checker.check_statement(statement), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_in_other_function() {
|
||||
// def foo():
|
||||
// a = 1
|
||||
// def bar():
|
||||
// return a
|
||||
// should fail
|
||||
let foo_args = Vec::<Parameter>::new();
|
||||
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
foo_statements.push(Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Number(FieldPrime::from(1)))
|
||||
);
|
||||
let foo = Function {
|
||||
id: "foo".to_string(),
|
||||
arguments: foo_args,
|
||||
statements: foo_statements,
|
||||
};
|
||||
|
||||
let bar_args = Vec::<Parameter>::new();
|
||||
let mut bar_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
bar_statements.push(Statement::Return(
|
||||
Expression::Identifier(String::from("a"))
|
||||
));
|
||||
let bar = Function {
|
||||
id: "bar".to_string(),
|
||||
arguments: bar_args,
|
||||
statements: bar_statements,
|
||||
};
|
||||
|
||||
let mut funcs = Vec::<Function<FieldPrime>>::new();
|
||||
funcs.push(foo);
|
||||
funcs.push(bar);
|
||||
let prog = Prog {
|
||||
functions: funcs
|
||||
};
|
||||
|
||||
let mut checker = Checker::new();
|
||||
assert_eq!(checker.check_program(prog), Err("\"a\" is undefined".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declared_in_two_scopes() {
|
||||
// def foo():
|
||||
// a = 1
|
||||
// def bar():
|
||||
// a = 2
|
||||
// return a
|
||||
// should pass
|
||||
let foo_args = Vec::<Parameter>::new();
|
||||
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
foo_statements.push(Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Number(FieldPrime::from(1)))
|
||||
);
|
||||
let foo = Function {
|
||||
id: "foo".to_string(),
|
||||
arguments: foo_args,
|
||||
statements: foo_statements,
|
||||
};
|
||||
|
||||
let bar_args = Vec::<Parameter>::new();
|
||||
let mut bar_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
bar_statements.push(Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Number(FieldPrime::from(2))
|
||||
));
|
||||
bar_statements.push(Statement::Return(
|
||||
Expression::Identifier(String::from("a"))
|
||||
));
|
||||
let bar = Function {
|
||||
id: "bar".to_string(),
|
||||
arguments: bar_args,
|
||||
statements: bar_statements,
|
||||
};
|
||||
|
||||
let mut funcs = Vec::<Function<FieldPrime>>::new();
|
||||
funcs.push(foo);
|
||||
funcs.push(bar);
|
||||
let prog = Prog {
|
||||
functions: funcs
|
||||
};
|
||||
|
||||
let mut checker = Checker::new();
|
||||
assert_eq!(checker.check_program(prog), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_index_after_end() {
|
||||
// def foo():
|
||||
// for i in 0..10 do
|
||||
// endfor
|
||||
// return i
|
||||
// should fail
|
||||
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
foo_statements.push(Statement::For(
|
||||
String::from("i"),
|
||||
FieldPrime::from(0),
|
||||
FieldPrime::from(10),
|
||||
Vec::<Statement<FieldPrime>>::new())
|
||||
);
|
||||
foo_statements.push(Statement::Return(
|
||||
Expression::Identifier(String::from("i"))
|
||||
));
|
||||
let foo = Function {
|
||||
id: "foo".to_string(),
|
||||
arguments: Vec::<Parameter>::new(),
|
||||
statements: foo_statements
|
||||
};
|
||||
|
||||
let mut checker = Checker::new();
|
||||
assert_eq!(checker.check_function(foo), Err("\"i\" is undefined".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_index_in_for() {
|
||||
// def foo():
|
||||
// for i in 0..10 do
|
||||
// a = i
|
||||
// endfor
|
||||
// should pass
|
||||
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
let mut for_statements = Vec::<Statement<FieldPrime>>::new();
|
||||
for_statements.push(Statement::Definition(
|
||||
String::from("a"),
|
||||
Expression::Identifier(String::from("i"))
|
||||
));
|
||||
foo_statements.push(Statement::For(
|
||||
String::from("i"),
|
||||
FieldPrime::from(0),
|
||||
FieldPrime::from(10),
|
||||
for_statements
|
||||
));
|
||||
let foo = Function {
|
||||
id: "foo".to_string(),
|
||||
arguments: Vec::<Parameter>::new(),
|
||||
statements: foo_statements
|
||||
};
|
||||
|
||||
let mut checker = Checker::new();
|
||||
assert_eq!(checker.check_function(foo), Ok(()));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue