1use core::fmt;
2use core::marker::PhantomData;
3use core::mem::take;
4
5use crate::alloc::borrow::TryToOwned;
6use crate::alloc::{self, String, Vec};
7use crate::ast::{Span, Spanned};
8#[cfg(feature = "std")]
9use crate::compile::FileSourceLoader as DefaultSourceLoader;
10#[cfg(not(feature = "std"))]
11use crate::compile::NoopSourceLoader as DefaultSourceLoader;
12use crate::compile::{
13 self, CompileVisitor, Located, MetaError, Options, ParseOptionError, Pool, SourceLoader,
14};
15use crate::runtime::unit::{DefaultStorage, UnitEncoder};
16use crate::runtime::{Globals, Unit};
17use crate::sync::Arc;
18use crate::{parse, Context, Diagnostics, Item, SourceId, Sources, Statics, Vm};
19
20#[derive(Default, Debug)]
24#[non_exhaustive]
25pub struct BuildError {
26 kind: BuildErrorKind,
27}
28
29impl From<ParseOptionError> for BuildError {
30 #[inline]
31 fn from(error: ParseOptionError) -> Self {
32 Self {
33 kind: BuildErrorKind::ParseOptionError(error),
34 }
35 }
36}
37
38impl From<alloc::Error> for BuildError {
39 #[inline]
40 fn from(error: alloc::Error) -> Self {
41 Self {
42 kind: BuildErrorKind::Alloc(error),
43 }
44 }
45}
46
47#[derive(Default, Debug)]
48enum BuildErrorKind {
49 #[default]
50 Default,
51 ParseOptionError(ParseOptionError),
52 Alloc(alloc::Error),
53}
54
55impl fmt::Display for BuildError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match &self.kind {
58 BuildErrorKind::Default => write!(
59 f,
60 "Failed to build rune sources (see diagnostics for details)"
61 ),
62 BuildErrorKind::ParseOptionError(error) => error.fmt(f),
63 BuildErrorKind::Alloc(error) => error.fmt(f),
64 }
65 }
66}
67
68impl core::error::Error for BuildError {
69 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
70 match &self.kind {
71 BuildErrorKind::Alloc(error) => Some(error),
72 _ => None,
73 }
74 }
75}
76
77pub fn prepare(sources: &mut Sources) -> Build<'_, DefaultStorage> {
132 prepare_with(sources)
133}
134
135pub fn prepare_with<S>(sources: &mut Sources) -> Build<'_, S>
137where
138 S: UnitEncoder,
139{
140 Build {
141 sources,
142 context: None,
143 diagnostics: None,
144 options: None,
145 args: Vec::new(),
146 statics: None,
147 visitors: Vec::new(),
148 source_loader: None,
149 _unit_storage: PhantomData,
150 }
151}
152
153pub struct Build<'a, S> {
159 sources: &'a mut Sources,
160 context: Option<&'a Context>,
161 diagnostics: Option<&'a mut Diagnostics>,
162 options: Option<&'a Options>,
163 args: Vec<String>,
164 statics: Option<&'a Statics>,
165 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
166 source_loader: Option<&'a mut dyn SourceLoader>,
167 _unit_storage: PhantomData<S>,
168}
169
170struct CompileVisitorGroup<'a> {
172 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
173}
174
175impl compile::CompileVisitor for CompileVisitorGroup<'_> {
176 fn register_meta(&mut self, meta: compile::MetaRef<'_>) -> Result<(), MetaError> {
177 for v in self.visitors.iter_mut() {
178 v.register_meta(meta)?;
179 }
180
181 Ok(())
182 }
183
184 fn visit_meta(
185 &mut self,
186 location: &dyn Located,
187 meta: compile::MetaRef<'_>,
188 ) -> Result<(), MetaError> {
189 for v in self.visitors.iter_mut() {
190 v.visit_meta(location, meta)?;
191 }
192
193 Ok(())
194 }
195
196 fn visit_variable_use(
197 &mut self,
198 source_id: SourceId,
199 var_span: &dyn Spanned,
200 span: &dyn Spanned,
201 ) -> Result<(), MetaError> {
202 for v in self.visitors.iter_mut() {
203 v.visit_variable_use(source_id, var_span, span)?;
204 }
205
206 Ok(())
207 }
208
209 fn visit_mod(&mut self, location: &dyn Located) -> Result<(), MetaError> {
210 for v in self.visitors.iter_mut() {
211 v.visit_mod(location)?;
212 }
213
214 Ok(())
215 }
216
217 fn visit_doc_comment(
218 &mut self,
219 location: &dyn Located,
220 item: &Item,
221 hash: crate::Hash,
222 doc: &str,
223 ) -> Result<(), MetaError> {
224 for v in self.visitors.iter_mut() {
225 v.visit_doc_comment(location, item, hash, doc)?;
226 }
227
228 Ok(())
229 }
230
231 fn visit_field_doc_comment(
232 &mut self,
233 location: &dyn Located,
234 item: &Item,
235 hash: crate::Hash,
236 field: &str,
237 doc: &str,
238 ) -> Result<(), MetaError> {
239 for v in self.visitors.iter_mut() {
240 v.visit_field_doc_comment(location, item, hash, field, doc)?;
241 }
242
243 Ok(())
244 }
245}
246
247impl<'a, S> Build<'a, S> {
248 #[inline]
255 pub fn with_context(mut self, context: &'a Context) -> Self {
256 self.context = Some(context);
257 self
258 }
259
260 #[inline]
262 pub fn with_diagnostics(mut self, diagnostics: &'a mut Diagnostics) -> Self {
263 self.diagnostics = Some(diagnostics);
264 self
265 }
266
267 #[inline]
269 pub fn with_options(mut self, options: &'a Options) -> Self {
270 self.options = Some(options);
271 self
272 }
273
274 pub fn with_arg(mut self, arg: impl AsRef<str>) -> alloc::Result<Self> {
303 self.args.try_push(arg.as_ref().try_to_owned()?)?;
304 Ok(self)
305 }
306
307 pub fn with_args(mut self, args: impl IntoIterator<Item: AsRef<str>>) -> alloc::Result<Self> {
336 for arg in args {
337 self.args.try_push(arg.as_ref().try_to_owned()?)?;
338 }
339
340 Ok(self)
341 }
342
343 #[inline]
374 pub fn with_statics(mut self, statics: &'a Statics) -> Self {
375 self.statics = Some(statics);
376 self
377 }
378
379 #[inline]
385 pub fn with_visitor(mut self, visitor: &'a mut dyn CompileVisitor) -> alloc::Result<Self> {
386 self.visitors.try_push(visitor)?;
387 Ok(self)
388 }
389
390 #[inline]
395 pub fn with_source_loader(mut self, source_loader: &'a mut dyn SourceLoader) -> Self {
396 self.source_loader = Some(source_loader);
397 self
398 }
399
400 pub fn build(self) -> Result<Unit<S>, BuildError>
406 where
407 S: Default + UnitEncoder,
408 {
409 let (unit, ()) = self.build_inner(|_| ())?;
410 Ok(unit)
411 }
412
413 #[inline]
414 fn build_inner<O>(
415 mut self,
416 extra: impl FnOnce(&Context) -> O,
417 ) -> Result<(Unit<S>, O), BuildError>
418 where
419 S: Default + UnitEncoder,
420 {
421 let default_context;
422
423 let context = match self.context.take() {
424 Some(context) => context,
425 None => {
426 default_context = Context::new();
427 &default_context
428 }
429 };
430
431 let mut unit = compile::UnitBuilder::default();
432
433 let prelude = if context.has_default_modules() {
434 compile::Prelude::with_default_prelude()?
435 } else {
436 compile::Prelude::default()
437 };
438
439 let mut default_diagnostics;
440
441 let diagnostics = match self.diagnostics {
442 Some(diagnostics) => diagnostics,
443 None => {
444 default_diagnostics = Diagnostics::new();
445 &mut default_diagnostics
446 }
447 };
448
449 let default_options;
450
451 let options = match self.options {
452 Some(options) => options,
453 None => {
454 default_options = Options::from_default_env()?;
455 &default_options
456 }
457 };
458
459 let mut default_visitors;
460 let visitors = match self.visitors.is_empty() {
461 true => {
462 default_visitors = CompileVisitorGroup {
463 visitors: Vec::new(),
464 };
465 &mut default_visitors
466 }
467 false => {
468 let v = take(&mut self.visitors);
469 default_visitors = CompileVisitorGroup { visitors: v };
470
471 &mut default_visitors
472 }
473 };
474
475 let mut default_source_loader;
476
477 let source_loader = match self.source_loader.take() {
478 Some(source_loader) => source_loader,
479 None => {
480 default_source_loader = DefaultSourceLoader::default();
481 &mut default_source_loader
482 }
483 };
484
485 if !self.args.is_empty() {
486 if let Some(options) = &mut self.options {
487 if !options.script {
488 diagnostics.internal(
489 SourceId::empty(),
490 "cannot set script arguments without enabling script mode",
491 )?;
492
493 return Err(BuildError::default());
494 }
495 }
496
497 for arg in &self.args {
498 if !parse::is_ident(arg) {
499 diagnostics.internal(
500 SourceId::empty(),
501 format!("script argument '{arg}' is not a valid identifier"),
502 )?;
503 }
504 }
505 }
506
507 let default_statics;
508
509 let statics = match self.statics.take() {
510 Some(statics) => statics,
511 None => {
512 default_statics = Statics::new();
513 &default_statics
514 }
515 };
516
517 for s in statics.iter() {
518 let item = s.item();
519
520 if item.is_empty() {
521 diagnostics.internal(SourceId::empty(), "static must be declared with a name")?;
522 continue;
523 }
524
525 for c in item.iter() {
526 if !c.as_str().is_some_and(parse::is_ident) {
527 diagnostics.internal(
528 SourceId::empty(),
529 format!("static '{item}' is not a valid item"),
530 )?;
531 break;
532 }
533 }
534 }
535
536 if diagnostics.has_error() {
537 return Err(BuildError::default());
538 }
539
540 let mut pool = Pool::new()?;
541 let mut unit_storage = S::default();
542
543 compile::compile(
544 &mut unit,
545 &prelude,
546 self.sources,
547 &mut pool,
548 context,
549 visitors,
550 diagnostics,
551 source_loader,
552 options,
553 &self.args,
554 statics,
555 &mut unit_storage,
556 )?;
557
558 if diagnostics.has_error() {
559 return Err(BuildError::default());
560 }
561
562 if options.link_checks {
563 unit.link(context, diagnostics)?;
564 }
565
566 if diagnostics.has_error() {
567 return Err(BuildError::default());
568 }
569
570 match unit.build(Span::empty(), unit_storage) {
571 Ok(unit) => Ok((unit, extra(context))),
572 Err(error) => {
573 diagnostics.error(SourceId::empty(), error)?;
574 Err(BuildError::default())
575 }
576 }
577 }
578}
579
580impl<'a> Build<'a, DefaultStorage> {
581 pub fn build_vm(self) -> Result<Vm, BuildError> {
588 let (unit, runtime) = self.build_inner(|context| context.runtime())?;
589 let runtime = runtime?;
590 let runtime = Arc::try_new(runtime)?;
591 let unit = Arc::try_new(unit)?;
592 let globals = Globals::new(unit.clone())?;
593 Ok(Vm::new(runtime, unit).with_globals(globals))
594 }
595}