From f0a1573058e9977a540918011599df0faea8cfd8 Mon Sep 17 00:00:00 2001 From: collin <16715212+collinc97@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:27:23 -0400 Subject: [PATCH] impl method call expression parsing --- compiler/ast/src/expression/method.rs | 46 +++++++++++++++++++ compiler/ast/src/expression/mod.rs | 9 +++- .../ast/src/passes/reconstructing_director.rs | 13 ++++++ .../ast/src/passes/reconstructing_reducer.rs | 15 ++++++ compiler/ast/src/passes/visitor.rs | 4 ++ compiler/ast/src/passes/visitor_director.rs | 11 +++++ compiler/parser/src/parser/expression.rs | 16 ++++++- .../src/type_checker/check_expressions.rs | 35 ++++++++++++++ examples/hello-world/src/main.leo | 5 +- 9 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 compiler/ast/src/expression/method.rs diff --git a/compiler/ast/src/expression/method.rs b/compiler/ast/src/expression/method.rs new file mode 100644 index 0000000000..6af3cbc79e --- /dev/null +++ b/compiler/ast/src/expression/method.rs @@ -0,0 +1,46 @@ +// Copyright (C) 2019-2022 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use super::*; + +/// A method call expression, e.g., `1u8.add(2u8)`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MethodCallExpression { + /// The receiver of a method call, e.g. `1u8` in `1u8.add(2u8)`. + pub receiver: Box, + /// The identifier of the called method. + pub method: Identifier, + /// Expressions for the arguments passed to the methods parameters. + pub arguments: Vec, + /// Span of the entire call `receiver.method(arguments)`. + pub span: Span, +} + +impl fmt::Display for MethodCallExpression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}.", self.receiver)?; + write!(f, "{}(", self.method)?; + for (i, param) in self.arguments.iter().enumerate() { + write!(f, "{}", param)?; + if i < self.arguments.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")") + } +} + +crate::simple_node_impl!(MethodCallExpression); diff --git a/compiler/ast/src/expression/mod.rs b/compiler/ast/src/expression/mod.rs index 4b6ee17b44..3f2ece2cf7 100644 --- a/compiler/ast/src/expression/mod.rs +++ b/compiler/ast/src/expression/mod.rs @@ -24,6 +24,8 @@ mod binary; pub use binary::*; mod unary; pub use unary::*; +mod method; +pub use method::*; mod ternary; pub use ternary::*; mod value; @@ -46,8 +48,10 @@ pub enum Expression { Unary(UnaryExpression), /// A ternary conditional expression `cond ? if_expr : else_expr`. Ternary(TernaryExpression), - /// A call expression like `my_fun(args)`. + /// A call expression, e.g., `my_fun(args)`. Call(CallExpression), + /// A method call expression, e.g., `a.add(b)`. + Method(MethodCallExpression), /// An expression of type "error". /// Will result in a compile error eventually. Err(ErrExpression), @@ -63,6 +67,7 @@ impl Node for Expression { Unary(n) => n.span(), Ternary(n) => n.span(), Call(n) => n.span(), + Method(n) => n.span(), Err(n) => n.span(), } } @@ -76,6 +81,7 @@ impl Node for Expression { Unary(n) => n.set_span(span), Ternary(n) => n.set_span(span), Call(n) => n.set_span(span), + Method(n) => n.set_span(span), Err(n) => n.set_span(span), } } @@ -91,6 +97,7 @@ impl fmt::Display for Expression { Unary(n) => n.fmt(f), Ternary(n) => n.fmt(f), Call(n) => n.fmt(f), + Method(n) => n.fmt(f), Err(n) => n.fmt(f), } } diff --git a/compiler/ast/src/passes/reconstructing_director.rs b/compiler/ast/src/passes/reconstructing_director.rs index 78dd55bc0a..cd05767d33 100644 --- a/compiler/ast/src/passes/reconstructing_director.rs +++ b/compiler/ast/src/passes/reconstructing_director.rs @@ -46,6 +46,7 @@ impl ReconstructingDirector { Expression::Unary(unary) => Expression::Unary(self.reduce_unary(unary)?), Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(ternary)?), Expression::Call(call) => Expression::Call(self.reduce_call(call)?), + Expression::Method(method) => Expression::Method(self.reduce_method(method)?), Expression::Err(s) => Expression::Err(s.clone()), }; @@ -106,6 +107,18 @@ impl ReconstructingDirector { self.reducer.reduce_ternary(ternary, condition, if_true, if_false) } + pub fn reduce_method(&mut self, method: &MethodCallExpression) -> Result { + let receiver = self.reduce_expression(&method.receiver)?; + let method_id = self.reduce_identifier(&method.method)?; + + let mut arguments = vec![]; + for argument in method.arguments.iter() { + arguments.push(self.reduce_expression(argument)?); + } + + self.reducer.reduce_method(method, receiver, method_id, arguments) + } + pub fn reduce_call(&mut self, call: &CallExpression) -> Result { let function = self.reduce_expression(&call.function)?; diff --git a/compiler/ast/src/passes/reconstructing_reducer.rs b/compiler/ast/src/passes/reconstructing_reducer.rs index f45dcdd62a..ede43d74e7 100644 --- a/compiler/ast/src/passes/reconstructing_reducer.rs +++ b/compiler/ast/src/passes/reconstructing_reducer.rs @@ -123,6 +123,21 @@ pub trait ReconstructingReducer { }) } + fn reduce_method( + &mut self, + method: &MethodCallExpression, + receiver: Expression, + method_id: Identifier, + arguments: Vec, + ) -> Result { + Ok(MethodCallExpression { + receiver: Box::new(receiver), + method: method_id, + arguments, + span: method.span, + }) + } + // Statements fn reduce_statement(&mut self, _statement: &Statement, new: Statement) -> Result { Ok(new) diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 79c449cd3d..c41d8c0144 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -58,6 +58,10 @@ pub trait ExpressionVisitor<'a> { Default::default() } + fn visit_method(&mut self, _input: &'a MethodCallExpression) -> VisitResult { + Default::default() + } + fn visit_err(&mut self, _input: &'a ErrExpression) -> VisitResult { Default::default() } diff --git a/compiler/ast/src/passes/visitor_director.rs b/compiler/ast/src/passes/visitor_director.rs index 5ebe2bfd8d..d8b4a8641b 100644 --- a/compiler/ast/src/passes/visitor_director.rs +++ b/compiler/ast/src/passes/visitor_director.rs @@ -41,6 +41,7 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> { Expression::Unary(expr) => self.visit_unary(expr, additional), Expression::Ternary(expr) => self.visit_ternary(expr, additional), Expression::Call(expr) => self.visit_call(expr, additional), + Expression::Method(expr) => self.visit_method(expr, additional), Expression::Err(expr) => self.visit_err(expr, additional), }; } @@ -99,6 +100,16 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> { None } + fn visit_method(&mut self, input: &'a MethodCallExpression, additional: &Self::AdditionalInput) -> Option { + self.visit_expression(&input.receiver, additional); + if let VisitResult::VisitChildren = self.visitor_ref().visit_method(input) { + input.arguments.iter().for_each(|expr| { + self.visit_expression(expr, additional); + }); + } + None + } + fn visit_err(&mut self, input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Option { self.visitor_ref().visit_err(input); None diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index c01d374b1f..bb62bec9be 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -229,10 +229,24 @@ impl ParserContext<'_> { let mut expr = self.parse_primary_expression()?; loop { if self.eat(&Token::Dot) { + // Handle method call expression. + if let Some(method) = self.eat_identifier() { + if self.check(&Token::LeftParen) { + let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?; + expr = Expression::Method(MethodCallExpression { + span: expr.span() + span, + receiver: Box::new(expr), + method, + arguments, + }); + println!("expr {}", expr); + continue; + } + } let curr = &self.token; return Err(ParserError::unexpected_str(&curr.token, "int or ident", curr.span).into()); - } + } if !self.check(&Token::LeftParen) { break; } diff --git a/compiler/passes/src/type_checker/check_expressions.rs b/compiler/passes/src/type_checker/check_expressions.rs index 097aaf85b6..6f954bc8ef 100644 --- a/compiler/passes/src/type_checker/check_expressions.rs +++ b/compiler/passes/src/type_checker/check_expressions.rs @@ -54,6 +54,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> { Expression::Unary(expr) => self.visit_unary(expr, expected), Expression::Ternary(expr) => self.visit_ternary(expr, expected), Expression::Call(expr) => self.visit_call(expr, expected), + Expression::Method(expr) => self.visit_method(expr, expected), Expression::Err(expr) => self.visit_err(expr, expected), }; } @@ -403,4 +404,38 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> { expr => self.visit_expression(expr, expected), } } + + fn visit_method(&mut self, input: &'a MethodCallExpression, expected: &Self::AdditionalInput) -> Option { + None + // input.receiver. + // + // if let Some(func) = self.visitor.symbol_table.clone().lookup_fn(&input.method.name) { + // let ret = self.visitor.assert_type(func.output, expected, func.span()); + // + // if func.input.len() != input.arguments.len() { + // self.visitor.handler.emit_err( + // TypeCheckerError::incorrect_num_args_to_call( + // func.input.len(), + // input.arguments.len(), + // input.span(), + // ) + // .into(), + // ); + // } + // + // func.input + // .iter() + // .zip(input.arguments.iter()) + // .for_each(|(expected, argument)| { + // self.visit_expression(argument, &Some(expected.get_variable().type_)); + // }); + // + // Some(ret) + // } else { + // self.visitor + // .handler + // .emit_err(TypeCheckerError::unknown_sym("method", &ident.name, ident.span()).into()); + // None + // } + } } diff --git a/examples/hello-world/src/main.leo b/examples/hello-world/src/main.leo index fef974c85e..c35404f1b3 100644 --- a/examples/hello-world/src/main.leo +++ b/examples/hello-world/src/main.leo @@ -1,4 +1,3 @@ -function main() -> bool { - console.log("hello world"); - return true; +function main(a: u8, b: u8) -> u8 { + return a.add(b); } \ No newline at end of file