From a634b1cf800500186c308d05225dd77621615076 Mon Sep 17 00:00:00 2001 From: schaeff Date: Wed, 28 Aug 2019 18:11:58 +0200 Subject: [PATCH] simplify pest ast, add tests for from_ast --- zokrates_core/src/absy/from_ast.rs | 184 ++++++++++++++++++++++++++++- zokrates_core/src/absy/mod.rs | 4 +- zokrates_core/src/semantics.rs | 2 +- zokrates_pest_ast/src/lib.rs | 21 ++-- 4 files changed, 196 insertions(+), 15 deletions(-) diff --git a/zokrates_core/src/absy/from_ast.rs b/zokrates_core/src/absy/from_ast.rs index 124e12fe..1a317d94 100644 --- a/zokrates_core/src/absy/from_ast.rs +++ b/zokrates_core/src/absy/from_ast.rs @@ -530,7 +530,7 @@ impl<'ast> From> for Type { pest::BasicType::Boolean(_) => Type::Boolean, }; - t.size + t.dimensions .into_iter() .map(|s| match s { pest::Expression::Constant(c) => match c { @@ -668,4 +668,186 @@ mod tests { assert_eq!(absy::Module::::from(ast), expected); } + + mod types { + use super::*; + + /// Helper method to generate the ast for `def main(private {ty} a) -> (): return` which we use to check ty + fn wrap(ty: types::Type) -> absy::Module<'static, FieldPrime> { + absy::Module { + functions: vec![absy::FunctionDeclaration { + id: "main", + symbol: absy::FunctionSymbol::Here( + absy::Function { + arguments: vec![absy::Parameter::private( + absy::Variable::new("a", ty.clone()).into(), + ) + .into()], + statements: vec![absy::Statement::Return( + absy::ExpressionList { + expressions: vec![], + } + .into(), + ) + .into()], + signature: absy::Signature::new().inputs(vec![ty]), + } + .into(), + ), + } + .into()], + imports: vec![], + } + } + + #[test] + fn array() { + let vectors = vec![ + ("field", types::Type::FieldElement), + ("bool", types::Type::Boolean), + ( + "field[2]", + types::Type::Array(box types::Type::FieldElement, 2), + ), + ( + "field[2][3]", + types::Type::Array(box Type::Array(box types::Type::FieldElement, 2), 3), + ), + ( + "bool[2][3]", + types::Type::Array(box Type::Array(box types::Type::Boolean, 2), 3), + ), + ]; + + for (ty, expected) in vectors { + let source = format!("def main(private {} a) -> (): return", ty); + let expected = wrap(expected); + let ast = pest::generate_ast(&source).unwrap(); + assert_eq!(absy::Module::::from(ast), expected); + } + } + } + + mod postfix { + use super::*; + fn wrap(expression: absy::Expression<'static, FieldPrime>) -> absy::Module { + absy::Module { + functions: vec![absy::FunctionDeclaration { + id: "main", + symbol: absy::FunctionSymbol::Here( + absy::Function { + arguments: vec![], + statements: vec![absy::Statement::Return( + absy::ExpressionList { + expressions: vec![expression.into()], + } + .into(), + ) + .into()], + signature: absy::Signature::new(), + } + .into(), + ), + } + .into()], + imports: vec![], + } + } + + #[test] + fn success() { + // we basically accept `()?[]*` : an optional call at first, then only array accesses + + let vectors = vec![ + ("a", absy::Expression::Identifier("a").into()), + ( + "a[3]", + absy::Expression::Select( + box absy::Expression::Identifier("a").into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(3)).into(), + ) + .into(), + ), + ), + ( + "a[3][4]", + absy::Expression::Select( + box absy::Expression::Select( + box absy::Expression::Identifier("a").into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(3)).into(), + ) + .into(), + ) + .into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(4)).into(), + ) + .into(), + ), + ), + ( + "a(3)[4]", + absy::Expression::Select( + box absy::Expression::FunctionCall( + "a", + vec![absy::Expression::FieldConstant(FieldPrime::from(3)).into()], + ) + .into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(4)).into(), + ) + .into(), + ), + ), + ( + "a(3)[4][5]", + absy::Expression::Select( + box absy::Expression::Select( + box absy::Expression::FunctionCall( + "a", + vec![absy::Expression::FieldConstant(FieldPrime::from(3)).into()], + ) + .into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(4)).into(), + ) + .into(), + ) + .into(), + box absy::RangeOrExpression::Expression( + absy::Expression::FieldConstant(FieldPrime::from(5)).into(), + ) + .into(), + ), + ), + ]; + + for (source, expected) in vectors { + let source = format!("def main() -> (): return {}", source); + let expected = wrap(expected); + let ast = pest::generate_ast(&source).unwrap(); + assert_eq!(absy::Module::::from(ast), expected); + } + } + + #[test] + #[should_panic] + fn call_array_element() { + // a call after an array access should be rejected + let source = "def main() -> (): return a[2](3)"; + let ast = pest::generate_ast(&source).unwrap(); + absy::Module::::from(ast); + } + + #[test] + #[should_panic] + fn call_call_result() { + // a call after a call should be rejected + let source = "def main() -> (): return a(2)(3)"; + let ast = pest::generate_ast(&source).unwrap(); + absy::Module::::from(ast); + } + } } diff --git a/zokrates_core/src/absy/mod.rs b/zokrates_core/src/absy/mod.rs index 620029db..4250b833 100644 --- a/zokrates_core/src/absy/mod.rs +++ b/zokrates_core/src/absy/mod.rs @@ -505,7 +505,9 @@ impl<'ast, T: Field> fmt::Debug for Expression<'ast, T> { f.debug_list().entries(exprs.iter()).finish()?; write!(f, "]") } - Expression::Select(ref array, ref index) => write!(f, "{}[{}]", array, index), + Expression::Select(ref array, ref index) => { + write!(f, "Select({:?}, {:?})", array, index) + } Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs), } } diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 15956572..438adeb8 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -691,7 +691,7 @@ impl<'ast> Checker<'ast> { pos: Some(pos), message: format!( - "Expected spread operator to apply on field element array, found {}", + "Expected spread operator to apply on array, found {}", e.get_type() ), }), diff --git a/zokrates_pest_ast/src/lib.rs b/zokrates_pest_ast/src/lib.rs index 68a7467e..8eacafc6 100644 --- a/zokrates_pest_ast/src/lib.rs +++ b/zokrates_pest_ast/src/lib.rs @@ -9,8 +9,8 @@ extern crate lazy_static; pub use ast::{ Access, ArrayAccess, ArrayInitializerExpression, ArrayType, AssertionStatement, Assignee, - AssignmentStatement, BasicType, BinaryExpression, BinaryOperator, CallAccess, - ConstantExpression, DefinitionStatement, Expression, File, FromExpression, Function, + AssignmentStatement, BasicType, BinaryExpression, BinaryOperator, BooleanType, CallAccess, + ConstantExpression, DefinitionStatement, Expression, FieldType, File, FromExpression, Function, IdentifierExpression, ImportDirective, ImportSource, InlineArrayExpression, IterationStatement, MultiAssignmentStatement, Parameter, PostfixExpression, Range, RangeOrExpression, ReturnStatement, Span, Spread, SpreadOrExpression, Statement, TernaryExpression, ToExpression, @@ -193,36 +193,33 @@ mod ast { #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty))] pub enum Type<'ast> { - Basic(BasicType<'ast>), + Basic(BasicType), Array(ArrayType<'ast>), } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_basic))] - pub enum BasicType<'ast> { + pub enum BasicType { Field(FieldType), - Boolean(BooleanType<'ast>), + Boolean(BooleanType), } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_field))] - pub struct FieldType {} + pub struct FieldType; #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_array))] pub struct ArrayType<'ast> { - pub ty: BasicType<'ast>, - pub size: Vec>, + pub ty: BasicType, + pub dimensions: Vec>, #[pest_ast(outer())] pub span: Span<'ast>, } #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::ty_bool))] - pub struct BooleanType<'ast> { - #[pest_ast(outer())] - pub span: Span<'ast>, - } + pub struct BooleanType; #[derive(Debug, FromPest, PartialEq, Clone)] #[pest_ast(rule(Rule::parameter))]