diff --git a/compiler/ast/src/expressions/circuit_init.rs b/compiler/ast/src/expressions/circuit_init.rs index a1e59ab4b4..9ab6548926 100644 --- a/compiler/ast/src/expressions/circuit_init.rs +++ b/compiler/ast/src/expressions/circuit_init.rs @@ -13,7 +13,9 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . + use super::*; +use leo_span::sym; /// An initializer for a single field / variable of a circuit initializer expression. /// That is, in `Foo { bar: 42, baz }`, this is either `bar: 42`, or `baz`. @@ -50,6 +52,34 @@ pub struct CircuitExpression { pub span: Span, } +impl CircuitExpression { + /// Returns true if the record has all required fields and visibility. + pub fn check_record(&self) -> bool { + let has_member = |symbol| self.members.iter().any(|variable| variable.identifier.name == symbol); + + has_member(sym::owner) && has_member(sym::gates) && has_member(sym::_nonce) + } + + /// Returns the circuit as a record interface with visibility. + pub fn to_record_string(&self) -> String { + format!( + "{{{}}}", + self.members + .iter() + .map(|variable| { + // Write default visibility. + if variable.identifier.name == sym::_nonce { + format!("{}.public", variable) + } else { + format!("{}.private", variable) + } + }) + .collect::>() + .join(", ") + ) + } +} + impl fmt::Display for CircuitExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( diff --git a/compiler/ast/src/input/input_ast.rs b/compiler/ast/src/input/input_ast.rs index ad8922305b..c1f99dba43 100644 --- a/compiler/ast/src/input/input_ast.rs +++ b/compiler/ast/src/input/input_ast.rs @@ -58,17 +58,8 @@ impl InputAst { Some(circuit) => match circuit.is_record { false => definition.value.to_string(), true => match &definition.value { - Expression::Circuit(circuit_expression) => { - format!( - "{{{}}}", - circuit_expression - .members - .iter() - .map(|x| format!("{}.private", x)) - .collect::>() - .join(", ") - ) - } + // Print out the record interface with visibility. + Expression::Circuit(circuit_expression) => circuit_expression.to_record_string(), _ => panic!("Input error: Expected a circuit expression."), }, }, diff --git a/compiler/parser/src/parser/context.rs b/compiler/parser/src/parser/context.rs index 23e19e5a1d..0e43a02c08 100644 --- a/compiler/parser/src/parser/context.rs +++ b/compiler/parser/src/parser/context.rs @@ -39,6 +39,8 @@ pub(crate) struct ParserContext<'a> { pub(crate) prev_token: SpannedToken, /// true if parsing an expression for if and loop statements -- means circuit inits are not legal pub(crate) disallow_circuit_construction: bool, + /// true if parsing an identifier inside an input file. + pub(crate) allow_identifier_underscores: bool, } /// Dummy span used to appease borrow checker. @@ -59,6 +61,7 @@ impl<'a> ParserContext<'a> { let mut p = Self { handler, disallow_circuit_construction: false, + allow_identifier_underscores: false, prev_token: token.clone(), token, tokens, diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index 92e4b6af65..ac23c183c1 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -17,6 +17,7 @@ use super::*; use leo_errors::{ParserError, Result}; +use leo_span::Symbol; use snarkvm_console::{account::Address, network::Testnet3}; const INT_TYPES: &[Token] = &[ @@ -463,7 +464,14 @@ impl ParserContext<'_> { } fn parse_circuit_member(&mut self) -> Result { - let identifier = self.expect_identifier()?; + let identifier = if self.allow_identifier_underscores && self.eat(&Token::Underscore) { + // Allow `_nonce` for circuit records. + let identifier_without_underscore = self.expect_identifier()?; + Identifier::new(Symbol::intern(&format!("_{}", identifier_without_underscore.name))) + } else { + self.expect_identifier()? + }; + let expression = if self.eat(&Token::Colon) { // Parse individual circuit variable declarations. Some(self.parse_expression()?) @@ -547,11 +555,11 @@ impl ParserContext<'_> { } Token::True => Expression::Literal(Literal::Boolean(true, span)), Token::False => Expression::Literal(Literal::Boolean(false, span)), - Token::AddressLit(addr) => { - if addr.parse::>().is_err() { - self.emit_err(ParserError::invalid_address_lit(&addr, span)); + Token::AddressLit(address_string) => { + if address_string.parse::>().is_err() { + self.emit_err(ParserError::invalid_address_lit(&address_string, span)); } - Expression::Literal(Literal::Address(addr, span)) + Expression::Literal(Literal::Address(address_string, span)) } Token::StaticString(value) => Expression::Literal(Literal::String(value, span)), Token::Identifier(name) => { diff --git a/compiler/parser/src/parser/input.rs b/compiler/parser/src/parser/input.rs index 70edcec25d..1db4e3d824 100644 --- a/compiler/parser/src/parser/input.rs +++ b/compiler/parser/src/parser/input.rs @@ -21,6 +21,8 @@ use leo_errors::{ParserError, Result}; impl ParserContext<'_> { /// Returns a [`ParsedInputFile`] struct filled with the data acquired in the file. pub(crate) fn parse_input(&mut self) -> Result { + // Allow underscores in identifiers for input record declarations. + self.allow_identifier_underscores = true; let mut sections = Vec::new(); while self.has_next() { @@ -31,6 +33,9 @@ impl ParserContext<'_> { } } + // Do not allow underscores in identifiers outside of input files. + self.allow_identifier_underscores = false; + Ok(InputAst { sections }) } diff --git a/compiler/parser/src/tokenizer/lexer.rs b/compiler/parser/src/tokenizer/lexer.rs index 9b8866f8e5..f527cdbc70 100644 --- a/compiler/parser/src/tokenizer/lexer.rs +++ b/compiler/parser/src/tokenizer/lexer.rs @@ -386,12 +386,12 @@ impl Token { '^' => return match_two(&mut input, Token::BitXor, '=', Token::BitXorAssign), _ => (), } - if let Some(ident) = eat_identifier(&mut input) { + if let Some(identifier) = eat_identifier(&mut input) { return Ok(( - ident.len(), + identifier.len(), // todo: match on symbols instead of hard-coded &str's - match &*ident { - x if x.starts_with("aleo1") => Token::AddressLit(ident), + match &*identifier { + x if x.starts_with("aleo1") => Token::AddressLit(identifier), "address" => Token::Address, "bool" => Token::Bool, "circuit" => Token::Circuit, @@ -424,7 +424,7 @@ impl Token { "u32" => Token::U32, "u64" => Token::U64, "u128" => Token::U128, - _ => Token::Identifier(Symbol::intern(&ident)), + _ => Token::Identifier(Symbol::intern(&identifier)), }, )); } diff --git a/compiler/span/src/symbol.rs b/compiler/span/src/symbol.rs index 79231424e3..3119d2be3d 100644 --- a/compiler/span/src/symbol.rs +++ b/compiler/span/src/symbol.rs @@ -217,6 +217,7 @@ symbols! { private, owner, gates, + _nonce, // input file registers, diff --git a/examples/token/inputs/token.in b/examples/token/inputs/token.in index 026e9c28b6..6e69f24922 100644 --- a/examples/token/inputs/token.in +++ b/examples/token/inputs/token.in @@ -8,6 +8,7 @@ token: Token = Token { owner: aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7, gates: 0u64, amount: 100u64, + _nonce: 0group, }; to: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; amount: u64 = 50u64; \ No newline at end of file diff --git a/tests/compiler/records/inputs/address.in b/tests/compiler/records/inputs/address.in deleted file mode 100644 index e69de29bb2..0000000000