amarok_parser/builders/
statements.rs

1use amarok_syntax::{Diagnostic, Spanned, Statement};
2use pest::iterators::Pair;
3
4use crate::grammar::Rule;
5
6use super::expressions::build_expression;
7use super::helpers::{collect_path_segments, find_child, span_of};
8
9pub(crate) fn build_assignment_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
10    // assignment_statement = { identifier ~ "=" ~ expression ~ ";" }
11    let outer_span = span_of(&pair);
12    let mut inner = pair.into_inner();
13
14    let name_pair = inner
15        .next()
16        .ok_or_else(|| Diagnostic::new("Assignment missing identifier.").with_span(outer_span))?;
17    if name_pair.as_rule() != Rule::identifier {
18        return Err(Diagnostic::new(format!(
19            "Assignment expected identifier, got {:?}",
20            name_pair.as_rule()
21        ))
22        .with_span(span_of(&name_pair)));
23    }
24    let name = name_pair.as_str().to_string();
25
26    let expression_pair = inner
27        .find(|p| p.as_rule() == Rule::expression)
28        .ok_or_else(|| Diagnostic::new("Assignment missing expression.").with_span(outer_span))?;
29
30    let value = build_expression(expression_pair)?;
31
32    Ok(Statement::Assignment { name, value })
33}
34
35pub(crate) fn build_expression_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
36    // expression_statement = { expression ~ ";" }
37    let expression_pair = find_child(pair, Rule::expression, "Expression statement")?;
38
39    Ok(Statement::Expression {
40        expression: build_expression(expression_pair)?,
41    })
42}
43
44pub(crate) fn build_block_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
45    // block_statement = { "{" ~ statement* ~ "}" }
46    let mut statements: Vec<Spanned<Statement>> = Vec::new();
47
48    for item in pair.into_inner() {
49        // statement is silent in the grammar, so these should be concrete statement rules.
50        statements.push(super::build_statement(item)?);
51    }
52
53    Ok(Statement::Block { statements })
54}
55
56pub(crate) fn build_if_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
57    // if_statement = { "if" ~ "(" ~ expression ~ ")" ~ block_statement ~ else_clause? }
58    let outer_span = span_of(&pair);
59    let mut inner = pair.into_inner();
60
61    let condition_pair = inner
62        .find(|p| p.as_rule() == Rule::expression)
63        .ok_or_else(|| {
64            Diagnostic::new("If statement missing condition expression.").with_span(outer_span)
65        })?;
66    let condition = build_expression(condition_pair)?;
67
68    let then_block_pair = inner
69        .find(|p| p.as_rule() == Rule::block_statement)
70        .ok_or_else(|| Diagnostic::new("If statement missing then block.").with_span(outer_span))?;
71    let then_branch = extract_block_statements(then_block_pair)?;
72
73    let mut else_branch: Vec<Spanned<Statement>> = Vec::new();
74    for item in inner {
75        if item.as_rule() == Rule::else_clause {
76            else_branch = extract_else_clause(item)?;
77        }
78    }
79
80    Ok(Statement::If {
81        condition,
82        then_branch,
83        else_branch,
84    })
85}
86
87fn extract_else_clause(pair: Pair<Rule>) -> Result<Vec<Spanned<Statement>>, Diagnostic> {
88    // else_clause = { "else" ~ block_statement }
89    extract_block_statements(find_child(pair, Rule::block_statement, "Else clause")?)
90}
91
92fn extract_block_statements(block_pair: Pair<Rule>) -> Result<Vec<Spanned<Statement>>, Diagnostic> {
93    // block_statement = { "{" ~ statement* ~ "}" }
94    let mut statements = Vec::new();
95    for item in block_pair.into_inner() {
96        statements.push(super::build_statement(item)?);
97    }
98    Ok(statements)
99}
100
101pub(crate) fn build_while_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
102    // while_statement = { "while" ~ "(" ~ expression ~ ")" ~ block_statement }
103    let outer_span = span_of(&pair);
104    let mut inner = pair.into_inner();
105
106    let condition_pair = inner
107        .find(|p| p.as_rule() == Rule::expression)
108        .ok_or_else(|| {
109            Diagnostic::new("While statement missing condition expression.").with_span(outer_span)
110        })?;
111    let condition = build_expression(condition_pair)?;
112
113    let body_block_pair = inner
114        .find(|p| p.as_rule() == Rule::block_statement)
115        .ok_or_else(|| {
116            Diagnostic::new("While statement missing body block.").with_span(outer_span)
117        })?;
118    let body = extract_block_statements(body_block_pair)?;
119
120    Ok(Statement::While { condition, body })
121}
122
123pub(crate) fn build_function_definition(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
124    // function_definition = { "def" ~ identifier ~ "(" ~ parameter_list? ~ ")" ~ block_statement }
125    let outer_span = span_of(&pair);
126    let mut inner = pair.into_inner();
127
128    let name_pair = inner
129        .find(|p| p.as_rule() == Rule::identifier)
130        .ok_or_else(|| {
131            Diagnostic::new("Function definition missing name.").with_span(outer_span)
132        })?;
133    let name = name_pair.as_str().to_string();
134
135    let mut parameters: Vec<String> = Vec::new();
136    let mut body: Vec<Spanned<Statement>> = Vec::new();
137
138    for item in inner {
139        match item.as_rule() {
140            Rule::parameter_list => {
141                parameters = item
142                    .into_inner()
143                    .filter(|p| p.as_rule() == Rule::identifier)
144                    .map(|p| p.as_str().to_string())
145                    .collect();
146            }
147            Rule::block_statement => {
148                body = extract_block_statements(item)?;
149            }
150            _ => {}
151        }
152    }
153
154    Ok(Statement::FunctionDefinition {
155        name,
156        parameters,
157        body,
158    })
159}
160
161pub(crate) fn build_return_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
162    // return_statement = { "return" ~ expression? ~ ";" }
163    let expression_pair = pair.into_inner().find(|p| p.as_rule() == Rule::expression);
164    let value = match expression_pair {
165        Some(p) => Some(build_expression(p)?),
166        None => None,
167    };
168
169    Ok(Statement::Return { value })
170}
171
172pub(crate) fn build_use_statement(pair: Pair<Rule>) -> Result<Statement, Diagnostic> {
173    // use_statement = { "use" ~ path ~ ";" }
174    let path_pair = find_child(pair, Rule::path, "Use statement")?;
175    let path = collect_path_segments(path_pair, "Use statement")?;
176
177    Ok(Statement::Use { path })
178}