maolang_core/
parser.rs

1//! The parser for generating an abstract syntax tree, built in with conditional rules
2
3use ast::ParseError;
4use rand::{RngCore, seq::IndexedRandom};
5
6use crate::tokenizer::{Token, TokenTag};
7
8pub mod ast;
9
10/// All valid statement ends
11pub const VALID_ENDS: &[TokenTag<'static>] = &[
12    TokenTag::Semicolon,
13    TokenTag::Dot,
14    TokenTag::Identifier("done"),
15];
16
17/// It's a 2/3 chance that you need parenthesis
18pub const PAREN_PROB: &[bool] = &[true, true, false];
19
20/// The core parser with dynamic rules
21pub struct Parser<'tok> {
22    /// The token stream to parse
23    tokens: &'tok [Token<'tok>],
24    /// Index into the token stream
25    idx: usize,
26    /// Dynamic parser rules
27    rules: ParserRules,
28}
29
30/// Dynamic parsing rules that affect how the AST is generated
31pub struct ParserRules {
32    /// What should a statement end with? (;, ., !, something crazy :O)
33    pub statements_end_with: TokenTag<'static>,
34    /// Should parenthesis exist in if statements, loops and function calls?
35    pub parenthesis: bool,
36}
37
38impl<'tok> Parser<'tok> {
39    /// Creates a new parser from RNG
40    pub fn from_rng<RNG: RngCore>(rng: &mut RNG) -> Self {
41        // Unwrap safety, VALID_ENDS is not empty that would be silly
42        let statements_end_with = *VALID_ENDS.choose(rng).unwrap();
43        // Unwrap safety, PAREN_PROB is not empty that would be silly
44        let parenthesis = *PAREN_PROB.choose(rng).unwrap();
45
46        let rules = ParserRules {
47            statements_end_with,
48            parenthesis,
49        };
50
51        Self {
52            tokens: &[],
53            idx: 0,
54            rules,
55        }
56    }
57
58    /// Assigns a token stream to the parser
59    pub fn with_tokens(mut self, tokens: &'tok [Token<'tok>]) -> Self {
60        self.tokens = tokens;
61
62        self
63    }
64
65    /// Peeks at the current token
66    #[inline]
67    pub fn peek(&self) -> TokenTag<'tok> {
68        self.tokens[self.idx.min(self.tokens.len() - 1)].tag
69    }
70
71    /// Peeks at the current token
72    #[inline]
73    pub fn peek_token(&self) -> Token<'tok> {
74        self.tokens[self.idx.min(self.tokens.len() - 1)]
75    }
76
77    /// Peeks at the previous token, does not advance the index
78    #[inline]
79    pub fn peek_back(&self) -> TokenTag<'tok> {
80        self.tokens[self.idx - 1].tag
81    }
82
83    /// Checks if stream is finished
84    fn at_end(&self) -> bool {
85        self.peek() == TokenTag::EOF
86    }
87
88    /// Advances forward and returns the previous token
89    pub fn advance(&mut self) -> TokenTag<'tok> {
90        if !self.at_end() {
91            self.idx += 1
92        }
93
94        if self.idx == 0 {
95            self.tokens[0].tag
96        } else {
97            self.tokens[self.idx - 1].tag
98        }
99    }
100
101    /// Consumes the current token assuming it's the provided Token, failing if not
102    pub fn consume(&mut self, token: &TokenTag<'_>) -> Result<(), ParseError> {
103        if &self.peek() == token {
104            self.idx += 1;
105            Ok(())
106        } else {
107            let Token {
108                tag,
109                line,
110                col,
111                len,
112            } = self.peek_token();
113            Err(ParseError {
114                message: format!("Expected `{token:?}`, found `{tag:?}`"),
115                line,
116                col,
117                len,
118            })
119        }
120    }
121
122    /// Consumes the proper end for the tree
123    pub fn consume_end(&mut self) -> Result<(), ParseError> {
124        let tok = self.rules.statements_end_with;
125        self.consume(&tok)
126    }
127
128    /// If the current rules require parenthesis, consume 'em
129    pub fn consume_close_paren_if_necessary(&mut self) -> Result<(), ParseError> {
130        if self.rules.parenthesis {
131            if let Ok(_) = self.consume(&TokenTag::CloseParen) {
132                Ok(())
133            } else {
134                let Token {
135                    tag: _,
136                    line,
137                    col,
138                    len,
139                } = self.peek_token();
140                Err(ParseError {
141                    message: format!("No closing parenthesis found for statement"),
142                    line,
143                    col,
144                    len,
145                })
146            }
147        } else {
148            Ok(())
149        }
150    }
151
152    /// If the current rules require parenthesis, consume 'em
153    pub fn consume_open_paren_if_necessary(&mut self) -> Result<(), ParseError> {
154        if self.rules.parenthesis {
155            if let Ok(_) = self.consume(&TokenTag::OpenParen) {
156                Ok(())
157            } else {
158                let Token {
159                    tag: _,
160                    line,
161                    col,
162                    len,
163                } = self.peek_token();
164                Err(ParseError {
165                    message: format!("No opening parenthesis found for statement"),
166                    line,
167                    col,
168                    len,
169                })
170            }
171        } else {
172            Ok(())
173        }
174    }
175}