1use std::any::Any;
2use std::ops::Deref;
3use std::rc::Rc;
4
5use mica_language::ast::DumpAst;
6use mica_language::bytecode::{
7 BuiltinDispatchTables, BuiltinTraits, Chunk, DispatchTable, Environment, Function, FunctionKind,
8 FunctionSignature, Opcode, Opr24,
9};
10use mica_language::codegen::{self, CodeGenerator};
11use mica_language::gc::{Gc, Memory};
12use mica_language::lexer::Lexer;
13use mica_language::parser::Parser;
14use mica_language::value::{Closure, RawValue};
15use mica_language::vm::{self, Globals};
16
17use crate::{
18 create_trait_value, ffvariants, BuiltType, Error, Fiber, ForeignFunction, MicaResultExt,
19 StandardLibrary, TraitBuilder, TryFromValue, TypeBuilder, UserData, Value,
20};
21
22pub use mica_language::bytecode::ForeignFunction as RawForeignFunction;
24pub use mica_language::bytecode::FunctionKind as RawFunctionKind;
26
27#[derive(Debug, Clone, Copy, Default)]
29pub struct DebugOptions {
30 pub dump_ast: bool,
32 pub dump_bytecode: bool,
34}
35
36pub struct Engine {
39 pub(crate) env: Environment,
40 pub(crate) builtin_traits: BuiltinTraits,
41 pub(crate) globals: Globals,
42 pub(crate) gc: Memory,
44 debug_options: DebugOptions,
45}
46
47impl Engine {
48 pub fn new(stdlib: impl StandardLibrary) -> Self {
50 Self::with_debug_options(stdlib, Default::default())
51 }
52
53 pub fn with_debug_options(
60 mut stdlib: impl StandardLibrary,
61 debug_options: DebugOptions,
62 ) -> Self {
63 let mut gc = Memory::new();
64 let mut env = Environment::new(BuiltinDispatchTables::empty());
66
67 let builtin_traits = BuiltinTraits::register_in(&mut env);
68 let iterator = create_trait_value(&mut env, &mut gc, builtin_traits.iterator);
69
70 macro_rules! get_dtables {
71 ($type_name:tt, $define:tt) => {{
72 let tb = TypeBuilder::new($type_name);
73 let tb = stdlib.$define(tb);
74 tb.build(&mut env, &mut gc, &builtin_traits).expect("stdlib declares too many methods")
75 }};
76 }
77 let nil = get_dtables!("Nil", define_nil);
78 let boolean = get_dtables!("Boolean", define_boolean);
79 let number = get_dtables!("Number", define_number);
80 let string = get_dtables!("String", define_string);
81 let list = get_dtables!("List", define_list);
82 let dict = get_dtables!("Dict", define_dict);
83 env.builtin_dtables = BuiltinDispatchTables {
84 nil: Gc::clone(&nil.instance_dtable),
85 boolean: Gc::clone(&boolean.instance_dtable),
86 number: Gc::clone(&number.instance_dtable),
87 string: Gc::clone(&string.instance_dtable),
88 function: Gc::new(DispatchTable::new_for_instance("Function")),
89 list: Gc::clone(&list.instance_dtable),
90 dict: Gc::clone(&dict.instance_dtable),
91 };
92
93 let mut engine = Self {
94 env,
95 builtin_traits,
96 globals: Globals::new(),
97 gc,
98 debug_options,
99 };
100 engine.set_built_type(&nil).unwrap();
103 engine.set_built_type(&boolean).unwrap();
104 engine.set_built_type(&number).unwrap();
105 engine.set_built_type(&string).unwrap();
106 engine.set_built_type(&list).unwrap();
107 engine.set("Iterator", iterator).unwrap();
108
109 engine
110 }
111
112 pub fn compile(
117 &mut self,
118 filename: impl AsRef<str>,
119 source: impl Into<String>,
120 ) -> Result<Script, Error> {
121 let module_name = Rc::from(filename.as_ref());
122 let lexer = Lexer::new(Rc::clone(&module_name), source.into());
123 let (ast, root_node) = Parser::new(lexer).parse()?;
124 if self.debug_options.dump_ast {
125 eprintln!("Mica - AST dump:");
126 eprintln!("{:?}", DumpAst(&ast, root_node));
127 }
128
129 let main_chunk = CodeGenerator::new(module_name, &mut self.env, &self.builtin_traits)
130 .generate(&ast, root_node)?;
131 if self.debug_options.dump_bytecode {
132 eprintln!("Mica - global environment:");
133 eprintln!("{:#?}", self.env);
134 eprintln!("Mica - main chunk disassembly:");
135 eprintln!("{:#?}", main_chunk);
136 }
137
138 Ok(Script {
139 engine: self,
140 main_chunk,
141 })
142 }
143
144 pub fn start(
151 &mut self,
152 filename: impl AsRef<str>,
153 source: impl Into<String>,
154 ) -> Result<Fiber, Error> {
155 let script = self.compile(filename, source)?;
156 Ok(script.into_fiber())
157 }
158
159 pub fn call<T>(
168 &mut self,
169 function: Value,
170 arguments: impl IntoIterator<Item = Value>,
171 ) -> Result<T, Error>
172 where
173 T: TryFromValue,
174 {
175 let stack: Vec<_> =
176 Some(function).into_iter().chain(arguments).map(|x| x.to_raw(&mut self.gc)).collect();
177 let mut chunk = Chunk::new(Rc::from("(call)"));
181 chunk.emit((
182 Opcode::Call,
183 Opr24::try_from(stack.len() - 1).map_err(|_| Error::TooManyArguments)?,
186 ));
187 chunk.emit(Opcode::Halt);
188 let chunk = Rc::new(chunk);
189 let fiber = Fiber {
190 engine: self,
191 inner: vm::Fiber::new(chunk, stack),
192 };
193 fiber.trampoline()
194 }
195
196 pub fn method_id(&mut self, signature: impl MethodSignature) -> Result<MethodId, Error> {
207 signature.to_method_id(&mut self.env)
208 }
209
210 pub fn call_method<T>(
227 &mut self,
228 receiver: Value,
229 signature: impl MethodSignature,
230 arguments: impl IntoIterator<Item = Value>,
231 ) -> Result<T, Error>
232 where
233 T: TryFromValue,
234 {
235 let method_id = signature.to_method_id(&mut self.env)?;
236 let signature = self.env.get_method_signature(method_id.0).unwrap();
239 let stack: Vec<_> =
240 Some(receiver).into_iter().chain(arguments).map(|x| x.to_raw(&mut self.gc)).collect();
241 let argument_count = u8::try_from(stack.len()).map_err(|_| Error::TooManyArguments)?;
242 if Some(argument_count as u16) != signature.arity {
243 return Err(Error::ArgumentCount {
244 expected: signature.arity.unwrap() as usize - 1,
247 got: argument_count as usize - 1,
248 });
249 }
250 let mut chunk = Chunk::new(Rc::from("(call)"));
251 chunk.emit((
252 Opcode::CallMethod,
253 Opr24::pack((method_id.0, argument_count)),
254 ));
255 chunk.emit(Opcode::Halt);
256 let chunk = Rc::new(chunk);
257 let fiber = Fiber {
258 engine: self,
259 inner: vm::Fiber::new(chunk, stack),
260 };
261 fiber.trampoline()
262 }
263
264 pub fn global_id(&mut self, name: impl GlobalName) -> Result<GlobalId, Error> {
274 name.to_global_id(&mut self.env)
275 }
276
277 pub fn set<G, T>(&mut self, id: G, value: T) -> Result<(), Error>
284 where
285 G: GlobalName,
286 T: Into<Value>,
287 {
288 let id = id.to_global_id(&mut self.env)?;
289 self.globals.set(id.0, value.into().to_raw(&mut self.gc));
290 Ok(())
291 }
292
293 pub fn get<G, T>(&self, id: G) -> Result<T, Error>
301 where
302 G: OptionalGlobalName,
303 T: TryFromValue,
304 {
305 if let Some(id) = id.try_to_global_id(&self.env) {
306 T::try_from_value(&Value::from_raw(self.globals.get(id.0)))
307 } else {
308 T::try_from_value(&Value::from_raw(RawValue::from(())))
309 }
310 }
311
312 pub fn add_raw_function(
328 &mut self,
329 name: &str,
330 parameter_count: impl Into<Option<u16>>,
331 f: FunctionKind,
332 ) -> Result<(), Error> {
333 let global_id = name.to_global_id(&mut self.env)?;
334 let function_id = self
335 .env
336 .create_function(Function {
337 name: Rc::from(name),
338 parameter_count: parameter_count.into(), kind: f,
340 hidden_in_stack_traces: false,
341 })
342 .map_err(|_| Error::TooManyFunctions)?;
343 let function = RawValue::from(self.gc.allocate(Closure {
344 function_id,
345 captures: Vec::new(),
346 }));
347 self.globals.set(global_id.0, function);
348 Ok(())
349 }
350
351 pub fn add_function<F, V>(&mut self, name: &str, f: F) -> Result<(), Error>
356 where
357 V: ffvariants::Bare,
358 F: ForeignFunction<V>,
359 {
360 self.add_raw_function(
361 name,
362 F::parameter_count(),
363 FunctionKind::Foreign(f.into_raw_foreign_function()),
364 )
365 }
366
367 pub fn add_type<T>(&mut self, builder: TypeBuilder<T>) -> Result<(), Error>
374 where
375 T: Any + UserData,
376 {
377 let built = builder.build(&mut self.env, &mut self.gc, &self.builtin_traits)?;
378 self.set_built_type(&built)?;
379 Ok(())
380 }
381
382 pub(crate) fn set_built_type<T>(&mut self, typ: &BuiltType<T>) -> Result<(), Error>
383 where
384 T: Any,
385 {
386 let value = typ.make_type(&mut self.gc);
387 self.set(typ.type_name.deref(), value)
388 }
389
390 pub fn build_trait(&mut self, name: &str) -> Result<TraitBuilder<'_>, Error> {
392 Ok(TraitBuilder {
393 inner: codegen::TraitBuilder::new(&mut self.env, None, Rc::from(name)).mica()?,
394 gc: &mut self.gc,
395 })
396 }
397}
398
399pub struct Script<'e> {
401 engine: &'e mut Engine,
402 main_chunk: Rc<Chunk>,
403}
404
405impl<'e> Script<'e> {
406 pub fn start(&mut self) -> Fiber {
408 Fiber {
409 engine: self.engine,
410 inner: vm::Fiber::new(Rc::clone(&self.main_chunk), Vec::new()),
411 }
412 }
413
414 pub fn into_fiber(self) -> Fiber<'e> {
416 Fiber {
417 engine: self.engine,
418 inner: vm::Fiber::new(Rc::clone(&self.main_chunk), Vec::new()),
419 }
420 }
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
427#[repr(transparent)]
428pub struct GlobalId(Opr24);
429
430mod global_id {
431 use crate::GlobalId;
432
433 pub trait Sealed {}
434 impl Sealed for GlobalId {}
435 impl Sealed for &str {}
436}
437
438pub trait GlobalName: global_id::Sealed {
440 #[doc(hidden)]
441 fn to_global_id(&self, env: &mut Environment) -> Result<GlobalId, Error>;
442}
443
444impl GlobalName for GlobalId {
445 fn to_global_id(&self, _: &mut Environment) -> Result<GlobalId, Error> {
446 Ok(*self)
447 }
448}
449
450impl GlobalName for &str {
451 fn to_global_id(&self, env: &mut Environment) -> Result<GlobalId, Error> {
452 Ok(if let Some(slot) = env.get_global(self) {
453 GlobalId(slot)
454 } else {
455 env.create_global(self).map(GlobalId).map_err(|_| Error::TooManyGlobals)?
456 })
457 }
458}
459
460pub trait OptionalGlobalName {
462 #[doc(hidden)]
463 fn try_to_global_id(&self, env: &Environment) -> Option<GlobalId>;
464}
465
466impl OptionalGlobalName for GlobalId {
467 fn try_to_global_id(&self, _: &Environment) -> Option<GlobalId> {
468 Some(*self)
469 }
470}
471
472impl OptionalGlobalName for &str {
473 fn try_to_global_id(&self, env: &Environment) -> Option<GlobalId> {
474 env.get_global(self).map(GlobalId)
475 }
476}
477
478mod method_id {
479 use crate::MethodId;
480
481 pub trait Sealed {}
482
483 impl Sealed for MethodId {}
484 impl Sealed for (&str, u8) {}
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
491#[repr(transparent)]
492pub struct MethodId(pub(crate) u16);
493
494pub trait MethodSignature: method_id::Sealed {
498 #[doc(hidden)]
499 fn to_method_id(&self, env: &mut Environment) -> Result<MethodId, Error>;
500}
501
502impl MethodSignature for MethodId {
503 fn to_method_id(&self, _: &mut Environment) -> Result<MethodId, Error> {
504 Ok(*self)
505 }
506}
507
508impl MethodSignature for (&str, u8) {
511 fn to_method_id(&self, env: &mut Environment) -> Result<MethodId, Error> {
512 env.get_method_index(&FunctionSignature {
513 name: Rc::from(self.0),
514 arity: Some(self.1 as u16 + 1),
515 trait_id: None,
516 })
517 .map(MethodId)
518 .map_err(|_| Error::TooManyMethods)
519 }
520}