diff --git a/AGENTS.md b/AGENTS.md index 0912a246..23f8cd7d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -169,17 +169,24 @@ Tree-sitter: `((a) (b))` — Plotnik: `{(a) (b)}`. The #1 syntax error. ``` crates/ plotnik-cli/ # CLI tool - src/commands/ # Subcommands (debug, exec, langs, types) - plotnik-core/ # Common code + src/commands/ # Subcommands (check, dump, infer, tree, langs) + plotnik-core/ # Common code (Interner, Symbol) plotnik-lib/ # Plotnik as library src/ - diagnostics/ # Diagnostics (user-friendly errors) - parser/ # Syntactic parsing of the query - query/ # Analysis and representation of the parsed query - plotnik-langs/ # Tree-sitter language bindings (wrapped) - plotnik-macros/ # Proc macros of the project + analyze/ # Semantic analysis (symbol_table, dependencies, type_check, validation) + bytecode/ # Binary format definitions + typegen/ # Type declaration extraction (bytecode → .d.ts) + compile/ # Thompson NFA construction (AST → IR) + diagnostics/ # User-friendly error reporting + emit/ # Bytecode emission (IR → binary) + parser/ # Syntactic parsing (lexer, grammar, AST) + query/ # Query facade (Query, QueryBuilder, SourceMap) + type_system/ # Shared type primitives + plotnik-langs/ # Tree-sitter language bindings + plotnik-macros/ # Proc macros docs/ - adr/ # Architecture Decision Records (ADRs) + adr/ # Architecture Decision Records + binary-format/ # Bytecode format specification lang-reference.md # Language specification ``` diff --git a/crates/plotnik-cli/src/commands/infer.rs b/crates/plotnik-cli/src/commands/infer.rs index 8cfe02cc..839201b3 100644 --- a/crates/plotnik-cli/src/commands/infer.rs +++ b/crates/plotnik-cli/src/commands/infer.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use plotnik_lib::QueryBuilder; use plotnik_lib::bytecode::Module; -use plotnik_lib::codegen::typescript; +use plotnik_lib::typegen::typescript; use super::lang_resolver::{resolve_lang, resolve_lang_required, suggest_language}; use super::query_loader::load_query_source; diff --git a/crates/plotnik-lib/src/query/dependencies.rs b/crates/plotnik-lib/src/analyze/dependencies.rs similarity index 99% rename from crates/plotnik-lib/src/query/dependencies.rs rename to crates/plotnik-lib/src/analyze/dependencies.rs index 2ee73529..2437bb67 100644 --- a/crates/plotnik-lib/src/query/dependencies.rs +++ b/crates/plotnik-lib/src/analyze/dependencies.rs @@ -12,16 +12,15 @@ use std::collections::{HashMap, HashSet}; use indexmap::{IndexMap, IndexSet}; use plotnik_core::{Interner, Symbol}; - -use super::source_map::SourceId; use rowan::TextRange; +use super::symbol_table::SymbolTable; +use super::type_check::DefId; +use super::visitor::{Visitor, walk_expr}; use crate::Diagnostics; use crate::diagnostics::DiagnosticKind; use crate::parser::{AnonymousNode, Def, Expr, NamedNode, Ref, Root, SeqExpr}; -use crate::query::symbol_table::SymbolTable; -use crate::query::type_check::DefId; -use crate::query::visitor::{Visitor, walk_expr}; +use crate::query::source_map::SourceId; /// Result of dependency analysis. #[derive(Clone, Debug, Default)] diff --git a/crates/plotnik-lib/src/query/dependencies_tests.rs b/crates/plotnik-lib/src/analyze/dependencies_tests.rs similarity index 100% rename from crates/plotnik-lib/src/query/dependencies_tests.rs rename to crates/plotnik-lib/src/analyze/dependencies_tests.rs diff --git a/crates/plotnik-lib/src/query/invariants.rs b/crates/plotnik-lib/src/analyze/invariants.rs similarity index 100% rename from crates/plotnik-lib/src/query/invariants.rs rename to crates/plotnik-lib/src/analyze/invariants.rs diff --git a/crates/plotnik-lib/src/query/link.rs b/crates/plotnik-lib/src/analyze/link.rs similarity index 99% rename from crates/plotnik-lib/src/query/link.rs rename to crates/plotnik-lib/src/analyze/link.rs index 84107723..820c2eab 100644 --- a/crates/plotnik-lib/src/query/link.rs +++ b/crates/plotnik-lib/src/analyze/link.rs @@ -20,16 +20,15 @@ pub struct LinkOutput { pub node_field_ids: IndexMap, } +use super::symbol_table::SymbolTable; +use super::utils::find_similar; +use super::visitor::{Visitor, walk}; use crate::diagnostics::{DiagnosticKind, Diagnostics}; use crate::parser::ast::{self, Expr, NamedNode}; use crate::parser::cst::{SyntaxKind, SyntaxToken}; use crate::parser::token_src; - -use super::query::AstMap; -use super::source_map::{SourceId, SourceMap}; -use super::symbol_table::SymbolTable; -use super::utils::find_similar; -use super::visitor::{Visitor, walk}; +use crate::query::query::AstMap; +use crate::query::source_map::{SourceId, SourceMap}; /// Link query against a language grammar. /// diff --git a/crates/plotnik-lib/src/query/link_tests.rs b/crates/plotnik-lib/src/analyze/link_tests.rs similarity index 100% rename from crates/plotnik-lib/src/query/link_tests.rs rename to crates/plotnik-lib/src/analyze/link_tests.rs diff --git a/crates/plotnik-lib/src/analyze/mod.rs b/crates/plotnik-lib/src/analyze/mod.rs new file mode 100644 index 00000000..f487cede --- /dev/null +++ b/crates/plotnik-lib/src/analyze/mod.rs @@ -0,0 +1,32 @@ +//! Semantic analysis passes. +//! +//! Provides analysis passes that transform parsed AST into analyzed form: +//! - Name resolution (symbol_table) +//! - Dependency analysis and recursion detection (dependencies) +//! - Type inference (type_check) +//! - Grammar linking (link) +//! - Semantic validation (validation) + +pub mod dependencies; +mod invariants; +pub mod link; +pub mod refs; +pub mod symbol_table; +pub mod type_check; +mod utils; +pub mod validation; +pub mod visitor; + +#[cfg(test)] +mod dependencies_tests; +#[cfg(all(test, feature = "plotnik-langs"))] +mod link_tests; +#[cfg(test)] +mod symbol_table_tests; + +pub use dependencies::DependencyAnalysis; +pub use link::LinkOutput; +pub use symbol_table::{SymbolTable, UNNAMED_DEF}; +pub use type_check::{TypeContext, infer_types, primary_def_name}; +pub use validation::{validate_alt_kinds, validate_anchors}; +pub use visitor::{Visitor, walk_expr}; diff --git a/crates/plotnik-lib/src/query/refs.rs b/crates/plotnik-lib/src/analyze/refs.rs similarity index 100% rename from crates/plotnik-lib/src/query/refs.rs rename to crates/plotnik-lib/src/analyze/refs.rs diff --git a/crates/plotnik-lib/src/query/symbol_table.rs b/crates/plotnik-lib/src/analyze/symbol_table.rs similarity index 99% rename from crates/plotnik-lib/src/query/symbol_table.rs rename to crates/plotnik-lib/src/analyze/symbol_table.rs index 7bd530c5..e83fdc7f 100644 --- a/crates/plotnik-lib/src/query/symbol_table.rs +++ b/crates/plotnik-lib/src/analyze/symbol_table.rs @@ -10,8 +10,8 @@ use crate::Diagnostics; use crate::diagnostics::DiagnosticKind; use crate::parser::{Root, ast, token_src}; -use super::source_map::{SourceId, SourceMap}; use super::visitor::Visitor; +use crate::query::source_map::{SourceId, SourceMap}; /// Sentinel name for unnamed definitions (bare expressions at root level). /// Code generators can emit whatever name they want for this. diff --git a/crates/plotnik-lib/src/query/symbol_table_tests.rs b/crates/plotnik-lib/src/analyze/symbol_table_tests.rs similarity index 100% rename from crates/plotnik-lib/src/query/symbol_table_tests.rs rename to crates/plotnik-lib/src/analyze/symbol_table_tests.rs diff --git a/crates/plotnik-lib/src/query/type_check/context.rs b/crates/plotnik-lib/src/analyze/type_check/context.rs similarity index 99% rename from crates/plotnik-lib/src/query/type_check/context.rs rename to crates/plotnik-lib/src/analyze/type_check/context.rs index 92533ce0..fa5685ea 100644 --- a/crates/plotnik-lib/src/query/type_check/context.rs +++ b/crates/plotnik-lib/src/analyze/type_check/context.rs @@ -233,7 +233,6 @@ mod tests { use std::collections::BTreeMap; use super::*; - use crate::query::type_check::types::FieldInfo; #[test] fn builtin_types_have_correct_ids() { diff --git a/crates/plotnik-lib/src/query/type_check/infer.rs b/crates/plotnik-lib/src/analyze/type_check/infer.rs similarity index 99% rename from crates/plotnik-lib/src/query/type_check/infer.rs rename to crates/plotnik-lib/src/analyze/type_check/infer.rs index ab8954a0..d6baac3a 100644 --- a/crates/plotnik-lib/src/query/type_check/infer.rs +++ b/crates/plotnik-lib/src/analyze/type_check/infer.rs @@ -16,6 +16,8 @@ use super::types::{ }; use super::unify::{UnifyError, unify_flows}; +use crate::analyze::symbol_table::SymbolTable; +use crate::analyze::visitor::{Visitor, walk_alt_expr, walk_def, walk_named_node, walk_seq_expr}; use crate::diagnostics::{DiagnosticKind, Diagnostics}; use crate::parser::ast::{ AltExpr, AltKind, AnonymousNode, CapturedExpr, Def, Expr, FieldExpr, NamedNode, QuantifiedExpr, @@ -23,8 +25,6 @@ use crate::parser::ast::{ }; use crate::parser::cst::SyntaxKind; use crate::query::source_map::SourceId; -use crate::query::symbol_table::SymbolTable; -use crate::query::visitor::{Visitor, walk_alt_expr, walk_def, walk_named_node, walk_seq_expr}; /// Inference context for a single pass over the AST. pub struct InferenceVisitor<'a, 'd> { diff --git a/crates/plotnik-lib/src/query/type_check/mod.rs b/crates/plotnik-lib/src/analyze/type_check/mod.rs similarity index 96% rename from crates/plotnik-lib/src/query/type_check/mod.rs rename to crates/plotnik-lib/src/analyze/type_check/mod.rs index 66932018..5134a751 100644 --- a/crates/plotnik-lib/src/query/type_check/mod.rs +++ b/crates/plotnik-lib/src/analyze/type_check/mod.rs @@ -6,7 +6,7 @@ mod context; mod infer; mod symbol; -mod types; +pub(crate) mod types; mod unify; #[cfg(test)] @@ -24,11 +24,11 @@ use std::collections::BTreeMap; use indexmap::IndexMap; +use crate::analyze::dependencies::DependencyAnalysis; +use crate::analyze::symbol_table::{SymbolTable, UNNAMED_DEF}; use crate::diagnostics::Diagnostics; use crate::parser::ast::Root; -use crate::query::dependencies::DependencyAnalysis; use crate::query::source_map::SourceId; -use crate::query::symbol_table::{SymbolTable, UNNAMED_DEF}; use infer::infer_root; diff --git a/crates/plotnik-lib/src/query/type_check/symbol.rs b/crates/plotnik-lib/src/analyze/type_check/symbol.rs similarity index 100% rename from crates/plotnik-lib/src/query/type_check/symbol.rs rename to crates/plotnik-lib/src/analyze/type_check/symbol.rs diff --git a/crates/plotnik-lib/src/query/type_check/tests.rs b/crates/plotnik-lib/src/analyze/type_check/tests.rs similarity index 100% rename from crates/plotnik-lib/src/query/type_check/tests.rs rename to crates/plotnik-lib/src/analyze/type_check/tests.rs diff --git a/crates/plotnik-lib/src/query/type_check/types.rs b/crates/plotnik-lib/src/analyze/type_check/types.rs similarity index 100% rename from crates/plotnik-lib/src/query/type_check/types.rs rename to crates/plotnik-lib/src/analyze/type_check/types.rs diff --git a/crates/plotnik-lib/src/query/type_check/unify.rs b/crates/plotnik-lib/src/analyze/type_check/unify.rs similarity index 99% rename from crates/plotnik-lib/src/query/type_check/unify.rs rename to crates/plotnik-lib/src/analyze/type_check/unify.rs index f44014b5..444e88ec 100644 --- a/crates/plotnik-lib/src/query/type_check/unify.rs +++ b/crates/plotnik-lib/src/analyze/type_check/unify.rs @@ -143,7 +143,7 @@ fn unify_type_ids(a: TypeId, b: TypeId, field: Symbol) -> Result String { @@ -167,7 +167,7 @@ impl<'q> QueryPrinter<'q> { visited.insert(name.to_string()); if let Some(body) = self.query.symbol_table.get(name) { - let refs_set = super::refs::collect_ref_names(body); + let refs_set = crate::analyze::refs::collect_ref_names(body); let mut refs: Vec<_> = refs_set.iter().map(|s| s.as_str()).collect(); refs.sort(); for r in refs { diff --git a/crates/plotnik-lib/src/query/query.rs b/crates/plotnik-lib/src/query/query.rs index 15c0920c..8d96e981 100644 --- a/crates/plotnik-lib/src/query/query.rs +++ b/crates/plotnik-lib/src/query/query.rs @@ -6,14 +6,13 @@ use plotnik_core::{Interner, NodeFieldId, NodeTypeId, Symbol}; use plotnik_langs::Lang; use crate::Diagnostics; +use crate::analyze::dependencies; +use crate::analyze::link; +use crate::analyze::symbol_table::{SymbolTable, resolve_names}; +use crate::analyze::type_check::{self, Arity, TypeContext}; +use crate::analyze::validation::{validate_alt_kinds, validate_anchors}; use crate::parser::{Parser, Root, SyntaxNode, lexer::lex}; -use crate::query::alt_kinds::validate_alt_kinds; -use crate::query::anchors::validate_anchors; -use crate::query::dependencies; -use crate::query::link; use crate::query::source_map::{SourceId, SourceMap}; -use crate::query::symbol_table::{SymbolTable, resolve_names}; -use crate::query::type_check::{self, Arity, TypeContext}; const DEFAULT_QUERY_PARSE_FUEL: u32 = 1_000_000; const DEFAULT_QUERY_PARSE_MAX_DEPTH: u32 = 4096; @@ -203,11 +202,11 @@ impl QueryAnalyzed { /// Emit bytecode without language linking (no node type/field validation). /// /// Returns `Err(EmitError::InvalidQuery)` if the query has validation errors. - pub fn emit(&self) -> Result, super::codegen::EmitError> { + pub fn emit(&self) -> Result, crate::emit::EmitError> { if !self.is_valid() { - return Err(super::codegen::EmitError::InvalidQuery); + return Err(crate::emit::EmitError::InvalidQuery); } - super::codegen::emit(&self.type_context, &self.interner, &self.symbol_table) + crate::emit::emit(&self.type_context, &self.interner, &self.symbol_table) } pub fn link(mut self, lang: &Lang) -> LinkedQuery { @@ -277,11 +276,11 @@ impl LinkedQuery { /// Emit bytecode with node type/field symbols from language linking. /// /// Returns `Err(EmitError::InvalidQuery)` if the query has validation errors. - pub fn emit(&self) -> Result, super::codegen::EmitError> { + pub fn emit(&self) -> Result, crate::emit::EmitError> { if !self.is_valid() { - return Err(super::codegen::EmitError::InvalidQuery); + return Err(crate::emit::EmitError::InvalidQuery); } - super::codegen::emit_linked(self) + crate::emit::emit_linked(self) } } diff --git a/crates/plotnik-lib/src/query/query_tests.rs b/crates/plotnik-lib/src/query/query_tests.rs index 2593b0df..bea5c1b6 100644 --- a/crates/plotnik-lib/src/query/query_tests.rs +++ b/crates/plotnik-lib/src/query/query_tests.rs @@ -94,7 +94,7 @@ impl QueryAnalyzed { // Emit to bytecode and then emit TypeScript from the bytecode module let bytecode = query.emit().expect("bytecode emission should succeed"); let module = Module::from_bytes(bytecode).expect("module loading should succeed"); - crate::bytecode::emit::emit_typescript(&module) + crate::typegen::typescript::emit(&module) } #[track_caller] diff --git a/crates/plotnik-lib/src/typegen/mod.rs b/crates/plotnik-lib/src/typegen/mod.rs new file mode 100644 index 00000000..5c94f6cf --- /dev/null +++ b/crates/plotnik-lib/src/typegen/mod.rs @@ -0,0 +1,16 @@ +//! Type declaration generation from compiled bytecode. +//! +//! Extracts type metadata from bytecode modules and generates type declarations +//! for target languages. Currently supports TypeScript `.d.ts` generation. +//! +//! # Example +//! +//! ```ignore +//! use plotnik_lib::typegen::typescript; +//! use plotnik_lib::bytecode::Module; +//! +//! let module = Module::from_bytes(bytecode)?; +//! let output = typescript::emit(&module); +//! ``` + +pub mod typescript; diff --git a/crates/plotnik-lib/src/codegen/typescript.rs b/crates/plotnik-lib/src/typegen/typescript.rs similarity index 100% rename from crates/plotnik-lib/src/codegen/typescript.rs rename to crates/plotnik-lib/src/typegen/typescript.rs