Skip to main content

microcad_lang/
lib.rs

1// Copyright © 2024-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Processing of µcad source code.
5//!
6//! This module includes all components to parse, resolve and evaluate µcad code and diagnose errors.
7//!
8//! - Load and parse source files in [`mod@parse`] and [`syntax`]
9//! - Resolve parsed sources in [`resolve`]
10//! - Evaluate resolved sources in [`eval`]
11//! - Diagnose any evaluation errors in [`diag`]
12//!
13//! The syntax definitions and parser of µcad can be found [here](../../../syntax).
14//!
15//! Good starting point to understand how µcad syntax works: [`syntax::SourceFile::load()`] loads a µcad source file.
16
17pub mod builtin;
18pub mod diag;
19pub mod eval;
20pub mod model;
21pub mod ord_map;
22pub mod parse;
23pub mod parser;
24pub mod rc;
25pub mod render;
26pub mod resolve;
27pub mod src_ref;
28pub mod syntax;
29pub mod tree_display;
30pub mod ty;
31pub mod value;
32
33/// Id type (base of all identifiers)
34pub type Id = compact_str::CompactString;
35
36/// List of valid µcad extensions.
37pub const MICROCAD_EXTENSIONS: &[&str] = &["µcad", "mcad", "ucad"];
38
39/// Shortens given string to it's first line and to `max_chars` characters.
40pub fn shorten(what: &str, max_chars: usize) -> String {
41    let short: String = what
42        .chars()
43        .enumerate()
44        .filter_map(|(p, ch)| {
45            if p == max_chars {
46                Some('…')
47            } else if p < max_chars {
48                if ch == '\n' {
49                    Some('⏎')
50                } else {
51                    Some(ch)
52                }
53            } else {
54                None
55            }
56        })
57        .collect();
58
59    if cfg!(feature = "ansi-color") && short.contains('\x1b') {
60        short + "\x1b[0m"
61    } else {
62        short
63    }
64}
65
66/// Shortens given string to it's first line and to maximum characters.
67#[macro_export]
68macro_rules! shorten {
69    ($what:expr) => {
70        $crate::shorten(&format!("{}", $what), 140)
71    };
72    ($what:expr,$shorten:expr) => {
73        if $shorten {
74            $crate::shorten!($what)
75        } else {
76            $what
77        }
78    };
79    ($what:expr, $max_chars:literal) => {
80        shorten(format!("{}", $what).lines(), max_chars)
81    };
82}
83
84/// Create a marker string which is colored with ANSI.
85#[cfg(feature = "ansi-color")]
86#[macro_export]
87macro_rules! mark {
88    (FOUND!) => {
89        color_print::cformat!("<G!,k,s> FOUND </>")
90    };
91    (FOUND) => {
92        color_print::cformat!("<W!,k,s> FOUND </>")
93    };
94    (MATCH) => {
95        color_print::cformat!("<Y!,k,s> MATCH </>")
96    };
97    (NO_MATCH) => {
98        color_print::cformat!("<Y,k,s> NO MATCH </>")
99    };
100    (MATCH!) => {
101        color_print::cformat!("<G!,k,s> MATCH </>")
102    };
103    (NO_MATCH!) => {
104        color_print::cformat!("<R,k,s> NO MATCH </>")
105    };
106    (CALL) => {
107        color_print::cformat!("<B,k,s> CALL </>")
108    };
109    (LOOKUP) => {
110        color_print::cformat!("<c,s>LOOKUP</>")
111    };
112    (LOAD) => {
113        color_print::cformat!("<Y,k,s> LOADING </>")
114    };
115    (RESOLVE) => {
116        color_print::cformat!("<M,k,s> RESOLVE </>")
117    };
118    (AMBIGUOUS) => {
119        color_print::cformat!("<R,k,s> AMBIGUOUS </>")
120    };
121    (NOT_FOUND!) => {
122        color_print::cformat!("<R,k,s> NOT FOUND </>")
123    };
124    (NOT_FOUND) => {
125        color_print::cformat!("<Y,k,s> NOT FOUND </>")
126    };
127}
128
129#[cfg(not(feature = "ansi-color"))]
130#[macro_export]
131macro_rules! found {
132    (FOUND) => {
133        "Found"
134    };
135    (FINAL) => {
136        "Found"
137    };
138    (INTERMEDIATE) => {
139        "Found"
140    };
141    (MATCH) => {
142        "Match"
143    };
144    (NO_MATCH) => {
145        "No Match"
146    };
147    (CALL) => {
148        "Call"
149    };
150    (LOOKUP) => {
151        "Lookup"
152    };
153    (LOAD) => {
154        "Loading"
155    };
156    (RESOLVE) => {
157        "Resolve"
158    };
159    (AMBIGUOUS) => {
160        "Ambiguous"
161    };
162    (NOT_FOUND) => {
163        "Not found"
164    };
165    (NOT_FOUND) => {
166        "Not found"
167    };
168}
169
170/// Generate string literal ` INVALID `*XXX*` ` with ANSI color.
171#[cfg(feature = "ansi-color")]
172#[macro_export]
173macro_rules! invalid {
174    (VALUE) => {
175        color_print::cstr!("<R!,k,s> NO VALUE </>")
176    };
177    (TYPE) => {
178        color_print::cstr!("<R!,k,s> NO TYPE </>")
179    };
180    (OUTPUT) => {
181        color_print::cstr!("<R!,k,s> NO OUTPUT </>")
182    };
183    (STACK) => {
184        color_print::cstr!("<W,k,s> EMPTY STACK </>")
185    };
186    (REF) => {
187        color_print::cstr!("<Y!,k,s> NO REF </>")
188    };
189    (FILE) => {
190        color_print::cstr!("<Y!,k,s> NO FILE </>")
191    };
192    (RESULT) => {
193        color_print::cstr!("<Y!,k,s> NO RESULT </>")
194    };
195    (LINE) => {
196        color_print::cstr!("<Y!,k,s> NO LINE </>")
197    };
198    (SOURCE) => {
199        color_print::cstr!("<C!,k,s> FROM STR </>")
200    };
201    (UNKNOWN) => {
202        color_print::cstr!("<M!,k,s> UNKNOWN </>")
203    };
204    (ID) => {
205        color_print::cstr!("<M!,k,s> NO ID </>")
206    };
207    (NAME) => {
208        color_print::cstr!("<M!,k,s> NO NAME </>")
209    };
210    (EXPRESSION) => {
211        color_print::cstr!("<R!,k,s> INVALID EXPRESSION </>")
212    };
213}
214
215/// Generate string literal `<INVALID `*XXX*`>`.
216#[macro_export]
217macro_rules! invalid_no_ansi {
218    (VALUE) => {
219        "<NO VALUE>"
220    };
221    (TYPE) => {
222        "<NO TYPE>"
223    };
224    (OUTPUT) => {
225        "<NO OUTPUT>"
226    };
227    (STACK) => {
228        "<EMPTY STACK>"
229    };
230    (REF) => {
231        "<NO REF>"
232    };
233    (FILE) => {
234        "<NO FILE>"
235    };
236    (RESULT) => {
237        "<NO RESULT>"
238    };
239    (LINE) => {
240        "<NO LINE>"
241    };
242    (SOURCE) => {
243        "<FROM STR>"
244    };
245    (UNKNOWN) => {
246        "<UNKNOWN>"
247    };
248    (ID) => {
249        "<NO ID>"
250    };
251    (NAME) => {
252        "<INVALID NAME>"
253    };
254    (EXPRESSION) => {
255        "<INVALID EXPRESSION>"
256    };
257}
258
259#[cfg(not(feature = "ansi-color"))]
260macro_rules! invalid {
261    ($x:literal) => {
262        invalid_no_ansi!($x)
263    };
264}