rimu_parse/
lib.rs

1use rimu_ast::{SpannedBlock, SpannedExpression};
2use rimu_meta::{SourceId, Span};
3
4mod compiler;
5mod error;
6mod lexer;
7mod token;
8
9pub use crate::error::Error;
10pub(crate) use compiler::{compile_block, compile_expression};
11pub(crate) use lexer::{tokenize_block, tokenize_expression};
12pub(crate) use token::{SpannedToken, Token};
13
14pub fn parse_expression(code: &str, source: SourceId) -> (Option<SpannedExpression>, Vec<Error>) {
15    let mut errors = Vec::new();
16
17    let len = code.chars().count();
18    let eoi = Span::new(source.clone(), len, len);
19
20    let (tokens, lex_errors) = tokenize_expression(code, source.clone());
21    errors.append(&mut lex_errors.into_iter().map(Error::Lexer).collect());
22
23    let Some(tokens) = tokens else {
24        return (None, errors);
25    };
26
27    let (output, compile_errors) = compile_expression(tokens, eoi);
28    errors.append(&mut compile_errors.into_iter().map(Error::Compiler).collect());
29
30    (output, errors)
31}
32
33pub fn parse_block(code: &str, source: SourceId) -> (Option<SpannedBlock>, Vec<Error>) {
34    let mut errors = Vec::new();
35
36    let len = code.chars().count();
37    let eoi = Span::new(source.clone(), len, len);
38
39    let (tokens, lex_errors) = tokenize_block(code, source.clone());
40    errors.append(&mut lex_errors.into_iter().map(Error::Lexer).collect());
41
42    let Some(tokens) = tokens else {
43        return (None, errors);
44    };
45
46    let (output, compile_errors) = compile_block(tokens, eoi);
47    errors.append(&mut compile_errors.into_iter().map(Error::Compiler).collect());
48
49    (output, errors)
50}
51
52#[cfg(test)]
53mod tests {
54    use std::ops::Range;
55
56    use pretty_assertions::assert_eq;
57    use rimu_ast::{BinaryOperator, Block, Expression, SpannedBlock, SpannedExpression};
58    use rimu_meta::{SourceId, Span, Spanned};
59
60    use crate::{parse_block, parse_expression, Error};
61
62    fn span(range: Range<usize>) -> Span {
63        Span::new(SourceId::empty(), range.start, range.end)
64    }
65
66    fn test_block(code: &str) -> (Option<SpannedBlock>, Vec<Error>) {
67        parse_block(code, SourceId::empty())
68    }
69
70    fn test_expression(code: &str) -> (Option<SpannedExpression>, Vec<Error>) {
71        parse_expression(code, SourceId::empty())
72    }
73
74    #[test]
75    fn expr_arithmetic() {
76        let (actual_expr, errors) = test_expression("x + y * (z / w)");
77
78        let expected_expr = Some(Spanned::new(
79            Expression::Binary {
80                left: Box::new(Spanned::new(Expression::Identifier("x".into()), span(0..1))),
81                right: Box::new(Spanned::new(
82                    Expression::Binary {
83                        left: Box::new(Spanned::new(
84                            Expression::Identifier("y".into()),
85                            span(4..5),
86                        )),
87                        right: Box::new(Spanned::new(
88                            Expression::Binary {
89                                left: Box::new(Spanned::new(
90                                    Expression::Identifier("z".into()),
91                                    span(9..10),
92                                )),
93                                right: Box::new(Spanned::new(
94                                    Expression::Identifier("w".into()),
95                                    span(13..14),
96                                )),
97                                operator: BinaryOperator::Divide,
98                            },
99                            span(8..15),
100                        )),
101                        operator: BinaryOperator::Multiply,
102                    },
103                    span(4..15),
104                )),
105                operator: BinaryOperator::Add,
106            },
107            span(0..15),
108        ));
109
110        assert_eq!(actual_expr, expected_expr);
111        assert_eq!(errors.len(), 0);
112    }
113
114    #[test]
115    fn block_misc() {
116        let (actual_block, errors) = test_block(
117            "
118a:
119  b:
120    - c + d
121    - e: f
122  g: h
123",
124        );
125
126        let expected_block = Some(Spanned::new(
127            Block::Object(vec![(
128                Spanned::new("a".into(), span(1..2)),
129                Spanned::new(
130                    Block::Object(vec![
131                        (
132                            Spanned::new("b".into(), span(6..7)),
133                            Spanned::new(
134                                Block::List(vec![
135                                    Spanned::new(
136                                        Block::Expression(Expression::Binary {
137                                            left: Box::new(Spanned::new(
138                                                Expression::Identifier("c".into()),
139                                                span(15..16),
140                                            )),
141                                            right: Box::new(Spanned::new(
142                                                Expression::Identifier("d".into()),
143                                                span(19..20),
144                                            )),
145                                            operator: BinaryOperator::Add,
146                                        }),
147                                        span(15..20),
148                                    ),
149                                    Spanned::new(
150                                        Block::Object(vec![(
151                                            Spanned::new("e".into(), span(27..28)),
152                                            Spanned::new(
153                                                Block::Expression(Expression::Identifier(
154                                                    "f".into(),
155                                                )),
156                                                span(30..31),
157                                            ),
158                                        )]),
159                                        span(27..32),
160                                    ),
161                                ]),
162                                span(13..34),
163                            ),
164                        ),
165                        (
166                            Spanned::new("g".into(), span(34..35)),
167                            Spanned::new(
168                                Block::Expression(Expression::Identifier("h".into())),
169                                span(37..38),
170                            ),
171                        ),
172                    ]),
173                    span(6..39),
174                ),
175            )]),
176            span(1..39),
177        ));
178
179        assert_eq!(actual_block, expected_block);
180        assert_eq!(errors.len(), 0);
181    }
182}