1use std::cell::OnceCell;
79
80use crate::arena::Arena;
81use crate::gc::{Gc, GcContext, GcTrace, GcView};
82use crate::interner::{InternedStr, StrInterner};
83use crate::lexer::Lexer;
84use crate::parser::Parser;
85use crate::span::{SourceId, SpanContextId, SpanId, SpanManager};
86use crate::{ast, FHashMap};
87
88mod analyze;
89mod data;
90mod error;
91mod eval;
92mod ir;
93mod stdlib;
94
95use data::{
96 ArrayData, BuiltInFunc, FuncData, FuncKind, FuncParams, ObjectData, ObjectField, ObjectLayer,
97 PendingThunk, ThunkData, ThunkEnv, ThunkEnvData, ThunkState, ValueData,
98};
99pub use error::{AnalyzeError, EvalError, EvalErrorKind, EvalErrorValueType, LoadError};
100
101#[derive(Clone, Debug)]
106pub struct ImportError;
107
108#[derive(Clone, Debug)]
112pub struct NativeError;
113
114pub trait Callbacks<'p> {
119 fn import(
121 &mut self,
122 program: &mut Program<'p>,
123 from: SpanId,
124 path: &str,
125 ) -> Result<Thunk<'p>, ImportError>;
126
127 fn import_str(
129 &mut self,
130 program: &mut Program<'p>,
131 from: SpanId,
132 path: &str,
133 ) -> Result<String, ImportError>;
134
135 fn import_bin(
137 &mut self,
138 program: &mut Program<'p>,
139 from: SpanId,
140 path: &str,
141 ) -> Result<Vec<u8>, ImportError>;
142
143 fn trace(&mut self, program: &mut Program<'p>, message: &str, stack: &[EvalStackTraceItem]);
145
146 fn native_call(
151 &mut self,
152 program: &mut Program<'p>,
153 name: InternedStr<'p>,
154 args: &[Value<'p>],
155 ) -> Result<Value<'p>, NativeError>;
156}
157
158#[derive(Clone, Debug, PartialEq, Eq)]
159pub enum EvalStackTraceItem {
160 Expr {
161 span: SpanId,
162 },
163 Call {
164 span: Option<SpanId>,
165 name: Option<String>,
166 },
167 Variable {
168 span: SpanId,
169 name: String,
170 },
171 ArrayItem {
172 span: Option<SpanId>,
173 index: usize,
174 },
175 ObjectField {
176 span: Option<SpanId>,
177 name: String,
178 },
179 CompareArrayItem {
180 index: usize,
181 },
182 CompareObjectField {
183 name: String,
184 },
185 ManifestArrayItem {
186 index: usize,
187 },
188 ManifestObjectField {
189 name: String,
190 },
191 Import {
192 span: SpanId,
193 },
194}
195
196pub struct Program<'p> {
200 arena: &'p Arena,
201 str_interner: StrInterner<'p>,
202 span_mgr: SpanManager,
203 gc_ctx: GcContext<'p>,
204 objs_after_last_gc: usize,
205 max_stack: usize,
206 exprs: Exprs<'p>,
207 stdlib_src_id: SourceId,
208 stdlib_data: &'static [u8],
209 stdlib_base_obj: Option<GcView<ObjectData<'p>>>,
210 stdlib_extra: FHashMap<InternedStr<'p>, GcView<ThunkData<'p>>>,
211 empty_array: GcView<ArrayData<'p>>,
212 identity_func: GcView<FuncData<'p>>,
213 ext_vars: FHashMap<InternedStr<'p>, GcView<ThunkData<'p>>>,
214 native_funcs: FHashMap<InternedStr<'p>, GcView<FuncData<'p>>>,
215}
216
217struct Exprs<'p> {
218 null: &'p ir::Expr<'p>,
219 false_: &'p ir::Expr<'p>,
220 true_: &'p ir::Expr<'p>,
221 self_obj: &'p ir::Expr<'p>,
222 top_obj: &'p ir::Expr<'p>,
223}
224
225impl<'p> Program<'p> {
226 pub fn new(arena: &'p Arena) -> Self {
228 let str_interner = StrInterner::new();
229 let gc_ctx = GcContext::new();
230 let exprs = Exprs {
231 null: arena.alloc(ir::Expr::Null),
232 false_: arena.alloc(ir::Expr::Bool(false)),
233 true_: arena.alloc(ir::Expr::Bool(true)),
234 self_obj: arena.alloc(ir::Expr::SelfObj),
235 top_obj: arena.alloc(ir::Expr::TopObj),
236 };
237
238 let stdlib_data = stdlib::STDLIB_DATA;
239 let mut span_mgr = SpanManager::new();
240 let (stdlib_span_ctx, stdlib_src_id) = span_mgr.insert_source_context(stdlib_data.len());
241
242 let stdlib_extra = Self::build_stdlib_extra(arena, &str_interner, &gc_ctx, &exprs);
243
244 let empty_array: GcView<ArrayData<'p>> = gc_ctx.alloc_view(Box::new([]));
245 let identity_func = gc_ctx.alloc_view(FuncData::new_identity_func(
246 Some(str_interner.intern(arena, "id")),
247 arena.alloc([(str_interner.intern(arena, "x"), None)]),
248 ));
249
250 let mut this = Self {
251 arena,
252 str_interner,
253 span_mgr,
254 gc_ctx,
255 objs_after_last_gc: 0,
256 max_stack: 500,
257 exprs,
258 stdlib_src_id,
259 stdlib_data,
260 stdlib_base_obj: None,
261 stdlib_extra,
262 empty_array,
263 identity_func,
264 ext_vars: FHashMap::default(),
265 native_funcs: FHashMap::default(),
266 };
267 this.load_stdlib(stdlib_span_ctx);
268 this
269 }
270
271 #[inline]
272 pub fn str_interner(&self) -> &StrInterner<'p> {
273 &self.str_interner
274 }
275
276 #[inline]
277 pub fn intern_str(&self, s: &str) -> InternedStr<'p> {
278 self.str_interner.intern(self.arena, s)
279 }
280
281 #[inline]
282 pub fn span_manager(&self) -> &SpanManager {
283 &self.span_mgr
284 }
285
286 #[inline]
287 pub fn span_manager_mut(&mut self) -> &mut SpanManager {
288 &mut self.span_mgr
289 }
290
291 pub fn gc(&mut self) {
293 self.gc_ctx.gc();
294 self.objs_after_last_gc = self.gc_ctx.num_objects();
295 }
296
297 pub fn maybe_gc(&mut self) {
299 let num_objects = self.gc_ctx.num_objects();
300 if num_objects > 1000 && (num_objects / 2) > self.objs_after_last_gc {
301 self.gc();
302 }
303 }
304
305 pub fn set_max_stack(&mut self, max_stack: usize) {
309 self.max_stack = max_stack;
310 }
311
312 pub fn get_stdlib_source(&self) -> (SourceId, &[u8]) {
315 (self.stdlib_src_id, self.stdlib_data)
316 }
317
318 pub fn add_ext_var(&mut self, name: InternedStr<'p>, thunk: &Thunk<'p>) {
323 match self.ext_vars.entry(name) {
324 std::collections::hash_map::Entry::Occupied(entry) => {
325 panic!("external variable {:?} already set", entry.key());
326 }
327 std::collections::hash_map::Entry::Vacant(entry) => {
328 entry.insert(thunk.data.clone());
329 }
330 }
331 }
332
333 pub fn register_native_func(&mut self, name: InternedStr<'p>, params: &[InternedStr<'p>]) {
346 match self.native_funcs.entry(name) {
347 std::collections::hash_map::Entry::Occupied(entry) => {
348 panic!("native function {:?} already registered", entry.key());
349 }
350 std::collections::hash_map::Entry::Vacant(entry) => {
351 let params_order: Vec<_> = params.iter().map(|&name| (name, None)).collect();
352 entry.insert(self.gc_ctx.alloc_view(FuncData::new(
353 self.arena.alloc_slice(¶ms_order),
354 FuncKind::Native { name },
355 )));
356 }
357 }
358 }
359
360 #[must_use]
361 #[inline]
362 fn gc_alloc<T: GcTrace + 'p>(&self, data: T) -> Gc<T> {
363 self.gc_ctx.alloc(data)
364 }
365
366 #[must_use]
367 #[inline]
368 fn gc_alloc_view<T: GcTrace + 'p>(&self, data: T) -> GcView<T> {
369 self.gc_ctx.alloc_view(data)
370 }
371
372 fn insert_thunk_with_value(&mut self, value: ValueData<'p>) -> GcView<ThunkData<'p>> {
373 self.gc_alloc_view(ThunkData::new_done(value))
374 }
375
376 pub fn value_to_thunk(&mut self, value: &Value<'p>) -> Thunk<'p> {
378 Thunk::new(self.insert_thunk_with_value(value.inner.clone()))
379 }
380
381 pub fn make_array(&mut self, items: &[Value<'p>]) -> Value<'p> {
383 let array = self.make_value_array(items.iter().map(|item| item.inner.clone()));
384 Value::from_value(ValueData::Array(array))
385 }
386
387 pub fn make_object(&mut self, obj_fields: &[(InternedStr<'p>, Value<'p>)]) -> Value<'p> {
389 let mut fields = FHashMap::default();
390 for (name, value) in obj_fields.iter() {
391 let value_thunk = self.gc_alloc(ThunkData::new_done(value.inner.clone()));
392 let prev = fields.insert(
393 *name,
394 ObjectField {
395 base_env: None,
396 visibility: ast::Visibility::Default,
397 expr: None,
398 thunk: OnceCell::from(value_thunk),
399 },
400 );
401 assert!(prev.is_none(), "repeated field name {name:?}");
402 }
403
404 let obj = self.gc_alloc(ObjectData::new_simple(fields));
405 Value::from_value(ValueData::Object(obj))
406 }
407
408 pub fn load_source(
413 &mut self,
414 span_ctx: SpanContextId,
415 input: &[u8],
416 with_stdlib: bool,
417 this_file: &str,
418 ) -> Result<Thunk<'p>, LoadError> {
419 let ast_arena = Arena::new();
420 let lexer = Lexer::new(
421 self.arena,
422 &ast_arena,
423 &self.str_interner,
424 &mut self.span_mgr,
425 span_ctx,
426 input,
427 );
428 let tokens = lexer.lex_to_eof(false)?;
429
430 let parser = Parser::new(
431 self.arena,
432 &ast_arena,
433 &self.str_interner,
434 &mut self.span_mgr,
435 tokens,
436 );
437 let root_expr = parser.parse_root_expr()?;
438
439 let env = if with_stdlib {
440 let stdlib_obj = self.make_custom_stdlib(this_file);
441 let stdlib_thunk =
442 self.gc_alloc_view(ThunkData::new_done(ValueData::Object(stdlib_obj)));
443
444 let mut env = FHashMap::default();
445 env.insert(self.intern_str("std"), Thunk::new(stdlib_thunk));
446
447 Some(env)
448 } else {
449 None
450 };
451 let thunk = self.analyze(&root_expr, env)?;
452 Ok(thunk)
453 }
454
455 fn analyze(
456 &mut self,
457 ast: &ast::Expr<'p, '_>,
458 env: Option<FHashMap<InternedStr<'p>, Thunk<'p>>>,
459 ) -> Result<Thunk<'p>, AnalyzeError> {
460 let analyze_env = env
461 .as_ref()
462 .map(|env| env.keys().cloned().collect())
463 .unwrap_or_default();
464 let ir_expr = analyze::Analyzer::new(self).analyze(ast, analyze_env)?;
465
466 let mut thunk_env_data = ThunkEnvData::new(None);
467 if let Some(env) = env {
468 for (name, value) in env.iter() {
469 thunk_env_data.set_var(*name, Gc::from(&value.data));
470 }
471 }
472 let thunk_env = self.gc_alloc(ThunkEnv::from(thunk_env_data));
473
474 let thunk = self.gc_alloc_view(ThunkData::new_pending_expr(ir_expr, thunk_env));
475
476 Ok(Thunk::new(thunk))
477 }
478
479 pub fn eval_value(
481 &mut self,
482 thunk: &Thunk<'p>,
483 callbacks: &mut dyn Callbacks<'p>,
484 ) -> Result<Value<'p>, EvalError> {
485 let output = eval::Evaluator::eval(
486 self,
487 Some(callbacks),
488 eval::EvalInput::Value(thunk.data.clone()),
489 )
490 .map_err(|e| *e)?;
491 let eval::EvalOutput::Value(value) = output else {
492 unreachable!();
493 };
494 Ok(Value::from_value(value))
495 }
496
497 fn eval_value_internal(&mut self, thunk: &Thunk<'p>) -> Result<ValueData<'p>, EvalError> {
498 let output = eval::Evaluator::eval(self, None, eval::EvalInput::Value(thunk.data.clone()))
499 .map_err(|e| *e)?;
500 let eval::EvalOutput::Value(value) = output else {
501 unreachable!();
502 };
503 Ok(value)
504 }
505
506 pub fn eval_call(
508 &mut self,
509 func: &Thunk<'p>,
510 pos_args: &[Thunk<'p>],
511 named_args: &[(InternedStr<'p>, Thunk<'p>)],
512 callbacks: &mut dyn Callbacks<'p>,
513 ) -> Result<Value<'p>, EvalError> {
514 let output = eval::Evaluator::eval(
515 self,
516 Some(callbacks),
517 eval::EvalInput::Call(
518 func.data.clone(),
519 eval::TopLevelArgs {
520 positional: pos_args.iter().map(|thunk| thunk.data.clone()).collect(),
521 named: named_args
522 .iter()
523 .map(|(name, thunk)| (*name, thunk.data.clone()))
524 .collect(),
525 },
526 ),
527 )
528 .map_err(|e| *e)?;
529 let eval::EvalOutput::Value(value) = output else {
530 unreachable!();
531 };
532 Ok(Value::from_value(value))
533 }
534
535 pub fn manifest_json(
537 &mut self,
538 value: &Value<'p>,
539 multiline: bool,
540 ) -> Result<String, EvalError> {
541 let thunk = self.insert_thunk_with_value(value.inner.clone());
542 let output =
543 eval::Evaluator::eval(self, None, eval::EvalInput::ManifestJson(thunk, multiline))
544 .map_err(|e| *e)?;
545 let eval::EvalOutput::String(s) = output else {
546 unreachable!();
547 };
548 Ok(s)
549 }
550}
551
552#[derive(Clone)]
558pub struct Thunk<'p> {
559 data: GcView<ThunkData<'p>>,
560}
561
562impl<'p> Thunk<'p> {
563 #[inline]
564 fn new(data: GcView<ThunkData<'p>>) -> Self {
565 Self { data }
566 }
567}
568
569#[derive(Clone)]
575pub struct Value<'p> {
576 inner: ValueData<'p>,
577}
578
579impl<'p> Value<'p> {
580 #[inline]
581 fn from_value(inner: ValueData<'p>) -> Self {
582 Self { inner }
583 }
584
585 #[inline]
586 fn from_thunk(thunk: &ThunkData<'p>) -> Self {
587 Self {
588 inner: match *thunk.state() {
589 ThunkState::Done(ref value) => value.clone(),
590 _ => panic!("thunk not evaluated"),
591 },
592 }
593 }
594
595 #[inline]
600 pub fn null() -> Self {
601 Self::from_value(ValueData::Null)
602 }
603
604 #[inline]
609 pub fn bool(value: bool) -> Self {
610 Self::from_value(ValueData::Bool(value))
611 }
612
613 #[inline]
618 pub fn number(value: f64) -> Self {
619 Self::from_value(ValueData::Number(value))
620 }
621
622 #[inline]
627 pub fn string(s: &str) -> Self {
628 Self::from_value(ValueData::String(s.into()))
629 }
630
631 #[must_use]
632 pub fn kind(&self) -> ValueKind<'p> {
633 match self.inner {
634 ValueData::Null => ValueKind::Null,
635 ValueData::Bool(value) => ValueKind::Bool(value),
636 ValueData::Number(value) => ValueKind::Number(value),
637 ValueData::String(ref s) => ValueKind::String((**s).into()),
638 ValueData::Array(ref array) => ValueKind::Array(Self::extract_array(&array.view())),
639 ValueData::Object(ref object) => {
640 ValueKind::Object(Self::extract_object(&object.view()))
641 }
642 ValueData::Function(_) => ValueKind::Function,
643 }
644 }
645
646 #[must_use]
647 #[inline]
648 pub fn is_null(&self) -> bool {
649 matches!(self.inner, ValueData::Null)
650 }
651
652 #[must_use]
653 #[inline]
654 pub fn is_bool(&self) -> bool {
655 matches!(self.inner, ValueData::Bool(_))
656 }
657
658 #[must_use]
659 #[inline]
660 pub fn as_bool(&self) -> Option<bool> {
661 if let ValueData::Bool(value) = self.inner {
662 Some(value)
663 } else {
664 None
665 }
666 }
667
668 #[must_use]
669 #[inline]
670 pub fn is_number(&self) -> bool {
671 matches!(self.inner, ValueData::Number(_))
672 }
673
674 #[must_use]
675 #[inline]
676 pub fn as_number(&self) -> Option<f64> {
677 if let ValueData::Number(value) = self.inner {
678 Some(value)
679 } else {
680 None
681 }
682 }
683
684 #[must_use]
685 #[inline]
686 pub fn is_string(&self) -> bool {
687 matches!(self.inner, ValueData::String(_))
688 }
689
690 #[must_use]
691 #[inline]
692 pub fn to_string(&self) -> Option<String> {
693 if let ValueData::String(ref s) = self.inner {
694 Some((**s).into())
695 } else {
696 None
697 }
698 }
699
700 #[must_use]
701 #[inline]
702 pub fn is_array(&self) -> bool {
703 matches!(self.inner, ValueData::Array(_))
704 }
705
706 #[must_use]
707 pub fn to_array(&self) -> Option<Vec<Self>> {
708 if let ValueData::Array(ref array) = self.inner {
709 Some(Self::extract_array(&array.view()))
710 } else {
711 None
712 }
713 }
714
715 #[must_use]
716 #[inline]
717 pub fn is_object(&self) -> bool {
718 matches!(self.inner, ValueData::Object(_))
719 }
720
721 #[must_use]
722 pub fn to_object(&self) -> Option<Vec<(InternedStr<'p>, Self)>> {
723 if let ValueData::Object(ref object) = self.inner {
724 Some(Self::extract_object(&object.view()))
725 } else {
726 None
727 }
728 }
729
730 #[must_use]
731 #[inline]
732 pub fn is_function(&self) -> bool {
733 matches!(self.inner, ValueData::Function(_))
734 }
735
736 fn extract_array(array: &ArrayData<'p>) -> Vec<Self> {
737 array
738 .iter()
739 .map(|item| Self::from_thunk(&item.view()))
740 .collect()
741 }
742
743 fn extract_object(object: &ObjectData<'p>) -> Vec<(InternedStr<'p>, Self)> {
744 let mut fields = Vec::new();
745 for &(name, visible) in object.get_fields_order().iter() {
746 if visible {
747 let (_, field) = object.find_field(0, name).unwrap();
748 let thunk = field.thunk.get().unwrap().clone();
749 fields.push((name, Self::from_thunk(&thunk.view())));
750 }
751 }
752 fields
753 }
754}
755
756#[derive(Clone)]
757pub enum ValueKind<'p> {
758 Null,
759 Bool(bool),
760 Number(f64),
761 String(String),
762 Array(Vec<Value<'p>>),
763 Object(Vec<(InternedStr<'p>, Value<'p>)>),
764 Function,
765}