kcl_lib/
parser.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use crate::{
    ast::types::Program,
    errors::{KclError, KclErrorDetails},
    executor::SourceRange,
    token::{Token, TokenType},
};

mod bad_inputs;
mod math;
pub(crate) mod parser_impl;

pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
pub const PIPE_OPERATOR: &str = "|>";

pub struct Parser {
    pub tokens: Vec<Token>,
    pub unknown_tokens: Vec<Token>,
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Self {
        let (tokens, unknown_tokens): (Vec<Token>, Vec<Token>) = tokens
            .into_iter()
            .partition(|token| token.token_type != TokenType::Unknown);
        Self { tokens, unknown_tokens }
    }

    /// Run the parser
    pub fn ast(&self) -> Result<Program, KclError> {
        if !self.unknown_tokens.is_empty() {
            let source_ranges = self.unknown_tokens.iter().map(SourceRange::from).collect();
            let token_list = self.unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>();
            let message = if token_list.len() == 1 {
                format!("found unknown token '{}'", token_list[0])
            } else {
                format!("found unknown tokens [{}]", token_list.join(", "))
            };
            return Err(KclError::Lexical(KclErrorDetails { source_ranges, message }));
        }

        // Important, to not call this before the unknown tokens check.
        if self.tokens.is_empty() {
            // Empty file should just do nothing.
            return Ok(Program::default());
        }

        // Check all the tokens are whitespace or comments.
        if self
            .tokens
            .iter()
            .all(|t| t.token_type.is_whitespace() || t.token_type.is_comment())
        {
            return Ok(Program::default());
        }

        parser_impl::run_parser(&mut self.tokens.as_slice())
    }
}