Skip to content

Commit 1228e97

Browse files
committed
feat: integrate ZRTL plugin signatures with type checker
Implement end-to-end plugin signature integration to provide proper type checking for extern function declarations: **Runtime Layer:** - Add plugin_signatures HashMap to ZyntaxRuntime and TieredRuntime - Modify load_plugin() to collect signatures from RuntimeSymbolInfo - Modify load_plugins_from_directory() to collect batch signatures - Add plugin_signatures() getter method **Grammar Layer:** - Add parse_with_signatures() method accepting signature map - Modify inject_builtin_externs() to accept Option<signatures> - Implement type_tag_to_type() for TypeTag -> Type conversion - Create TypedParameter entries from ZrtlSymbolSig data **Integration:** - Wire up parse_with_signatures() in load_module() methods - Thread signatures from runtime through to grammar parsing - Support proper parameter types for extern functions The implementation successfully collects 30+ signatures from loaded plugins and creates extern declarations with typed parameters. Signatures are looked up by symbol name and converted from ZRTL TypeTag format to TypedAST Type format. Current limitation: ZRTL plugins report param_count=0, likely due to plugin compilation or signature reading code needing updates.
1 parent 1cc55cc commit 1228e97

2 files changed

Lines changed: 197 additions & 33 deletions

File tree

