dash_lang/
parser.rs

1use pest::Parser;
2use pest_derive::Parser;
3use crate::eval::exec_stmt;
4use crate::ast::{Stmt, Expr, Op, Context};
5
6#[derive(Parser)]
7#[grammar = "dash.pest"]
8pub struct DashParser;
9
10/// Parses and executes a source program written in the custom language.
11///
12/// This function uses the Pest parser to convert the source string into an AST,
13/// then executes each statement in order using a fresh `Context`.
14///
15/// # Arguments
16/// * `source` - A string slice containing the source code to run.
17pub fn run(source: &str) {
18    match DashParser::parse(Rule::program, source) {
19        Ok(mut pairs) => {
20            let pair = pairs.next().unwrap();
21            let ast = build_ast(pair.into_inner());
22            let mut ctx = Context::default();
23            for stmt in ast {
24                exec_stmt(&stmt, &mut ctx);
25            }
26        }
27        Err(e) => {
28            println!("Parse error: {}", e);
29        }
30    }
31}
32
33/// Converts a sequence of Pest pairs into a list of statements (AST).
34///
35/// Filters out non-statement rules and delegates to `build_stmt` for each.
36///
37/// # Arguments
38/// * `pairs` - Pest pairs representing parsed tokens.
39///
40/// # Returns
41/// A vector of `Stmt` representing the program's abstract syntax tree.
42fn build_ast(pairs: pest::iterators::Pairs<Rule>) -> Vec<Stmt> {
43    pairs
44        .filter_map(|pair| match pair.as_rule() {
45            Rule::statement => Some(build_stmt(pair.into_inner())),
46            _ => None,
47        })
48        .collect()
49}
50
51/// Builds a single statement from its Pest pair representation.
52///
53/// Matches the rule type and constructs the corresponding `Stmt` variant.
54///
55/// # Arguments
56/// * `pairs` - Pest pairs representing a statement.
57///
58/// # Returns
59/// A `Stmt` enum variant representing the parsed statement.
60fn build_stmt(mut pairs: pest::iterators::Pairs<Rule>) -> Stmt {
61    let pair = pairs.next().unwrap();
62    match pair.as_rule() {
63        Rule::print_stmt => {
64            let mut inner = pair.into_inner();
65            let expr_pair = inner.find(|p| p.as_rule() == Rule::expr).unwrap();
66            let expr = build_expr(expr_pair);
67            Stmt::Print(expr)
68        }
69        Rule::let_stmt => {
70            let mut inner = pair.into_inner();
71            let name = inner.next().unwrap().as_str().to_string();
72            let expr = build_expr(inner.next().unwrap());
73            Stmt::Let(name, expr)
74        }
75        Rule::if_stmt => {
76            let mut inner = pair.into_inner();
77            let condition = build_expr(inner.next().unwrap());
78            let then_block = build_block(inner.next().unwrap());
79            let else_block = inner.next().map(build_block);
80            Stmt::If {
81                condition,
82                then_branch: then_block,
83                else_branch: else_block,
84            }
85        }
86        Rule::while_stmt => {
87            let mut inner = pair.into_inner();
88            let condition = build_expr(inner.next().unwrap());
89            let body = build_block(inner.next().unwrap());
90            Stmt::While { condition, body }
91        }
92        Rule::break_stmt => Stmt::Break,
93        Rule::continue_stmt => Stmt::Continue,
94        Rule::fn_stmt => {
95            let mut inner = pair.into_inner();
96            let name = inner.next().unwrap().as_str().to_string();
97            let param_list = inner.next().unwrap();
98            let params = param_list
99                .into_inner()
100                .map(|p| p.as_str().to_string())
101                .collect();
102            let body = build_block(inner.next().unwrap());
103            Stmt::Fn { name, params, body }
104        }
105        Rule::call_stmt => {
106            let expr = build_expr(pair.into_inner().next().unwrap());
107            if let Expr::Call(name, args) = expr {
108                Stmt::Call(name, args)
109            } else {
110                panic!("Expected call expression in call_stmt");
111            }
112        }
113        Rule::return_stmt => {
114            let expr = build_expr(pair.into_inner().next().unwrap());
115            Stmt::Return(expr)
116        }
117        _ => unreachable!(),
118    }
119}
120
121/// Builds an expression from its Pest pair representation.
122///
123/// Handles literals, variables, binary operations, comparisons, and function calls.
124///
125/// # Arguments
126/// * `pair` - A Pest pair representing an expression.
127///
128/// # Returns
129/// An `Expr` enum variant representing the parsed expression.
130fn build_expr(pair: pest::iterators::Pair<Rule>) -> Expr {
131    match pair.as_rule() {
132        Rule::expr => {
133            let mut inner = pair.into_inner();
134            let mut left = build_expr(inner.next().unwrap());
135            while let Some(op_pair) = inner.next() {
136                let right = build_expr(inner.next().unwrap());
137                let op = match op_pair.as_str() {
138                    "+" => Op::Add,
139                    "-" => Op::Sub,
140                    _ => unreachable!(),
141                };
142                left = Expr::Binary(Box::new(left), op, Box::new(right));
143            }
144            left
145        }
146        Rule::term => {
147            let mut inner = pair.into_inner();
148            let mut left = build_expr(inner.next().unwrap());
149            while let Some(op_pair) = inner.next() {
150                let op = match op_pair.as_str() {
151                    "*" => Op::Mul,
152                    "/" => Op::Div,
153                    _ => panic!("Unexpected operator in term: {:?}", op_pair.as_str()),
154                };
155                let right = build_expr(inner.next().unwrap());
156                left = Expr::Binary(Box::new(left), op, Box::new(right));
157            }
158            left
159        }
160        Rule::factor => build_expr(pair.into_inner().next().unwrap()),
161        Rule::number => Expr::Int(pair.as_str().parse().unwrap()),
162        Rule::string => {
163            let s = pair.as_str();
164            Expr::Str(s[1..s.len() - 1].to_string()) // remove quotes
165        }
166        Rule::ident => Expr::Var(pair.as_str().to_string()),
167        Rule::comparison => {
168            let mut inner = pair.into_inner();
169            let left = build_expr(inner.next().unwrap());
170            if let Some(op_pair) = inner.next() {
171                let right = build_expr(inner.next().unwrap());
172                let op = match op_pair.as_str() {
173                    ">" => Op::Greater,
174                    "<" => Op::Less,
175                    ">=" => Op::GreaterEq,
176                    "<=" => Op::LessEq,
177                    "==" => Op::Equal,
178                    "!=" => Op::NotEqual,
179                    _ => unreachable!(),
180                };
181                Expr::Binary(Box::new(left), op, Box::new(right))
182            } else {
183                left
184            }
185        }
186        Rule::call_expr => {
187            let mut inner = pair.into_inner();
188            let name = inner.next().unwrap().as_str().to_string();
189            let args = if let Some(arg_list) = inner.next() {
190                arg_list.into_inner().map(build_expr).collect()
191            } else {
192                Vec::new()
193            };
194            Expr::Call(name, args)
195        }
196        Rule::primary => build_expr(pair.into_inner().next().unwrap()),
197        _ => unreachable!(),
198    }
199}
200
201/// Builds a block of statements from a Pest pair.
202///
203/// Delegates to `build_ast` to convert the inner pairs into a vector of statements.
204///
205/// # Arguments
206/// * `pair` - A Pest pair representing a block.
207///
208/// # Returns
209/// A vector of `Stmt` representing the block's contents.
210fn build_block(pair: pest::iterators::Pair<Rule>) -> Vec<Stmt> {
211    build_ast(pair.into_inner())
212}