1#![recursion_limit = "1024"]
6#![allow(clippy::boxed_local)]
7
8#[allow(unused_macros)]
9macro_rules! println {
10 ($($rest:tt)*) => {
11 #[cfg(feature = "disable-println")]
12 {
13 let _ = format!($($rest)*);
14 }
15 #[cfg(not(feature = "disable-println"))]
16 std::println!($($rest)*)
17 }
18}
19
20#[allow(unused_macros)]
21macro_rules! eprintln {
22 ($($rest:tt)*) => {
23 #[cfg(feature = "disable-println")]
24 {
25 let _ = format!($($rest)*);
26 }
27 #[cfg(not(feature = "disable-println"))]
28 std::eprintln!($($rest)*)
29 }
30}
31
32#[allow(unused_macros)]
33macro_rules! print {
34 ($($rest:tt)*) => {
35 #[cfg(feature = "disable-println")]
36 {
37 let _ = format!($($rest)*);
38 }
39 #[cfg(not(feature = "disable-println"))]
40 std::print!($($rest)*)
41 }
42}
43
44#[allow(unused_macros)]
45macro_rules! eprint {
46 ($($rest:tt)*) => {
47 #[cfg(feature = "disable-println")]
48 {
49 let _ = format!($($rest)*);
50 }
51 #[cfg(not(feature = "disable-println"))]
52 std::eprint!($($rest)*)
53 }
54}
55#[cfg(feature = "dhat-heap")]
56#[global_allocator]
57static ALLOC: dhat::Alloc = dhat::Alloc;
58
59mod coredump;
60mod docs;
61mod engine;
62mod errors;
63mod execution;
64mod fs;
65pub mod lint;
66mod log;
67mod lsp;
68mod modules;
69mod parsing;
70mod settings;
71#[cfg(test)]
72mod simulation_tests;
73mod source_range;
74pub mod std;
75#[cfg(not(target_arch = "wasm32"))]
76pub mod test_server;
77mod thread;
78mod unparser;
79mod walk;
80#[cfg(target_arch = "wasm32")]
81mod wasm;
82
83pub use coredump::CoreDump;
84pub use engine::{EngineManager, ExecutionKind};
85pub use errors::{
86 CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs, Report, ReportWithOutputs,
87};
88pub use execution::{
89 bust_cache, clear_mem_cache, ExecOutcome, ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d,
90};
91pub use lsp::{
92 copilot::Backend as CopilotLspBackend,
93 kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
94};
95pub use modules::ModuleId;
96pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
97pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
98pub use source_range::SourceRange;
99
100pub mod exec {
103 pub use crate::execution::{ArtifactCommand, DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch};
104}
105
106#[cfg(target_arch = "wasm32")]
107pub mod wasm_engine {
108 pub use crate::{
109 coredump::wasm::{CoreDumpManager, CoreDumper},
110 engine::conn_wasm::{EngineCommandManager, EngineConnection},
111 fs::wasm::FileSystemManager,
112 };
113}
114
115#[cfg(not(target_arch = "wasm32"))]
116pub mod native_engine {
117 pub use crate::engine::conn::EngineConnection;
118}
119
120pub mod std_utils {
121 pub use crate::std::utils::{get_tangential_arc_to_info, is_points_ccw_wasm, TangentialArcInfoInput};
122}
123
124pub mod pretty {
125 pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
126}
127
128use serde::{Deserialize, Serialize};
129
130#[allow(unused_imports)]
131use crate::log::{log, logln};
132
133#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
134pub struct Program {
135 #[serde(flatten)]
136 pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
137 #[serde(skip)]
141 pub original_file_contents: String,
142}
143
144#[cfg(any(test, feature = "lsp-test-util"))]
145pub use lsp::test_util::copilot_lsp_server;
146#[cfg(any(test, feature = "lsp-test-util"))]
147pub use lsp::test_util::kcl_lsp_server;
148
149impl Program {
150 pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationError>), KclError> {
151 let module_id = ModuleId::default();
152 let tokens = parsing::token::lex(input, module_id)?;
153 let (ast, errs) = parsing::parse_tokens(tokens).0?;
154
155 Ok((
156 ast.map(|ast| Program {
157 ast,
158 original_file_contents: input.to_string(),
159 }),
160 errs,
161 ))
162 }
163
164 pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
165 let module_id = ModuleId::default();
166 let tokens = parsing::token::lex(input, module_id)?;
167 let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
168
169 Ok(Program {
170 ast,
171 original_file_contents: input.to_string(),
172 })
173 }
174
175 pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
176 self.ast.compute_digest()
177 }
178
179 pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
181 self.ast.meta_settings()
182 }
183
184 pub fn change_meta_settings(&self, settings: crate::MetaSettings) -> Result<Self, KclError> {
186 Ok(Self {
187 ast: self.ast.change_meta_settings(settings)?,
188 original_file_contents: self.original_file_contents.clone(),
189 })
190 }
191
192 pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
193 self.ast.lint_all()
194 }
195
196 pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
197 self.ast.lint(rule)
198 }
199
200 pub fn recast(&self) -> String {
201 self.ast.recast(&Default::default(), 0)
203 }
204
205 pub fn recast_with_options(&self, options: &FormatOptions) -> String {
206 self.ast.recast(options, 0)
207 }
208}
209
210#[inline]
211fn try_f64_to_usize(f: f64) -> Option<usize> {
212 let i = f as usize;
213 if i as f64 == f {
214 Some(i)
215 } else {
216 None
217 }
218}
219
220#[inline]
221fn try_f64_to_u32(f: f64) -> Option<u32> {
222 let i = f as u32;
223 if i as f64 == f {
224 Some(i)
225 } else {
226 None
227 }
228}
229
230#[inline]
231fn try_f64_to_u64(f: f64) -> Option<u64> {
232 let i = f as u64;
233 if i as f64 == f {
234 Some(i)
235 } else {
236 None
237 }
238}
239
240#[inline]
241fn try_f64_to_i64(f: f64) -> Option<i64> {
242 let i = f as i64;
243 if i as f64 == f {
244 Some(i)
245 } else {
246 None
247 }
248}
249
250pub fn version() -> &'static str {
252 env!("CARGO_PKG_VERSION")
253}
254
255#[cfg(test)]
256mod test {
257 use super::*;
258
259 #[test]
260 fn convert_int() {
261 assert_eq!(try_f64_to_usize(0.0), Some(0));
262 assert_eq!(try_f64_to_usize(42.0), Some(42));
263 assert_eq!(try_f64_to_usize(0.00000000001), None);
264 assert_eq!(try_f64_to_usize(-1.0), None);
265 assert_eq!(try_f64_to_usize(f64::NAN), None);
266 assert_eq!(try_f64_to_usize(f64::INFINITY), None);
267 assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
268
269 assert_eq!(try_f64_to_u32(0.0), Some(0));
270 assert_eq!(try_f64_to_u32(42.0), Some(42));
271 assert_eq!(try_f64_to_u32(0.00000000001), None);
272 assert_eq!(try_f64_to_u32(-1.0), None);
273 assert_eq!(try_f64_to_u32(f64::NAN), None);
274 assert_eq!(try_f64_to_u32(f64::INFINITY), None);
275 assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
276
277 assert_eq!(try_f64_to_u64(0.0), Some(0));
278 assert_eq!(try_f64_to_u64(42.0), Some(42));
279 assert_eq!(try_f64_to_u64(0.00000000001), None);
280 assert_eq!(try_f64_to_u64(-1.0), None);
281 assert_eq!(try_f64_to_u64(f64::NAN), None);
282 assert_eq!(try_f64_to_u64(f64::INFINITY), None);
283 assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
284
285 assert_eq!(try_f64_to_i64(0.0), Some(0));
286 assert_eq!(try_f64_to_i64(42.0), Some(42));
287 assert_eq!(try_f64_to_i64(0.00000000001), None);
288 assert_eq!(try_f64_to_i64(-1.0), Some(-1));
289 assert_eq!(try_f64_to_i64(f64::NAN), None);
290 assert_eq!(try_f64_to_i64(f64::INFINITY), None);
291 assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
292 }
293}