voltaic_parser/
support.rs1use crate::parse;
2
3use voltaic_ast::BinaryOp;
4
5use voltaic_ast::Program;
6
7use voltaic_diag::Code;
8
9use voltaic_diag::Span;
10
11use voltaic_lexer::Keyword;
12
13use voltaic_lexer::LexError;
14
15use voltaic_lexer::Token;
16
17use voltaic_lexer::TokenKind;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct ParseError {
21 pub code: Code,
22 pub message: String,
23 pub span: Span,
24 pub help: Option<String>,
25}
26
27impl ParseError {
28 pub(crate) fn expected(message: impl Into<String>, span: Span) -> Self {
29 ParseError {
30 code: Code::ExpectedToken,
31 message: message.into(),
32 span,
33 help: None,
34 }
35 }
36
37 pub(crate) fn r#with(code: Code, message: impl Into<String>, span: Span) -> Self {
38 ParseError {
39 code,
40 message: message.into(),
41 span,
42 help: None,
43 }
44 }
45
46 pub(crate) fn with_help(
47 code: Code,
48 message: impl Into<String>,
49 help: impl Into<String>,
50 span: Span,
51 ) -> Self {
52 ParseError {
53 code,
54 message: message.into(),
55 span,
56 help: Some(help.into()),
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum ParseFailure {
63 Lex(LexError),
64 Parse(Vec<ParseError>),
65}
66#[allow(unused_imports)]
67use ParseFailure::*;
68
69pub const MAX_NESTING: usize = 16;
70
71pub(crate) fn nesting_depth(toks: &[Token]) -> usize {
72 let mut depth: usize = 0;
73 let mut deepest: usize = 0;
74 for token in toks {
75 match token.kind {
76 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => {
77 depth += 1;
78 deepest = deepest.max(depth);
79 }
80 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
81 depth = depth.saturating_sub(1);
82 }
83 _ => (),
84 };
85 }
86 deepest
87}
88
89pub(crate) fn above_bitor() -> u8 {
90 let found = binary_power(&TokenKind::Or).expect("`|` is in the precedence table");
91 found.1 + 1
92}
93
94pub(crate) fn binary_power(kind: &TokenKind) -> Option<(BinaryOp, u8, u8)> {
95 let op = match kind {
96 TokenKind::Pipe => BinaryOp::Pipeline,
97 TokenKind::Coalesce => BinaryOp::Coalesce,
98 TokenKind::Kw(Keyword::Catch) => BinaryOp::Catch,
99 TokenKind::DotDot => BinaryOp::Range,
100 TokenKind::DotDotEq => BinaryOp::RangeInclusive,
101 TokenKind::OrOr => BinaryOp::Or,
102 TokenKind::AndAnd => BinaryOp::And,
103 TokenKind::EqEq => BinaryOp::Eq,
104 TokenKind::Ne => BinaryOp::Ne,
105 TokenKind::Lt => BinaryOp::Lt,
106 TokenKind::Gt => BinaryOp::Gt,
107 TokenKind::Le => BinaryOp::Le,
108 TokenKind::Ge => BinaryOp::Ge,
109 TokenKind::Or => BinaryOp::BitOr,
110 TokenKind::Caret => BinaryOp::BitXor,
111 TokenKind::Amp => BinaryOp::BitAnd,
112 TokenKind::Shl => BinaryOp::Shl,
113 TokenKind::Shr => BinaryOp::Shr,
114 TokenKind::Plus => BinaryOp::Add,
115 TokenKind::Minus => BinaryOp::Sub,
116 TokenKind::Star => BinaryOp::Mul,
117 TokenKind::Slash => BinaryOp::Div,
118 TokenKind::Percent => BinaryOp::Rem,
119 TokenKind::Compose => BinaryOp::Compose,
120 TokenKind::ComposeBack => BinaryOp::ComposeBack,
121 _ => {
122 return None;
123 }
124 };
125 let left = op.power();
126 Some((op, left, left + 1))
127}
128
129#[cfg(test)]
130fn power(kind: TokenKind) -> u8 {
131 binary_power(&kind).expect("should have a precedence").1
132}
133
134pub(crate) const PREFIX_POWER: u8 = 25;
135
136pub fn parse_source(src: &str) -> Result<Program, ParseFailure> {
137 let toks = voltaic_lexer::lex(src).map_err(ParseFailure::Lex)?;
138 parse(&toks, src.len()).map_err(ParseFailure::Parse)
139}
140
141#[cfg(test)]
142mod __vlt_tests {
143 use super::*;
144
145 #[test]
146 fn precedence_follows_rust() {
147 assert!(power(TokenKind::Star) > power(TokenKind::Plus));
148 assert!(power(TokenKind::Plus) > power(TokenKind::Shl));
149 assert!(power(TokenKind::Shl) > power(TokenKind::Amp));
150 assert!(power(TokenKind::Amp) > power(TokenKind::Caret));
151 assert!(power(TokenKind::Caret) > power(TokenKind::Or));
152 assert!(power(TokenKind::Or) > power(TokenKind::EqEq));
153 assert!(power(TokenKind::EqEq) > power(TokenKind::AndAnd));
154 assert!(power(TokenKind::AndAnd) > power(TokenKind::OrOr));
155 }
156
157 #[test]
158 fn the_new_operators_sit_where_doc_03_places_them() {
159 assert!(power(TokenKind::Coalesce) < power(TokenKind::OrOr));
160 assert!(power(TokenKind::Pipe) < power(TokenKind::Coalesce));
161 }
162
163 #[test]
164 fn unary_and_postfix_bind_tighter_than_any_binary() {
165 assert!(crate::support::PREFIX_POWER > power(TokenKind::Star));
166 }
167
168 #[test]
169 fn binary_operators_are_left_associative() {
170 for kind in [TokenKind::Plus, TokenKind::Star, TokenKind::Pipe] {
171 let rung = binary_power(&kind).expect("should have a precedence");
172 assert_eq!(rung.2, rung.1 + 1, "{:?} should be left-associative", kind);
173 }
174 }
175
176 #[test]
177 fn a_non_operator_has_no_precedence() {
178 assert!(binary_power(&TokenKind::Ident("x".into())).is_none());
179 assert!(binary_power(&TokenKind::Newline).is_none());
180 }
181
182 #[test]
183 fn the_powers_come_from_the_operator() {
184 for kind in [
185 TokenKind::Pipe,
186 TokenKind::Coalesce,
187 TokenKind::OrOr,
188 TokenKind::EqEq,
189 TokenKind::Plus,
190 TokenKind::Percent,
191 ] {
192 let rung = binary_power(&kind).expect("should have a precedence");
193 assert_eq!(
194 rung.1,
195 rung.0.power(),
196 "{:?} reports a power of its own",
197 kind
198 );
199 assert_eq!(rung.2, rung.0.power() + 1);
200 }
201 }
202
203 #[test]
204 fn maps_tokens_to_their_operators() {
205 assert_eq!(
206 binary_power(&TokenKind::Plus).map(|pair| pair.0),
207 Some(BinaryOp::Add)
208 );
209 assert_eq!(
210 binary_power(&TokenKind::DotDot).map(|pair| pair.0),
211 Some(BinaryOp::Range),
212 "`..` is exclusive (doc 03 ยง4.2)"
213 );
214 }
215}