1#![allow(clippy::too_many_lines)] use std::cell::{Cell, RefCell};
3use std::collections::{HashMap, HashSet, VecDeque};
4
5pub use crate::kernel::Form;
6use crate::kernel::{NamespaceLoadState, NamespaceRegistry, Var as KernelVar, VarOrigin};
7use crate::lang::data::List as PList;
8use crate::lang::data::{
9 Atom as PAtom, Cons as PCons, Deque as PDeque, Keyword, Map as PMap, MapEntry as PMapEntry,
10 OrderedMap as POrderedMap, OrderedSet as POrderedSet, Pointer as PPointer,
11 PriorityMap as PPriorityMap, Queue as PQueue, Seq as PSeq, Set as PSet,
12 SortedMap as PSortedMap, SortedSet as PSortedSet, Symbol, TaggedLiteral as PTaggedLiteral,
13 Trie as PTrie, Tuple as PTuple, Vector as PVector,
14};
15use crate::lang::data::{Metadata, MetadataValue};
16use crate::lang::data::{
17 MutableList, MutableMap, MutableOrderedMap, MutableOrderedSet, MutableQueue, MutableSet,
18 MutableSortedMap, MutableSortedSet, MutableTrie, MutableVector,
19};
20use crate::lang::hash::JavaHash;
21use crate::lang::protocol::{
22 IDisplay, IEmpty, IFn, IMetadata, INamespaced, IPopFirst, IPopLast, IToMutable, IToPersistent,
23};
24use crate::numeric::{self, ArithmeticOp};
25pub use crate::task::{
26 LocalPromiseProvider, Promise, PromiseProvider, PromiseRejection, PromiseState,
27};
28use num_bigint::BigInt;
29use std::hash::{Hash, Hasher};
30use std::rc::Rc;
31
32thread_local! {
33 static ACTIVE_EVALUATION_INTERRUPT: RefCell<Option<Rc<dyn Fn() -> Option<String>>>> =
34 const { RefCell::new(None) };
35 static ACTIVE_EXCEPTION_SITE: RefCell<Option<ExceptionSite>> = const { RefCell::new(None) };
36}
37
38pub(crate) fn with_exception_site<R>(site: ExceptionSite, operation: impl FnOnce() -> R) -> R {
39 ACTIVE_EXCEPTION_SITE.with(|active| {
40 let previous = active.replace(Some(site));
41 let result = operation();
42 active.replace(previous);
43 result
44 })
45}
46
47pub(crate) fn current_exception_site() -> Option<ExceptionSite> {
48 ACTIVE_EXCEPTION_SITE.with(|active| active.borrow().clone())
49}
50
51pub(crate) fn exception_site_at(line: usize, column: usize) -> Option<ExceptionSite> {
52 Some(current_exception_site().map_or(
53 ExceptionSite {
54 namespace: None,
55 resource: None,
56 line,
57 column,
58 },
59 |mut site| {
60 site.line = line;
61 site.column = column;
62 site
63 },
64 ))
65}
66
67const SOURCE_LOCATION_KEY: &str = "hara/source-location";
68
69fn source_location_metadata(line: usize, column: usize) -> Form {
70 Form::Map(vec![(
71 Form::Keyword(SOURCE_LOCATION_KEY.into()),
72 Form::Map(vec![
73 (Form::Keyword("line".into()), Form::Number(line as i64)),
74 (Form::Keyword("column".into()), Form::Number(column as i64)),
75 ]),
76 )])
77}
78
79pub fn attach_exception_sites(node: &crate::kernel::SpannedForm) -> Form {
84 let rebuilt = match &node.form {
85 Form::List(values)
86 if values.first().is_some_and(
87 |value| matches!(value, Form::Symbol(name) if name == "quote" || name == "'"),
88 ) =>
89 {
90 Form::List(values.clone())
91 }
92 Form::List(values) if node.children.len() == values.len() => {
93 Form::List(node.children.iter().map(attach_exception_sites).collect())
94 }
95 Form::List(values) if node.children.len() + 1 == values.len() => {
96 let mut rebuilt = vec![values[0].clone()];
97 rebuilt.extend(node.children.iter().map(attach_exception_sites));
98 Form::List(rebuilt)
99 }
100 Form::Vector(values) if node.children.len() == values.len() => {
101 Form::Vector(node.children.iter().map(attach_exception_sites).collect())
102 }
103 Form::Set(values) if node.children.len() == values.len() => {
104 Form::Set(node.children.iter().map(attach_exception_sites).collect())
105 }
106 Form::Map(values) if node.children.len() == values.len() * 2 => Form::Map(
107 node.children
108 .chunks_exact(2)
109 .map(|pair| {
110 (
111 attach_exception_sites(&pair[0]),
112 attach_exception_sites(&pair[1]),
113 )
114 })
115 .collect(),
116 ),
117 Form::Tagged(tag, _) if node.children.len() == 1 => Form::Tagged(
118 tag.clone(),
119 Box::new(attach_exception_sites(&node.children[0])),
120 ),
121 Form::Metadata(metadata, _) if node.children.len() == 1 => Form::Metadata(
122 metadata.clone(),
123 Box::new(attach_exception_sites(&node.children[0])),
124 ),
125 form => form.clone(),
126 };
127 let Form::List(values) = form_without_metadata(&rebuilt) else {
128 return rebuilt;
129 };
130 let Some(Form::Symbol(operator)) = values.first() else {
131 return rebuilt;
132 };
133 if !matches!(operator.as_str(), "throw" | "ex" | "std.foundation/ex") {
134 return rebuilt;
135 }
136 Form::Metadata(
137 Box::new(source_location_metadata(
138 node.span.start.line,
139 node.span.start.column,
140 )),
141 Box::new(rebuilt),
142 )
143}
144
145pub(crate) fn exception_location_from_metadata(metadata: &Form) -> Option<(usize, usize)> {
146 let Form::Map(entries) = form_without_metadata(metadata) else {
147 return None;
148 };
149 let location = entries.iter().find_map(|(key, value)| {
150 matches!(key, Form::Keyword(name) if name == SOURCE_LOCATION_KEY).then_some(value)
151 })?;
152 let Form::Map(entries) = form_without_metadata(location) else {
153 return None;
154 };
155 let number = |name: &str| {
156 entries.iter().find_map(|(key, value)| {
157 matches!(key, Form::Keyword(candidate) if candidate == name).then(|| match value {
158 Form::Number(value) if *value >= 0 => Some(*value as usize),
159 _ => None,
160 })?
161 })
162 };
163 Some((number("line")?, number("column")?))
164}
165
166pub(crate) fn with_evaluation_interrupt<R>(
167 interrupt: Rc<dyn Fn() -> Option<String>>,
168 operation: impl FnOnce() -> R,
169) -> R {
170 ACTIVE_EVALUATION_INTERRUPT.with(|active| {
171 let previous = active.replace(Some(interrupt));
172 let result = operation();
173 active.replace(previous);
174 result
175 })
176}
177
178pub(crate) fn check_evaluation_interrupt() -> Result<(), String> {
179 ACTIVE_EVALUATION_INTERRUPT.with(|active| {
180 active
181 .borrow()
182 .as_ref()
183 .and_then(|interrupt| interrupt())
184 .map_or(Ok(()), Err)
185 })
186}
187
188#[path = "fiber.rs"]
189mod fiber;
190#[path = "core/native_result.rs"]
191mod native_result;
192pub use native_result::{ResultStatus, ResultValue};
193#[cfg(not(feature = "raw-wasm"))]
194#[path = "native_crypto.rs"]
195mod native_crypto;
196#[cfg(feature = "raw-wasm")]
197mod native_crypto {
198 use super::Value;
199
200 pub(super) fn operation(_operation: &str, _arguments: Vec<Value>) -> Result<Value, String> {
201 Err("std.native.Crypto is unavailable in raw Wasm".into())
202 }
203}
204pub(crate) use fiber::Cont;
205pub use fiber::{EvalFiber, EvalFiberState, Step};
206
207include!("core/registry.rs");
208include!("core/native_declarations.rs");
209include!("core/value.rs");
210include!("core/vm_tool.rs");
211#[cfg(all(feature = "bytecode-vm", not(feature = "raw-wasm")))]
212include!("core/package_tool.rs");
213#[cfg(any(not(feature = "bytecode-vm"), feature = "raw-wasm"))]
214pub(crate) fn package_tool_provider_values() -> Vec<(&'static str, Value)> {
215 Vec::new()
216}
217include!("core/inspection.rs");
218include!("core/environment.rs");
219include!("core/native.rs");
220include!("core/provider.rs");
221include!("core/async_value.rs");
222include!("core/primitive.rs");
223include!("core/protocol.rs");
224include!("core/operation.rs");
225include!("core/form.rs");
226include!("core/namespace.rs");
227include!("core/special_forms.rs");
232include!("core/bootstrap.rs");