diff --git a/compiler/ast/src/expressions/call.rs b/compiler/ast/src/expressions/call.rs index 8d1dec4613..5dc8871698 100644 --- a/compiler/ast/src/expressions/call.rs +++ b/compiler/ast/src/expressions/call.rs @@ -16,7 +16,7 @@ use super::*; -/// A function call expression, e.g., `foo(args)` or `Foo::bar(args)`. +/// A function call expression, e.g.`foo(args)` or `Foo::bar(args)`. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CallExpression { /// An expression evaluating to a callable function, @@ -24,13 +24,23 @@ pub struct CallExpression { pub function: Box, // todo: make this identifier? /// Expressions for the arguments passed to the functions parameters. pub arguments: Vec, + /// The name of the external program call, e.g.`bar` in `bar.leo`. + pub external: Option>, /// Span of the entire call `function(arguments)`. pub span: Span, } impl fmt::Display for CallExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}(", self.function)?; + match &self.external { + Some(external) => { + write!(f, "{}.leo/{}(", external, self.function)?; + } + None => { + write!(f, "{}(", self.function)?; + } + } + for (i, param) in self.arguments.iter().enumerate() { write!(f, "{}", param)?; if i < self.arguments.len() - 1 { diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index 7600c29396..fb112692bd 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -91,6 +91,7 @@ pub trait ExpressionReconstructor { .into_iter() .map(|arg| self.reconstruct_expression(arg).0) .collect(), + external: input.external, span: input.span, }), Default::default(), diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index b252acffda..9368cb3574 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -364,6 +364,21 @@ impl ParserContext<'_> { index, span, })) + } else if self.eat(&Token::Leo) { + // 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))?; + expr = Expression::Call(CallExpression { + span: expr.span() + span, + function: Box::new(Expression::Identifier(name)), + external: Some(Box::new(expr)), + arguments, + }); } else { // Parse identifier name. let name = self.expect_identifier()?; @@ -389,6 +404,7 @@ impl ParserContext<'_> { expr = Expression::Call(CallExpression { span: expr.span() + span, function: Box::new(expr), + external: None, arguments, }); } diff --git a/compiler/parser/src/parser/file.rs b/compiler/parser/src/parser/file.rs index 0ff8a50433..048ae8ac11 100644 --- a/compiler/parser/src/parser/file.rs +++ b/compiler/parser/src/parser/file.rs @@ -95,12 +95,11 @@ impl ParserContext<'_> { // Parse `.leo`. self.expect(&Token::Dot)?; - let leo_file_extension = self.expect_identifier()?; - - // Throw error for non-leo files. - if leo_file_extension.name.ne(&sym::leo) { - return Err(ParserError::leo_imports_only(leo_file_extension, self.token.span).into()); + if !self.eat(&Token::Leo) { + // Throw error for non-leo files. + return Err(ParserError::leo_imports_only(self.token.span).into()); } + let _end = self.expect(&Token::Semicolon)?; // Tokenize and parse import file. diff --git a/compiler/parser/src/tokenizer/lexer.rs b/compiler/parser/src/tokenizer/lexer.rs index b86c5ffda4..2c788d5fe2 100644 --- a/compiler/parser/src/tokenizer/lexer.rs +++ b/compiler/parser/src/tokenizer/lexer.rs @@ -417,6 +417,7 @@ impl Token { "increment" => Token::Increment, "import" => Token::Import, "let" => Token::Let, + "leo" => Token::Leo, "mapping" => Token::Mapping, "public" => Token::Public, "record" => Token::Record, diff --git a/compiler/parser/src/tokenizer/token.rs b/compiler/parser/src/tokenizer/token.rs index 0aef6cc5bb..c60d266907 100644 --- a/compiler/parser/src/tokenizer/token.rs +++ b/compiler/parser/src/tokenizer/token.rs @@ -130,6 +130,8 @@ pub enum Token { Return, SelfLower, Static, + // For imports. + Leo, // Meta Tokens Eof, @@ -213,6 +215,7 @@ impl Token { Token::Increment => sym::increment, Token::Import => sym::import, Token::Let => sym::Let, + Token::Leo => sym::leo, Token::Mapping => sym::mapping, Token::Public => sym::Public, Token::Record => sym::record, @@ -335,6 +338,7 @@ impl fmt::Display for Token { Public => write!(f, "public"), Return => write!(f, "return"), Static => write!(f, "static"), + Leo => write!(f, "leo"), Eof => write!(f, ""), } } diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index 00a29823d2..5ecd6c8cd2 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -276,7 +276,10 @@ impl<'a> CodeGenerator<'a> { } fn visit_call(&mut self, input: &'a CallExpression) -> (String, String) { - let mut call_instruction = format!(" call {} ", input.function); + let mut call_instruction = match &input.external { + Some(external) => format!(" call {}.aleo/{} ", external, input.function), + None => format!(" call {} ", input.function), + }; let mut instructions = String::new(); for argument in input.arguments.iter() { diff --git a/compiler/passes/src/static_single_assignment/rename_expression.rs b/compiler/passes/src/static_single_assignment/rename_expression.rs index 89f7aede36..28c06cecb9 100644 --- a/compiler/passes/src/static_single_assignment/rename_expression.rs +++ b/compiler/passes/src/static_single_assignment/rename_expression.rs @@ -133,6 +133,7 @@ impl ExpressionConsumer for StaticSingleAssigner { function: input.function, // Consume the arguments. arguments, + external: input.external, span: input.span, })); statements.push(statement); diff --git a/errors/src/errors/parser/parser_errors.rs b/errors/src/errors/parser/parser_errors.rs index 309368fe02..dc13a42d7b 100644 --- a/errors/src/errors/parser/parser_errors.rs +++ b/errors/src/errors/parser/parser_errors.rs @@ -249,8 +249,8 @@ create_messages!( @formatted leo_imports_only { - args: (name: impl Display), - msg: format!("Invalid import call to non-leo file `{name}`."), + args: (), + msg: "Invalid import call to non-leo file.", help: Some("Only imports of Leo `.leo` files are currently supported.".to_string()), } diff --git a/examples/helloworld/imports/point.leo b/examples/helloworld/imports/point.leo new file mode 100644 index 0000000000..e69de29bb2