sus_proc_macro/
lib.rs

1use proc_macro::TokenStream;
2
3use quote::{quote, quote_spanned};
4use regex::Regex;
5use syn::{LitStr, parse_macro_input};
6
7#[proc_macro]
8pub fn kind(token_stream: TokenStream) -> TokenStream {
9    let string_literal: LitStr = parse_macro_input!(token_stream);
10
11    let requested_kind = string_literal.value();
12
13    let language = tree_sitter_sus::language();
14    let found_id = language.id_for_node_kind(&requested_kind, true);
15
16    if found_id != 0 {
17        quote! {
18            #found_id
19        }
20    } else {
21        quote_spanned!(
22            string_literal.span() =>
23            compile_error!("This is not a valid node kind in the SUS language")
24        )
25    }
26    .into()
27}
28
29#[proc_macro]
30pub fn kw(token_stream: TokenStream) -> TokenStream {
31    let string_literal: LitStr = parse_macro_input!(token_stream);
32
33    let requested_keyword = string_literal.value();
34
35    let language = tree_sitter_sus::language();
36    let found_id = language.id_for_node_kind(&requested_keyword, false);
37
38    if found_id != 0 {
39        quote! {
40            #found_id
41        }
42    } else {
43        quote_spanned!(
44            string_literal.span() =>
45            compile_error!("This is not a valid keyword in the SUS language")
46        )
47    }
48    .into()
49}
50
51#[proc_macro]
52pub fn field(token_stream: TokenStream) -> TokenStream {
53    let string_literal: LitStr = parse_macro_input!(token_stream);
54
55    let requested_keyword = string_literal.value();
56
57    let language = tree_sitter_sus::language();
58    let found_id = language.field_id_for_name(&requested_keyword);
59
60    if let Some(found_id) = found_id {
61        let id_number: u16 = found_id.into();
62        quote! {
63            std::num::NonZeroU16::new(#id_number).unwrap()
64        }
65    } else {
66        quote_spanned!(
67            string_literal.span() =>
68            compile_error!("This is not a valid field in the SUS language")
69        )
70    }
71    .into()
72}
73
74#[proc_macro]
75pub fn get_builtin_type(token_stream: TokenStream) -> TokenStream {
76    let string_literal: LitStr = parse_macro_input!(token_stream);
77
78    let object_name = string_literal.value();
79
80    let core_file_text = std::fs::read_to_string("std/core.sus").unwrap();
81
82    let re = Regex::new(r"__builtin__\s+struct\s+([a-zA-Z0-9_]+)\s*(?:#\(.*\))?\s*\{").unwrap();
83
84    for (idx, c) in re.captures_iter(&core_file_text).enumerate() {
85        let (_full, [found_name]) = c.extract();
86        if found_name == object_name {
87            return quote! {
88                crate::alloc::UUID::<crate::prelude::TypeUUIDMarker>(#idx, std::marker::PhantomData)
89            }
90            .into();
91        }
92    }
93
94    quote_spanned!(
95        string_literal.span() =>
96        compile_error!("Unknown builtin type was not found in std/core.sus")
97    )
98    .into()
99}
100
101#[proc_macro]
102pub fn get_builtin_const(token_stream: TokenStream) -> TokenStream {
103    let string_literal: LitStr = parse_macro_input!(token_stream);
104
105    let object_name = string_literal.value();
106
107    let core_file_text = std::fs::read_to_string("std/core.sus").unwrap();
108
109    let re = Regex::new(r"__builtin__\s+const\s+.+\s+([a-zA-Z0-9_]+)\s*(?:#\(.*\))?\s*\{").unwrap();
110
111    for (idx, c) in re.captures_iter(&core_file_text).enumerate() {
112        let (_full, [found_name]) = c.extract();
113        if found_name == object_name {
114            return quote! {
115                crate::alloc::UUID::<crate::prelude::ConstantUUIDMarker>(#idx, std::marker::PhantomData)
116            }
117            .into();
118        }
119    }
120
121    quote_spanned!(
122        string_literal.span() =>
123        compile_error!("Unknown builtin const was not found in std/core.sus")
124    )
125    .into()
126}
127
128/// This could be a macro_rules!, but then rust insists on binding the line number to the macro.
129/// By wrapping it in a proc_macro rust can only assign the `__debug_breakpoint!` usage location for the lldb breakpoint
130#[proc_macro]
131pub fn __debug_breakpoint(_input: TokenStream) -> TokenStream {
132    quote! {
133        if crate::debug::debugging_enabled() {
134            #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
135            unsafe {
136                core::arch::asm!("int3");
137            }
138
139            #[cfg(all(target_arch = "x86_64", target_os = "windows"))]
140            unsafe {
141                core::arch::asm!("int3");
142            }
143
144            #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
145            unsafe {
146                core::arch::asm!("brk #0");
147            }
148
149            #[cfg(all(target_arch = "aarch64", target_os = "windows"))]
150            unsafe {
151                core::arch::asm!("brk #0");
152            }
153        }
154    }
155    .into()
156}
157
158/// This could be a macro_rules!, but then rust insists on binding the line number to the macro.
159/// By wrapping it in a proc_macro rust can only assign the `__debug_breakpoint_if!` usage location for the lldb breakpoint
160#[proc_macro]
161pub fn __debug_breakpoint_if(input: TokenStream) -> TokenStream {
162    let expr: syn::Expr = syn::parse_macro_input!(input as syn::Expr);
163    quote! {
164        if crate::debug::debugging_enabled() && (#expr) {
165            #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
166            unsafe {
167                core::arch::asm!("int3");
168            }
169
170            #[cfg(all(target_arch = "x86_64", target_os = "windows"))]
171            unsafe {
172                core::arch::asm!("int3");
173            }
174
175            #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
176            unsafe {
177                core::arch::asm!("brk #0");
178            }
179
180            #[cfg(all(target_arch = "aarch64", target_os = "windows"))]
181            unsafe {
182                core::arch::asm!("brk #0");
183            }
184        }
185    }
186    .into()
187}