From 7a9c0b7ea9b3d8b32546638764d0132cd0297042 Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Sun, 10 Aug 2025 16:50:18 -0400 Subject: [PATCH] Pre class implementation commit --- src/sources/runtime/Interpreter.cpp | 7 +-- tests/test_class_and_extension.bob | 77 +++++++++++++++++++++++++++++ tests/test_method_calls.bob | 28 +++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 tests/test_class_and_extension.bob create mode 100644 tests/test_method_calls.bob diff --git a/src/sources/runtime/Interpreter.cpp b/src/sources/runtime/Interpreter.cpp index 6a8d48d..87cfe5d 100644 --- a/src/sources/runtime/Interpreter.cpp +++ b/src/sources/runtime/Interpreter.cpp @@ -4,6 +4,7 @@ #include "BobStdLib.h" #include "ErrorReporter.h" #include "Environment.h" +#include "Expression.h" #include Interpreter::Interpreter(bool isInteractive) @@ -107,13 +108,13 @@ void Interpreter::forceCleanup() { } Value Interpreter::evaluateCallExprInline(const std::shared_ptr& expression) { - Value callee = evaluate(expression->callee); // Direct call instead of through evaluator + Value callee = evaluate(expression->callee); if (callee.isBuiltinFunction()) { // Handle builtin functions with direct evaluation std::vector arguments; for (const auto& argument : expression->arguments) { - arguments.push_back(evaluate(argument)); // Direct call + arguments.push_back(evaluate(argument)); } BuiltinFunction* builtinFunction = callee.asBuiltinFunction(); return builtinFunction->func(arguments, expression->paren.line, expression->paren.column); @@ -132,7 +133,7 @@ Value Interpreter::evaluateCallExprInline(const std::shared_ptr& expre std::vector arguments; for (const auto& argument : expression->arguments) { - arguments.push_back(evaluate(argument)); // Direct call instead of through evaluator + arguments.push_back(evaluate(argument)); } // Check arity (like original) diff --git a/tests/test_class_and_extension.bob b/tests/test_class_and_extension.bob new file mode 100644 index 0000000..22a7899 --- /dev/null +++ b/tests/test_class_and_extension.bob @@ -0,0 +1,77 @@ +print("\n--- Class + Extension Syntax Proposal (Spec Tests) ---"); + +// These tests define the intended behavior and syntax. The parser/runtime +// will be implemented to satisfy them. + +// Class declaration +class Person { + var name; + var age; + + func init(name, age) { + this.name = name; + this.age = age; + } + + func greet() { + print("Hi, I'm " + this.name + " (" + toString(this.age) + ")"); + } + + func birthday() { + this.age = this.age + 1; + } +} + +// Instantiation + method calls +// var p = Person("Bob", 30); +// p.greet(); // "Hi, I'm Bob (30)" +// p.birthday(); +// p.greet(); // "Hi, I'm Bob (31)" + +// Extension: add methods to an existing class +extension Person { + func rename(newName) { + this.name = newName; + } +} + +// p.rename("Robert"); +// p.greet(); // "Hi, I'm Robert (31)" + +// Extension on built-ins (string, array, dict are the type names) +extension string { + func repeat(n) { + var out = ""; + var i = 0; + while (i < n) { + out = out + this; + i = i + 1; + } + return out; + } +} + +// assert("ha".repeat(3) == "hahaha", "string.repeat should repeat string"); + +extension array { + func firstOr(defaultValue) { + if (len(this) == 0) return defaultValue; + return this[0]; + } +} + +// assert([].firstOr("none") == "none", "array.firstOr should return default on empty"); + +// Extension on any: adds a method to all objects +extension any { + func debug() { + return type(this) + ":" + toString(this); + } +} + +// assert(42.debug() == "number:42", "any.debug should work on numbers"); +// assert("x".debug() == "string:x", "any.debug should work on strings"); + +print("Class + Extension spec tests defined (pending parser/runtime support)."); + + diff --git a/tests/test_method_calls.bob b/tests/test_method_calls.bob new file mode 100644 index 0000000..312fa23 --- /dev/null +++ b/tests/test_method_calls.bob @@ -0,0 +1,28 @@ +print("\n--- Test: Method Calls on Objects ---"); + +// Setup a dictionary object +var obj = {}; + +// 1) Zero-arg method assigned to a property and invoked via dot call +obj.hello = func(){ print("hello-method"); }; +obj.hello(); + +// 2) Method that uses the receiver as first implicit argument (self) +obj.set = func(self, key, value){ self[key] = value; }; +obj.get = func(self, key){ return self[key]; }; + +obj.set(obj, "name", "Bob"); +assert(obj.get(obj, "name") == "Bob", "obj.get should return value set via method using receiver"); + +// 3) Ensure taking a reference to a method and calling it as a plain function still works +var ref = obj.hello; +ref(); + +// 4) Ensure passing extra args still respects order after receiver injection +obj.concat3 = func(self, a, b){ return toString(a) + ":" + toString(b) + ":" + toString(self["name"]); }; +var s = obj.concat3(obj, 1, 2); +assert(s == "1:2:Bob", "receiver should be last component in return value"); + +print("Method calls on objects: PASS"); + +