amarok_parser/builders/
expressions.rs

1use amarok_syntax::{BinaryOperator, Diagnostic, Expression, Spanned};
2use pest::iterators::Pair;
3
4use crate::grammar::Rule;
5
6use super::helpers::{
7    collect_path_segments, expect_single_inner, find_child, span_of, unquote_string,
8};
9
10pub(crate) fn build_expression(pair: Pair<Rule>) -> Result<Spanned<Expression>, Diagnostic> {
11    let expression_span = span_of(&pair);
12
13    match pair.as_rule() {
14        Rule::expression => build_expression(expect_single_inner(pair, "expression")?),
15
16        Rule::addition => {
17            build_left_associative_binary(pair, Rule::add_operator, operator_from_add_text)
18        }
19
20        Rule::multiplication => build_left_associative_binary(
21            pair,
22            Rule::multiply_operator,
23            operator_from_multiply_text,
24        ),
25
26        Rule::primary => build_expression(expect_single_inner(pair, "primary")?),
27
28        Rule::parenthesized => {
29            // parenthesized = { "(" ~ expression ~ ")" }
30            build_expression(find_child(
31                pair,
32                Rule::expression,
33                "Parenthesized expression",
34            )?)
35        }
36
37        Rule::function_call => build_function_call(pair),
38
39        Rule::variable => {
40            let inner = expect_single_inner(pair, "variable")?;
41            if inner.as_rule() != Rule::identifier {
42                return Err(Diagnostic::new(format!(
43                    "Expected identifier inside variable, got {:?}",
44                    inner.as_rule()
45                ))
46                .with_span(span_of(&inner)));
47            }
48            Ok(Spanned::new(
49                expression_span,
50                Expression::Variable(inner.as_str().to_string()),
51            ))
52        }
53
54        Rule::integer => {
55            let text = pair.as_str();
56            let value: i64 = text.parse().map_err(|_| {
57                Diagnostic::new(format!("Invalid integer literal: {text}"))
58                    .with_span(expression_span)
59            })?;
60            Ok(Spanned::new(expression_span, Expression::Integer(value)))
61        }
62
63        Rule::string => Ok(Spanned::new(
64            expression_span,
65            Expression::String(unquote_string(pair.as_str(), expression_span)?),
66        )),
67
68        Rule::identifier => Ok(Spanned::new(
69            expression_span,
70            Expression::Variable(pair.as_str().to_string()),
71        )),
72
73        other => Err(
74            Diagnostic::new(format!("Unhandled rule in build_expression: {other:?}"))
75                .with_span(expression_span),
76        ),
77    }
78}
79
80fn build_function_call(pair: Pair<Rule>) -> Result<Spanned<Expression>, Diagnostic> {
81    // function_call = { path ~ "(" ~ argument_list? ~ ")" }
82    let call_span = span_of(&pair);
83
84    let mut inner = pair.into_inner();
85
86    let path_pair = inner
87        .next()
88        .ok_or_else(|| Diagnostic::new("Function call missing path.").with_span(call_span))?;
89
90    if path_pair.as_rule() != Rule::path {
91        return Err(Diagnostic::new(format!(
92            "Function call expected path, got {:?}",
93            path_pair.as_rule()
94        ))
95        .with_span(span_of(&path_pair)));
96    }
97
98    let path = collect_path_segments(path_pair, "Function call")?;
99
100    let mut arguments: Vec<Spanned<Expression>> = Vec::new();
101    for item in inner {
102        if item.as_rule() == Rule::argument_list {
103            arguments = build_argument_list(item)?;
104        }
105    }
106
107    Ok(Spanned::new(
108        call_span,
109        Expression::FunctionCall { path, arguments },
110    ))
111}
112
113fn build_argument_list(pair: Pair<Rule>) -> Result<Vec<Spanned<Expression>>, Diagnostic> {
114    // argument_list = { expression ~ ("," ~ expression)* }
115    let mut arguments: Vec<Spanned<Expression>> = Vec::new();
116
117    for item in pair.into_inner() {
118        if item.as_rule() == Rule::expression {
119            arguments.push(build_expression(item)?);
120        }
121    }
122
123    Ok(arguments)
124}
125
126fn build_left_associative_binary(
127    pair: Pair<Rule>,
128    expected_operator_rule: Rule,
129    operator_from_text: fn(&str) -> Result<BinaryOperator, String>,
130) -> Result<Spanned<Expression>, Diagnostic> {
131    // addition = { multiplication ~ (add_operator ~ multiplication)* }
132    // multiplication = { primary ~ (multiply_operator ~ primary)* }
133    //
134    // Children look like: operand, operator, operand, operator, operand...
135    let full_span = span_of(&pair);
136    let mut inner = pair.into_inner();
137
138    let first_operand_pair = inner.next().ok_or_else(|| {
139        Diagnostic::new("Expected left operand, found nothing.").with_span(full_span)
140    })?;
141
142    let mut expression = build_expression(first_operand_pair)?;
143
144    while let Some(operator_pair) = inner.next() {
145        if operator_pair.as_rule() != expected_operator_rule {
146            return Err(Diagnostic::new(format!(
147                "Expected operator rule {:?}, got {:?}",
148                expected_operator_rule,
149                operator_pair.as_rule()
150            ))
151            .with_span(span_of(&operator_pair)));
152        }
153
154        let operator_span = span_of(&operator_pair);
155        let operator = operator_from_text(operator_pair.as_str())
156            .map_err(|message| Diagnostic::new(message).with_span(operator_span))?;
157
158        let right_operand_pair = inner.next().ok_or_else(|| {
159            Diagnostic::new("Expected right operand after operator.").with_span(operator_span)
160        })?;
161
162        let right_expression = build_expression(right_operand_pair)?;
163
164        expression = Spanned::new(
165            full_span,
166            Expression::Binary {
167                left: Box::new(expression),
168                operator,
169                right: Box::new(right_expression),
170            },
171        );
172    }
173
174    Ok(expression)
175}
176
177fn operator_from_add_text(text: &str) -> Result<BinaryOperator, String> {
178    match text {
179        "+" => Ok(BinaryOperator::Add),
180        "-" => Ok(BinaryOperator::Subtract),
181        _ => Err(format!("Unknown add operator: {text}")),
182    }
183}
184
185fn operator_from_multiply_text(text: &str) -> Result<BinaryOperator, String> {
186    match text {
187        "*" => Ok(BinaryOperator::Multiply),
188        "/" => Ok(BinaryOperator::Divide),
189        _ => Err(format!("Unknown multiply operator: {text}")),
190    }
191}