amarok_parser/builders/
expressions.rs1use 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 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 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 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 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}