// Copyright (C) 2019-2021 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 . //! Abstract syntax tree (ast) representation from leo.pest. use crate::{ common::Identifier, expressions::{ ArrayInitializerExpression, ArrayInlineExpression, CircuitInlineExpression, Expression, PostfixExpression, SelfPostfixExpression, TernaryExpression, UnaryExpression, }, operations::{BinaryOperation, UnaryOperation}, values::Value, }; use crate::expressions::TupleExpression; use from_pest::{ConversionError, FromPest, Void}; use pest::{ error::Error, iterators::{Pair, Pairs}, prec_climber::{Assoc, Operator, PrecClimber}, Parser, Span, }; #[derive(Parser)] #[grammar = "leo.pest"] pub struct LanguageParser; pub fn parse(program_string: &str) -> Result, Error> { LanguageParser::parse(Rule::file, program_string) } pub(crate) fn span_into_string(span: Span) -> String { span.as_str().to_string() } lazy_static! { static ref PRECEDENCE_CLIMBER: PrecClimber = precedence_climber(); } // Expressions fn precedence_climber() -> PrecClimber { PrecClimber::new(vec![ Operator::new(Rule::operation_or, Assoc::Left), Operator::new(Rule::operation_and, Assoc::Left), Operator::new(Rule::operation_eq, Assoc::Left) | Operator::new(Rule::operation_ne, Assoc::Left) | Operator::new(Rule::operation_ge, Assoc::Left) | Operator::new(Rule::operation_gt, Assoc::Left) | Operator::new(Rule::operation_le, Assoc::Left) | Operator::new(Rule::operation_lt, Assoc::Left), Operator::new(Rule::operation_add, Assoc::Left) | Operator::new(Rule::operation_sub, Assoc::Left), Operator::new(Rule::operation_mul, Assoc::Left) | Operator::new(Rule::operation_div, Assoc::Left), Operator::new(Rule::operation_pow, Assoc::Left), ]) } fn parse_term(pair: Pair) -> Expression { match pair.as_rule() { Rule::expression_term => { let clone = pair.clone(); let next = clone.into_inner().next().unwrap(); match next.as_rule() { Rule::expression => Expression::from_pest(&mut pair.into_inner()).unwrap(), // Parenthesis case Rule::expression_tuple => { Expression::Tuple(TupleExpression::from_pest(&mut pair.into_inner()).unwrap()) } Rule::expression_array_inline => { Expression::ArrayInline(ArrayInlineExpression::from_pest(&mut pair.into_inner()).unwrap()) } Rule::expression_array_initializer => Expression::ArrayInitializer(Box::new( ArrayInitializerExpression::from_pest(&mut pair.into_inner()).unwrap(), )), Rule::expression_circuit_inline => { Expression::CircuitInline(CircuitInlineExpression::from_pest(&mut pair.into_inner()).unwrap()) } Rule::expression_conditional => { Expression::Ternary(Box::new(TernaryExpression::from_pest(&mut pair.into_inner()).unwrap())) } Rule::expression_unary => { // The following is necessary to match with the unary operator and its unary expression let span = next.as_span(); let mut inner = next.into_inner(); let operation = match inner.next().unwrap().as_rule() { Rule::operation_unary => { UnaryOperation::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap() } rule => unreachable!("`expression_unary` should yield `operation_unary`, found {:#?}", rule), }; let expression = parse_term(inner.next().unwrap()); Expression::Unary(Box::new(UnaryExpression { operation, expression, span, })) } Rule::expression_postfix => { Expression::Postfix(PostfixExpression::from_pest(&mut pair.into_inner()).unwrap()) } Rule::self_expression_postfix => { Expression::SelfPostfix(SelfPostfixExpression::from_pest(&mut pair.into_inner()).unwrap()) } Rule::value => Expression::Value(Value::from_pest(&mut pair.into_inner()).unwrap()), Rule::identifier => Expression::Identifier(Identifier::from_pest(&mut pair.into_inner()).unwrap()), rule => unreachable!( "`term` should contain one of ['value', 'identifier', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}", rule ), } } rule => unreachable!( "`parse_expression_term` should be invoked on `Rule::expression_term`, found {:#?}", rule ), } } fn binary_expression<'ast>(lhs: Expression<'ast>, pair: Pair<'ast, Rule>, rhs: Expression<'ast>) -> Expression<'ast> { let (start, _) = lhs.span().clone().split(); let (_, end) = rhs.span().clone().split(); let span = start.span(&end); match pair.as_rule() { Rule::operation_or => Expression::binary(BinaryOperation::Or, lhs, rhs, span), Rule::operation_and => Expression::binary(BinaryOperation::And, lhs, rhs, span), Rule::operation_eq => Expression::binary(BinaryOperation::Eq, lhs, rhs, span), Rule::operation_ne => Expression::binary(BinaryOperation::Ne, lhs, rhs, span), Rule::operation_ge => Expression::binary(BinaryOperation::Ge, lhs, rhs, span), Rule::operation_gt => Expression::binary(BinaryOperation::Gt, lhs, rhs, span), Rule::operation_le => Expression::binary(BinaryOperation::Le, lhs, rhs, span), Rule::operation_lt => Expression::binary(BinaryOperation::Lt, lhs, rhs, span), Rule::operation_add => Expression::binary(BinaryOperation::Add, lhs, rhs, span), Rule::operation_sub => Expression::binary(BinaryOperation::Sub, lhs, rhs, span), Rule::operation_mul => Expression::binary(BinaryOperation::Mul, lhs, rhs, span), Rule::operation_div => Expression::binary(BinaryOperation::Div, lhs, rhs, span), Rule::operation_pow => Expression::binary(BinaryOperation::Pow, lhs, rhs, span), _ => unreachable!(), } } impl<'ast> FromPest<'ast> for Expression<'ast> { type FatalError = Void; type Rule = Rule; fn from_pest(pest: &mut Pairs<'ast, Rule>) -> Result> { let pair = pest.peek().ok_or(::from_pest::ConversionError::NoMatch)?; match pair.as_rule() { Rule::expression => { // advance the iterator pest.next(); Ok(PRECEDENCE_CLIMBER.climb(pair.into_inner(), parse_term, binary_expression)) } _ => Err(ConversionError::NoMatch), } } }