mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-14 04:38:05 +03:00
parse record type declaration
This commit is contained in:
parent
9d0fd00072
commit
d45ab61e40
@ -17,7 +17,7 @@
|
||||
//! A Leo program consists of import, circuit, and function definitions.
|
||||
//! Each defined type consists of ast statements and expressions.
|
||||
|
||||
use crate::{Circuit, Function, FunctionInput, Identifier};
|
||||
use crate::{Circuit, Function, FunctionInput, Identifier, Record};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -36,6 +36,8 @@ pub struct Program {
|
||||
pub functions: IndexMap<Identifier, Function>,
|
||||
/// A map from circuit names to circuit definitions.
|
||||
pub circuits: IndexMap<Identifier, Circuit>,
|
||||
/// A map from record names to record definitions.
|
||||
pub records: IndexMap<Identifier, Record>,
|
||||
}
|
||||
|
||||
impl AsRef<Program> for Program {
|
||||
@ -50,6 +52,14 @@ impl fmt::Display for Program {
|
||||
function.fmt(f)?;
|
||||
writeln!(f,)?;
|
||||
}
|
||||
for (_, circuit) in self.circuits.iter() {
|
||||
circuit.fmt(f)?;
|
||||
writeln!(f,)?;
|
||||
}
|
||||
for (_, record) in self.records.iter() {
|
||||
record.fmt(f)?;
|
||||
writeln!(f,)?;
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
@ -62,6 +72,7 @@ impl Program {
|
||||
expected_input: vec![],
|
||||
functions: IndexMap::new(),
|
||||
circuits: IndexMap::new(),
|
||||
records: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,15 +19,20 @@ use crate::{Identifier, Type};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
/// A variable definition in a record;
|
||||
/// For example: `foobar: u8;`.
|
||||
/// For example: `owner: address;`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RecordVariable {
|
||||
/// The identifier of the constant.
|
||||
ident: Identifier,
|
||||
/// The type the constant has.
|
||||
type_: Type
|
||||
type_: Type,
|
||||
}
|
||||
|
||||
impl RecordVariable {
|
||||
pub fn new(ident: Identifier, type_: Type) -> Self {
|
||||
Self { ident, type_ }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RecordVariable {
|
||||
|
@ -17,13 +17,14 @@
|
||||
use super::*;
|
||||
|
||||
use leo_errors::{ParserError, ParserWarning, Result};
|
||||
use leo_span::sym;
|
||||
use leo_span::{sym, Symbol};
|
||||
|
||||
impl ParserContext<'_> {
|
||||
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
|
||||
pub fn parse_program(&mut self) -> Result<Program> {
|
||||
let mut functions = IndexMap::new();
|
||||
let mut circuits = IndexMap::new();
|
||||
let mut records = IndexMap::new();
|
||||
|
||||
while self.has_next() {
|
||||
match &self.token.token {
|
||||
@ -36,11 +37,14 @@ impl ParserContext<'_> {
|
||||
functions.insert(id, function);
|
||||
}
|
||||
Token::Ident(sym::test) => return Err(ParserError::test_function(self.token.span).into()),
|
||||
// Const functions share the first token with the global Const.
|
||||
Token::Function => {
|
||||
let (id, function) = self.parse_function()?;
|
||||
functions.insert(id, function);
|
||||
}
|
||||
Token::Record => {
|
||||
let (id, record) = self.parse_record()?;
|
||||
records.insert(id, record);
|
||||
}
|
||||
|
||||
_ => return Err(Self::unexpected_item(&self.token).into()),
|
||||
}
|
||||
@ -50,6 +54,7 @@ impl ParserContext<'_> {
|
||||
expected_input: Vec::new(),
|
||||
functions,
|
||||
circuits,
|
||||
records,
|
||||
})
|
||||
}
|
||||
|
||||
@ -65,9 +70,9 @@ impl ParserContext<'_> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member variable
|
||||
/// Returns a [`Vec<CircuitMember>`] AST node if the next tokens represent a circuit member variable
|
||||
/// or circuit member function or circuit member constant.
|
||||
pub fn parse_circuit_declaration(&mut self) -> Result<(Vec<CircuitMember>, Span)> {
|
||||
pub fn parse_circuit_members(&mut self) -> Result<(Vec<CircuitMember>, Span)> {
|
||||
let mut members = Vec::new();
|
||||
|
||||
let (mut semi_colons, mut commas) = (false, false);
|
||||
@ -106,7 +111,7 @@ impl ParserContext<'_> {
|
||||
}
|
||||
|
||||
/// Parses `IDENT: TYPE`.
|
||||
fn parse_typed_field_name(&mut self) -> Result<(Identifier, Type)> {
|
||||
fn parse_member(&mut self) -> Result<(Identifier, Type)> {
|
||||
let name = self.expect_ident()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_all_types()?.0;
|
||||
@ -120,7 +125,7 @@ impl ParserContext<'_> {
|
||||
self.expect(&Token::Const)?;
|
||||
|
||||
// `IDENT: TYPE = EXPR`:
|
||||
let (_name, _type_) = self.parse_typed_field_name()?;
|
||||
let (_name, _type_) = self.parse_member()?;
|
||||
self.expect(&Token::Assign)?;
|
||||
let expr = self.parse_expression()?;
|
||||
|
||||
@ -134,7 +139,7 @@ impl ParserContext<'_> {
|
||||
|
||||
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member variable.
|
||||
pub fn parse_member_variable_declaration(&mut self) -> Result<CircuitMember> {
|
||||
let (name, type_) = self.parse_typed_field_name()?;
|
||||
let (name, type_) = self.parse_member()?;
|
||||
|
||||
Ok(CircuitMember::CircuitVariable(name, type_))
|
||||
}
|
||||
@ -152,13 +157,13 @@ impl ParserContext<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an [`(Identifier, Function)`] ast node if the next tokens represent a circuit declaration.
|
||||
/// Returns an [`(Identifier, Circuit)`] ast node if the next tokens represent a circuit declaration.
|
||||
pub(super) fn parse_circuit(&mut self) -> Result<(Identifier, Circuit)> {
|
||||
let start = self.expect(&Token::Circuit)?;
|
||||
let circuit_name = self.expect_ident()?;
|
||||
|
||||
self.expect(&Token::LeftCurly)?;
|
||||
let (members, end) = self.parse_circuit_declaration()?;
|
||||
let (members, end) = self.parse_circuit_members()?;
|
||||
|
||||
Ok((
|
||||
circuit_name.clone(),
|
||||
@ -170,6 +175,84 @@ impl ParserContext<'_> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse the member name and type or emit an error.
|
||||
/// Only used for `record` type.
|
||||
/// We complete this check at parse time rather than during type checking to enforce
|
||||
/// ordering, naming, and type as early as possible for program records.
|
||||
fn parse_record_variable_exact(&mut self, expected_name: Symbol, expected_type: Type) -> Result<RecordVariable> {
|
||||
let actual_name = self.expect_ident()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let actual_type = self.parse_all_types()?.0;
|
||||
|
||||
if expected_name != actual_name.name || expected_type != actual_type {
|
||||
return Err(ParserError::required_record_variable(expected_name, expected_type, actual_name.span()).into());
|
||||
}
|
||||
|
||||
Ok(RecordVariable::new(actual_name, actual_type))
|
||||
}
|
||||
|
||||
/// Returns a [`RecordVariable`] AST node if the next tokens represent a record variable.
|
||||
pub fn parse_record_variable(&mut self) -> Result<RecordVariable> {
|
||||
let (ident, type_) = self.parse_member()?;
|
||||
|
||||
Ok(RecordVariable::new(ident, type_))
|
||||
}
|
||||
|
||||
/// Returns a [`Vec<RecordVariable>`] AST node if the next tokens represent one or more
|
||||
/// user defined record variables.
|
||||
pub fn parse_record_data(&mut self) -> Result<(Vec<RecordVariable>, Span)> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let (mut semi_colons, mut commas) = (false, false);
|
||||
while !self.check(&Token::RightCurly) {
|
||||
data.push({
|
||||
let variable = self.parse_record_variable()?;
|
||||
|
||||
if self.eat(&Token::Semicolon) {
|
||||
if commas {
|
||||
self.emit_err(ParserError::mixed_commas_and_semicolons(self.token.span));
|
||||
}
|
||||
semi_colons = true;
|
||||
}
|
||||
|
||||
if self.eat(&Token::Comma) {
|
||||
if semi_colons {
|
||||
self.emit_err(ParserError::mixed_commas_and_semicolons(self.token.span));
|
||||
}
|
||||
commas = true;
|
||||
}
|
||||
|
||||
variable
|
||||
});
|
||||
}
|
||||
|
||||
let span = self.expect(&Token::RightCurly)?;
|
||||
|
||||
Ok((data, span))
|
||||
}
|
||||
|
||||
/// Returns an [`(Identifier, Circuit)`] ast node if the next tokens represent a record declaration.
|
||||
pub(super) fn parse_record(&mut self) -> Result<(Identifier, Record)> {
|
||||
let start = self.expect(&Token::Record)?;
|
||||
let record_name = self.expect_ident()?;
|
||||
|
||||
self.expect(&Token::LeftCurly)?;
|
||||
let owner = self.parse_record_variable_exact(sym::owner, Type::Address)?;
|
||||
let balance = self.parse_record_variable_exact(sym::balance, Type::IntegerType(IntegerType::U64))?;
|
||||
let (data, end) = self.parse_record_data()?;
|
||||
|
||||
Ok((
|
||||
record_name.clone(),
|
||||
Record {
|
||||
identifier: record_name,
|
||||
owner,
|
||||
balance,
|
||||
data,
|
||||
span: start + end,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns a [`ParamMode`] AST node if the next tokens represent a function parameter mode.
|
||||
pub(super) fn parse_function_parameter_mode(&mut self) -> Result<ParamMode> {
|
||||
let public = self.eat(&Token::Public).then(|| self.prev_token.span);
|
||||
|
@ -389,4 +389,11 @@ create_messages!(
|
||||
msg: format!("Invalid associated access call to circuit {name}."),
|
||||
help: Some("Double colon `::` syntax is only supported for core circuits in Leo for testnet3.".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
required_record_variable {
|
||||
args: (name: impl Display, type_: impl Display),
|
||||
msg: format!("The `record` type requires the variable `{name}: {type_}` and enforces ordering."),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
@ -210,6 +210,8 @@ symbols! {
|
||||
|
||||
public,
|
||||
private,
|
||||
owner,
|
||||
balance,
|
||||
|
||||
// todo: remove these.
|
||||
CONTAINER_PSEUDO_CIRCUIT: "$InputContainer",
|
||||
|
Loading…
Reference in New Issue
Block a user