crates/zyntax_embed/src/grammar.rs

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,37 @@ impl LanguageGrammar {
277277

278278
// Inject extern function declarations for all builtins from @builtin directive
279279
// This ensures the type checker can find these symbols in scope
280-
self.inject_builtin_externs(&mut program)?;
280+
self.inject_builtin_externs(&mut program, None)?;
281+
282+
Ok(program)
283+
}
284+
285+
/// Parse source code with plugin signatures (for proper extern function declarations)
286+
///
287+
/// # Arguments
288+
/// * `source` - The source code to parse
289+
/// * `filename` - The filename to use for source location (for diagnostics)
290+
/// * `signatures` - Plugin signatures mapping symbol names to ZRTL signatures
291+
///
292+
/// # Returns
293+
/// The parsed TypedProgram ready for lowering to HIR
294+
pub fn parse_with_signatures(
295+
&self,
296+
source: &str,
297+
filename: &str,
298+
signatures: &std::collections::HashMap<String, zyntax_compiler::zrtl::ZrtlSymbolSig>,
299+
) -> GrammarResult<TypedProgram> {
300+
use zyntax_typed_ast::source::SourceFile;
301+
302+
let json = self.parse_to_json_with_filename(source, filename)?;
303+
let mut program: TypedProgram = serde_json::from_str(&json)
304+
.map_err(|e| GrammarError::AstBuildError(format!("Failed to deserialize TypedAST: {}", e)))?;
305+
306+
// Add source file for proper diagnostics
307+
program.source_files = vec![SourceFile::new(filename.to_string(), source.to_string())];
308+
309+
// Inject extern function declarations with signatures
310+
self.inject_builtin_externs(&mut program, Some(signatures))?;
281311

282312
Ok(program)
283313
}
@@ -350,36 +380,78 @@ impl LanguageGrammar {
350380
///
351381
/// This creates TypedDeclaration::Function entries with is_external=true for each
352382
/// builtin function so the type checker can find them in scope.
353-
fn inject_builtin_externs(&self, program: &mut TypedProgram) -> GrammarResult<()> {
383+
///
384+
/// # Arguments
385+
/// * `program` - The TypedProgram to inject declarations into
386+
/// * `signatures` - Optional plugin signatures for proper parameter types
387+
fn inject_builtin_externs(
388+
&self,
389+
program: &mut TypedProgram,
390+
signatures: Option<&std::collections::HashMap<String, zyntax_compiler::zrtl::ZrtlSymbolSig>>,
391+
) -> GrammarResult<()> {
354392
use zyntax_typed_ast::typed_ast::{TypedDeclaration, TypedFunction, TypedParameter};
355393
use zyntax_typed_ast::type_registry::{Type, PrimitiveType};
356394
use zyntax_typed_ast::{typed_node, Span, Visibility, CallingConvention, Mutability, InternedString};
357395

358396
eprintln!("[DEBUG inject_builtin_externs] Called with {} existing declarations", program.declarations.len());
359397
eprintln!("[DEBUG inject_builtin_externs] Builtins.functions has {} entries", self.module.metadata.builtins.functions.len());
398+
if let Some(sigs) = signatures {
399+
eprintln!("[DEBUG inject_builtin_externs] Plugin signatures available: {} entries", sigs.len());
400+
}
360401

361402
let span = Span::new(0, 0); // Synthetic span for injected declarations
362403

363404
// Iterate over all builtins from @builtin directive
364405
for (source_name, target_symbol) in &self.module.metadata.builtins.functions {
365-
// Get return type from @types.function_returns if available, otherwise use Any
366-
// All return types in @types are extern/opaque types
367-
let return_type = self.module.metadata.types.function_returns.get(source_name)
368-
.map(|type_str| {
369-
Type::Extern {
370-
name: InternedString::new_global(type_str),
371-
layout: None, // Layout determined by ZRTL at runtime
372-
}
373-
})
374-
.unwrap_or(Type::Any);
406+
// Get return type from @types.function_returns if available, otherwise use signature or Any
407+
let return_type = if let Some(type_str) = self.module.metadata.types.function_returns.get(source_name) {
408+
// Use type from @types directive
409+
Type::Extern {
410+
name: InternedString::new_global(type_str),
411+
layout: None, // Layout determined by ZRTL at runtime
412+
}
413+
} else if let Some(sigs) = signatures {
414+
// Try to get return type from plugin signature
415+
sigs.get(target_symbol.as_str())
416+
.map(|sig| Self::type_tag_to_type(&sig.return_type))
417+
.unwrap_or(Type::Any)
418+
} else {
419+
Type::Any
420+
};
421+
422+
// Get parameters from signature if available
423+
let params = if let Some(sigs) = signatures {
424+
if let Some(sig) = sigs.get(target_symbol.as_str()) {
425+
eprintln!("[DEBUG inject_builtin_externs] Found signature for {}: param_count={}", target_symbol, sig.param_count);
426+
// Convert ZRTL signature parameters to TypedParameter
427+
use zyntax_typed_ast::typed_ast::ParameterKind;
428+
(0..sig.param_count)
429+
.map(|i| {
430+
let ty = Self::type_tag_to_type(&sig.params[i as usize]);
431+
TypedParameter {
432+
name: InternedString::new_global(&format!("p{}", i)),
433+
ty,
434+
mutability: Mutability::Immutable,
435+
kind: ParameterKind::Regular,
436+
default_value: None,
437+
attributes: vec![],
438+
span: span,
439+
}
440+
})
441+
.collect()
442+
} else {
443+
eprintln!("[DEBUG inject_builtin_externs] No signature found for {}", target_symbol);
444+
vec![] // No signature found - accept anything
445+
}
446+
} else {
447+
vec![] // No signatures provided - accept anything
448+
};
375449

376450
// Create extern function declaration
377-
// We don't declare any parameters - the type checker will allow any arguments
378-
// The actual signature is enforced by ZRTL at runtime
379451
let extern_func = TypedFunction {
380452
name: InternedString::new_global(target_symbol),
381453
type_params: vec![],
382-
params: vec![], // No parameters - accept anything
454+
params,
383455
return_type,
384456
body: None, // Extern functions have no body
385457
visibility: Visibility::Public,
@@ -403,6 +475,55 @@ impl LanguageGrammar {
403475

404476
Ok(())
405477
}
478+
479+
/// Convert ZRTL TypeTag to Type
480+
///
481+
/// Maps ZRTL runtime type tags to compile-time Type enum values
482+
fn type_tag_to_type(tag: &zyntax_compiler::zrtl::TypeTag) -> zyntax_typed_ast::type_registry::Type {
483+
use zyntax_compiler::zrtl::{TypeCategory, PrimitiveSize};
484+
use zyntax_typed_ast::type_registry::{Type, PrimitiveType};
485+
486+
match tag.category() {
487+
TypeCategory::Void => Type::Primitive(PrimitiveType::Unit),
488+
TypeCategory::Bool => Type::Primitive(PrimitiveType::Bool),
489+
TypeCategory::Int => {
490+
// Check size from type_id (PrimitiveSize enum values)
491+
let size = tag.type_id();
492+
match size {
493+
x if x == PrimitiveSize::Bits8 as u16 => Type::Primitive(PrimitiveType::I8),
494+
x if x == PrimitiveSize::Bits16 as u16 => Type::Primitive(PrimitiveType::I16),
495+
x if x == PrimitiveSize::Bits32 as u16 => Type::Primitive(PrimitiveType::I32),
496+
x if x == PrimitiveSize::Bits64 as u16 => Type::Primitive(PrimitiveType::I64),
497+
_ => Type::Primitive(PrimitiveType::I32), // Default to i32
498+
}
499+
}
500+
TypeCategory::UInt => {
501+
let size = tag.type_id();
502+
match size {
503+
x if x == PrimitiveSize::Bits8 as u16 => Type::Primitive(PrimitiveType::U8),
504+
x if x == PrimitiveSize::Bits16 as u16 => Type::Primitive(PrimitiveType::U16),
505+
x if x == PrimitiveSize::Bits32 as u16 => Type::Primitive(PrimitiveType::U32),
506+
x if x == PrimitiveSize::Bits64 as u16 => Type::Primitive(PrimitiveType::U64),
507+
_ => Type::Primitive(PrimitiveType::U32), // Default to u32
508+
}
509+
}
510+
TypeCategory::Float => {
511+
let size = tag.type_id();
512+
match size {
513+
x if x == PrimitiveSize::Bits32 as u16 => Type::Primitive(PrimitiveType::F32),
514+
x if x == PrimitiveSize::Bits64 as u16 => Type::Primitive(PrimitiveType::F64),
515+
_ => Type::Primitive(PrimitiveType::F32), // Default to f32
516+
}
517+
}
518+
TypeCategory::String => Type::Primitive(PrimitiveType::String),
519+
TypeCategory::Opaque => {
520+
// For opaque types, use Any for now
521+
// TODO: Could use Type::Extern with proper name if we had type registry
522+
Type::Any
523+
}
524+
_ => Type::Any, // Fallback for complex types
525+
}
526+
}
406527
}
407528

408529
/// Recursively walk the pest parse tree and execute zpeg commands

crates/zyntax_embed/src/runtime.rs

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,9 @@ pub struct ZyntaxRuntime {
653653
extension_map: HashMap<String, String>,
654654
/// Names of async functions (original name, not _new suffix)
655655
async_functions: std::collections::HashSet<String>,
656+
/// Plugin signatures (symbol name -> ZRTL signature)
657+
/// Collected from loaded plugins for proper extern function type checking
658+
plugin_signatures: HashMap<String, zyntax_compiler::zrtl::ZrtlSymbolSig>,
656659
}
657660

658661
/// An external function that can be called from Zyntax code
@@ -691,6 +694,7 @@ impl ZyntaxRuntime {
691694
grammars: HashMap::new(),
692695
extension_map: HashMap::new(),
693696
async_functions: std::collections::HashSet::new(),
697+
plugin_signatures: HashMap::new(),
694698
})
695699
}
696700

@@ -710,6 +714,7 @@ impl ZyntaxRuntime {
710714
grammars: HashMap::new(),
711715
extension_map: HashMap::new(),
712716
async_functions: std::collections::HashSet::new(),
717+
plugin_signatures: HashMap::new(),
713718
})
714719
}
715720

