@@ -42,7 +42,7 @@ use std::collections::HashMap;
4242use std:: path:: Path ;
4343
4444use crate :: error:: { Result , ZynPegError } ;
45- use crate :: { ZynGrammar , BuiltinMappings } ;
45+ use crate :: { ZynGrammar , BuiltinMappings , TypeDeclarations } ;
4646
4747// Re-export types from typed_ast for host function implementations
4848pub use zyntax_typed_ast:: {
@@ -88,6 +88,10 @@ pub struct ZpegMetadata {
8888 /// - Operators: "$*" -> "vec_dot" (x * y -> vec_dot(x, y))
8989 #[ serde( default ) ]
9090 pub builtins : BuiltinMappings ,
91+ /// Type declarations for opaque types and function return types
92+ /// Used for proper type tracking during lowering for operator trait dispatch
93+ #[ serde( default ) ]
94+ pub types : TypeDeclarations ,
9195}
9296
9397/// Commands for a single grammar rule
@@ -302,23 +306,42 @@ pub trait AstHostFunctions {
302306 /// Create a function call expression
303307 fn create_call ( & mut self , callee : NodeHandle , args : Vec < NodeHandle > ) -> NodeHandle ;
304308
305- /// Create a function call expression with builtin resolution
309+ /// Create a function call expression with explicit return type
310+ /// Used when we know the return type from @types directive
311+ fn create_call_with_return_type ( & mut self , callee : NodeHandle , args : Vec < NodeHandle > , return_type : Option < & str > ) -> NodeHandle {
312+ // Default implementation ignores return type
313+ self . create_call ( callee, args)
314+ }
315+
316+ /// Create a function call expression with builtin and type resolution
306317 /// If the callee is a simple identifier matching a builtin function, use the runtime symbol instead
318+ /// Also looks up return types from @types directive for proper opaque type tracking
307319 fn create_call_with_builtin_resolution (
308320 & mut self ,
309321 callee : NodeHandle ,
310322 args : Vec < NodeHandle > ,
311323 builtins : & BuiltinMappings ,
324+ types : & TypeDeclarations ,
312325 ) -> NodeHandle {
313326 // Check if callee is an identifier that matches a builtin function
314327 if let Some ( name) = self . get_identifier_name ( callee) {
315328 log:: trace!( "[builtin resolution] callee name='{}', checking {} builtins" , name, builtins. functions. len( ) ) ;
329+
330+ // Look up return type from @types directive
331+ let return_type = types. function_returns . get ( & name) . map ( |s| s. as_str ( ) ) ;
332+ if return_type. is_some ( ) {
333+ log:: trace!( "[builtin resolution] found return type for '{}': {:?}" , name, return_type) ;
334+ }
335+
316336 if let Some ( symbol) = builtins. functions . get ( & name) {
317337 log:: trace!( "[builtin resolution] found builtin '{}' -> '{}'" , name, symbol) ;
318338 // Create a new identifier with the runtime symbol name
319339 let resolved_callee = self . create_identifier ( symbol) ;
320- return self . create_call ( resolved_callee, args) ;
340+ return self . create_call_with_return_type ( resolved_callee, args, return_type ) ;
321341 }
342+
343+ // Not a builtin, but might have return type info
344+ return self . create_call_with_return_type ( callee, args, return_type) ;
322345 } else {
323346 log:: trace!( "[builtin resolution] callee is not an identifier" ) ;
324347 }
@@ -543,6 +566,7 @@ impl ZpegCompiler {
543566 entry_point : grammar. language . entry_point . clone ( ) ,
544567 zpeg_version : env ! ( "CARGO_PKG_VERSION" ) . to_string ( ) ,
545568 builtins : grammar. builtins . clone ( ) ,
569+ types : grammar. types . clone ( ) ,
546570 } ;
547571
548572 // Generate pest grammar
@@ -1483,6 +1507,10 @@ impl AstHostFunctions for TypedAstBuilder {
14831507 }
14841508
14851509 fn create_call ( & mut self , callee : NodeHandle , args : Vec < NodeHandle > ) -> NodeHandle {
1510+ self . create_call_with_return_type ( callee, args, None )
1511+ }
1512+
1513+ fn create_call_with_return_type ( & mut self , callee : NodeHandle , args : Vec < NodeHandle > , return_type : Option < & str > ) -> NodeHandle {
14861514 let span = self . default_span ( ) ;
14871515
14881516 let callee_expr = self . get_expr ( callee)
@@ -1492,7 +1520,35 @@ impl AstHostFunctions for TypedAstBuilder {
14921520 . filter_map ( |h| self . get_expr ( * h) )
14931521 . collect ( ) ;
14941522
1495- let expr = self . inner . call_positional ( callee_expr, arg_exprs, Type :: Primitive ( PrimitiveType :: I32 ) , span) ;
1523+ // Convert return type string to Type
1524+ // Opaque types (starting with $) become Extern types which are pointers at the HIR level
1525+ let ty = match return_type {
1526+ Some ( type_name) if type_name. starts_with ( '$' ) => {
1527+ // This is an opaque type - create an Extern type
1528+ // The type name without $ is used as the extern type name
1529+ log:: trace!( "[create_call] opaque return type: {}" , type_name) ;
1530+ Type :: Extern {
1531+ name : InternedString :: new_global ( type_name) ,
1532+ layout : None ,
1533+ }
1534+ }
1535+ Some ( type_name) => {
1536+ // Regular named type - try to parse it
1537+ log:: trace!( "[create_call] named return type: {}" , type_name) ;
1538+ match type_name {
1539+ "i32" | "I32" => Type :: Primitive ( PrimitiveType :: I32 ) ,
1540+ "i64" | "I64" => Type :: Primitive ( PrimitiveType :: I64 ) ,
1541+ "f32" | "F32" => Type :: Primitive ( PrimitiveType :: F32 ) ,
1542+ "f64" | "F64" => Type :: Primitive ( PrimitiveType :: F64 ) ,
1543+ "bool" | "Bool" => Type :: Primitive ( PrimitiveType :: Bool ) ,
1544+ "void" | "Void" | "()" => Type :: Primitive ( PrimitiveType :: Unit ) ,
1545+ _ => Type :: Primitive ( PrimitiveType :: I32 ) , // Default for unknown types
1546+ }
1547+ }
1548+ None => Type :: Primitive ( PrimitiveType :: I32 ) , // Default when no return type specified
1549+ } ;
1550+
1551+ let expr = self . inner . call_positional ( callee_expr, arg_exprs, ty, span) ;
14961552 self . store_expr ( expr)
14971553 }
14981554
@@ -3066,6 +3122,7 @@ impl<'a, H: AstHostFunctions> CommandInterpreter<'a, H> {
30663122 base_h. clone ( ) ,
30673123 call_args,
30683124 & self . module . metadata . builtins ,
3125+ & self . module . metadata . types ,
30693126 ) ;
30703127 result = RuntimeValue :: Node ( new_node) ;
30713128 } else if op_info. starts_with ( "field:" ) {
@@ -3983,6 +4040,7 @@ impl<'a, H: AstHostFunctions> CommandInterpreter<'a, H> {
39834040 callee,
39844041 call_args,
39854042 & self . module . metadata . builtins ,
4043+ & self . module . metadata . types ,
39864044 ) ;
39874045 Ok ( RuntimeValue :: Node ( handle) )
39884046 }
0 commit comments