From a3b95befbc602126085407da45b625683e9a8084 Mon Sep 17 00:00:00 2001 From: tohrnii <100405913+tohrnii@users.noreply.github.com> Date: Fri, 4 Nov 2022 15:59:39 +0000 Subject: [PATCH 1/5] feat(parser): add support for parsing of constants --- parser/src/ast/constants.rs | 32 +++++++++++++++++++++++++++++++ parser/src/ast/mod.rs | 4 ++++ parser/src/lexer/mod.rs | 4 ++++ parser/src/parser/grammar.lalrpop | 28 +++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 parser/src/ast/constants.rs diff --git a/parser/src/ast/constants.rs b/parser/src/ast/constants.rs new file mode 100644 index 00000000..f508f624 --- /dev/null +++ b/parser/src/ast/constants.rs @@ -0,0 +1,32 @@ +// CONSTANTS +// ================================================================================================ + +use super::Identifier; + +/// Stores a constant's name and value. There are three types of constants: +/// - Scalar: 1, 2, 3 +/// - Vector: \[1, 2, 3\] +/// - Matrix: \[\[1, 2, 3\], \[4, 5, 6\]\] +#[derive(Debug, PartialEq, Eq)] +pub struct Constant { + pub name: Identifier, + pub value: ConstantType, +} + +impl Constant { + /// Returns a new instance of a [Constant] + pub fn new(name: Identifier, value: ConstantType) -> Self { + Self { name, value } + } +} + +/// Type of constant. Constants can be of 3 types: +/// - Scalar: 1, 2, 3 +/// - Vector: \[1, 2, 3\] +/// - Matrix: \[\[1, 2, 3\], \[4, 5, 6\]\] +#[derive(Debug, PartialEq, Eq)] +pub enum ConstantType { + Scalar(u64), + Vector(Vec), + Matrix(Vec>), +} diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 35f319ee..7b23b6a1 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -1,5 +1,8 @@ use std::fmt; +pub mod constants; +use constants::Constant; + pub mod pub_inputs; pub use pub_inputs::PublicInput; @@ -36,6 +39,7 @@ pub struct Source(pub Vec); #[derive(Debug, PartialEq)] pub enum SourceSection { AirDef(Identifier), + Constants(Vec), TraceCols(TraceCols), PublicInputs(Vec), PeriodicColumns(Vec), diff --git a/parser/src/lexer/mod.rs b/parser/src/lexer/mod.rs index 753daebd..ba63db0f 100644 --- a/parser/src/lexer/mod.rs +++ b/parser/src/lexer/mod.rs @@ -27,6 +27,10 @@ pub enum Token { #[token("def")] Def, + /// Used to declare constants in the AIR constraints module. + #[token("constants")] + Constants, + /// Used to declare trace columns section in the AIR constraints module. #[token("trace_columns")] TraceColumnns, diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index 305f21a9..11ef86fe 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -2,6 +2,7 @@ use crate::{ ast::{ boundary_constraints::{Boundary, BoundaryConstraints, BoundaryConstraint, BoundaryExpr}, transition_constraints::{TransitionConstraint, TransitionConstraints, TransitionExpr}, + constants::{Constant, ConstantType}, Identifier, Source, SourceSection, TraceCols, PublicInput, PeriodicColumn }, error::{Error, ParseError::{InvalidInt, InvalidTraceCols, MissingMainTraceCols}}, lexer::Token }; @@ -19,6 +20,7 @@ pub Source: Source = { SourceSection: SourceSection = { AirDef => SourceSection::AirDef(<>), + Constants => SourceSection::Constants(<>), TraceCols => SourceSection::TraceCols(<>), PublicInputs => SourceSection::PublicInputs(<>), PeriodicColumns => SourceSection::PeriodicColumns(<>), @@ -58,6 +60,22 @@ AuxCols: Vec = { "aux" ":" "[" > "]" => aux_cols, } +// CONSTANTS +// ================================================================================================ + +// Constants section is optional but cannot be empty. Atleast one constant is required. +Constants: Vec = { + "constants" ":" => constants +} + +Constant: Constant = { + ":" => + Constant::new(name, ConstantType::Scalar(scalar_value)), + ":" "[" > "]" => + Constant::new(name, ConstantType::Vector(vector_value)), + ":" "[" >> "]" => + Constant::new(name, ConstantType::Matrix(matrix_value)), +} // PUBLIC INPUTS // ================================================================================================ @@ -166,6 +184,15 @@ CommaElems: Vec = { v } } + +Vector: Vec = { + "[" )*> "]" => { + let mut v = v; + v.insert(0, e); + v + } +} + Identifier: Identifier = { => Identifier(n.to_string()) } @@ -189,6 +216,7 @@ extern { r"[0-9]+" => Token::Num(), "def" => Token::Def, "trace_columns" => Token::TraceColumnns, + "constants" => Token::Constants, "main" => Token::Main, "aux" => Token::Aux, "public_inputs" => Token::PublicInputs, From 5738f0afa867307f8009072e5a5c11a7828e4395 Mon Sep 17 00:00:00 2001 From: tohrnii <100405913+tohrnii@users.noreply.github.com> Date: Fri, 4 Nov 2022 15:59:56 +0000 Subject: [PATCH 2/5] test: add tests for parsing and lexing of constants --- parser/src/ast/constants.rs | 4 +- parser/src/lexer/tests/constants.rs | 101 +++++++++++++++++++++++++++ parser/src/lexer/tests/mod.rs | 1 + parser/src/parser/tests/constants.rs | 62 ++++++++++++++++ parser/src/parser/tests/mod.rs | 1 + 5 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 parser/src/lexer/tests/constants.rs create mode 100644 parser/src/parser/tests/constants.rs diff --git a/parser/src/ast/constants.rs b/parser/src/ast/constants.rs index f508f624..a84766cf 100644 --- a/parser/src/ast/constants.rs +++ b/parser/src/ast/constants.rs @@ -4,7 +4,7 @@ use super::Identifier; /// Stores a constant's name and value. There are three types of constants: -/// - Scalar: 1, 2, 3 +/// - Scalar: 123 /// - Vector: \[1, 2, 3\] /// - Matrix: \[\[1, 2, 3\], \[4, 5, 6\]\] #[derive(Debug, PartialEq, Eq)] @@ -21,7 +21,7 @@ impl Constant { } /// Type of constant. Constants can be of 3 types: -/// - Scalar: 1, 2, 3 +/// - Scalar: 123 /// - Vector: \[1, 2, 3\] /// - Matrix: \[\[1, 2, 3\], \[4, 5, 6\]\] #[derive(Debug, PartialEq, Eq)] diff --git a/parser/src/lexer/tests/constants.rs b/parser/src/lexer/tests/constants.rs new file mode 100644 index 00000000..22fd3ca8 --- /dev/null +++ b/parser/src/lexer/tests/constants.rs @@ -0,0 +1,101 @@ +use super::{expect_valid_tokenization, Token}; + +#[test] +fn constants_scalar() { + let source = " +constants: + a: 1 + b: 2"; + + let tokens = vec![ + Token::Constants, + Token::Colon, + Token::Ident("a".to_string()), + Token::Colon, + Token::Num("1".to_string()), + Token::Ident("b".to_string()), + Token::Colon, + Token::Num("2".to_string()), + ]; + expect_valid_tokenization(source, tokens); +} + +#[test] +fn constants_vector() { + let source = " +constants: + a: [1, 2, 3, 4] + b: [5, 6, 7, 8]"; + + let tokens = vec![ + Token::Constants, + Token::Colon, + Token::Ident("a".to_string()), + Token::Colon, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Comma, + Token::Num("2".to_string()), + Token::Comma, + Token::Num("3".to_string()), + Token::Comma, + Token::Num("4".to_string()), + Token::Rsqb, + Token::Ident("b".to_string()), + Token::Colon, + Token::Lsqb, + Token::Num("5".to_string()), + Token::Comma, + Token::Num("6".to_string()), + Token::Comma, + Token::Num("7".to_string()), + Token::Comma, + Token::Num("8".to_string()), + Token::Rsqb, + ]; + expect_valid_tokenization(source, tokens); +} + +#[test] +fn constants_matrix() { + let source = " + constants: + a: [[1, 2], [3, 4]] + b: [[5, 6], [7, 8]]"; + + let tokens = vec![ + Token::Constants, + Token::Colon, + Token::Ident("a".to_string()), + Token::Colon, + Token::Lsqb, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Comma, + Token::Num("2".to_string()), + Token::Rsqb, + Token::Comma, + Token::Lsqb, + Token::Num("3".to_string()), + Token::Comma, + Token::Num("4".to_string()), + Token::Rsqb, + Token::Rsqb, + Token::Ident("b".to_string()), + Token::Colon, + Token::Lsqb, + Token::Lsqb, + Token::Num("5".to_string()), + Token::Comma, + Token::Num("6".to_string()), + Token::Rsqb, + Token::Comma, + Token::Lsqb, + Token::Num("7".to_string()), + Token::Comma, + Token::Num("8".to_string()), + Token::Rsqb, + Token::Rsqb, + ]; + expect_valid_tokenization(source, tokens); +} diff --git a/parser/src/lexer/tests/mod.rs b/parser/src/lexer/tests/mod.rs index 943a0dc4..c6ad3fae 100644 --- a/parser/src/lexer/tests/mod.rs +++ b/parser/src/lexer/tests/mod.rs @@ -4,6 +4,7 @@ use crate::{ }; mod boundary_constraints; +mod constants; mod expressions; mod identifiers; mod periodic_columns; diff --git a/parser/src/parser/tests/constants.rs b/parser/src/parser/tests/constants.rs new file mode 100644 index 00000000..6693841b --- /dev/null +++ b/parser/src/parser/tests/constants.rs @@ -0,0 +1,62 @@ +use crate::ast::constants::{Constant, ConstantType}; + +use super::{build_parse_test, Identifier, Source, SourceSection}; + +// CONSTANTS +// ================================================================================================ + +#[test] +fn constants_scalars() { + let source = "constants: + a: 1 + b: 2"; + let expected = Source(vec![SourceSection::Constants(vec![ + Constant::new(Identifier("a".to_string()), ConstantType::Scalar(1)), + Constant::new(Identifier("b".to_string()), ConstantType::Scalar(2)), + ])]); + build_parse_test!(source).expect_ast(expected); +} + +#[test] +fn constants_vectors() { + let source = "constants: + a: [1, 2, 3, 4] + b: [5, 6, 7, 8]"; + let expected = Source(vec![SourceSection::Constants(vec![ + Constant::new( + Identifier("a".to_string()), + ConstantType::Vector(vec![1, 2, 3, 4]), + ), + Constant::new( + Identifier("b".to_string()), + ConstantType::Vector(vec![5, 6, 7, 8]), + ), + ])]); + build_parse_test!(source).expect_ast(expected); +} + +#[test] +fn constants_matrices() { + let source = "constants: + a: [[1, 2], [3, 4]] + b: [[5, 6], [7, 8]]"; + let expected = Source(vec![SourceSection::Constants(vec![ + Constant::new( + Identifier("a".to_string()), + ConstantType::Matrix(vec![vec![1, 2], vec![3, 4]]), + ), + Constant::new( + Identifier("b".to_string()), + ConstantType::Matrix(vec![vec![5, 6], vec![7, 8]]), + ), + ])]); + build_parse_test!(source).expect_ast(expected); +} + +#[test] +fn error_empty_constants_section() { + let source = " + constants: + "; + assert!(build_parse_test!(source).parse().is_err()); +} diff --git a/parser/src/parser/tests/mod.rs b/parser/src/parser/tests/mod.rs index 4d5d810f..281f43e6 100644 --- a/parser/src/parser/tests/mod.rs +++ b/parser/src/parser/tests/mod.rs @@ -16,6 +16,7 @@ mod trace_columns; mod transition_constraints; mod comments; +mod constants; mod expressions; mod identifiers; From 35a7f436dead69a9cd17bf381655aa52703b0b70 Mon Sep 17 00:00:00 2001 From: tohrnii <100405913+tohrnii@users.noreply.github.com> Date: Sun, 13 Nov 2022 07:23:14 +0000 Subject: [PATCH 3/5] feat(parser): add support for constant access in constraints --- .../src/air/boundary_constraints.rs | 3 +- ir/src/boundary_constraints.rs | 2 +- ir/src/transition_constraints/graph.rs | 1 + parser/src/ast/boundary_constraints.rs | 11 ++- parser/src/ast/transition_constraints.rs | 2 + parser/src/lexer/tests/constants.rs | 70 +++++++++++++++++++ parser/src/parser/grammar.lalrpop | 12 +++- .../src/parser/tests/boundary_constraints.rs | 49 +++++++++++-- .../parser/tests/transition_constraints.rs | 47 +++++++++++++ 9 files changed, 182 insertions(+), 15 deletions(-) diff --git a/codegen/winterfell/src/air/boundary_constraints.rs b/codegen/winterfell/src/air/boundary_constraints.rs index bc3a3b68..eb8ea45e 100644 --- a/codegen/winterfell/src/air/boundary_constraints.rs +++ b/codegen/winterfell/src/air/boundary_constraints.rs @@ -103,7 +103,7 @@ impl Codegen for BoundaryExpr { format!("Felt::new({})", value) } } - Self::PubInput(name, index) => format!("self.{}[{}]", name, index), + Self::VecElem(name, index) => format!("self.{}[{}]", name, index), Self::Rand(index) => { format!("aux_rand_elements.get_segment_elements(0)[{}]", index) } @@ -131,6 +131,7 @@ impl Codegen for BoundaryExpr { Self::Exp(lhs, rhs) => { format!("({}).exp({})", lhs.to_string(is_aux_constraint), rhs) } + BoundaryExpr::Var(_) | BoundaryExpr::MatrixElem(_, _, _) => todo!(), } } } diff --git a/ir/src/boundary_constraints.rs b/ir/src/boundary_constraints.rs index a5662c3b..519cdf8d 100644 --- a/ir/src/boundary_constraints.rs +++ b/ir/src/boundary_constraints.rs @@ -121,7 +121,7 @@ fn validate_expression( expr: &ast::BoundaryExpr, ) -> Result<(), SemanticError> { match expr { - BoundaryExpr::PubInput(Identifier(name), index) => { + BoundaryExpr::VecElem(Identifier(name), index) => { symbol_table.validate_public_input(name, *index) } BoundaryExpr::Add(lhs, rhs) | BoundaryExpr::Sub(lhs, rhs) => { diff --git a/ir/src/transition_constraints/graph.rs b/ir/src/transition_constraints/graph.rs index f4b0677b..fe4e07eb 100644 --- a/ir/src/transition_constraints/graph.rs +++ b/ir/src/transition_constraints/graph.rs @@ -139,6 +139,7 @@ impl AlgebraicGraph { let node_index = self.insert_op(Operation::Exp(lhs, rhs as usize)); Ok((constraint_type, node_index)) } + TransitionExpr::VecElem(_, _) | TransitionExpr::MatrixElem(_, _, _) => todo!(), } } diff --git a/parser/src/ast/boundary_constraints.rs b/parser/src/ast/boundary_constraints.rs index dfa846d1..ad19809b 100644 --- a/parser/src/ast/boundary_constraints.rs +++ b/parser/src/ast/boundary_constraints.rs @@ -61,9 +61,14 @@ impl Display for Boundary { #[derive(Debug, PartialEq, Clone)] pub enum BoundaryExpr { Const(u64), - /// Reference to a public input element, identified by the name of a public input array and the - /// index of the cell. - PubInput(Identifier, usize), + /// Represents any named constant or variable. + Var(Identifier), + /// Represents an element inside a constant or variable vector. The index is the index of + /// this value inside the vector. + VecElem(Identifier, usize), + /// Represents an element inside a constant or variable matrix. Indices idx_row and idx_col + /// are the indices of this value inside the matrix. + MatrixElem(Identifier, usize, usize), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. Rand(usize), diff --git a/parser/src/ast/transition_constraints.rs b/parser/src/ast/transition_constraints.rs index 50244ced..7891c633 100644 --- a/parser/src/ast/transition_constraints.rs +++ b/parser/src/ast/transition_constraints.rs @@ -33,6 +33,8 @@ impl TransitionConstraint { pub enum TransitionExpr { Const(u64), Var(Identifier), + VecElem(Identifier, usize), + MatrixElem(Identifier, usize, usize), Next(Identifier), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. diff --git a/parser/src/lexer/tests/constants.rs b/parser/src/lexer/tests/constants.rs index 22fd3ca8..10509c7f 100644 --- a/parser/src/lexer/tests/constants.rs +++ b/parser/src/lexer/tests/constants.rs @@ -99,3 +99,73 @@ fn constants_matrix() { ]; expect_valid_tokenization(source, tokens); } + +#[test] +fn constants_access_inside_boundary_expr() { + let source = " + boundary_constraints: + enf clk.first = a + b[0] + enf clk.last = c[0][1] + "; + + let tokens = vec![ + Token::BoundaryConstraints, + Token::Colon, + Token::Enf, + Token::Ident("clk".to_string()), + Token::Dot, + Token::First, + Token::Equal, + Token::Ident("a".to_string()), + Token::Plus, + Token::Ident("b".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Enf, + Token::Ident("clk".to_string()), + Token::Dot, + Token::Last, + Token::Equal, + Token::Ident("c".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Rsqb, + ]; + expect_valid_tokenization(source, tokens); +} + +#[test] +fn constants_access_inside_transition_expr() { + let source = " + transition_constraints: + enf clk * 2^a = b[0] + c[0][1] + "; + let tokens = vec![ + Token::TransitionConstraints, + Token::Colon, + Token::Enf, + Token::Ident("clk".to_string()), + Token::Mul, + Token::Num("2".to_string()), + Token::Exp, + Token::Ident("a".to_string()), + Token::Equal, + Token::Ident("b".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Plus, + Token::Ident("c".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Rsqb, + ]; + expect_valid_tokenization(source, tokens); +} diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index 11ef86fe..3246a1a0 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -137,7 +137,10 @@ BoundaryAtom: BoundaryExpr = { "^" => BoundaryExpr::Exp(Box::new(lexpr), num), "$rand" "[" "]" => BoundaryExpr::Rand(n as usize), => BoundaryExpr::Const(n), - "[" "]" => BoundaryExpr::PubInput(ident, index as usize) + => BoundaryExpr::Var(ident), + "[" "]" => BoundaryExpr::VecElem(ident, idx as usize), + "[" "]" "[" "]" => + BoundaryExpr::MatrixElem(ident, idx_row as usize, idx_col as usize) } // TRANSITION CONSTRAINTS @@ -170,8 +173,11 @@ TransitionAtom: TransitionExpr = { "^" => TransitionExpr::Exp(Box::new(lexpr), num), "$rand" "[" "]" => TransitionExpr::Rand(n as usize), => TransitionExpr::Const(n), - => TransitionExpr::Var(s), - "'" => TransitionExpr::Next(s) + => TransitionExpr::Var(ident), + "[" "]" => TransitionExpr::VecElem(ident, idx as usize), + "[" "]" "[" "]" => + TransitionExpr::MatrixElem(ident, idx_row as usize, idx_col as usize), + "'" => TransitionExpr::Next(ident) } // ATOMS diff --git a/parser/src/parser/tests/boundary_constraints.rs b/parser/src/parser/tests/boundary_constraints.rs index 60072e3c..b0399ce9 100644 --- a/parser/src/parser/tests/boundary_constraints.rs +++ b/parser/src/parser/tests/boundary_constraints.rs @@ -2,6 +2,10 @@ use super::{ build_parse_test, Boundary, BoundaryConstraint, BoundaryConstraints, BoundaryExpr, Identifier, Source, SourceSection, }; +use crate::ast::constants::{ + Constant, + ConstantType::{Matrix, Scalar, Vector}, +}; // BOUNDARY CONSTRAINTS // ================================================================================================ @@ -83,7 +87,7 @@ fn boundary_constraint_with_pub_input() { boundary_constraints: vec![BoundaryConstraint::new( Identifier("clk".to_string()), Boundary::First, - BoundaryExpr::PubInput(Identifier("a".to_string()), 0), + BoundaryExpr::VecElem(Identifier("a".to_string()), 0), )], }, )]); @@ -103,7 +107,7 @@ fn boundary_constraint_with_expr() { BoundaryExpr::Add( Box::new(BoundaryExpr::Add( Box::new(BoundaryExpr::Const(5)), - Box::new(BoundaryExpr::PubInput(Identifier("a".to_string()), 3)), + Box::new(BoundaryExpr::VecElem(Identifier("a".to_string()), 3)), )), Box::new(BoundaryExpr::Const(6)), ), @@ -114,13 +118,44 @@ fn boundary_constraint_with_expr() { } #[test] -fn err_boundary_constraint_with_identifier() { - // TODO: ending the constraint with a gives "UnrecognizedEOF" error. These errors should be - // improved to be more useful and consistent. +fn boundary_constraint_with_const() { let source = " + constants: + a: 1 + b: [0, 1] + c: [[0, 1], [1, 0]] boundary_constraints: - enf clk.first = a + 5"; - build_parse_test!(source).expect_unrecognized_token(); + enf clk.first = a + b[1] - c[0][1]"; + let expected = Source(vec![ + SourceSection::Constants(vec![ + Constant { + name: Identifier("a".to_string()), + value: Scalar(1), + }, + Constant { + name: Identifier("b".to_string()), + value: Vector(vec![0, 1]), + }, + Constant { + name: Identifier("c".to_string()), + value: Matrix(vec![vec![0, 1], vec![1, 0]]), + }, + ]), + SourceSection::BoundaryConstraints(BoundaryConstraints { + boundary_constraints: vec![BoundaryConstraint::new( + Identifier("clk".to_string()), + Boundary::First, + BoundaryExpr::Sub( + Box::new(BoundaryExpr::Add( + Box::new(BoundaryExpr::Var(Identifier("a".to_string()))), + Box::new(BoundaryExpr::VecElem(Identifier("b".to_string()), 1)), + )), + Box::new(BoundaryExpr::MatrixElem(Identifier("c".to_string()), 0, 1)), + ), + )], + }), + ]); + build_parse_test!(source).expect_ast(expected); } #[test] diff --git a/parser/src/parser/tests/transition_constraints.rs b/parser/src/parser/tests/transition_constraints.rs index f3c0ae41..9983d0b6 100644 --- a/parser/src/parser/tests/transition_constraints.rs +++ b/parser/src/parser/tests/transition_constraints.rs @@ -2,6 +2,9 @@ use super::{ build_parse_test, Identifier, Source, SourceSection, TransitionConstraint, TransitionConstraints, TransitionExpr, }; +use crate::ast::constants::{ + Constant, ConstantType::Matrix, ConstantType::Scalar, ConstantType::Vector, +}; // TRANSITION CONSTRAINTS // ================================================================================================ @@ -99,6 +102,50 @@ fn transition_constraint_with_random_value() { build_parse_test!(source).expect_ast(expected); } +#[test] +fn transition_constraint_with_constants() { + let source = " + constants: + a: 0 + b: [0, 1] + c: [[0, 1], [1, 0]] + transition_constraints: + enf clk + a = b[1] + c[1][1]"; + let expected = Source(vec![ + SourceSection::Constants(vec![ + Constant { + name: Identifier("a".to_string()), + value: Scalar(0), + }, + Constant { + name: Identifier("b".to_string()), + value: Vector(vec![0, 1]), + }, + Constant { + name: Identifier("c".to_string()), + value: Matrix(vec![vec![0, 1], vec![1, 0]]), + }, + ]), + SourceSection::TransitionConstraints(TransitionConstraints { + transition_constraints: vec![TransitionConstraint::new( + TransitionExpr::Add( + Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Var(Identifier("a".to_string()))), + ), + TransitionExpr::Add( + Box::new(TransitionExpr::VecElem(Identifier("b".to_string()), 1)), + Box::new(TransitionExpr::MatrixElem( + Identifier("c".to_string()), + 1, + 1, + )), + ), + )], + }), + ]); + build_parse_test!(source).expect_ast(expected); +} + // UNRECOGNIZED TOKEN ERRORS // ================================================================================================ From 2e436440c885f7836568980e497d0d2c07070946 Mon Sep 17 00:00:00 2001 From: tohrnii <100405913+tohrnii@users.noreply.github.com> Date: Tue, 29 Nov 2022 02:38:52 +0000 Subject: [PATCH 4/5] refactor: Refactor constant parsing - Add VectorAccess and MatrixAccess atomic types - Enforce uppercase for constant names --- .../src/air/boundary_constraints.rs | 6 +- ir/src/boundary_constraints.rs | 6 +- ir/src/transition_constraints/graph.rs | 4 +- parser/src/ast/boundary_constraints.rs | 8 +-- parser/src/ast/constants.rs | 14 +++- parser/src/ast/mod.rs | 52 ++++++++++++++ parser/src/ast/transition_constraints.rs | 8 +-- parser/src/error.rs | 1 + parser/src/parser/grammar.lalrpop | 70 +++++++++++++------ .../src/parser/tests/boundary_constraints.rs | 66 +++++++++-------- parser/src/parser/tests/constants.rs | 59 ++++++++++++---- parser/src/parser/tests/expressions.rs | 20 +++--- parser/src/parser/tests/mod.rs | 2 +- .../parser/tests/transition_constraints.rs | 58 ++++++++------- 14 files changed, 252 insertions(+), 122 deletions(-) diff --git a/codegen/winterfell/src/air/boundary_constraints.rs b/codegen/winterfell/src/air/boundary_constraints.rs index eb8ea45e..50ba0ce5 100644 --- a/codegen/winterfell/src/air/boundary_constraints.rs +++ b/codegen/winterfell/src/air/boundary_constraints.rs @@ -103,7 +103,9 @@ impl Codegen for BoundaryExpr { format!("Felt::new({})", value) } } - Self::VecElem(name, index) => format!("self.{}[{}]", name, index), + Self::VecElem(vector_access) => { + format!("self.{}[{}]", vector_access.name(), vector_access.idx()) + } Self::Rand(index) => { format!("aux_rand_elements.get_segment_elements(0)[{}]", index) } @@ -131,7 +133,7 @@ impl Codegen for BoundaryExpr { Self::Exp(lhs, rhs) => { format!("({}).exp({})", lhs.to_string(is_aux_constraint), rhs) } - BoundaryExpr::Var(_) | BoundaryExpr::MatrixElem(_, _, _) => todo!(), + BoundaryExpr::Elem(_) | BoundaryExpr::MatrixElem(_) => todo!(), } } } diff --git a/ir/src/boundary_constraints.rs b/ir/src/boundary_constraints.rs index 519cdf8d..1e4e1105 100644 --- a/ir/src/boundary_constraints.rs +++ b/ir/src/boundary_constraints.rs @@ -1,4 +1,4 @@ -use super::{BTreeMap, BoundaryExpr, Identifier, IdentifierType, SemanticError, SymbolTable}; +use super::{BTreeMap, BoundaryExpr, IdentifierType, SemanticError, SymbolTable}; use parser::ast; // BOUNDARY CONSTRAINTS @@ -121,8 +121,8 @@ fn validate_expression( expr: &ast::BoundaryExpr, ) -> Result<(), SemanticError> { match expr { - BoundaryExpr::VecElem(Identifier(name), index) => { - symbol_table.validate_public_input(name, *index) + BoundaryExpr::VecElem(vector_access) => { + symbol_table.validate_public_input(vector_access.name(), vector_access.idx() as usize) } BoundaryExpr::Add(lhs, rhs) | BoundaryExpr::Sub(lhs, rhs) => { validate_expression(symbol_table, lhs)?; diff --git a/ir/src/transition_constraints/graph.rs b/ir/src/transition_constraints/graph.rs index fe4e07eb..179e289f 100644 --- a/ir/src/transition_constraints/graph.rs +++ b/ir/src/transition_constraints/graph.rs @@ -98,7 +98,7 @@ impl AlgebraicGraph { let node_index = self.insert_op(Operation::Const(value)); Ok((constraint_type, node_index)) } - TransitionExpr::Var(Identifier(ident)) => self.insert_variable(symbol_table, &ident), + TransitionExpr::Elem(Identifier(ident)) => self.insert_variable(symbol_table, &ident), TransitionExpr::Next(Identifier(ident)) => self.insert_next(symbol_table, &ident), TransitionExpr::Rand(index) => { let constraint_type = ConstraintType::Auxiliary; @@ -139,7 +139,7 @@ impl AlgebraicGraph { let node_index = self.insert_op(Operation::Exp(lhs, rhs as usize)); Ok((constraint_type, node_index)) } - TransitionExpr::VecElem(_, _) | TransitionExpr::MatrixElem(_, _, _) => todo!(), + TransitionExpr::VecElem(_) | TransitionExpr::MatrixElem(_) => todo!(), } } diff --git a/parser/src/ast/boundary_constraints.rs b/parser/src/ast/boundary_constraints.rs index ad19809b..7581af45 100644 --- a/parser/src/ast/boundary_constraints.rs +++ b/parser/src/ast/boundary_constraints.rs @@ -1,4 +1,4 @@ -use super::Identifier; +use super::{Identifier, MatrixAccess, VectorAccess}; use std::fmt::Display; // BOUNDARY CONSTRAINTS @@ -62,13 +62,13 @@ impl Display for Boundary { pub enum BoundaryExpr { Const(u64), /// Represents any named constant or variable. - Var(Identifier), + Elem(Identifier), /// Represents an element inside a constant or variable vector. The index is the index of /// this value inside the vector. - VecElem(Identifier, usize), + VecElem(VectorAccess), /// Represents an element inside a constant or variable matrix. Indices idx_row and idx_col /// are the indices of this value inside the matrix. - MatrixElem(Identifier, usize, usize), + MatrixElem(MatrixAccess), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. Rand(usize), diff --git a/parser/src/ast/constants.rs b/parser/src/ast/constants.rs index a84766cf..87862d25 100644 --- a/parser/src/ast/constants.rs +++ b/parser/src/ast/constants.rs @@ -9,8 +9,8 @@ use super::Identifier; /// - Matrix: \[\[1, 2, 3\], \[4, 5, 6\]\] #[derive(Debug, PartialEq, Eq)] pub struct Constant { - pub name: Identifier, - pub value: ConstantType, + name: Identifier, + value: ConstantType, } impl Constant { @@ -18,6 +18,16 @@ impl Constant { pub fn new(name: Identifier, value: ConstantType) -> Self { Self { name, value } } + + /// Returns the name of the [Constant] + pub fn name(&self) -> &Identifier { + &self.name + } + + /// Returns the value of the [Constant] + pub fn value(&self) -> &ConstantType { + &self.value + } } /// Type of constant. Constants can be of 3 types: diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 7b23b6a1..a0798171 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -69,3 +69,55 @@ impl fmt::Display for Identifier { write!(f, "{}", &self.0) } } + +/// [VectorAccess] is used to represent an element inside vector at the specified index. +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] +pub struct VectorAccess { + name: Identifier, + idx: u64, +} + +impl VectorAccess { + pub fn new(name: Identifier, idx: u64) -> Self { + Self { name, idx } + } + + pub fn name(&self) -> &str { + &self.name.0 + } + + pub fn idx(&self) -> u64 { + self.idx + } +} + +/// [MatrixAccess] is used to represent an element inside vector at the specified row and column +/// indices. +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] +pub struct MatrixAccess { + name: Identifier, + col_idx: u64, + row_idx: u64, +} + +impl MatrixAccess { + pub fn new(name: Identifier, col_idx: u64, row_idx: u64) -> Self { + Self { + name, + col_idx, + row_idx, + } + } + + pub fn name(&self) -> &str { + &self.name.0 + } + + pub fn col_idx(&self) -> u64 { + self.col_idx + } + + pub fn row_idx(&self) -> u64 { + self.row_idx + } +} diff --git a/parser/src/ast/transition_constraints.rs b/parser/src/ast/transition_constraints.rs index 7891c633..ec289974 100644 --- a/parser/src/ast/transition_constraints.rs +++ b/parser/src/ast/transition_constraints.rs @@ -1,4 +1,4 @@ -use super::Identifier; +use super::{Identifier, MatrixAccess, VectorAccess}; // TRANSITION CONSTRAINTS // ================================================================================================ @@ -32,9 +32,9 @@ impl TransitionConstraint { #[derive(Debug, PartialEq, Clone)] pub enum TransitionExpr { Const(u64), - Var(Identifier), - VecElem(Identifier, usize), - MatrixElem(Identifier, usize, usize), + Elem(Identifier), + VecElem(VectorAccess), + MatrixElem(MatrixAccess), Next(Identifier), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. diff --git a/parser/src/error.rs b/parser/src/error.rs index 0bfa104f..49ebc8d0 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -11,4 +11,5 @@ pub enum ParseError { InvalidInt(String), InvalidTraceCols(String), MissingMainTraceCols(String), + LowercaseConstName(String), } diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index 3246a1a0..5b70b974 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -3,8 +3,10 @@ use crate::{ boundary_constraints::{Boundary, BoundaryConstraints, BoundaryConstraint, BoundaryExpr}, transition_constraints::{TransitionConstraint, TransitionConstraints, TransitionExpr}, constants::{Constant, ConstantType}, - Identifier, Source, SourceSection, TraceCols, PublicInput, PeriodicColumn - }, error::{Error, ParseError::{InvalidInt, InvalidTraceCols, MissingMainTraceCols}}, lexer::Token + Identifier, Source, SourceSection, TraceCols, PublicInput, PeriodicColumn, VectorAccess, + MatrixAccess + }, error::{Error, ParseError::{InvalidInt, InvalidTraceCols, MissingMainTraceCols, + LowercaseConstName}}, lexer::Token }; use std::str::FromStr; use lalrpop_util::ParseError; @@ -44,7 +46,9 @@ TraceCols: TraceCols = { (Some(main_cols), Some(aux_cols)) => Ok(TraceCols { main_cols, aux_cols }), (Some(main_cols), None) => Ok(TraceCols { main_cols, aux_cols: vec![] }), (None, Some(_aux_cols)) => Err(ParseError::User { - error: Error::ParseError(MissingMainTraceCols("Declaration of main trace columns is required".to_string())) + error: Error::ParseError( + MissingMainTraceCols("Declaration of main trace columns is required".to_string()) + ) }), (None, None) => Err(ParseError::User { error: Error::ParseError(InvalidTraceCols("Trace Columns cannot be empty".to_string())) @@ -63,20 +67,33 @@ AuxCols: Vec = { // CONSTANTS // ================================================================================================ -// Constants section is optional but cannot be empty. Atleast one constant is required. +// Constants section is optional but cannot be empty. If the section is declared, then at least one +// constant is required. Constants: Vec = { "constants" ":" => constants } Constant: Constant = { - ":" => + ":" => Constant::new(name, ConstantType::Scalar(scalar_value)), - ":" "[" > "]" => + ":" "[" > "]" => Constant::new(name, ConstantType::Vector(vector_value)), - ":" "[" >> "]" => + ":" "[" >> "]" => Constant::new(name, ConstantType::Matrix(matrix_value)), } +ConstName: Identifier = { + =>? if n.0.chars().all(|v| v.is_uppercase()) { + Ok(Identifier(n.to_string())) + } else { + Err(ParseError::User { + error: Error::ParseError(LowercaseConstName( + format!("The constant name should be uppercase: {}", <>).to_string() + )) + }) + } +} + // PUBLIC INPUTS // ================================================================================================ @@ -98,7 +115,8 @@ PeriodicColumns: Vec = { } PeriodicColumn: PeriodicColumn = { - ":" "[" > "]" => PeriodicColumn::new(name, values), + ":" "[" > "]" => + PeriodicColumn::new(name, values), } // BOUNDARY CONSTRAINTS @@ -122,13 +140,16 @@ Boundary: Boundary = { // --- BOUNDARY CONSTRAINT EXPRESSIONS WITH PRECEDENCE (LOWEST TO HIGHEST) ---------------------- BoundaryExpr: BoundaryExpr = { - "+" => BoundaryExpr::Add(Box::new(lexpr), Box::new(rexpr)), - "-" => BoundaryExpr::Sub(Box::new(lexpr), Box::new(rexpr)), + "+" => + BoundaryExpr::Add(Box::new(lexpr), Box::new(rexpr)), + "-" => + BoundaryExpr::Sub(Box::new(lexpr), Box::new(rexpr)), BoundaryFactor } BoundaryFactor: BoundaryExpr = { - "*" => BoundaryExpr::Mul(Box::new(lexpr), Box::new(rexpr)), + "*" => + BoundaryExpr::Mul(Box::new(lexpr), Box::new(rexpr)), BoundaryAtom } @@ -137,10 +158,11 @@ BoundaryAtom: BoundaryExpr = { "^" => BoundaryExpr::Exp(Box::new(lexpr), num), "$rand" "[" "]" => BoundaryExpr::Rand(n as usize), => BoundaryExpr::Const(n), - => BoundaryExpr::Var(ident), - "[" "]" => BoundaryExpr::VecElem(ident, idx as usize), - "[" "]" "[" "]" => - BoundaryExpr::MatrixElem(ident, idx_row as usize, idx_col as usize) + => BoundaryExpr::Elem(ident), + "[" "]" => + BoundaryExpr::VecElem(VectorAccess::new(ident, idx)), + "[" "]" "[" "]" => + BoundaryExpr::MatrixElem(MatrixAccess::new(ident, col_idx, row_idx)) } // TRANSITION CONSTRAINTS @@ -158,13 +180,16 @@ TransitionConstraint: TransitionConstraint = { // --- TRANSITION CONSTRAINT EXPRESSIONS WITH PRECEDENCE (LOWEST TO HIGHEST) ---------------------- TransitionExpr: TransitionExpr = { - "+" => TransitionExpr::Add(Box::new(lexpr), Box::new(rexpr)), - "-" => TransitionExpr::Sub(Box::new(lexpr), Box::new(rexpr)), + "+" => + TransitionExpr::Add(Box::new(lexpr), Box::new(rexpr)), + "-" => + TransitionExpr::Sub(Box::new(lexpr), Box::new(rexpr)), TransitionFactor } TransitionFactor: TransitionExpr = { - "*" => TransitionExpr::Mul(Box::new(lexpr), Box::new(rexpr)), + "*" => + TransitionExpr::Mul(Box::new(lexpr), Box::new(rexpr)), TransitionAtom } @@ -173,10 +198,11 @@ TransitionAtom: TransitionExpr = { "^" => TransitionExpr::Exp(Box::new(lexpr), num), "$rand" "[" "]" => TransitionExpr::Rand(n as usize), => TransitionExpr::Const(n), - => TransitionExpr::Var(ident), - "[" "]" => TransitionExpr::VecElem(ident, idx as usize), - "[" "]" "[" "]" => - TransitionExpr::MatrixElem(ident, idx_row as usize, idx_col as usize), + => TransitionExpr::Elem(ident), + "[" "]" => + TransitionExpr::VecElem(VectorAccess::new(ident, idx)), + "[" "]" "[" "]" => + TransitionExpr::MatrixElem(MatrixAccess::new(ident, col_idx, row_idx)), "'" => TransitionExpr::Next(ident) } diff --git a/parser/src/parser/tests/boundary_constraints.rs b/parser/src/parser/tests/boundary_constraints.rs index b0399ce9..3fdff6d9 100644 --- a/parser/src/parser/tests/boundary_constraints.rs +++ b/parser/src/parser/tests/boundary_constraints.rs @@ -2,9 +2,12 @@ use super::{ build_parse_test, Boundary, BoundaryConstraint, BoundaryConstraints, BoundaryExpr, Identifier, Source, SourceSection, }; -use crate::ast::constants::{ - Constant, - ConstantType::{Matrix, Scalar, Vector}, +use crate::ast::{ + constants::{ + Constant, + ConstantType::{Matrix, Scalar, Vector}, + }, + MatrixAccess, PublicInput, VectorAccess, }; // BOUNDARY CONSTRAINTS @@ -80,17 +83,20 @@ fn multiple_boundary_constraints() { #[test] fn boundary_constraint_with_pub_input() { let source = " + public_inputs: + a: [16] boundary_constraints: enf clk.first = a[0]"; - let expected = Source(vec![SourceSection::BoundaryConstraints( - BoundaryConstraints { + let expected = Source(vec![ + SourceSection::PublicInputs(vec![PublicInput::new(Identifier("a".to_string()), 16)]), + SourceSection::BoundaryConstraints(BoundaryConstraints { boundary_constraints: vec![BoundaryConstraint::new( Identifier("clk".to_string()), Boundary::First, - BoundaryExpr::VecElem(Identifier("a".to_string()), 0), + BoundaryExpr::VecElem(VectorAccess::new(Identifier("a".to_string()), 0)), )], - }, - )]); + }), + ]); build_parse_test!(source).expect_ast(expected); } @@ -107,7 +113,10 @@ fn boundary_constraint_with_expr() { BoundaryExpr::Add( Box::new(BoundaryExpr::Add( Box::new(BoundaryExpr::Const(5)), - Box::new(BoundaryExpr::VecElem(Identifier("a".to_string()), 3)), + Box::new(BoundaryExpr::VecElem(VectorAccess::new( + Identifier("a".to_string()), + 3, + ))), )), Box::new(BoundaryExpr::Const(6)), ), @@ -121,25 +130,19 @@ fn boundary_constraint_with_expr() { fn boundary_constraint_with_const() { let source = " constants: - a: 1 - b: [0, 1] - c: [[0, 1], [1, 0]] + A: 1 + B: [0, 1] + C: [[0, 1], [1, 0]] boundary_constraints: - enf clk.first = a + b[1] - c[0][1]"; + enf clk.first = A + B[1] - C[0][1]"; let expected = Source(vec![ SourceSection::Constants(vec![ - Constant { - name: Identifier("a".to_string()), - value: Scalar(1), - }, - Constant { - name: Identifier("b".to_string()), - value: Vector(vec![0, 1]), - }, - Constant { - name: Identifier("c".to_string()), - value: Matrix(vec![vec![0, 1], vec![1, 0]]), - }, + Constant::new(Identifier("A".to_string()), Scalar(1)), + Constant::new(Identifier("B".to_string()), Vector(vec![0, 1])), + Constant::new( + Identifier("C".to_string()), + Matrix(vec![vec![0, 1], vec![1, 0]]), + ), ]), SourceSection::BoundaryConstraints(BoundaryConstraints { boundary_constraints: vec![BoundaryConstraint::new( @@ -147,10 +150,17 @@ fn boundary_constraint_with_const() { Boundary::First, BoundaryExpr::Sub( Box::new(BoundaryExpr::Add( - Box::new(BoundaryExpr::Var(Identifier("a".to_string()))), - Box::new(BoundaryExpr::VecElem(Identifier("b".to_string()), 1)), + Box::new(BoundaryExpr::Elem(Identifier("A".to_string()))), + Box::new(BoundaryExpr::VecElem(VectorAccess::new( + Identifier("B".to_string()), + 1, + ))), )), - Box::new(BoundaryExpr::MatrixElem(Identifier("c".to_string()), 0, 1)), + Box::new(BoundaryExpr::MatrixElem(MatrixAccess::new( + Identifier("C".to_string()), + 0, + 1, + ))), ), )], }), diff --git a/parser/src/parser/tests/constants.rs b/parser/src/parser/tests/constants.rs index 6693841b..65d83589 100644 --- a/parser/src/parser/tests/constants.rs +++ b/parser/src/parser/tests/constants.rs @@ -1,6 +1,8 @@ -use crate::ast::constants::{Constant, ConstantType}; - use super::{build_parse_test, Identifier, Source, SourceSection}; +use crate::{ + ast::constants::{Constant, ConstantType}, + error::{Error, ParseError}, +}; // CONSTANTS // ================================================================================================ @@ -8,11 +10,11 @@ use super::{build_parse_test, Identifier, Source, SourceSection}; #[test] fn constants_scalars() { let source = "constants: - a: 1 - b: 2"; + A: 1 + B: 2"; let expected = Source(vec![SourceSection::Constants(vec![ - Constant::new(Identifier("a".to_string()), ConstantType::Scalar(1)), - Constant::new(Identifier("b".to_string()), ConstantType::Scalar(2)), + Constant::new(Identifier("A".to_string()), ConstantType::Scalar(1)), + Constant::new(Identifier("B".to_string()), ConstantType::Scalar(2)), ])]); build_parse_test!(source).expect_ast(expected); } @@ -20,15 +22,33 @@ fn constants_scalars() { #[test] fn constants_vectors() { let source = "constants: - a: [1, 2, 3, 4] - b: [5, 6, 7, 8]"; + A: [1, 2, 3, 4] + B: [5, 6, 7, 8]"; + let expected = Source(vec![SourceSection::Constants(vec![ + Constant::new( + Identifier("A".to_string()), + ConstantType::Vector(vec![1, 2, 3, 4]), + ), + Constant::new( + Identifier("B".to_string()), + ConstantType::Vector(vec![5, 6, 7, 8]), + ), + ])]); + build_parse_test!(source).expect_ast(expected); +} + +#[test] +fn constants_vectors_wrong() { + let source = "constants: + A: [1, 2, 3, 4] + B: [5, 6, 7, 8]"; let expected = Source(vec![SourceSection::Constants(vec![ Constant::new( - Identifier("a".to_string()), + Identifier("A".to_string()), ConstantType::Vector(vec![1, 2, 3, 4]), ), Constant::new( - Identifier("b".to_string()), + Identifier("B".to_string()), ConstantType::Vector(vec![5, 6, 7, 8]), ), ])]); @@ -38,15 +58,15 @@ fn constants_vectors() { #[test] fn constants_matrices() { let source = "constants: - a: [[1, 2], [3, 4]] - b: [[5, 6], [7, 8]]"; + ABC: [[1, 2], [3, 4]] + XYZ: [[5, 6], [7, 8]]"; let expected = Source(vec![SourceSection::Constants(vec![ Constant::new( - Identifier("a".to_string()), + Identifier("ABC".to_string()), ConstantType::Matrix(vec![vec![1, 2], vec![3, 4]]), ), Constant::new( - Identifier("b".to_string()), + Identifier("XYZ".to_string()), ConstantType::Matrix(vec![vec![5, 6], vec![7, 8]]), ), ])]); @@ -60,3 +80,14 @@ fn error_empty_constants_section() { "; assert!(build_parse_test!(source).parse().is_err()); } + +#[test] +fn err_lowercase_constant_name() { + let source = "constants: + Ab: [[1, 2], [3, 4]] + C: [[5, 6], [7, 8]]"; + let error = Error::ParseError(ParseError::LowercaseConstName( + "The constant name should be uppercase: Ab".to_string(), + )); + build_parse_test!(source).expect_error(error); +} diff --git a/parser/src/parser/tests/expressions.rs b/parser/src/parser/tests/expressions.rs index c5824aa2..f0b38a8d 100644 --- a/parser/src/parser/tests/expressions.rs +++ b/parser/src/parser/tests/expressions.rs @@ -17,7 +17,7 @@ fn single_addition() { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Add( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), ), TransitionExpr::Const(0), )], @@ -38,7 +38,7 @@ fn multi_addition() { TransitionExpr::Add( Box::new(TransitionExpr::Add( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), )), Box::new(TransitionExpr::Const(2)), ), @@ -60,7 +60,7 @@ fn single_subtraction() { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Sub( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), ), TransitionExpr::Const(0), )], @@ -81,7 +81,7 @@ fn multi_subtraction() { TransitionExpr::Sub( Box::new(TransitionExpr::Sub( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), )), Box::new(TransitionExpr::Const(1)), ), @@ -103,7 +103,7 @@ fn single_multiplication() { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Mul( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), ), TransitionExpr::Const(0), )], @@ -124,7 +124,7 @@ fn multi_multiplication() { TransitionExpr::Mul( Box::new(TransitionExpr::Mul( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), )), Box::new(TransitionExpr::Const(2)), ), @@ -167,7 +167,7 @@ fn ops_with_parens() { TransitionExpr::Mul( Box::new(TransitionExpr::Add( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), )), Box::new(TransitionExpr::Const(2)), ), @@ -238,7 +238,7 @@ fn multi_arithmetic_ops_same_precedence() { Box::new(TransitionExpr::Sub( Box::new(TransitionExpr::Sub( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), )), Box::new(TransitionExpr::Const(2)), )), @@ -272,7 +272,7 @@ fn multi_arithmetic_ops_different_precedence() { 2, )), Box::new(TransitionExpr::Mul( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), Box::new(TransitionExpr::Const(2)), )), )), @@ -304,7 +304,7 @@ fn multi_arithmetic_ops_different_precedence_w_parens() { Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), Box::new(TransitionExpr::Mul( Box::new(TransitionExpr::Exp( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), 2, )), Box::new(TransitionExpr::Sub( diff --git a/parser/src/parser/tests/mod.rs b/parser/src/parser/tests/mod.rs index 281f43e6..eedeaacd 100644 --- a/parser/src/parser/tests/mod.rs +++ b/parser/src/parser/tests/mod.rs @@ -47,7 +47,7 @@ fn full_air_file() { // clk' = clk + 1 TransitionExpr::Next(Identifier("clk".to_string())), TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), Box::new(TransitionExpr::Const(1)), ), )], diff --git a/parser/src/parser/tests/transition_constraints.rs b/parser/src/parser/tests/transition_constraints.rs index 9983d0b6..286567cb 100644 --- a/parser/src/parser/tests/transition_constraints.rs +++ b/parser/src/parser/tests/transition_constraints.rs @@ -2,8 +2,9 @@ use super::{ build_parse_test, Identifier, Source, SourceSection, TransitionConstraint, TransitionConstraints, TransitionExpr, }; -use crate::ast::constants::{ - Constant, ConstantType::Matrix, ConstantType::Scalar, ConstantType::Vector, +use crate::ast::{ + constants::{Constant, ConstantType::Matrix, ConstantType::Scalar, ConstantType::Vector}, + MatrixAccess, VectorAccess, }; // TRANSITION CONSTRAINTS @@ -19,7 +20,7 @@ fn transition_constraints() { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Next(Identifier("clk".to_string())), TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), Box::new(TransitionExpr::Const(1)), ), )], @@ -47,14 +48,14 @@ fn multiple_transition_constraints() { TransitionConstraint::new( TransitionExpr::Next(Identifier("clk".to_string())), TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), Box::new(TransitionExpr::Const(1)), ), ), TransitionConstraint::new( TransitionExpr::Sub( Box::new(TransitionExpr::Next(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), ), TransitionExpr::Const(1), ), @@ -73,8 +74,8 @@ fn transition_constraint_with_periodic_col() { TransitionConstraints { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("k0".to_string()))), - Box::new(TransitionExpr::Var(Identifier("b".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("k0".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("b".to_string()))), ), TransitionExpr::Const(0), )], @@ -92,7 +93,7 @@ fn transition_constraint_with_random_value() { TransitionConstraints { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("a".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("a".to_string()))), Box::new(TransitionExpr::Rand(1)), ), TransitionExpr::Const(0), @@ -106,39 +107,36 @@ fn transition_constraint_with_random_value() { fn transition_constraint_with_constants() { let source = " constants: - a: 0 - b: [0, 1] - c: [[0, 1], [1, 0]] + A: 0 + B: [0, 1] + C: [[0, 1], [1, 0]] transition_constraints: - enf clk + a = b[1] + c[1][1]"; + enf clk + A = B[1] + C[1][1]"; let expected = Source(vec![ SourceSection::Constants(vec![ - Constant { - name: Identifier("a".to_string()), - value: Scalar(0), - }, - Constant { - name: Identifier("b".to_string()), - value: Vector(vec![0, 1]), - }, - Constant { - name: Identifier("c".to_string()), - value: Matrix(vec![vec![0, 1], vec![1, 0]]), - }, + Constant::new(Identifier("A".to_string()), Scalar(0)), + Constant::new(Identifier("B".to_string()), Vector(vec![0, 1])), + Constant::new( + Identifier("C".to_string()), + Matrix(vec![vec![0, 1], vec![1, 0]]), + ), ]), SourceSection::TransitionConstraints(TransitionConstraints { transition_constraints: vec![TransitionConstraint::new( TransitionExpr::Add( - Box::new(TransitionExpr::Var(Identifier("clk".to_string()))), - Box::new(TransitionExpr::Var(Identifier("a".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("clk".to_string()))), + Box::new(TransitionExpr::Elem(Identifier("A".to_string()))), ), TransitionExpr::Add( - Box::new(TransitionExpr::VecElem(Identifier("b".to_string()), 1)), - Box::new(TransitionExpr::MatrixElem( - Identifier("c".to_string()), + Box::new(TransitionExpr::VecElem(VectorAccess::new( + Identifier("B".to_string()), 1, + ))), + Box::new(TransitionExpr::MatrixElem(MatrixAccess::new( + Identifier("C".to_string()), 1, - )), + 1, + ))), ), )], }), From c1b9bd0041cb9d8701327f3015c77ead0fe1742f Mon Sep 17 00:00:00 2001 From: tohrnii <100405913+tohrnii@users.noreply.github.com> Date: Thu, 1 Dec 2022 02:50:53 +0000 Subject: [PATCH 5/5] test: add more tests for edge cases for const parsing --- .../src/air/boundary_constraints.rs | 4 +- ir/src/boundary_constraints.rs | 4 +- ir/src/transition_constraints/graph.rs | 2 +- parser/src/ast/boundary_constraints.rs | 12 +-- parser/src/ast/mod.rs | 42 +++++--- parser/src/ast/transition_constraints.rs | 9 +- parser/src/error.rs | 2 +- parser/src/lexer/tests/constants.rs | 101 +++++++++++++++--- parser/src/parser/grammar.lalrpop | 47 +++++--- .../{expressions.rs => arithmetic_ops.rs} | 0 .../src/parser/tests/boundary_constraints.rs | 8 +- parser/src/parser/tests/constants.rs | 77 +++++++++---- parser/src/parser/tests/mod.rs | 9 +- .../parser/tests/transition_constraints.rs | 4 +- 14 files changed, 229 insertions(+), 92 deletions(-) rename parser/src/parser/tests/{expressions.rs => arithmetic_ops.rs} (100%) diff --git a/codegen/winterfell/src/air/boundary_constraints.rs b/codegen/winterfell/src/air/boundary_constraints.rs index 50ba0ce5..4e06099c 100644 --- a/codegen/winterfell/src/air/boundary_constraints.rs +++ b/codegen/winterfell/src/air/boundary_constraints.rs @@ -103,7 +103,7 @@ impl Codegen for BoundaryExpr { format!("Felt::new({})", value) } } - Self::VecElem(vector_access) => { + Self::VectorAccess(vector_access) => { format!("self.{}[{}]", vector_access.name(), vector_access.idx()) } Self::Rand(index) => { @@ -133,7 +133,7 @@ impl Codegen for BoundaryExpr { Self::Exp(lhs, rhs) => { format!("({}).exp({})", lhs.to_string(is_aux_constraint), rhs) } - BoundaryExpr::Elem(_) | BoundaryExpr::MatrixElem(_) => todo!(), + BoundaryExpr::Elem(_) | BoundaryExpr::MatrixAccess(_) => todo!(), } } } diff --git a/ir/src/boundary_constraints.rs b/ir/src/boundary_constraints.rs index 1e4e1105..a28682b6 100644 --- a/ir/src/boundary_constraints.rs +++ b/ir/src/boundary_constraints.rs @@ -121,8 +121,8 @@ fn validate_expression( expr: &ast::BoundaryExpr, ) -> Result<(), SemanticError> { match expr { - BoundaryExpr::VecElem(vector_access) => { - symbol_table.validate_public_input(vector_access.name(), vector_access.idx() as usize) + BoundaryExpr::VectorAccess(vector_access) => { + symbol_table.validate_public_input(vector_access.name(), vector_access.idx()) } BoundaryExpr::Add(lhs, rhs) | BoundaryExpr::Sub(lhs, rhs) => { validate_expression(symbol_table, lhs)?; diff --git a/ir/src/transition_constraints/graph.rs b/ir/src/transition_constraints/graph.rs index 179e289f..0b0e2530 100644 --- a/ir/src/transition_constraints/graph.rs +++ b/ir/src/transition_constraints/graph.rs @@ -139,7 +139,7 @@ impl AlgebraicGraph { let node_index = self.insert_op(Operation::Exp(lhs, rhs as usize)); Ok((constraint_type, node_index)) } - TransitionExpr::VecElem(_) | TransitionExpr::MatrixElem(_) => todo!(), + TransitionExpr::VectorAccess(_) | TransitionExpr::MatrixAccess(_) => todo!(), } } diff --git a/parser/src/ast/boundary_constraints.rs b/parser/src/ast/boundary_constraints.rs index 7581af45..c93ac989 100644 --- a/parser/src/ast/boundary_constraints.rs +++ b/parser/src/ast/boundary_constraints.rs @@ -63,12 +63,12 @@ pub enum BoundaryExpr { Const(u64), /// Represents any named constant or variable. Elem(Identifier), - /// Represents an element inside a constant or variable vector. The index is the index of - /// this value inside the vector. - VecElem(VectorAccess), - /// Represents an element inside a constant or variable matrix. Indices idx_row and idx_col - /// are the indices of this value inside the matrix. - MatrixElem(MatrixAccess), + /// Represents an element inside a constant or variable vector. [VectorAccess] contains the + /// name of the vector and the index of the element to access. + VectorAccess(VectorAccess), + /// Represents an element inside a constant or variable matrix. [MatrixAccess] contains the + /// name of the matrix and indices of the element to access. + MatrixAccess(MatrixAccess), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. Rand(usize), diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index a0798171..6b4a0a5b 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -64,6 +64,13 @@ pub struct TraceCols { #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] pub struct Identifier(pub String); +impl Identifier { + /// Returns the name of the identifier. + pub fn name(&self) -> &str { + &self.0 + } +} + impl fmt::Display for Identifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", &self.0) @@ -74,50 +81,57 @@ impl fmt::Display for Identifier { #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] pub struct VectorAccess { name: Identifier, - idx: u64, + idx: usize, } impl VectorAccess { - pub fn new(name: Identifier, idx: u64) -> Self { + /// Creates a new [VectorAccess] instance with the specified identifier name and index. + pub fn new(name: Identifier, idx: usize) -> Self { Self { name, idx } } + /// Returns the name of the vector. pub fn name(&self) -> &str { - &self.name.0 + self.name.name() } - pub fn idx(&self) -> u64 { + /// Returns the index of the vector access. + pub fn idx(&self) -> usize { self.idx } } -/// [MatrixAccess] is used to represent an element inside vector at the specified row and column +/// [MatrixAccess] is used to represent an element inside a matrix at the specified row and column /// indices. #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] pub struct MatrixAccess { name: Identifier, - col_idx: u64, - row_idx: u64, + row_idx: usize, + col_idx: usize, } impl MatrixAccess { - pub fn new(name: Identifier, col_idx: u64, row_idx: u64) -> Self { + /// Creates a new [MatrixAccess] instance with the specified identifier name and indices. + pub fn new(name: Identifier, col_idx: usize, row_idx: usize) -> Self { Self { name, - col_idx, row_idx, + col_idx, } } + /// Returns the name of the matrix. pub fn name(&self) -> &str { - &self.name.0 + self.name.name() } - pub fn col_idx(&self) -> u64 { - self.col_idx + /// Returns the row index of the matrix access. + pub fn row_idx(&self) -> usize { + self.row_idx } - pub fn row_idx(&self) -> u64 { - self.row_idx + /// Returns the column index of the matrix access. + pub fn col_idx(&self) -> usize { + self.col_idx } } diff --git a/parser/src/ast/transition_constraints.rs b/parser/src/ast/transition_constraints.rs index ec289974..a0d460e4 100644 --- a/parser/src/ast/transition_constraints.rs +++ b/parser/src/ast/transition_constraints.rs @@ -32,9 +32,14 @@ impl TransitionConstraint { #[derive(Debug, PartialEq, Clone)] pub enum TransitionExpr { Const(u64), + /// Represents any named constant or variable. Elem(Identifier), - VecElem(VectorAccess), - MatrixElem(MatrixAccess), + /// Represents an element inside a constant or variable vector. [VectorAccess] contains the + /// name of the vector and the index of the element to access. + VectorAccess(VectorAccess), + /// Represents an element inside a constant or variable matrix. [MatrixAccess] contains the + /// name of the matrix and indices of the element to access. + MatrixAccess(MatrixAccess), Next(Identifier), /// Represents a random value provided by the verifier. The inner value is the index of this /// random value in the array of all random values. diff --git a/parser/src/error.rs b/parser/src/error.rs index 49ebc8d0..7fa382fe 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -11,5 +11,5 @@ pub enum ParseError { InvalidInt(String), InvalidTraceCols(String), MissingMainTraceCols(String), - LowercaseConstName(String), + InvalidConst(String), } diff --git a/parser/src/lexer/tests/constants.rs b/parser/src/lexer/tests/constants.rs index 10509c7f..2fc527cb 100644 --- a/parser/src/lexer/tests/constants.rs +++ b/parser/src/lexer/tests/constants.rs @@ -4,16 +4,16 @@ use super::{expect_valid_tokenization, Token}; fn constants_scalar() { let source = " constants: - a: 1 - b: 2"; + A: 1 + B: 2"; let tokens = vec![ Token::Constants, Token::Colon, - Token::Ident("a".to_string()), + Token::Ident("A".to_string()), Token::Colon, Token::Num("1".to_string()), - Token::Ident("b".to_string()), + Token::Ident("B".to_string()), Token::Colon, Token::Num("2".to_string()), ]; @@ -24,13 +24,13 @@ constants: fn constants_vector() { let source = " constants: - a: [1, 2, 3, 4] - b: [5, 6, 7, 8]"; + A: [1, 2, 3, 4] + B: [5, 6, 7, 8]"; let tokens = vec![ Token::Constants, Token::Colon, - Token::Ident("a".to_string()), + Token::Ident("A".to_string()), Token::Colon, Token::Lsqb, Token::Num("1".to_string()), @@ -41,7 +41,7 @@ constants: Token::Comma, Token::Num("4".to_string()), Token::Rsqb, - Token::Ident("b".to_string()), + Token::Ident("B".to_string()), Token::Colon, Token::Lsqb, Token::Num("5".to_string()), @@ -60,13 +60,13 @@ constants: fn constants_matrix() { let source = " constants: - a: [[1, 2], [3, 4]] - b: [[5, 6], [7, 8]]"; + A: [[1, 2], [3, 4]] + B: [[5, 6], [7, 8]]"; let tokens = vec![ Token::Constants, Token::Colon, - Token::Ident("a".to_string()), + Token::Ident("A".to_string()), Token::Colon, Token::Lsqb, Token::Lsqb, @@ -81,7 +81,7 @@ fn constants_matrix() { Token::Num("4".to_string()), Token::Rsqb, Token::Rsqb, - Token::Ident("b".to_string()), + Token::Ident("B".to_string()), Token::Colon, Token::Lsqb, Token::Lsqb, @@ -102,10 +102,12 @@ fn constants_matrix() { #[test] fn constants_access_inside_boundary_expr() { + // This is invalid since the constants are not declared but this error will be thrown at the + // IR level. let source = " boundary_constraints: - enf clk.first = a + b[0] - enf clk.last = c[0][1] + enf clk.first = A + B[0] + enf clk.last = C[0][1] "; let tokens = vec![ @@ -116,9 +118,9 @@ fn constants_access_inside_boundary_expr() { Token::Dot, Token::First, Token::Equal, - Token::Ident("a".to_string()), + Token::Ident("A".to_string()), Token::Plus, - Token::Ident("b".to_string()), + Token::Ident("B".to_string()), Token::Lsqb, Token::Num("0".to_string()), Token::Rsqb, @@ -127,7 +129,7 @@ fn constants_access_inside_boundary_expr() { Token::Dot, Token::Last, Token::Equal, - Token::Ident("c".to_string()), + Token::Ident("C".to_string()), Token::Lsqb, Token::Num("0".to_string()), Token::Rsqb, @@ -140,6 +142,71 @@ fn constants_access_inside_boundary_expr() { #[test] fn constants_access_inside_transition_expr() { + let source = " + constants: + A: 1 + B: [1, 0] + C: [[1, 0], [0, 1]] + transition_constraints: + enf clk * 2^A = B[0] + C[0][1] + "; + let tokens = vec![ + Token::Constants, + Token::Colon, + Token::Ident("A".to_string()), + Token::Colon, + Token::Num("1".to_string()), + Token::Ident("B".to_string()), + Token::Colon, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Comma, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Ident("C".to_string()), + Token::Colon, + Token::Lsqb, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Comma, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Comma, + Token::Lsqb, + Token::Num("0".to_string()), + Token::Comma, + Token::Num("1".to_string()), + Token::Rsqb, + Token::Rsqb, + Token::TransitionConstraints, + Token::Colon, + Token::Enf, + Token::Ident("clk".to_string()), + Token::Mul, + Token::Num("2".to_string()), + Token::Exp, + Token::Ident("A".to_string()), + Token::Equal, + Token::Ident("B".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Plus, + Token::Ident("C".to_string()), + Token::Lsqb, + Token::Num("0".to_string()), + Token::Rsqb, + Token::Lsqb, + Token::Num("1".to_string()), + Token::Rsqb, + ]; + expect_valid_tokenization(source, tokens); +} + +#[test] +fn constants_access_inside_transition_expr_invalid() { + // This is invalid since the constants are not declared and the constant names should be + // capitalized but these errors will be thrown at the IR level and parsing level respectively. let source = " transition_constraints: enf clk * 2^a = b[0] + c[0][1] diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index 5b70b974..ce5d0400 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -6,7 +6,7 @@ use crate::{ Identifier, Source, SourceSection, TraceCols, PublicInput, PeriodicColumn, VectorAccess, MatrixAccess }, error::{Error, ParseError::{InvalidInt, InvalidTraceCols, MissingMainTraceCols, - LowercaseConstName}}, lexer::Token + InvalidConst}}, lexer::Token }; use std::str::FromStr; use lalrpop_util::ParseError; @@ -67,8 +67,8 @@ AuxCols: Vec = { // CONSTANTS // ================================================================================================ -// Constants section is optional but cannot be empty. If the section is declared, then at least one -// constant is required. +// The constants section is optional but cannot be empty. If the section is declared, then at least +// one constant is required. Constants: Vec = { "constants" ":" => constants } @@ -83,11 +83,11 @@ Constant: Constant = { } ConstName: Identifier = { - =>? if n.0.chars().all(|v| v.is_uppercase()) { - Ok(Identifier(n.to_string())) + =>? if name.0.chars().all(|v| v.is_uppercase()) { + Ok(Identifier(name.to_string())) } else { Err(ParseError::User { - error: Error::ParseError(LowercaseConstName( + error: Error::ParseError(InvalidConst( format!("The constant name should be uppercase: {}", <>).to_string() )) }) @@ -103,7 +103,7 @@ PublicInputs: Vec = { } PublicInput: PublicInput = { - ":" "[" "]" => PublicInput::new(name, size), + ":" => PublicInput::new(name, size), } // PERIODIC COLUMNS @@ -156,13 +156,11 @@ BoundaryFactor: BoundaryExpr = { BoundaryAtom: BoundaryExpr = { "(" ")", "^" => BoundaryExpr::Exp(Box::new(lexpr), num), - "$rand" "[" "]" => BoundaryExpr::Rand(n as usize), + "$rand" => BoundaryExpr::Rand(idx), => BoundaryExpr::Const(n), => BoundaryExpr::Elem(ident), - "[" "]" => - BoundaryExpr::VecElem(VectorAccess::new(ident, idx)), - "[" "]" "[" "]" => - BoundaryExpr::MatrixElem(MatrixAccess::new(ident, col_idx, row_idx)) + => BoundaryExpr::VectorAccess(vector_access), + => BoundaryExpr::MatrixAccess(matrix_access) } // TRANSITION CONSTRAINTS @@ -196,13 +194,11 @@ TransitionFactor: TransitionExpr = { TransitionAtom: TransitionExpr = { "(" ")", "^" => TransitionExpr::Exp(Box::new(lexpr), num), - "$rand" "[" "]" => TransitionExpr::Rand(n as usize), + "$rand" => TransitionExpr::Rand(idx), => TransitionExpr::Const(n), => TransitionExpr::Elem(ident), - "[" "]" => - TransitionExpr::VecElem(VectorAccess::new(ident, idx)), - "[" "]" "[" "]" => - TransitionExpr::MatrixElem(MatrixAccess::new(ident, col_idx, row_idx)), + => TransitionExpr::VectorAccess(vector_access), + => TransitionExpr::MatrixAccess(matrix_access), "'" => TransitionExpr::Next(ident) } @@ -225,6 +221,23 @@ Vector: Vec = { } } +Size: u64 = { + "[" "]" => size +} + +Index: usize = { + "[" "]" => idx as usize +} + +VectorAccess: VectorAccess = { + => VectorAccess::new(ident, idx) +} + +MatrixAccess: MatrixAccess = { + => + MatrixAccess::new(ident, row, col) +} + Identifier: Identifier = { => Identifier(n.to_string()) } diff --git a/parser/src/parser/tests/expressions.rs b/parser/src/parser/tests/arithmetic_ops.rs similarity index 100% rename from parser/src/parser/tests/expressions.rs rename to parser/src/parser/tests/arithmetic_ops.rs diff --git a/parser/src/parser/tests/boundary_constraints.rs b/parser/src/parser/tests/boundary_constraints.rs index 3fdff6d9..33b91258 100644 --- a/parser/src/parser/tests/boundary_constraints.rs +++ b/parser/src/parser/tests/boundary_constraints.rs @@ -93,7 +93,7 @@ fn boundary_constraint_with_pub_input() { boundary_constraints: vec![BoundaryConstraint::new( Identifier("clk".to_string()), Boundary::First, - BoundaryExpr::VecElem(VectorAccess::new(Identifier("a".to_string()), 0)), + BoundaryExpr::VectorAccess(VectorAccess::new(Identifier("a".to_string()), 0)), )], }), ]); @@ -113,7 +113,7 @@ fn boundary_constraint_with_expr() { BoundaryExpr::Add( Box::new(BoundaryExpr::Add( Box::new(BoundaryExpr::Const(5)), - Box::new(BoundaryExpr::VecElem(VectorAccess::new( + Box::new(BoundaryExpr::VectorAccess(VectorAccess::new( Identifier("a".to_string()), 3, ))), @@ -151,12 +151,12 @@ fn boundary_constraint_with_const() { BoundaryExpr::Sub( Box::new(BoundaryExpr::Add( Box::new(BoundaryExpr::Elem(Identifier("A".to_string()))), - Box::new(BoundaryExpr::VecElem(VectorAccess::new( + Box::new(BoundaryExpr::VectorAccess(VectorAccess::new( Identifier("B".to_string()), 1, ))), )), - Box::new(BoundaryExpr::MatrixElem(MatrixAccess::new( + Box::new(BoundaryExpr::MatrixAccess(MatrixAccess::new( Identifier("C".to_string()), 0, 1, diff --git a/parser/src/parser/tests/constants.rs b/parser/src/parser/tests/constants.rs index 65d83589..23af97c6 100644 --- a/parser/src/parser/tests/constants.rs +++ b/parser/src/parser/tests/constants.rs @@ -37,24 +37,6 @@ fn constants_vectors() { build_parse_test!(source).expect_ast(expected); } -#[test] -fn constants_vectors_wrong() { - let source = "constants: - A: [1, 2, 3, 4] - B: [5, 6, 7, 8]"; - let expected = Source(vec![SourceSection::Constants(vec![ - Constant::new( - Identifier("A".to_string()), - ConstantType::Vector(vec![1, 2, 3, 4]), - ), - Constant::new( - Identifier("B".to_string()), - ConstantType::Vector(vec![5, 6, 7, 8]), - ), - ])]); - build_parse_test!(source).expect_ast(expected); -} - #[test] fn constants_matrices() { let source = "constants: @@ -73,6 +55,19 @@ fn constants_matrices() { build_parse_test!(source).expect_ast(expected); } +#[test] +fn const_matrix_unequal_number_of_cols() { + // This is invalid since the number of columns for the two rows are unequal. However this + // validation happens at the IR level. + let source = "constants: + A: [[1, 2], [3, 4, 5]]"; + let expected = Source(vec![SourceSection::Constants(vec![Constant::new( + Identifier("A".to_string()), + ConstantType::Matrix(vec![vec![1, 2], vec![3, 4, 5]]), + )])]); + build_parse_test!(source).expect_ast(expected); +} + #[test] fn error_empty_constants_section() { let source = " @@ -86,8 +81,52 @@ fn err_lowercase_constant_name() { let source = "constants: Ab: [[1, 2], [3, 4]] C: [[5, 6], [7, 8]]"; - let error = Error::ParseError(ParseError::LowercaseConstName( + let error = Error::ParseError(ParseError::InvalidConst( "The constant name should be uppercase: Ab".to_string(), )); build_parse_test!(source).expect_error(error); } + +#[test] +fn err_consts_with_non_int_values() { + let source = "constants: + A: a + B: 2"; + build_parse_test!(source).expect_unrecognized_token(); +} + +#[test] +fn err_const_vectors_with_non_int_values() { + let source = "constants: + A: [1, a] + B: [2, 4]"; + build_parse_test!(source).expect_unrecognized_token(); +} + +#[test] +fn err_vector_with_trailing_comma() { + let source = "constants: + A: [1, ]"; + build_parse_test!(source).expect_unrecognized_token(); +} + +#[test] +fn err_matrix_with_trailing_comma() { + let source = "constants: + A: [[1, 2], ]"; + build_parse_test!(source).expect_unrecognized_token(); +} + +#[test] +fn err_matrix_mixed_element_types() { + let source = "constants: + A: [1, [1, 2]]"; + build_parse_test!(source).expect_unrecognized_token(); +} + +#[test] +fn err_invalid_matrix_element() { + let source = "constants: + A: [[1, 2], [3, [4, 5]]]"; + build_parse_test!(source).expect_unrecognized_token(); +} diff --git a/parser/src/parser/tests/mod.rs b/parser/src/parser/tests/mod.rs index eedeaacd..0865b72c 100644 --- a/parser/src/parser/tests/mod.rs +++ b/parser/src/parser/tests/mod.rs @@ -8,18 +8,17 @@ use std::fs; mod utils; +mod arithmetic_ops; mod boundary_constraints; +mod comments; +mod constants; +mod identifiers; mod periodic_columns; mod pub_inputs; mod sections; mod trace_columns; mod transition_constraints; -mod comments; -mod constants; -mod expressions; -mod identifiers; - // FULL AIR FILE // ================================================================================================ diff --git a/parser/src/parser/tests/transition_constraints.rs b/parser/src/parser/tests/transition_constraints.rs index 286567cb..18f11a0e 100644 --- a/parser/src/parser/tests/transition_constraints.rs +++ b/parser/src/parser/tests/transition_constraints.rs @@ -128,11 +128,11 @@ fn transition_constraint_with_constants() { Box::new(TransitionExpr::Elem(Identifier("A".to_string()))), ), TransitionExpr::Add( - Box::new(TransitionExpr::VecElem(VectorAccess::new( + Box::new(TransitionExpr::VectorAccess(VectorAccess::new( Identifier("B".to_string()), 1, ))), - Box::new(TransitionExpr::MatrixElem(MatrixAccess::new( + Box::new(TransitionExpr::MatrixAccess(MatrixAccess::new( Identifier("C".to_string()), 1, 1,