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