1use crate::{
2 errors::{CompilationError, KclError, KclErrorDetails},
3 parsing::{
4 ast::types::{Node, Program},
5 token::TokenStream,
6 },
7 source_range::SourceRange,
8 ModuleId,
9};
10
11pub(crate) mod ast;
12mod math;
13pub(crate) mod parser;
14pub(crate) mod token;
15
16pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
17pub const PIPE_OPERATOR: &str = "|>";
18
19macro_rules! pr_try {
21 ($e: expr) => {
22 match $e {
23 Ok(a) => a,
24 Err(e) => return e.into(),
25 }
26 };
27}
28
29#[cfg(test)]
30pub fn top_level_parse(code: &str) -> ParseResult {
32 let module_id = ModuleId::default();
33 parse_str(code, module_id)
34}
35
36pub fn parse_str(code: &str, module_id: ModuleId) -> ParseResult {
38 let tokens = pr_try!(crate::parsing::token::lex(code, module_id));
39 parse_tokens(tokens)
40}
41
42pub fn parse_tokens(mut tokens: TokenStream) -> ParseResult {
44 let unknown_tokens = tokens.remove_unknown();
45
46 if !unknown_tokens.is_empty() {
47 let source_ranges = unknown_tokens.iter().map(SourceRange::from).collect();
48 let token_list = unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>();
49 let message = if token_list.len() == 1 {
50 format!("found unknown token '{}'", token_list[0])
51 } else {
52 format!("found unknown tokens [{}]", token_list.join(", "))
53 };
54 return KclError::new_lexical(KclErrorDetails::new(message, source_ranges)).into();
55 }
56
57 if tokens.is_empty() {
59 return Node::<Program>::default().into();
61 }
62
63 if tokens.iter().all(|t| t.token_type.is_whitespace()) {
65 return Node::<Program>::default().into();
66 }
67
68 parser::run_parser(tokens.as_slice())
69}
70
71#[derive(Debug, Clone)]
82pub(crate) struct ParseResult(pub Result<(Option<Node<Program>>, Vec<CompilationError>), KclError>);
83
84impl ParseResult {
85 #[cfg(test)]
86 #[track_caller]
87 pub fn unwrap(self) -> Node<Program> {
88 if self.0.is_err() || self.0.as_ref().unwrap().0.is_none() {
89 eprint!("{self:#?}");
90 }
91 self.0.unwrap().0.unwrap()
92 }
93
94 #[cfg(test)]
95 pub fn is_ok(&self) -> bool {
96 match &self.0 {
97 Ok((p, errs)) => p.is_some() && !errs.iter().any(|e| e.severity.is_err()),
98 Err(_) => false,
99 }
100 }
101
102 #[cfg(test)]
103 #[track_caller]
104 pub fn unwrap_errs(&self) -> impl Iterator<Item = &CompilationError> {
105 self.0.as_ref().unwrap().1.iter().filter(|e| e.severity.is_err())
106 }
107
108 pub fn parse_errs_as_err(self) -> Result<Node<Program>, KclError> {
110 let (p, errs) = self.0?;
111
112 if let Some(err) = errs.iter().find(|e| e.severity.is_err()) {
113 return Err(KclError::new_syntax(err.clone().into()));
114 }
115 match p {
116 Some(p) => Ok(p),
117 None => Err(KclError::internal("Unknown parsing error".to_owned())),
118 }
119 }
120}
121
122impl From<Result<(Option<Node<Program>>, Vec<CompilationError>), KclError>> for ParseResult {
123 fn from(r: Result<(Option<Node<Program>>, Vec<CompilationError>), KclError>) -> ParseResult {
124 ParseResult(r)
125 }
126}
127
128impl From<(Option<Node<Program>>, Vec<CompilationError>)> for ParseResult {
129 fn from(p: (Option<Node<Program>>, Vec<CompilationError>)) -> ParseResult {
130 ParseResult(Ok(p))
131 }
132}
133
134impl From<Node<Program>> for ParseResult {
135 fn from(p: Node<Program>) -> ParseResult {
136 ParseResult(Ok((Some(p), vec![])))
137 }
138}
139
140impl From<KclError> for ParseResult {
141 fn from(e: KclError) -> ParseResult {
142 ParseResult(Err(e))
143 }
144}
145
146const STR_DEPRECATIONS: [(&str, &str); 16] = [
147 ("XY", "XY"),
148 ("XZ", "XZ"),
149 ("YZ", "YZ"),
150 ("-XY", "-XY"),
151 ("-XZ", "-XZ"),
152 ("-YZ", "-YZ"),
153 ("xy", "XY"),
154 ("xz", "XZ"),
155 ("yz", "YZ"),
156 ("-xy", "-XY"),
157 ("-xz", "-XZ"),
158 ("-yz", "-YZ"),
159 ("START", "START"),
160 ("start", "START"),
161 ("END", "END"),
162 ("end", "END"),
163];
164const FN_DEPRECATIONS: [(&str, &str); 3] = [("pi", "PI"), ("e", "E"), ("tau", "TAU")];
165const CONST_DEPRECATIONS: [(&str, &str); 4] = [
166 ("ZERO", "turns::ZERO"),
167 ("QUARTER_TURN", "turns::QUARTER_TURN"),
168 ("HALF_TURN", "turns::HALF_TURN"),
169 ("THREE_QUARTER_TURN", "turns::THREE_QUARTER_TURN"),
170];
171
172#[derive(Clone, Copy)]
173pub enum DeprecationKind {
174 String,
175 Function,
176 Const,
177}
178
179pub fn deprecation(s: &str, kind: DeprecationKind) -> Option<&'static str> {
180 match kind {
181 DeprecationKind::String => STR_DEPRECATIONS.iter().find_map(|(a, b)| (*a == s).then_some(*b)),
182 DeprecationKind::Function => FN_DEPRECATIONS.iter().find_map(|(a, b)| (*a == s).then_some(*b)),
183 DeprecationKind::Const => CONST_DEPRECATIONS.iter().find_map(|(a, b)| (*a == s).then_some(*b)),
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 macro_rules! parse_and_lex {
190 ($func_name:ident, $test_kcl_program:expr) => {
191 #[test]
192 fn $func_name() {
193 let _ = crate::parsing::top_level_parse($test_kcl_program);
194 }
195 };
196 }
197
198 parse_and_lex!(crash_eof_1, "{\"ގގ\0\0\0\"\".");
199 parse_and_lex!(crash_eof_2, "(/=e\"\u{616}ݝ\"\"");
200}