Skip to main content

hara_native/
core.rs

1#![allow(clippy::too_many_lines)] // Temporary compatibility facade during Java-port split.
2use 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
79/// Carries parser locations through the evaluator's plain `Form` function
80/// bodies without inventing callable marker symbols. Direct-native callers
81/// compile the original `SpannedForm` instead; this adapter is only used at
82/// evaluator boundaries where a function body must survive as a `Form`.
83pub 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");
227// Synchronous declaration and compatibility forms remain isolated from the
228// fiber execution target. Runtime source evaluation enters through EvalFiber;
229// this module is only the small compatibility seam used by namespace and
230// bytecode declaration machinery.
231include!("core/special_forms.rs");
232include!("core/bootstrap.rs");