use super::*;
use crate::parse_ast;
use leo_errors::{CompilerError, ParserError, Result};
use leo_span::{source_map::FileName, symbol::with_session_globals};
use std::fs;
impl ParserContext<'_> {
pub fn parse_program(&mut self) -> Result<Program> {
let mut imports = IndexMap::new();
let mut program_scopes = IndexMap::new();
let mut parsed_program_scope = false;
while self.has_next() {
match &self.token.token {
Token::Import => {
let (id, import) = self.parse_import()?;
imports.insert(id, import);
}
Token::Program => {
match parsed_program_scope {
true => return Err(ParserError::only_one_program_scope_is_allowed(self.token.span).into()),
false => {
parsed_program_scope = true;
let program_scope = self.parse_program_scope()?;
program_scopes.insert(program_scope.program_id, program_scope);
}
}
}
_ => return Err(Self::unexpected_item(&self.token, &[Token::Import, Token::Program]).into()),
}
}
if !parsed_program_scope {
return Err(ParserError::missing_program_scope(self.token.span).into());
}
Ok(Program { imports, program_scopes })
}
fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError {
ParserError::unexpected(
&token.token,
expected.iter().map(|x| format!("'{x}'")).collect::<Vec<_>>().join(", "),
token.span,
)
}
pub(super) fn parse_import(&mut self) -> Result<(Symbol, (Program, Span))> {
let start = self.expect(&Token::Import)?;
let import_name = self.expect_identifier()?;
self.expect(&Token::Dot)?;
if !self.eat(&Token::Leo) {
return Err(ParserError::leo_imports_only(self.token.span).into());
}
let end = self.expect(&Token::Semicolon)?;
let mut import_file_path =
std::env::current_dir().map_err(|err| CompilerError::cannot_open_cwd(err, self.token.span))?;
import_file_path.push("imports");
import_file_path.push(format!("{}.leo", import_name.name));
if !import_file_path.exists() {
return Err(CompilerError::import_not_found(import_file_path.display(), self.prev_token.span).into());
}
let program_string =
fs::read_to_string(&import_file_path).map_err(|e| CompilerError::file_read_error(&import_file_path, e))?;
let name: FileName = FileName::Real(import_file_path);
let prg_sf = with_session_globals(|s| s.source_map.new_source(&program_string, name));
let program_ast = parse_ast(self.handler, &prg_sf.src, prg_sf.start_pos)?;
Ok((import_name.name, (program_ast.into_repr(), start + end)))
}
fn parse_program_scope(&mut self) -> Result<ProgramScope> {
let start = self.expect(&Token::Program)?;
let name = self.expect_identifier()?;
self.expect(&Token::Dot)?;
let network = self.expect_identifier()?;
let program_id = ProgramId { name, network };
if network.name != sym::aleo {
return Err(ParserError::invalid_network(network.span).into());
}
self.expect(&Token::LeftCurly)?;
let mut functions = IndexMap::new();
let mut structs = IndexMap::new();
let mut mappings = IndexMap::new();
while self.has_next() {
match &self.token.token {
Token::Struct | Token::Record => {
let (id, struct_) = self.parse_struct()?;
structs.insert(id, struct_);
}
Token::Mapping => {
let (id, mapping) = self.parse_mapping()?;
mappings.insert(id, mapping);
}
Token::At | Token::Function | Token::Transition | Token::Inline => {
let (id, function) = self.parse_function()?;
functions.insert(id, function);
}
Token::RightCurly => break,
_ => {
return Err(Self::unexpected_item(&self.token, &[
Token::Struct,
Token::Record,
Token::Mapping,
Token::At,
Token::Function,
Token::Transition,
Token::Inline,
])
.into());
}
}
}
let end = self.expect(&Token::RightCurly)?;
Ok(ProgramScope { program_id, functions, structs, mappings, span: start + end })
}
fn parse_struct_members(&mut self) -> Result<(Vec<Member>, Span)> {
let mut members = Vec::new();
let (mut semi_colons, mut commas) = (false, false);
while !self.check(&Token::RightCurly) {
let variable = self.parse_member_variable_declaration()?;
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;
}
members.push(variable);
}
let span = self.expect(&Token::RightCurly)?;
Ok((members, span))
}
pub(super) fn parse_typed_ident(&mut self) -> Result<(Identifier, Type, Span)> {
let name = self.expect_identifier()?;
self.expect(&Token::Colon)?;
let (type_, span) = self.parse_type()?;
Ok((name, type_, name.span + span))
}
fn parse_member_variable_declaration(&mut self) -> Result<Member> {
let mode = self.parse_mode()?;
let (identifier, type_, span) = self.parse_typed_ident()?;
Ok(Member { mode, identifier, type_, span })
}
pub(super) fn parse_struct(&mut self) -> Result<(Symbol, Struct)> {
let is_record = matches!(&self.token.token, Token::Record);
let start = self.expect_any(&[Token::Struct, Token::Record])?;
let struct_name = self.expect_identifier()?;
self.expect(&Token::LeftCurly)?;
let (members, end) = self.parse_struct_members()?;
Ok((struct_name.name, Struct { identifier: struct_name, members, is_record, span: start + end }))
}
pub(super) fn parse_mapping(&mut self) -> Result<(Symbol, Mapping)> {
let start = self.expect(&Token::Mapping)?;
let identifier = self.expect_identifier()?;
self.expect(&Token::Colon)?;
let (key_type, _) = self.parse_type()?;
self.expect(&Token::BigArrow)?;
let (value_type, _) = self.parse_type()?;
let end = self.expect(&Token::Semicolon)?;
Ok((identifier.name, Mapping { identifier, key_type, value_type, span: start + end }))
}
pub(super) fn parse_mode(&mut self) -> Result<Mode> {
let private = self.eat(&Token::Private).then_some(self.prev_token.span);
let public = self.eat(&Token::Public).then_some(self.prev_token.span);
let constant = self.eat(&Token::Constant).then_some(self.prev_token.span);
match (private, public, constant) {
(None, None, None) => Ok(Mode::None),
(Some(_), None, None) => Ok(Mode::Private),
(None, Some(_), None) => Ok(Mode::Public),
(None, None, Some(_)) => Ok(Mode::Constant),
_ => {
let mut spans = [private, public, constant].into_iter().flatten();
let starting_span = spans.next().unwrap();
let summed_span = spans.fold(starting_span, |span, next| span + next);
Err(ParserError::inputs_multiple_variable_modes_specified(summed_span).into())
}
}
}
fn parse_input(&mut self) -> Result<functions::Input> {
let mode = self.parse_mode()?;
let name = self.expect_identifier()?;
self.expect(&Token::Colon)?;
if self.peek_is_external() {
let external = self.expect_identifier()?;
let mut span = name.span + external.span;
self.eat(&Token::Dot);
self.eat(&Token::Leo);
self.eat(&Token::Div);
let record = self.expect_identifier()?;
self.eat(&Token::Dot);
self.eat(&Token::Record);
span = span + self.prev_token.span;
Ok(functions::Input::External(External { identifier: name, program_name: external, record, span }))
} else {
let type_ = self.parse_type()?.0;
Ok(functions::Input::Internal(FunctionInput { identifier: name, mode, type_, span: name.span }))
}
}
fn parse_function_output(&mut self) -> Result<FunctionOutput> {
let mode = self.parse_mode()?;
let (type_, span) = self.parse_type()?;
Ok(FunctionOutput { mode, type_, span })
}
fn parse_output(&mut self) -> Result<Output> {
if self.peek_is_external() {
let external = self.expect_identifier()?;
let mut span = external.span;
self.eat(&Token::Dot);
self.eat(&Token::Leo);
self.eat(&Token::Div);
let record = self.expect_identifier()?;
self.eat(&Token::Dot);
self.eat(&Token::Record);
span = span + self.prev_token.span;
Ok(Output::External(External {
identifier: Identifier::new(Symbol::intern("dummy")),
program_name: external,
record,
span,
}))
} else {
Ok(Output::Internal(self.parse_function_output()?))
}
}
fn peek_is_external(&self) -> bool {
matches!((&self.token.token, self.look_ahead(1, |t| &t.token)), (Token::Identifier(_), Token::Dot))
}
fn parse_annotation(&mut self) -> Result<Annotation> {
let start = self.expect(&Token::At)?;
let identifier = match self.token.token {
Token::Program => Identifier { name: sym::program, span: self.expect(&Token::Program)? },
_ => self.expect_identifier()?,
};
let span = start + identifier.span;
match identifier.span.hi.0 - start.lo.0 > 1 + identifier.name.to_string().len() as u32 {
true => Err(ParserError::space_in_annotation(span).into()),
false => Ok(Annotation { identifier, span }),
}
}
fn parse_function(&mut self) -> Result<(Symbol, Function)> {
let mut annotations = Vec::new();
while self.look_ahead(0, |t| &t.token) == &Token::At {
annotations.push(self.parse_annotation()?)
}
let (variant, start) = match self.token.token {
Token::Inline => (Variant::Inline, self.expect(&Token::Inline)?),
Token::Function => (Variant::Standard, self.expect(&Token::Function)?),
Token::Transition => (Variant::Transition, self.expect(&Token::Transition)?),
_ => self.unexpected("'function', 'transition', or 'inline'")?,
};
let name = self.expect_identifier()?;
let (inputs, ..) = self.parse_paren_comma_list(|p| p.parse_input().map(Some))?;
let output = match self.eat(&Token::Arrow) {
false => vec![],
true => {
self.disallow_struct_construction = true;
let output = match self.peek_is_left_par() {
true => self.parse_paren_comma_list(|p| p.parse_output().map(Some))?.0,
false => vec![self.parse_output()?],
};
self.disallow_struct_construction = false;
output
}
};
let block = self.parse_block()?;
let finalize = match self.eat(&Token::Finalize) {
false => None,
true => {
let start = self.prev_token.span;
let identifier = self.expect_identifier()?;
let (input, ..) = self.parse_paren_comma_list(|p| p.parse_input().map(Some))?;
let output = match self.eat(&Token::Arrow) {
false => vec![],
true => {
self.disallow_struct_construction = true;
let output = match self.peek_is_left_par() {
true => self.parse_paren_comma_list(|p| p.parse_output().map(Some))?.0,
false => vec![self.parse_output()?],
};
self.disallow_struct_construction = false;
output
}
};
let block = self.parse_block()?;
let span = start + block.span;
Some(Finalize::new(identifier, input, output, block, span))
}
};
let span = start + block.span;
Ok((name.name, Function::new(annotations, variant, name, inputs, output, block, finalize, span)))
}
}
use leo_span::{sym, Symbol};