//! Minimal source-to-wordcode compiler. Analysis and emission do not allocate //! VM objects; a compiled unit can be loaded into independent VMs explicitly. mod analysis; mod codegen; pub use analysis::{NameBinding, NameUse, Scope, ScopeKind, SymbolTable, analyze}; pub use codegen::CompiledCode; use frontend::ast::{AstDocument, AstRoot}; use frontend::{Diagnostic, DiagnosticKind, ParseMode, SourceDiagnostic, SourceUnit, Span}; /// Names supplied by the host's globals or builtins dictionary. These are /// compile-time declarations only; loading code never injects their values. #[derive(Debug, Clone, Default)] pub struct CompileOptions { pub globals: Vec, } pub fn compile_source( source: &SourceUnit, mode: ParseMode, options: &CompileOptions, ) -> Result { let root = frontend::parse_source_ast(source, mode)?; compile_ast(&root, source, options).map_err(|error| source.annotate(error)) } /// Compile an AST against its original source snapshot. Span validation does /// not prove the AST's semantic equivalence to the supplied text. pub fn compile_ast( root: &AstRoot, source: &SourceUnit, options: &CompileOptions, ) -> Result { AstDocument::new(root.clone()) .and_then(|document| document.validate_source(source.text())) .map_err(|error| { diagnostic( DiagnosticKind::Compilation, Span::default(), error.to_string(), ) })?; let symbols = analyze(root, options)?; codegen::generate(root, source, &symbols) } fn diagnostic(kind: DiagnosticKind, span: Span, message: impl Into) -> Diagnostic { Diagnostic { kind, span, message: message.into(), expected: Vec::new(), } }