// Copyright 2020 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::collections::HashMap;use std::num::ParseIntError;use std::{error, fmt, iter, mem}; use itertools::Itertools as _;use once_cell::sync::Lazy;use pest::iterators::{Pair, Pairs};use pest::pratt_parser::{Assoc, Op, PrattParser};use pest::Parser;use pest_derive::Parser;use thiserror::Error; #[derive(Parser)]#[grammar = "template.pest"]struct TemplateParser; impl Rule { fn to_symbol(self) -> Option<&'static str> { match self { Rule::EOI => None, Rule::whitespace => None, Rule::escape => None, Rule::literal_char => None, Rule::raw_literal => None, Rule::literal => None, Rule::integer_literal => None, Rule::identifier => None, Rule::concat_op => Some("++"), Rule::logical_or_op => Some("||"), Rule::logical_and_op => Some("&&"), Rule::logical_not_op => Some("!"), Rule::negate_op => Some("-"), Rule::prefix_ops => None, Rule::infix_ops => None, Rule::function => None, Rule::function_arguments => None, Rule::lambda => None, Rule::formal_parameters => None, Rule::primary => None, Rule::term => None, Rule::expression => None, Rule::concat => None, Rule::template => None, Rule::program => None, Rule::function_alias_declaration => None, Rule::alias_declaration => None, } }} pub type TemplateParseResult<T> = Result<T, TemplateParseError>; #[derive(Clone, Debug)]pub struct TemplateParseError { kind: TemplateParseErrorKind, pest_error: Box<pest::error::Error<Rule>>, origin: Option<Box<TemplateParseError>>,} #[derive(Clone, Debug, Eq, Error, PartialEq)]pub enum TemplateParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("Invalid integer literal")] ParseIntError(#[source] ParseIntError), #[error(r#"Keyword "{name}" doesn't exist"#)] NoSuchKeyword { name: String, candidates: Vec<String>, }, #[error(r#"Function "{name}" doesn't exist"#)] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error(r#"Method "{name}" doesn't exist for type "{type_name}""#)] NoSuchMethod { type_name: String, name: String, candidates: Vec<String>, }, #[error(r#"Function "{name}": {message}"#)] InvalidArguments { name: String, message: String }, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error("{0}")] UnexpectedExpression(String), #[error(r#"Alias "{0}" cannot be expanded"#)] BadAliasExpansion(String), #[error(r#"Alias "{0}" expanded recursively"#)] RecursiveAlias(String),} impl TemplateParseError { pub fn with_span(kind: TemplateParseErrorKind, span: pest::Span<'_>) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); TemplateParseError { kind, pest_error, origin: None, } } pub fn with_span_and_origin( kind: TemplateParseErrorKind, span: pest::Span<'_>, origin: Self, ) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); TemplateParseError { kind, pest_error, origin: Some(Box::new(origin)), } } // TODO: migrate callers to something like lookup_method() pub(crate) fn no_such_function(function: &FunctionCallNode) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchFunction { name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } // TODO: migrate all callers to table-based lookup_method() pub(crate) fn no_such_method( type_name: impl Into<String>, function: &FunctionCallNode, ) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchMethod { type_name: type_name.into(), name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } pub fn invalid_arguments(function: &FunctionCallNode, message: impl Into<String>) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::InvalidArguments { name: function.name.to_owned(), message: message.into(), }, function.args_span, ) } pub fn expected_type(type_name: &str, span: pest::Span<'_>) -> Self { let message = format!(r#"Expected expression of type "{type_name}""#); TemplateParseError::unexpected_expression(message, span) } pub fn unexpected_expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::UnexpectedExpression(message.into()), span, ) } pub fn within_alias_expansion(self, id: TemplateAliasId<'_>, span: pest::Span<'_>) -> Self { TemplateParseError::with_span_and_origin( TemplateParseErrorKind::BadAliasExpansion(id.to_string()), span, self, ) } /// If this is a `NoSuchKeyword` error, expands the candidates list with the /// given `other_keywords`. pub fn extend_keyword_candidates<I>(mut self, other_keywords: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchKeyword { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_keywords); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchFunction { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// Expands keyword/function candidates with the given aliases. pub fn extend_alias_candidates(self, aliases_map: &TemplateAliasesMap) -> Self { self.extend_keyword_candidates(aliases_map.symbol_aliases.keys()) .extend_function_candidates(aliases_map.function_aliases.keys()) } pub fn kind(&self) -> &TemplateParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.origin.as_deref() }} impl From<pest::error::Error<Rule>> for TemplateParseError { fn from(err: pest::error::Error<Rule>) -> Self { TemplateParseError { kind: TemplateParseErrorKind::SyntaxError, pest_error: Box::new(rename_rules_in_pest_error(err)), origin: None, } }} impl fmt::Display for TemplateParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.pest_error.fmt(f) }} impl error::Error for TemplateParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { if let Some(e) = self.origin() { return Some(e as &dyn error::Error); } match &self.kind { // SyntaxError is a wrapper for pest::error::Error. TemplateParseErrorKind::SyntaxError => Some(&self.pest_error as &dyn error::Error), // Otherwise the kind represents this error. e => e.source(), } }} fn rename_rules_in_pest_error(err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) })} /// AST node without type or name checking.#[derive(Clone, Debug, PartialEq)]pub struct ExpressionNode<'i> { pub kind: ExpressionKind<'i>, pub span: pest::Span<'i>,} impl<'i> ExpressionNode<'i> { fn new(kind: ExpressionKind<'i>, span: pest::Span<'i>) -> Self { ExpressionNode { kind, span } }} #[derive(Clone, Debug, PartialEq)]pub enum ExpressionKind<'i> { Identifier(&'i str), Boolean(bool), Integer(i64), String(String), Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), Concat(Vec<ExpressionNode<'i>>), FunctionCall(FunctionCallNode<'i>), MethodCall(MethodCallNode<'i>), Lambda(LambdaNode<'i>), /// Identity node to preserve the span in the source template text. AliasExpanded(TemplateAliasId<'i>, Box<ExpressionNode<'i>>),} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum UnaryOp { /// `!` LogicalNot, /// `-` Negate,} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd,} #[derive(Clone, Debug, PartialEq)]pub struct FunctionCallNode<'i> { pub name: &'i str, pub name_span: pest::Span<'i>, pub args: Vec<ExpressionNode<'i>>, pub args_span: pest::Span<'i>,} #[derive(Clone, Debug, PartialEq)]pub struct MethodCallNode<'i> { pub object: Box<ExpressionNode<'i>>, pub function: FunctionCallNode<'i>,} #[derive(Clone, Debug, PartialEq)]pub struct LambdaNode<'i> { pub params: Vec<&'i str>, pub params_span: pest::Span<'i>, pub body: Box<ExpressionNode<'i>>,} fn parse_identifier_or_literal(pair: Pair<Rule>) -> ExpressionKind { assert_eq!(pair.as_rule(), Rule::identifier); match pair.as_str() { "false" => ExpressionKind::Boolean(false), "true" => ExpressionKind::Boolean(true), name => ExpressionKind::Identifier(name), }} fn parse_identifier_name(pair: Pair<Rule>) -> TemplateParseResult<&str> { let span = pair.as_span(); if let ExpressionKind::Identifier(name) = parse_identifier_or_literal(pair) { Ok(name) } else { Err(TemplateParseError::unexpected_expression( "Expected identifier", span, )) }} fn parse_string_literal(pair: Pair<Rule>) -> String { assert_eq!(pair.as_rule(), Rule::literal); let mut result = String::new(); for part in pair.into_inner() { match part.as_rule() { Rule::raw_literal => { result.push_str(part.as_str()); } Rule::escape => match &part.as_str()[1..] { "\"" => result.push('"'), "\\" => result.push('\\'), "t" => result.push('\t'), "r" => result.push('\r'), "n" => result.push('\n'), "0" => result.push('\0'), char => panic!("invalid escape: \\{char:?}"), }, _ => panic!("unexpected part of string: {part:?}"), } } result} fn parse_formal_parameters(params_pair: Pair<Rule>) -> TemplateParseResult<Vec<&str>> { assert_eq!(params_pair.as_rule(), Rule::formal_parameters); let params_span = params_pair.as_span(); let params: Vec<_> = params_pair .into_inner() .map(parse_identifier_name) .try_collect()?; if params.iter().all_unique() { Ok(params) } else { Err(TemplateParseError::with_span( TemplateParseErrorKind::RedefinedFunctionParameter, params_span, )) }} fn parse_function_call_node(pair: Pair<Rule>) -> TemplateParseResult<FunctionCallNode> { assert_eq!(pair.as_rule(), Rule::function); let mut inner = pair.into_inner(); let name_pair = inner.next().unwrap(); let name_span = name_pair.as_span(); let args_pair = inner.next().unwrap(); let args_span = args_pair.as_span(); assert_eq!(args_pair.as_rule(), Rule::function_arguments); let name = parse_identifier_name(name_pair)?; let args = args_pair .into_inner() .map(parse_template_node) .try_collect()?; Ok(FunctionCallNode { name, name_span, args, args_span, })} fn parse_lambda_node(pair: Pair<Rule>) -> TemplateParseResult<LambdaNode> { assert_eq!(pair.as_rule(), Rule::lambda); let mut inner = pair.into_inner(); let params_pair = inner.next().unwrap(); let params_span = params_pair.as_span(); let body_pair = inner.next().unwrap(); let params = parse_formal_parameters(params_pair)?; let body = parse_template_node(body_pair)?; Ok(LambdaNode { params, params_span, body: Box::new(body), })} fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let expr = inner.next().unwrap(); let span = expr.as_span(); let primary = match expr.as_rule() { Rule::literal => { let text = parse_string_literal(expr); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::with_span(TemplateParseErrorKind::ParseIntError(err), span) })?; ExpressionNode::new(ExpressionKind::Integer(value), span) } Rule::identifier => ExpressionNode::new(parse_identifier_or_literal(expr), span), Rule::function => { let function = parse_function_call_node(expr)?; ExpressionNode::new(ExpressionKind::FunctionCall(function), span) } Rule::lambda => { let lambda = parse_lambda_node(expr)?; ExpressionNode::new(ExpressionKind::Lambda(lambda), span) } Rule::template => parse_template_node(expr)?, other => panic!("unexpected term: {other:?}"), }; inner.try_fold(primary, |object, chain| { assert_eq!(chain.as_rule(), Rule::function); let span = object.span.start_pos().span(&chain.as_span().end_pos()); let method = MethodCallNode { object: Box::new(object), function: parse_function_call_node(chain)?, }; Ok(ExpressionNode::new( ExpressionKind::MethodCall(method), span, )) })} fn parse_expression_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::expression); static PRATT: Lazy<PrattParser<Rule>> = Lazy::new(|| { PrattParser::new() .op(Op::infix(Rule::logical_or_op, Assoc::Left)) .op(Op::infix(Rule::logical_and_op, Assoc::Left)) .op(Op::prefix(Rule::logical_not_op) | Op::prefix(Rule::negate_op)) }); PRATT .map_primary(parse_term_node) .map_prefix(|op, rhs| { let op_kind = match op.as_rule() { Rule::logical_not_op => UnaryOp::LogicalNot, Rule::negate_op => UnaryOp::Negate, r => panic!("unexpected prefix operator rule {r:?}"),Lines 1–500 of 1500. Load more
// Copyright 2020 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::collections::HashMap; use std::num::ParseIntError; use std::{error, fmt, iter, mem}; use itertools::Itertools as _; use once_cell::sync::Lazy; use pest::iterators::{Pair, Pairs}; use pest::pratt_parser::{Assoc, Op, PrattParser}; use pest::Parser; use pest_derive::Parser; use thiserror::Error; #[derive(Parser)] #[grammar = "template.pest"] struct TemplateParser; impl Rule { fn to_symbol(self) -> Option<&'static str> { match self { Rule::EOI => None, Rule::whitespace => None, Rule::escape => None, Rule::literal_char => None, Rule::raw_literal => None, Rule::literal => None, Rule::integer_literal => None, Rule::identifier => None, Rule::concat_op => Some("++"), Rule::logical_or_op => Some("||"), Rule::logical_and_op => Some("&&"), Rule::logical_not_op => Some("!"), Rule::negate_op => Some("-"), Rule::prefix_ops => None, Rule::infix_ops => None, Rule::function => None, Rule::function_arguments => None, Rule::lambda => None, Rule::formal_parameters => None, Rule::primary => None, Rule::term => None, Rule::expression => None, Rule::concat => None, Rule::template => None, Rule::program => None, Rule::function_alias_declaration => None, Rule::alias_declaration => None, } } } pub type TemplateParseResult<T> = Result<T, TemplateParseError>; #[derive(Clone, Debug)] pub struct TemplateParseError { kind: TemplateParseErrorKind, pest_error: Box<pest::error::Error<Rule>>, origin: Option<Box<TemplateParseError>>, } #[derive(Clone, Debug, Eq, Error, PartialEq)] pub enum TemplateParseErrorKind { #[error("Syntax error")] SyntaxError, #[error("Invalid integer literal")] ParseIntError(#[source] ParseIntError), #[error(r#"Keyword "{name}" doesn't exist"#)] NoSuchKeyword { name: String, candidates: Vec<String>, }, #[error(r#"Function "{name}" doesn't exist"#)] NoSuchFunction { name: String, candidates: Vec<String>, }, #[error(r#"Method "{name}" doesn't exist for type "{type_name}""#)] NoSuchMethod { type_name: String, name: String, candidates: Vec<String>, }, #[error(r#"Function "{name}": {message}"#)] InvalidArguments { name: String, message: String }, #[error("Redefinition of function parameter")] RedefinedFunctionParameter, #[error("{0}")] UnexpectedExpression(String), #[error(r#"Alias "{0}" cannot be expanded"#)] BadAliasExpansion(String), #[error(r#"Alias "{0}" expanded recursively"#)] RecursiveAlias(String), } impl TemplateParseError { pub fn with_span(kind: TemplateParseErrorKind, span: pest::Span<'_>) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); TemplateParseError { kind, pest_error, origin: None, } } pub fn with_span_and_origin( kind: TemplateParseErrorKind, span: pest::Span<'_>, origin: Self, ) -> Self { let message = iter::successors(Some(&kind as &dyn error::Error), |e| e.source()).join(": "); let pest_error = Box::new(pest::error::Error::new_from_span( pest::error::ErrorVariant::CustomError { message }, span, )); TemplateParseError { kind, pest_error, origin: Some(Box::new(origin)), } } // TODO: migrate callers to something like lookup_method() pub(crate) fn no_such_function(function: &FunctionCallNode) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchFunction { name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } // TODO: migrate all callers to table-based lookup_method() pub(crate) fn no_such_method( type_name: impl Into<String>, function: &FunctionCallNode, ) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::NoSuchMethod { type_name: type_name.into(), name: function.name.to_owned(), candidates: vec![], }, function.name_span, ) } pub fn invalid_arguments(function: &FunctionCallNode, message: impl Into<String>) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::InvalidArguments { name: function.name.to_owned(), message: message.into(), }, function.args_span, ) } pub fn expected_type(type_name: &str, span: pest::Span<'_>) -> Self { let message = format!(r#"Expected expression of type "{type_name}""#); TemplateParseError::unexpected_expression(message, span) } pub fn unexpected_expression(message: impl Into<String>, span: pest::Span<'_>) -> Self { TemplateParseError::with_span( TemplateParseErrorKind::UnexpectedExpression(message.into()), span, ) } pub fn within_alias_expansion(self, id: TemplateAliasId<'_>, span: pest::Span<'_>) -> Self { TemplateParseError::with_span_and_origin( TemplateParseErrorKind::BadAliasExpansion(id.to_string()), span, self, ) } /// If this is a `NoSuchKeyword` error, expands the candidates list with the /// given `other_keywords`. pub fn extend_keyword_candidates<I>(mut self, other_keywords: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchKeyword { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_keywords); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// If this is a `NoSuchFunction` error, expands the candidates list with /// the given `other_functions`. pub fn extend_function_candidates<I>(mut self, other_functions: I) -> Self where I: IntoIterator, I::Item: AsRef<str>, { if let TemplateParseErrorKind::NoSuchFunction { name, candidates } = &mut self.kind { let other_candidates = collect_similar(name, other_functions); *candidates = itertools::merge(mem::take(candidates), other_candidates) .dedup() .collect(); } self } /// Expands keyword/function candidates with the given aliases. pub fn extend_alias_candidates(self, aliases_map: &TemplateAliasesMap) -> Self { self.extend_keyword_candidates(aliases_map.symbol_aliases.keys()) .extend_function_candidates(aliases_map.function_aliases.keys()) } pub fn kind(&self) -> &TemplateParseErrorKind { &self.kind } /// Original parsing error which typically occurred in an alias expression. pub fn origin(&self) -> Option<&Self> { self.origin.as_deref() } } impl From<pest::error::Error<Rule>> for TemplateParseError { fn from(err: pest::error::Error<Rule>) -> Self { TemplateParseError { kind: TemplateParseErrorKind::SyntaxError, pest_error: Box::new(rename_rules_in_pest_error(err)), origin: None, } } } impl fmt::Display for TemplateParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.pest_error.fmt(f) } } impl error::Error for TemplateParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { if let Some(e) = self.origin() { return Some(e as &dyn error::Error); } match &self.kind { // SyntaxError is a wrapper for pest::error::Error. TemplateParseErrorKind::SyntaxError => Some(&self.pest_error as &dyn error::Error), // Otherwise the kind represents this error. e => e.source(), } } } fn rename_rules_in_pest_error(err: pest::error::Error<Rule>) -> pest::error::Error<Rule> { err.renamed_rules(|rule| { rule.to_symbol() .map(|sym| format!("`{sym}`")) .unwrap_or_else(|| format!("<{rule:?}>")) }) } /// AST node without type or name checking. #[derive(Clone, Debug, PartialEq)] pub struct ExpressionNode<'i> { pub kind: ExpressionKind<'i>, pub span: pest::Span<'i>, } impl<'i> ExpressionNode<'i> { fn new(kind: ExpressionKind<'i>, span: pest::Span<'i>) -> Self { ExpressionNode { kind, span } } } #[derive(Clone, Debug, PartialEq)] pub enum ExpressionKind<'i> { Identifier(&'i str), Boolean(bool), Integer(i64), String(String), Unary(UnaryOp, Box<ExpressionNode<'i>>), Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>), Concat(Vec<ExpressionNode<'i>>), FunctionCall(FunctionCallNode<'i>), MethodCall(MethodCallNode<'i>), Lambda(LambdaNode<'i>), /// Identity node to preserve the span in the source template text. AliasExpanded(TemplateAliasId<'i>, Box<ExpressionNode<'i>>), } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum UnaryOp { /// `!` LogicalNot, /// `-` Negate, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum BinaryOp { /// `||` LogicalOr, /// `&&` LogicalAnd, } #[derive(Clone, Debug, PartialEq)] pub struct FunctionCallNode<'i> { pub name: &'i str, pub name_span: pest::Span<'i>, pub args: Vec<ExpressionNode<'i>>, pub args_span: pest::Span<'i>, } #[derive(Clone, Debug, PartialEq)] pub struct MethodCallNode<'i> { pub object: Box<ExpressionNode<'i>>, pub function: FunctionCallNode<'i>, } #[derive(Clone, Debug, PartialEq)] pub struct LambdaNode<'i> { pub params: Vec<&'i str>, pub params_span: pest::Span<'i>, pub body: Box<ExpressionNode<'i>>, } fn parse_identifier_or_literal(pair: Pair<Rule>) -> ExpressionKind { assert_eq!(pair.as_rule(), Rule::identifier); match pair.as_str() { "false" => ExpressionKind::Boolean(false), "true" => ExpressionKind::Boolean(true), name => ExpressionKind::Identifier(name), } } fn parse_identifier_name(pair: Pair<Rule>) -> TemplateParseResult<&str> { let span = pair.as_span(); if let ExpressionKind::Identifier(name) = parse_identifier_or_literal(pair) { Ok(name) } else { Err(TemplateParseError::unexpected_expression( "Expected identifier", span, )) } } fn parse_string_literal(pair: Pair<Rule>) -> String { assert_eq!(pair.as_rule(), Rule::literal); let mut result = String::new(); for part in pair.into_inner() { match part.as_rule() { Rule::raw_literal => { result.push_str(part.as_str()); } Rule::escape => match &part.as_str()[1..] { "\"" => result.push('"'), "\\" => result.push('\\'), "t" => result.push('\t'), "r" => result.push('\r'), "n" => result.push('\n'), "0" => result.push('\0'), char => panic!("invalid escape: \\{char:?}"), }, _ => panic!("unexpected part of string: {part:?}"), } } result } fn parse_formal_parameters(params_pair: Pair<Rule>) -> TemplateParseResult<Vec<&str>> { assert_eq!(params_pair.as_rule(), Rule::formal_parameters); let params_span = params_pair.as_span(); let params: Vec<_> = params_pair .into_inner() .map(parse_identifier_name) .try_collect()?; if params.iter().all_unique() { Ok(params) } else { Err(TemplateParseError::with_span( TemplateParseErrorKind::RedefinedFunctionParameter, params_span, )) } } fn parse_function_call_node(pair: Pair<Rule>) -> TemplateParseResult<FunctionCallNode> { assert_eq!(pair.as_rule(), Rule::function); let mut inner = pair.into_inner(); let name_pair = inner.next().unwrap(); let name_span = name_pair.as_span(); let args_pair = inner.next().unwrap(); let args_span = args_pair.as_span(); assert_eq!(args_pair.as_rule(), Rule::function_arguments); let name = parse_identifier_name(name_pair)?; let args = args_pair .into_inner() .map(parse_template_node) .try_collect()?; Ok(FunctionCallNode { name, name_span, args, args_span, }) } fn parse_lambda_node(pair: Pair<Rule>) -> TemplateParseResult<LambdaNode> { assert_eq!(pair.as_rule(), Rule::lambda); let mut inner = pair.into_inner(); let params_pair = inner.next().unwrap(); let params_span = params_pair.as_span(); let body_pair = inner.next().unwrap(); let params = parse_formal_parameters(params_pair)?; let body = parse_template_node(body_pair)?; Ok(LambdaNode { params, params_span, body: Box::new(body), }) } fn parse_term_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::term); let mut inner = pair.into_inner(); let expr = inner.next().unwrap(); let span = expr.as_span(); let primary = match expr.as_rule() { Rule::literal => { let text = parse_string_literal(expr); ExpressionNode::new(ExpressionKind::String(text), span) } Rule::integer_literal => { let value = expr.as_str().parse().map_err(|err| { TemplateParseError::with_span(TemplateParseErrorKind::ParseIntError(err), span) })?; ExpressionNode::new(ExpressionKind::Integer(value), span) } Rule::identifier => ExpressionNode::new(parse_identifier_or_literal(expr), span), Rule::function => { let function = parse_function_call_node(expr)?; ExpressionNode::new(ExpressionKind::FunctionCall(function), span) } Rule::lambda => { let lambda = parse_lambda_node(expr)?; ExpressionNode::new(ExpressionKind::Lambda(lambda), span) } Rule::template => parse_template_node(expr)?, other => panic!("unexpected term: {other:?}"), }; inner.try_fold(primary, |object, chain| { assert_eq!(chain.as_rule(), Rule::function); let span = object.span.start_pos().span(&chain.as_span().end_pos()); let method = MethodCallNode { object: Box::new(object), function: parse_function_call_node(chain)?, }; Ok(ExpressionNode::new( ExpressionKind::MethodCall(method), span, )) }) } fn parse_expression_node(pair: Pair<Rule>) -> TemplateParseResult<ExpressionNode> { assert_eq!(pair.as_rule(), Rule::expression); static PRATT: Lazy<PrattParser<Rule>> = Lazy::new(|| { PrattParser::new() .op(Op::infix(Rule::logical_or_op, Assoc::Left)) .op(Op::infix(Rule::logical_and_op, Assoc::Left)) .op(Op::prefix(Rule::logical_not_op) | Op::prefix(Rule::negate_op)) }); PRATT .map_primary(parse_term_node) .map_prefix(|op, rhs| { let op_kind = match op.as_rule() { Rule::logical_not_op => UnaryOp::LogicalNot, Rule::negate_op => UnaryOp::Negate, r => panic!("unexpected prefix operator rule {r:?}"),