error msgs

This commit is contained in:
evan-schott 2023-10-06 13:59:01 -07:00
parent cf7baa0132
commit 292f890369
5 changed files with 78 additions and 31 deletions

View File

@ -429,6 +429,25 @@ impl ParserContext<'_> {
self.parse_paren_comma_list(|p| p.parse_expression().map(Some)) self.parse_paren_comma_list(|p| p.parse_expression().map(Some))
} }
// Parses an externa function call `credits.aleo/transfer()` or `board.leo/make_move()`
fn parse_external_call(&mut self, expr: Expression) -> Result<Expression> {
// Eat an external function call.
self.eat(&Token::Div); // todo: Make `/` a more general token.
// Parse function name.
let name = self.expect_identifier()?;
// Parse the function call.
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
Ok(Expression::Call(CallExpression {
span: expr.span() + span,
function: Box::new(Expression::Identifier(name)),
external: Some(Box::new(expr)),
arguments,
id: self.node_builder.next_id(),
}))
}
/// Returns an [`Expression`] AST node if the next tokens represent an /// Returns an [`Expression`] AST node if the next tokens represent an
/// array access, struct member access, function call, or static function call expression. /// array access, struct member access, function call, or static function call expression.
/// ///
@ -449,22 +468,8 @@ impl ParserContext<'_> {
span, span,
id: self.node_builder.next_id(), id: self.node_builder.next_id(),
})) }))
} else if self.eat(&Token::Leo) { } else if self.eat(&Token::Leo) || self.eat(&Token::Aleo) {
// Eat an external function call. expr = self.parse_external_call(expr)?;
self.eat(&Token::Div); // todo: Make `/` a more general token.
// Parse function name.
let name = self.expect_identifier()?;
// Parse the function call.
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
expr = Expression::Call(CallExpression {
span: expr.span() + span,
function: Box::new(Expression::Identifier(name)),
external: Some(Box::new(expr)),
arguments,
id: self.node_builder.next_id(),
});
} else { } else {
// Parse identifier name. // Parse identifier name.
let name = self.expect_identifier()?; let name = self.expect_identifier()?;

View File

@ -25,6 +25,7 @@ impl ParserContext<'_> {
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program. /// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
pub fn parse_program(&mut self) -> Result<Program> { pub fn parse_program(&mut self) -> Result<Program> {
let mut imports = IndexMap::new(); let mut imports = IndexMap::new();
let mut stubs: IndexMap<Symbol, Stub> = IndexMap::new();
let mut program_scopes = IndexMap::new(); let mut program_scopes = IndexMap::new();
// TODO: Remove restrictions on multiple program scopes // TODO: Remove restrictions on multiple program scopes
@ -36,6 +37,10 @@ impl ParserContext<'_> {
let (id, import) = self.parse_import()?; let (id, import) = self.parse_import()?;
imports.insert(id, import); imports.insert(id, import);
} }
Token::Stub => {
let (id, stub) = self.parse_stub()?;
stubs.insert(id, stub);
}
Token::Program => { Token::Program => {
match parsed_program_scope { match parsed_program_scope {
// Only one program scope is allowed per file. // Only one program scope is allowed per file.
@ -56,7 +61,7 @@ impl ParserContext<'_> {
return Err(ParserError::missing_program_scope(self.token.span).into()); return Err(ParserError::missing_program_scope(self.token.span).into());
} }
Ok(Program { imports, program_scopes }) Ok(Program { imports, stubs, program_scopes })
} }
fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError { fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError {
@ -114,25 +119,20 @@ impl ParserContext<'_> {
Ok((import_name.name, (program_ast.into_repr(), start + end))) Ok((import_name.name, (program_ast.into_repr(), start + end)))
} }
/// Parsers a program scope `program foo.aleo { ... }`. /// Parses a program body `credits.aleo { ... }`
fn parse_program_scope(&mut self) -> Result<ProgramScope> { fn parse_program_body(&mut self, start: Span) -> Result<ProgramScope> {
// Parse `program` keyword.
let start = self.expect(&Token::Program)?;
// Parse the program name. // Parse the program name.
let name = self.expect_identifier()?; let name = self.expect_identifier()?;
// Parse the program network. // Parse the program network.
self.expect(&Token::Dot)?; self.expect(&Token::Dot)?;
let network = self.expect_identifier()?;
// Otherwise throw parser error
self.expect(&Token::Aleo).or_else(|_| Err(ParserError::invalid_network(self.token.span)))?;
// Construct the program id. // Construct the program id.
let program_id = ProgramId { name, network }; let program_id =
ProgramId { name, network: Identifier::new(Symbol::intern("aleo"), self.node_builder.next_id()) };
// Check that the program network is valid.
if network.name != sym::aleo {
return Err(ParserError::invalid_network(network.span).into());
}
// Parse `{`. // Parse `{`.
self.expect(&Token::LeftCurly)?; self.expect(&Token::LeftCurly)?;
@ -183,6 +183,23 @@ impl ParserContext<'_> {
Ok(ProgramScope { program_id, consts, functions, structs, mappings, span: start + end }) Ok(ProgramScope { program_id, consts, functions, structs, mappings, span: start + end })
} }
/// Parses a stub `stub credits.aleo { ... }`.
fn parse_stub(&mut self) -> Result<(Symbol, Stub)> {
// Parse `stub` keyword.
let start = self.expect(&Token::Stub)?;
let stub = Stub::from(self.parse_program_body(start)?);
Ok((stub.stub_id.name.name, stub))
}
/// Parses a program scope `program foo.aleo { ... }`.
fn parse_program_scope(&mut self) -> Result<ProgramScope> {
// Parse `program` keyword.
let start = self.expect(&Token::Program)?;
self.parse_program_body(start)
}
/// Returns a [`Vec<Member>`] AST node if the next tokens represent a struct member. /// Returns a [`Vec<Member>`] AST node if the next tokens represent a struct member.
fn parse_struct_members(&mut self) -> Result<(Vec<Member>, Span)> { fn parse_struct_members(&mut self) -> Result<(Vec<Member>, Span)> {
let mut members = Vec::new(); let mut members = Vec::new();
@ -433,13 +450,25 @@ impl ParserContext<'_> {
} }
}; };
// Parse the function body. // Parse the function body. Allow empty blocks. `fn foo(a:u8);`
let block = self.parse_block()?; let (has_empty_block, block) = match &self.token.token {
Token::LeftCurly => (false, self.parse_block()?),
Token::Semicolon => {
let semicolon = self.expect(&Token::Semicolon)?;
(true, Block { statements: Vec::new(), span: semicolon, id: self.node_builder.next_id() })
}
_ => self.unexpected("block or semicolon")?,
};
// Parse the `finalize` block if it exists. // Parse the `finalize` block if it exists.
let finalize = match self.eat(&Token::Finalize) { let finalize = match self.eat(&Token::Finalize) {
false => None, false => None,
true => { true => {
// Make sure has function body. Don't want `fn foo(); finalize foo { ... }` to be valid parsing.
if has_empty_block {
return Err(ParserError::empty_function_cannot_have_finalize(self.token.span).into());
}
// Get starting span. // Get starting span.
let start = self.prev_token.span; let start = self.prev_token.span;

View File

@ -379,6 +379,7 @@ impl Token {
match &*identifier { match &*identifier {
x if x.starts_with("aleo1") => Token::AddressLit(identifier), x if x.starts_with("aleo1") => Token::AddressLit(identifier),
"address" => Token::Address, "address" => Token::Address,
"aleo" => Token::Aleo,
"as" => Token::As, "as" => Token::As,
"assert" => Token::Assert, "assert" => Token::Assert,
"assert_eq" => Token::AssertEq, "assert_eq" => Token::AssertEq,

View File

@ -139,6 +139,7 @@ pub enum Token {
Transition, Transition,
// Meta Tokens // Meta Tokens
Aleo,
Block, Block,
Eof, Eof,
Leo, Leo,
@ -150,6 +151,7 @@ pub enum Token {
/// because true and false are also boolean literals, which are different tokens from keywords. /// because true and false are also boolean literals, which are different tokens from keywords.
pub const KEYWORD_TOKENS: &[Token] = &[ pub const KEYWORD_TOKENS: &[Token] = &[
Token::Address, Token::Address,
Token::Aleo,
Token::As, Token::As,
Token::Assert, Token::Assert,
Token::AssertEq, Token::AssertEq,
@ -207,6 +209,7 @@ impl Token {
pub fn keyword_to_symbol(&self) -> Option<Symbol> { pub fn keyword_to_symbol(&self) -> Option<Symbol> {
Some(match self { Some(match self {
Token::Address => sym::address, Token::Address => sym::address,
Token::Aleo => sym::aleo,
Token::As => sym::As, Token::As => sym::As,
Token::Assert => sym::assert, Token::Assert => sym::assert,
Token::AssertEq => sym::assert_eq, Token::AssertEq => sym::assert_eq,
@ -344,6 +347,7 @@ impl fmt::Display for Token {
U128 => write!(f, "u128"), U128 => write!(f, "u128"),
Record => write!(f, "record"), Record => write!(f, "record"),
Aleo => write!(f, "aleo"),
As => write!(f, "as"), As => write!(f, "as"),
Assert => write!(f, "assert"), Assert => write!(f, "assert"),
AssertEq => write!(f, "assert_eq"), AssertEq => write!(f, "assert_eq"),

View File

@ -292,6 +292,14 @@ create_messages!(
help: None, help: None,
} }
/// Enforce that empty functions cannot have finalize functions attached to them
@formatted
empty_function_cannot_have_finalize {
args: (),
msg: format!("Empty functions cannot have finalize functions attached to them."),
help: None,
}
@formatted @formatted
array_must_have_at_least_one_element { array_must_have_at_least_one_element {
args: (kind: impl Display), args: (kind: impl Display),