// 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),
}
}
}