Skip to content

Commit d41b5b2

Browse files
committed
feat: add type annotation support for function parameters
- Update fn_param grammar rule to support optional type annotations (name: Type) - Add alloc_handle and store_type methods to AstHostFunctions trait - Default to Type::Any for parameters without type annotations (for dynamic typing) - Type inference will resolve actual types based on use-case before Cranelift This enables both dynamic typing (fn add(x, y)) and static typing (fn add(x: Int, y: Int)) in the same grammar, supporting gradual typing in ZynML.
1 parent 956b692 commit d41b5b2

2 files changed

Lines changed: 72 additions & 16 deletions

File tree

crates/zyn_peg/src/runtime.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,12 @@ pub trait AstHostFunctions {
368368
None // Default implementation returns None
369369
}
370370

371+
/// Allocate a new node handle
372+
fn alloc_handle(&mut self) -> NodeHandle;
373+
374+
/// Store a type associated with a handle (for type resolution)
375+
fn store_type(&mut self, handle: NodeHandle, ty: zyntax_typed_ast::type_registry::Type);
376+
371377
/// Create a method call expression
372378
fn create_method_call(&mut self, receiver: NodeHandle, method: &str, args: Vec<NodeHandle>) -> NodeHandle;
373379

@@ -1127,13 +1133,6 @@ impl TypedAstBuilder {
11271133
}
11281134
}
11291135

1130-
/// Allocate a new handle ID
1131-
fn alloc_handle(&mut self) -> NodeHandle {
1132-
let handle = NodeHandle(self.next_id);
1133-
self.next_id += 1;
1134-
handle
1135-
}
1136-
11371136
/// Store an expression and return its handle
11381137
fn store_expr(&mut self, expr: TypedNode<TypedExpression>) -> NodeHandle {
11391138
let handle = self.alloc_handle();
@@ -1293,6 +1292,16 @@ impl AstHostFunctions for TypedAstBuilder {
12931292
.unwrap_or_else(|e| format!(r#"{{"declarations": [], "error": "{}"}}"#, e))
12941293
}
12951294

1295+
fn alloc_handle(&mut self) -> NodeHandle {
1296+
let handle = NodeHandle(self.next_id);
1297+
self.next_id += 1;
1298+
handle
1299+
}
1300+
1301+
fn store_type(&mut self, handle: NodeHandle, ty: zyntax_typed_ast::type_registry::Type) {
1302+
self.types.insert(handle, ty);
1303+
}
1304+
12961305
fn create_function(
12971306
&mut self,
12981307
name: &str,
@@ -4025,9 +4034,15 @@ impl<'a, H: AstHostFunctions> CommandInterpreter<'a, H> {
40254034
}
40264035
_ => "arg".to_string(),
40274036
};
4037+
// If type is not provided, default to Any (dynamic box pointer)
40284038
let ty = match args.get("type") {
40294039
Some(RuntimeValue::Node(h)) => *h,
4030-
_ => self.host.create_primitive_type("i32"),
4040+
_ => {
4041+
// Create a handle for Type::Any
4042+
let handle = self.host.alloc_handle();
4043+
self.host.store_type(handle, zyntax_typed_ast::type_registry::Type::Any);
4044+
handle
4045+
}
40314046
};
40324047
let handle = self.host.create_param(&name, ty);
40334048
Ok(RuntimeValue::Node(handle))

crates/zynml/ml.zyn

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ impl_block = { "impl" ~ identifier ~ type_args_angle? ~ "for" ~ identifier ~ "{"
430430
]
431431
}
432432

433-
impl_item = { impl_assoc_type | impl_method }
433+
impl_item = { impl_assoc_type | impl_method_return | impl_method_params | impl_method_simple }
434434
-> TypedDeclaration {
435435
"get_child": { "index": 0 }
436436
}
@@ -446,9 +446,46 @@ impl_assoc_type = { "type" ~ identifier ~ "=" ~ type_expr }
446446
]
447447
}
448448

449-
impl_method = { fn_def }
449+
// Impl methods can't have their own type params (they use the impl block's)
450+
// So we define specific impl method rules without type params
451+
452+
// fn add(self, rhs: T) -> ReturnType { ... }
453+
impl_method_return = { "fn" ~ identifier ~ "(" ~ fn_params? ~ ")" ~ "->" ~ type_expr ~ block }
450454
-> TypedDeclaration {
451-
"get_child": { "index": 0 }
455+
"commands": [
456+
{ "define": "function", "args": {
457+
"name": "$1",
458+
"params": "$2",
459+
"return_type": "$3",
460+
"body": "$4"
461+
}}
462+
]
463+
}
464+
465+
// fn process(self, other: T) { ... }
466+
impl_method_params = { "fn" ~ identifier ~ "(" ~ fn_params ~ ")" ~ block }
467+
-> TypedDeclaration {
468+
"commands": [
469+
{ "define": "function", "args": {
470+
"name": "$1",
471+
"params": "$2",
472+
"return_type": "void",
473+
"body": "$3"
474+
}}
475+
]
476+
}
477+
478+
// fn simple() { ... }
479+
impl_method_simple = { "fn" ~ identifier ~ "(" ~ ")" ~ block }
480+
-> TypedDeclaration {
481+
"commands": [
482+
{ "define": "function", "args": {
483+
"name": "$1",
484+
"params": [],
485+
"return_type": "void",
486+
"body": "$2"
487+
}}
488+
]
452489
}
453490

454491
// Type arguments in angle brackets (for trait impl)
@@ -580,12 +617,16 @@ fn_params = { fn_param ~ ("," ~ fn_param)* }
580617
"get_all_children": true
581618
}
582619

583-
// Parameter is just identifier for now (dynamic typing)
584-
fn_param = { identifier }
620+
// Parameter with optional type annotation
621+
// Supports: x, x: Int, self, self: Tensor
622+
fn_param = { identifier ~ (":" ~ type_expr)? }
585623
-> TypedParameter {
586-
"get_text": true,
587-
"define": "param",
588-
"args": { "name": "$result", "type": { "define": "primitive_type", "args": { "name": "i64" } } }
624+
"commands": [
625+
{ "define": "param", "args": {
626+
"name": "$1",
627+
"type": "$2"
628+
}}
629+
]
589630
}
590631

591632
// ============================================================================

0 commit comments

Comments
 (0)