turtle/parser/
mod.rs

1use crate::{
2    Exception, ExceptionValue as EV, Expression, Keyword, Operator, Source, SourcePosition, Symbol,
3    Value,
4};
5use pest::iterators::Pair;
6use pest::Parser;
7
8use crate::Locker;
9
10#[derive(Parser)]
11#[grammar = "parser/syntax.pest"]
12pub struct SyntaxParser;
13
14pub fn parse(code: &str, location: &str) -> Result<Vec<Expression>, Exception> {
15    let source = Locker::new(Source::new(String::from(code), String::from(location)));
16
17    match SyntaxParser::parse(Rule::expressions, code) {
18        Ok(pairs) => {
19            let mut exps = Vec::new();
20            for pair in pairs {
21                if pair.as_rule() == Rule::EOI {
22                    // SOI isn't given
23                    continue;
24                }
25                exps.push(
26                    build_expression(pair.clone(), source.clone())?
27                        .with_source(SourcePosition::from_pair(&pair, &source)),
28                );
29            }
30            Ok(exps)
31        }
32        Err(err) => Err(Exception::from(err)),
33    }
34}
35
36fn build_expression(pair: Pair<'_, Rule>, source: Locker<Source>) -> Result<Expression, Exception> {
37    let pos = SourcePosition::new(
38        pair.as_span().start_pos().pos(),
39        pair.as_span().end_pos().pos(),
40        source.clone(),
41    );
42    match &pair.as_rule() {
43        Rule::list => {
44            let mut values: Vec<Expression> = Vec::new();
45            for elem in pair.into_inner() {
46                values.push(
47                    build_expression(elem.clone(), source.clone())?
48                        .with_source(SourcePosition::from_pair(&elem, &source)),
49                )
50            }
51            Ok(Expression::new(Value::List(values)))
52        }
53        Rule::symbol => Ok(Expression::new(Value::Symbol(Symbol::new(String::from(
54            pair.as_str(),
55        ))))
56        .with_source(pos)),
57        Rule::keyword => Ok(Expression::new(Value::Keyword(Keyword::new(String::from(
58            pair.into_inner().next().unwrap().as_str(),
59        ))))
60        .with_source(pos)),
61        Rule::number => match pair.as_str().parse::<f64>() {
62            Ok(num) => Ok(Expression::new(Value::Number(num)).with_source(pos)),
63            Err(_) => Err(Exception::new(
64                EV::Syntax,
65                None,
66                Some(format!("`{}` is not a valid number", pair.as_str())),
67            )),
68        },
69        Rule::byte => match pair.as_str().replace('b', "").parse::<u8>() {
70            Ok(num) => Ok(Expression::new(Value::Byte(num)).with_source(pos)),
71            Err(_) => Err(Exception::new(
72                EV::Syntax,
73                None,
74                Some(format!("`{}` is not a valid byte (0-255)", pair.as_str())),
75            )),
76        },
77        Rule::text => Ok(Expression::new(Value::Text(
78            pair.into_inner().as_str().to_string(),
79        ))),
80
81        // Sugar
82        Rule::quote | Rule::eval => {
83            let mut elements = vec![Expression::new(Value::Operator(match &pair.as_rule() {
84                Rule::quote => Operator::Quote,
85                Rule::eval => Operator::Eval,
86                _ => unreachable!(),
87            }))];
88            for elem in pair.into_inner() {
89                elements.push(
90                    build_expression(elem.clone(), source.clone())?
91                        .with_source(SourcePosition::from_pair(&elem, &source)),
92                );
93            }
94            Ok(Expression::new(Value::List(elements)))
95        }
96        _ => Err(Exception::new(
97            EV::Syntax,
98            None,
99            Some(format!("unknown syntax element `{}`", pair.as_str())),
100        )),
101    }
102}