diff --git a/kilo_editor.nyash b/kilo_editor.nyash new file mode 100644 index 00000000..c5c32d0d --- /dev/null +++ b/kilo_editor.nyash @@ -0,0 +1,237 @@ +// Kilo Text Editor - Memory Management Stress Test +// This tests Box method calls and memory management extensively + +// KiloEditor Box - stores editor state +box KiloEditor { + init { lines, cursor_row, cursor_col, filename, dirty } + + // Initialize empty editor + init_empty() { + me.lines = new ArrayBox() + me.cursor_row = 0 + me.cursor_col = 0 + me.filename = "" + me.dirty = false + + // Start with one empty line + local empty_line + empty_line = new StringBox() + me.lines.push(empty_line) + } + + // Get current line as StringBox + get_current_line() { + return me.lines.get(me.cursor_row) + } + + // Insert character at cursor position + insert_char(ch) { + local current_line + local left_part + local right_part + local new_line + + current_line = me.get_current_line() + + // Split line at cursor position + left_part = current_line.substring(0, me.cursor_col) + right_part = current_line.substring(me.cursor_col, current_line.length()) + + // Create new line with inserted character + new_line = left_part.concat(ch).concat(right_part) + + // Replace line in array + me.lines.set(me.cursor_row, new_line) + + // Move cursor + me.cursor_col = me.cursor_col + 1 + me.dirty = true + } + + // Delete character before cursor + delete_char() { + local current_line + local left_part + local right_part + local new_line + + if me.cursor_col > 0 { + current_line = me.get_current_line() + + // Split line at cursor position + left_part = current_line.substring(0, me.cursor_col - 1) + right_part = current_line.substring(me.cursor_col, current_line.length()) + + // Create new line without deleted character + new_line = left_part.concat(right_part) + + // Replace line in array + me.lines.set(me.cursor_row, new_line) + + // Move cursor back + me.cursor_col = me.cursor_col - 1 + me.dirty = true + } + } + + // Insert new line at cursor + insert_newline() { + local current_line + local left_part + local right_part + + current_line = me.get_current_line() + + // Split current line + left_part = current_line.substring(0, me.cursor_col) + right_part = current_line.substring(me.cursor_col, current_line.length()) + + // Update current line with left part + me.lines.set(me.cursor_row, left_part) + + // Insert new line with right part + me.lines.insert(me.cursor_row + 1, right_part) + + // Move cursor to start of new line + me.cursor_row = me.cursor_row + 1 + me.cursor_col = 0 + me.dirty = true + } + + // Get editor stats for memory testing + get_stats() { + local stats + local total_chars + local line_count + local i + local current_line + + stats = new StringBox() + line_count = me.lines.length() + total_chars = 0 + + // Count total characters across all lines + i = 0 + loop(i < line_count) { + current_line = me.lines.get(i) + total_chars = total_chars + current_line.length() + i = i + 1 + } + + stats = stats.concat("Lines: ").concat(line_count.toString()) + stats = stats.concat(" | Chars: ").concat(total_chars.toString()) + stats = stats.concat(" | Cursor: ").concat(me.cursor_row.toString()) + stats = stats.concat(",").concat(me.cursor_col.toString()) + + return stats + } + + // Render editor content (simplified) + render() { + local line_count + local i + local current_line + local line_display + + print("=== Kilo Editor ===") + + line_count = me.lines.length() + i = 0 + + loop(i < line_count) { + current_line = me.lines.get(i) + + // Create line display with line number + line_display = (i + 1).toString().concat(": ").concat(current_line.toString()) + + // Mark current line with cursor + if i == me.cursor_row { + line_display = line_display.concat(" <-- cursor at col ").concat(me.cursor_col.toString()) + } + + print(line_display) + i = i + 1 + } + + print("---") + print(me.get_stats()) + print("===================") + } +} + +// Test the kilo editor with intensive Box operations +local editor +local test_char +local i + +print("Starting Kilo Editor Memory Test...") + +// Create editor +editor = new KiloEditor() +editor.init_empty() + +print("Initial state:") +editor.render() + +// Test 1: Insert multiple characters (stress StringBox creation) +print("Test 1: Inserting 'Hello World'...") +editor.insert_char("H") +editor.insert_char("e") +editor.insert_char("l") +editor.insert_char("l") +editor.insert_char("o") +editor.insert_char(" ") +editor.insert_char("W") +editor.insert_char("o") +editor.insert_char("r") +editor.insert_char("l") +editor.insert_char("d") + +editor.render() + +// Test 2: Insert new lines (stress ArrayBox operations) +print("Test 2: Adding new lines...") +editor.insert_newline() +editor.insert_char("L") +editor.insert_char("i") +editor.insert_char("n") +editor.insert_char("e") +editor.insert_char(" ") +editor.insert_char("2") + +editor.insert_newline() +editor.insert_char("L") +editor.insert_char("i") +editor.insert_char("n") +editor.insert_char("e") +editor.insert_char(" ") +editor.insert_char("3") + +editor.render() + +// Test 3: Delete operations (memory cleanup test) +print("Test 3: Testing deletions...") +editor.delete_char() +editor.delete_char() +editor.delete_char() + +editor.render() + +// Test 4: Intensive operations for memory stress +print("Test 4: Memory stress test - 50 operations...") +i = 0 +loop(i < 10) { + editor.insert_char("X") + editor.delete_char() + editor.insert_char("Y") + editor.delete_char() + editor.insert_char("Z") + i = i + 1 +} + +editor.render() + +print("Kilo Editor Memory Test Complete!") +local final_stats +final_stats = editor.get_stats() +print(final_stats) \ No newline at end of file diff --git a/kilo_simple_test.nyash b/kilo_simple_test.nyash new file mode 100644 index 00000000..eb5e4bde --- /dev/null +++ b/kilo_simple_test.nyash @@ -0,0 +1,79 @@ +// Simple Kilo Editor Test - Step by step validation +// Test basic Box operations before full editor + +local s1 +local s2 +local s3 +local result + +print("=== StringBox Methods Test ===") + +// Test basic StringBox operations +s1 = "" // Create empty string instead of new StringBox() +print("Created empty StringBox") + +s2 = "Hello" +print("String length test:") +result = s2.length() +print(result) + +print("String substring test:") +result = s2.substring(1, 4) +print(result) + +print("String concat test:") +s3 = " World" +result = s2.concat(s3) +print(result) + +print("=== ArrayBox Methods Test ===") + +local arr +local item +local len + +arr = new ArrayBox() +print("Created empty ArrayBox") + +len = arr.length() +print("Array length:") +print(len) + +print("=== Basic Editor Component Test ===") + +// Test a simple editor component without full complexity +box SimpleEditor { + init { text, cursor } + + init_empty() { + me.text = "" + me.cursor = 0 + } + + append_char(ch) { + me.text = me.text.concat(ch) + me.cursor = me.cursor + 1 + } + + get_text() { + return me.text + } + + get_cursor() { + return me.cursor + } +} + +local editor +editor = new SimpleEditor() +editor.init_empty() + +print("Testing simple editor:") +editor.append_char("H") +editor.append_char("i") + +print("Editor text:") +print(editor.get_text()) + +print("Editor cursor:") +print(editor.get_cursor()) \ No newline at end of file diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 42b07a2a..3a3e608f 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -335,10 +335,36 @@ impl VM { Ok(ControlFlow::Continue) }, - MirInstruction::NewBox { dst, box_type: _, args: _ } => { - // For now, new box creates a placeholder string value - // TODO: Implement proper box creation - self.values.insert(*dst, VMValue::String("NewBox".to_string())); + MirInstruction::NewBox { dst, box_type, args: _ } => { + // Implement basic box creation for common types + let result = match box_type.as_str() { + "StringBox" => { + // Create empty StringBox - in real implementation would use args + let string_box = Box::new(StringBox::new("")); + VMValue::from_nyash_box(string_box) + }, + "ArrayBox" => { + // Create empty ArrayBox - in real implementation would use args + let array_box = Box::new(crate::boxes::array::ArrayBox::new()); + VMValue::from_nyash_box(array_box) + }, + "IntegerBox" => { + // Create IntegerBox with default value + let int_box = Box::new(IntegerBox::new(0)); + VMValue::from_nyash_box(int_box) + }, + "BoolBox" => { + // Create BoolBox with default value + let bool_box = Box::new(BoolBox::new(false)); + VMValue::from_nyash_box(bool_box) + }, + _ => { + // For unknown types, create a placeholder string + VMValue::String(format!("NewBox[{}]", box_type)) + } + }; + + self.values.insert(*dst, result); Ok(ControlFlow::Continue) }, @@ -630,6 +656,39 @@ impl VM { "toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); }, + "substring" => { + // substring(start, end) - simplified implementation + if _args.len() >= 2 { + if let (Some(start_box), Some(end_box)) = (_args.get(0), _args.get(1)) { + if let (Some(start_int), Some(end_int)) = ( + start_box.as_any().downcast_ref::(), + end_box.as_any().downcast_ref::() + ) { + let start = start_int.value.max(0) as usize; + let end = end_int.value.max(0) as usize; + let len = string_box.value.len(); + + if start <= len { + let end_idx = end.min(len); + if start <= end_idx { + let substr = &string_box.value[start..end_idx]; + return Ok(Box::new(StringBox::new(substr))); + } + } + } + } + } + return Ok(Box::new(StringBox::new(""))); // Return empty string on error + }, + "concat" => { + // concat(other) - concatenate with another string + if let Some(other_box) = _args.get(0) { + let other_str = other_box.to_string_box().value; + let result = string_box.value.clone() + &other_str; + return Ok(Box::new(StringBox::new(result))); + } + return Ok(Box::new(StringBox::new(string_box.value.clone()))); + }, _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } } @@ -657,6 +716,46 @@ impl VM { } } + // ArrayBox methods - needed for kilo editor + if let Some(array_box) = box_value.as_any().downcast_ref::() { + match method { + "length" | "len" => { + let items = array_box.items.lock().unwrap(); + return Ok(Box::new(IntegerBox::new(items.len() as i64))); + }, + "get" => { + // get(index) - get element at index + if let Some(index_box) = _args.get(0) { + if let Some(index_int) = index_box.as_any().downcast_ref::() { + let items = array_box.items.lock().unwrap(); + let index = index_int.value as usize; + if index < items.len() { + return Ok(items[index].clone_box()); + } + } + } + return Ok(Box::new(VoidBox::new())); // Return void for out of bounds + }, + "set" => { + // set(index, value) - simplified implementation + // Note: This is a read-only operation in the VM for now + // In a real implementation, we'd need mutable access + return Ok(Box::new(VoidBox::new())); + }, + "push" => { + // push(value) - simplified implementation + // Note: This is a read-only operation in the VM for now + return Ok(Box::new(VoidBox::new())); + }, + "insert" => { + // insert(index, value) - simplified implementation + // Note: This is a read-only operation in the VM for now + return Ok(Box::new(VoidBox::new())); + }, + _ => return Ok(Box::new(VoidBox::new())), // Unsupported method + } + } + // Default: return void for any unrecognized box type or method Ok(Box::new(VoidBox::new())) }