diff --git a/crates/plotnik-lib/src/emit/codegen_tests.rs b/crates/plotnik-lib/src/emit/emit_tests.rs similarity index 82% rename from crates/plotnik-lib/src/emit/codegen_tests.rs rename to crates/plotnik-lib/src/emit/emit_tests.rs index 4f654d77..2be50782 100644 --- a/crates/plotnik-lib/src/emit/codegen_tests.rs +++ b/crates/plotnik-lib/src/emit/emit_tests.rs @@ -18,9 +18,7 @@ macro_rules! snap { }}; } -// ============================================================================ -// 1. NODES -// ============================================================================ +// Nodes #[test] fn nodes_named() { @@ -64,9 +62,7 @@ fn nodes_missing() { "#}); } -// ============================================================================ -// 2. CAPTURES -// ============================================================================ +// Captures #[test] fn captures_basic() { @@ -147,9 +143,7 @@ fn captures_enum_with_type_annotation() { "#}); } -// ============================================================================ -// 3. FIELDS -// ============================================================================ +// Fields #[test] fn fields_single() { @@ -184,9 +178,7 @@ fn fields_alternation() { "#}); } -// ============================================================================ -// 4. QUANTIFIERS -// ============================================================================ +// Quantifiers #[test] fn quantifiers_optional() { @@ -262,9 +254,7 @@ fn quantifiers_sequence_in_called_def() { "#}); } -// ============================================================================ -// 5. SEQUENCES -// ============================================================================ +// Sequences #[test] fn sequences_basic() { @@ -294,9 +284,7 @@ fn sequences_in_quantifier() { "#}); } -// ============================================================================ -// 6. ALTERNATIONS -// ============================================================================ +// Alternations #[test] fn alternations_unlabeled() { @@ -368,9 +356,7 @@ fn alternations_tagged_in_field_constraint() { "#}); } -// ============================================================================ -// 7. ANCHORS -// ============================================================================ +// Anchors #[test] fn anchors_between_siblings() { @@ -407,9 +393,7 @@ fn anchors_no_anchor() { "#}); } -// ============================================================================ -// 8. NAMED EXPRESSIONS -// ============================================================================ +// Named expressions #[test] fn definitions_single() { @@ -442,9 +426,7 @@ fn definitions_nested_capture() { "#}); } -// ============================================================================ -// 9. RECURSION -// ============================================================================ +// Recursion #[test] fn recursion_simple() { @@ -468,9 +450,7 @@ fn recursion_with_structured_result() { "#}); } -// ============================================================================ -// 10. OPTIONALS -// ============================================================================ +// Optionals #[test] fn optional_first_child() { @@ -486,9 +466,7 @@ fn optional_null_injection() { "#}); } -// ============================================================================ -// 11. COMPREHENSIVE -// ============================================================================ +// Comprehensive #[test] fn comprehensive_multi_definition() { diff --git a/crates/plotnik-lib/src/emit/emitter.rs b/crates/plotnik-lib/src/emit/emitter.rs new file mode 100644 index 00000000..b1f6dd46 --- /dev/null +++ b/crates/plotnik-lib/src/emit/emitter.rs @@ -0,0 +1,291 @@ +//! Core bytecode emission logic. +//! +//! Contains the main entry points for emitting bytecode from compiled queries. + +use indexmap::IndexMap; +use plotnik_core::{Interner, NodeFieldId, NodeTypeId, Symbol}; + +use crate::analyze::symbol_table::SymbolTable; +use crate::analyze::type_check::{TypeContext, TypeId}; +use crate::bytecode::ir::Label; +use crate::bytecode::{ + Entrypoint, FieldSymbol, Header, NodeSymbol, SECTION_ALIGN, TriviaEntry, TypeMetaHeader, +}; +use crate::compile::Compiler; +use crate::query::query::LinkedQuery; + +use super::EmitError; +use super::layout::CacheAligned; +use super::string_table::StringTableBuilder; +use super::type_table::TypeTableBuilder; + +/// Emit bytecode from type context only (no node validation). +pub fn emit( + type_ctx: &TypeContext, + interner: &Interner, + symbol_table: &SymbolTable, +) -> Result, EmitError> { + emit_inner(type_ctx, interner, symbol_table, None, None) +} + +/// Emit bytecode from a LinkedQuery (includes node type/field validation info). +pub fn emit_linked(query: &LinkedQuery) -> Result, EmitError> { + emit_inner( + query.type_context(), + query.interner(), + &query.symbol_table, + Some(query.node_type_ids()), + Some(query.node_field_ids()), + ) +} + +/// Shared bytecode emission logic. +fn emit_inner( + type_ctx: &TypeContext, + interner: &Interner, + symbol_table: &SymbolTable, + node_type_ids: Option<&IndexMap>, + node_field_ids: Option<&IndexMap>, +) -> Result, EmitError> { + let is_linked = node_type_ids.is_some(); + let mut strings = StringTableBuilder::new(); + let mut types = TypeTableBuilder::new(); + types.build(type_ctx, interner, &mut strings)?; + + // Compile transitions (strings are interned here for unlinked mode) + let compile_result = Compiler::compile( + interner, + type_ctx, + symbol_table, + &mut strings, + node_type_ids, + node_field_ids, + ) + .map_err(EmitError::Compile)?; + + // Layout with cache alignment + // Preamble entry FIRST ensures it gets the lowest address (step 0) + let mut entry_labels: Vec