From 3a188082836a07183705525df5350e2fc6488c95 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 17 Mar 2026 20:50:51 +0530 Subject: [PATCH] fix(arrays): add length/push methods --- CHANGELOG.md | 5 +++++ compiler/v1/src/interpreter/executor.rs | 13 +++++++++++++ compiler/v1/src/tests/array_methods.rey | 16 +++++++++++++++ compiler/v1/src/typecheck.rs | 26 ++++++++++++++++++++++++- primer.md | 1 + 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 compiler/v1/src/tests/array_methods.rey diff --git a/CHANGELOG.md b/CHANGELOG.md index 548ad2c..7622e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [fix] — 2026-03-17 +- Added array methods: `.length()` and `.push()` +- Allowed typed empty array assignment like `var xs: [int] = []` +- Added `compiler/v1/src/tests/array_methods.rey` regression test + ## [init] — 2026-03-17 - Claude initialized as contributor - Created CLAUDE.md and primer.md diff --git a/compiler/v1/src/interpreter/executor.rs b/compiler/v1/src/interpreter/executor.rs index bc1e6ab..26c254a 100644 --- a/compiler/v1/src/interpreter/executor.rs +++ b/compiler/v1/src/interpreter/executor.rs @@ -271,6 +271,19 @@ impl Executor { fn evaluate_method_call(&self, receiver: Value, name: &str, args: &[Value]) -> Result { match (receiver, name) { + (Value::Array(arr), "length") => { + if !args.is_empty() { + return Err(format!("{}.length() expects 0 arguments, got {}", "Array", args.len())); + } + Ok(Value::Number(arr.borrow().len() as f64)) + } + (Value::Array(arr), "push") => { + if args.len() != 1 { + return Err(format!("{}.push() expects 1 argument, got {}", "Array", args.len())); + } + arr.borrow_mut().push(args[0].clone()); + Ok(Value::Null) + } (Value::String(s), "length") => { if !args.is_empty() { return Err(format!("{}.length() expects 0 arguments, got {}", "String", args.len())); diff --git a/compiler/v1/src/tests/array_methods.rey b/compiler/v1/src/tests/array_methods.rey new file mode 100644 index 0000000..90032ad --- /dev/null +++ b/compiler/v1/src/tests/array_methods.rey @@ -0,0 +1,16 @@ +// Array method support + +func main(): Void { + var ints: [int] = [1, 2, 3]; + println(ints.length()); + + var any = []; + any.push(1); + any.push("a"); + println(any.length()); + + var result: [int] = []; + result.push(1); + result.push(2); + println(result.length()); +} diff --git a/compiler/v1/src/typecheck.rs b/compiler/v1/src/typecheck.rs index 0ecf54e..6a44389 100644 --- a/compiler/v1/src/typecheck.rs +++ b/compiler/v1/src/typecheck.rs @@ -172,7 +172,12 @@ impl TypeChecker { let initTy = self.exprTy(initializer)?; let finalTy = if let Some(ann) = ty { let annTy = Ty::fromAnnotation(ann)?; - if !initTy.isAssignableTo(&annTy) { + let allowEmptyTypedArray = matches!( + (initializer, &initTy, &annTy), + (Expr::ArrayLiteral { elements }, Ty::Array(inner), Ty::Array(_)) + if elements.is_empty() && matches!(inner.as_ref(), Ty::Any) + ); + if !initTy.isAssignableTo(&annTy) && !allowEmptyTypedArray { return Err(format!( "Type error: variable '{}' expected {:?} but got {:?}", name, annTy, initTy @@ -393,6 +398,25 @@ impl TypeChecker { fn methodTy(&mut self, receiver: &Ty, name: &str, args: &[Expr]) -> Result { match (receiver, name) { + (Ty::Array(_), "length") => { + if !args.is_empty() { + return Err("Type error: Array.length() expects 0 arguments".to_string()); + } + Ok(Ty::Int) + } + (Ty::Array(inner), "push") => { + if args.len() != 1 { + return Err("Type error: Array.push() expects 1 argument".to_string()); + } + let a0 = self.exprTy(&args[0])?; + if !a0.isAssignableTo(inner.as_ref()) { + return Err(format!( + "Type error: Array.push() expected element {:?} but got {:?}", + inner, a0 + )); + } + Ok(Ty::Void) + } (Ty::String, "length") | (Ty::String, "upper") | (Ty::String, "lower") => { if !args.is_empty() { return Err(format!("Type error: String.{}() expects 0 arguments", name)); diff --git a/primer.md b/primer.md index eda4a13..805a737 100644 --- a/primer.md +++ b/primer.md @@ -32,6 +32,7 @@ cargo run -- src/tests/variables.rey - Functions with optional typed params and return types - Builtins: println(), len(), push(), pop(), input() - Arrays: literals, indexing, typed arrays ([int]) +- Array methods: length(), push() - Dictionaries: literals, indexing, typed dicts ({String:int}) - String methods: length/upper/lower/contains/split - Property access: obj.prop (dictionary key lookup)