@@ -1393,12 +1398,18 @@ impl ZyntaxRuntime {
13931398

13941399
let plugin = ZrtlPlugin::load(path).map_err(|e| RuntimeError::Execution(e.to_string()))?;
13951400

1396-
// Register all symbols from the plugin
1397-
for (name, ptr) in plugin.symbols() {
1398-
self.register_function(name, *ptr, 0); // Arity unknown without type info
1401+
// Register all symbols from the plugin as runtime symbols
1402+
// AND collect their signatures for type checking
1403+
for symbol_info in plugin.symbols_with_signatures() {
1404+
self.register_function(symbol_info.name, symbol_info.ptr, 0); // Arity unknown without type info
1405+
1406+
// Store signature if available
1407+
if let Some(sig) = symbol_info.sig {
1408+
self.plugin_signatures.insert(symbol_info.name.to_string(), sig);
1409+
}
13991410
}
14001411

1401-
// Register symbol signatures for auto-boxing support
1412+
// Register symbol signatures for auto-boxing support in backend
14021413
self.backend.register_symbol_signatures(plugin.symbols_with_signatures());
14031414

14041415
// Rebuild the JIT module to include the new symbols
@@ -1421,12 +1432,17 @@ impl ZyntaxRuntime {
14211432
let count = registry.load_directory(&dir)
14221433
.map_err(|e| RuntimeError::Execution(e.to_string()))?;
14231434

1424-
// Register all collected symbols
1425-
for (name, ptr) in registry.collect_symbols() {
1426-
self.register_function(name, ptr, 0);
1435+
// Register all collected symbols AND their signatures
1436+
for symbol_info in registry.collect_symbols_with_signatures() {
1437+
self.register_function(symbol_info.name, symbol_info.ptr, 0);
1438+
1439+
// Store signature if available
1440+
if let Some(sig) = symbol_info.sig {
1441+
self.plugin_signatures.insert(symbol_info.name.to_string(), sig);
1442+
}
14271443
}
14281444

1429-
// Register symbol signatures for auto-boxing support
1445+
// Register symbol signatures for auto-boxing support in backend
14301446
let symbols_with_sigs = registry.collect_symbols_with_signatures();
14311447
self.backend.register_symbol_signatures(&symbols_with_sigs);
14321448

@@ -1733,14 +1749,14 @@ impl ZyntaxRuntime {
17331749
self.languages()
17341750
)))?;
17351751

1736-
// Parse source to TypedAST with filename if provided
1752+
// Parse source to TypedAST with plugin signatures for proper extern declarations
17371753
let typed_program = if let Some(fname) = filename {
17381754
grammar
1739-
.parse_with_filename(source, fname)
1755+
.parse_with_signatures(source, fname, &self.plugin_signatures)
17401756
.map_err(|e| RuntimeError::Execution(e.to_string()))?
17411757
} else {
17421758
grammar
1743-
.parse(source)
1759+
.parse_with_signatures(source, "module.zynml", &self.plugin_signatures)
17441760
.map_err(|e| RuntimeError::Execution(e.to_string()))?
17451761
};
17461762

@@ -1912,6 +1928,9 @@ pub struct TieredRuntime {
19121928
grammars: HashMap<String, Arc<LanguageGrammar>>,
19131929
/// File extension to language mapping (e.g., ".zig" -> "zig")
19141930
extension_map: HashMap<String, String>,
1931+
/// Plugin signatures (symbol name -> ZRTL signature)
1932+
/// Collected from loaded plugins for proper extern function type checking
1933+
plugin_signatures: HashMap<String, zyntax_compiler::zrtl::ZrtlSymbolSig>,
19151934
}
19161935

19171936
impl TieredRuntime {
@@ -1926,6 +1945,7 @@ impl TieredRuntime {
19261945
config,
19271946
grammars: HashMap::new(),
19281947
extension_map: HashMap::new(),
1948+
plugin_signatures: HashMap::new(),
19291949
})
19301950
}
19311951

@@ -2150,8 +2170,14 @@ impl TieredRuntime {
21502170
let plugin = ZrtlPlugin::load(path).map_err(|e| RuntimeError::Execution(e.to_string()))?;
21512171

21522172
// Register all symbols from the plugin as runtime symbols
2153-
for (name, ptr) in plugin.symbols() {
2154-
self.backend.register_runtime_symbol(name, *ptr);
2173+
// AND collect their signatures for type checking
2174+
for symbol_info in plugin.symbols_with_signatures() {
2175+
self.backend.register_runtime_symbol(symbol_info.name, symbol_info.ptr);
2176+
2177+
// Store signature if available
2178+
if let Some(sig) = symbol_info.sig {
2179+
self.plugin_signatures.insert(symbol_info.name.to_string(), sig);
2180+
}
21552181
}
21562182

21572183
Ok(())
@@ -2171,14 +2197,31 @@ impl TieredRuntime {
21712197
let count = registry.load_directory(&dir)
21722198
.map_err(|e| RuntimeError::Execution(e.to_string()))?;
21732199

2174-
// Register all collected symbols
2175-
for (name, ptr) in registry.collect_symbols() {
2176-
self.backend.register_runtime_symbol(name, ptr);
2200+
// Register all collected symbols AND their signatures
2201+
for symbol_info in registry.collect_symbols_with_signatures() {
2202+
self.backend.register_runtime_symbol(symbol_info.name, symbol_info.ptr);
2203+
2204+
// Store signature if available
2205+
if let Some(sig) = symbol_info.sig {
2206+
self.plugin_signatures.insert(symbol_info.name.to_string(), sig);
2207+
}
21772208
}
21782209

21792210
Ok(count)
21802211
}
21812212

2213+
/// Get plugin signatures for all loaded plugins
2214+
///
2215+
/// Returns a reference to the mapping of symbol names to ZRTL signatures.
2216+
/// This can be used during parsing to create properly typed extern function declarations.
2217+
///
2218+
/// # Returns
2219+
///
2220+
/// A HashMap mapping symbol names (e.g., "$IO$println_dynamic") to their ZRTL signatures.
2221+
pub fn plugin_signatures(&self) -> &HashMap<String, zyntax_compiler::zrtl::ZrtlSymbolSig> {
2222+
&self.plugin_signatures
2223+
}
2224+
21822225
// ========================================================================
21832226
// Multi-Language Grammar Registry
21842227
// ========================================================================
@@ -2267,9 +2310,9 @@ impl TieredRuntime {
22672310
self.languages()
22682311
)))?;
22692312

2270-
// Parse source to TypedAST
2313+
// Parse source to TypedAST with plugin signatures for proper extern declarations
22712314
let typed_program = grammar
2272-
.parse(source)
2315+
.parse_with_signatures(source, "module.zynml", &self.plugin_signatures)
22732316
.map_err(|e| RuntimeError::Execution(e.to_string()))?;
22742317

22752318
// Lower to HIR

0 commit comments

Comments
 (0)