From 28894373f7a533a6c07cbf80d9050b47247a7d6d Mon Sep 17 00:00:00 2001 From: leftibot Date: Thu, 9 Apr 2026 22:15:47 -0600 Subject: [PATCH 1/2] Fix #591: Add comprehensive class definition docs to cheatsheet The cheatsheet's "ChaiScript Defined Types" section was minimal, showing only a basic class with one attribute and a getter. Expanded it to document block syntax, open syntax, attribute keywords (var/attr/auto), constructor and method guards, operator overloading, cloning, and added a test exercising all examples. Co-Authored-By: Claude Opus 4.6 (1M context) --- cheatsheet.md | 105 +++++++++++++++++++++++++++----- unittests/class_cheatsheet.chai | 103 +++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 unittests/class_cheatsheet.chai diff --git a/cheatsheet.md b/cheatsheet.md index 5835c36b..8c7d224c 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -477,34 +477,109 @@ n(2); // returns 20 -## ChaiScript Defined Types +## ChaiScript Defined Types (Classes) -Define a type called "MyType" with one member value "a" and a getter +ChaiScript supports user-defined types using the `class` keyword. Classes can have attributes, +constructors, methods, guards, and operator overloads. There is no inheritance between +ChaiScript-defined types, but C++ class hierarchies can be exposed (see *Class Hierarchies* above). -### Preferred +### Class Definition (Block Syntax) + +Define a type with attributes, a constructor, and methods inside a `class` block. +The keywords `var`, `attr`, and `auto` are interchangeable for declaring attributes. ``` -class MyType { - var value; - def MyType() { this.value = "a"; } - def get_value() { "Value Is: " + this.value; } -}; +class Rectangle { + var width + var height + def Rectangle(w, h) { this.width = w; this.height = h; } + def Rectangle() { this.width = 0; this.height = 0; } + def area() { this.width * this.height; } +} + +var r = Rectangle(3, 4) +print(r.area()) // prints 12 ``` -### Alternative +### Class Definition (Open Syntax) + +Equivalently, attributes and methods can be defined outside a block using the `TypeName::` prefix. ``` -attr MyType::value; -def MyType::MyType() { this.value = "a"; } -def MyType::get_value() { "Value Is: " + this.value; } +attr Circle::radius +def Circle::Circle(r) { this.radius = r; } +def Circle::circumference() { 2.0 * 3.14159 * this.radius; } +``` + +Methods can also be added to an existing class after its initial definition: + +``` +def Rectangle::perimeter() { 2 * (this.width + this.height); } ``` ### Using ``` -var m = MyType(); // calls constructor -print(m.get_value()); // prints "Value Is: a" -print(get_value(m)); // prints "Value Is: a" +var m = Rectangle(5, 10) +print(m.area()) // prints 50 — method call syntax +print(area(m)) // prints 50 — function call syntax (equivalent) +``` + +### Constructor and Method Guards + +Constructors and methods can have guard expressions (after `:`) that control which +overload is selected at call time. + +``` +class Clamped { + var value + def Clamped(x) : x >= 0 { this.value = x; } + def Clamped(x) { this.value = 0; } // fallback when guard fails +} + +Clamped(5).value // 5 +Clamped(-3).value // 0 + +class Abs { + var x + def Abs(v) { this.x = v; } + def get() : this.x >= 0 { this.x; } + def get() { -this.x; } +} +``` + +### Operator Overloading + +Operators can be overloaded on user-defined types using backtick-quoted operator names. + +``` +class Vec2 { + var x + var y + def Vec2(x, y) { this.x = x; this.y = y; } + def `+`(other) { Vec2(this.x + other.x, this.y + other.y); } +} + +var v = Vec2(1, 2) + Vec2(3, 4) // v.x == 4, v.y == 6 +``` + +Operators can also be overloaded as free functions with guards: + +``` +def `-`(a, b) : is_type(a, "Vec2") && is_type(b, "Vec2") { + Vec2(a.x - b.x, a.y - b.y) +} +``` + +### Cloning Objects + +Use `clone()` to create a deep copy of a ChaiScript-defined object. + +``` +var original = Rectangle(10, 20) +var copy = clone(original) +copy.width = 99 +print(original.width) // still 10 ``` ## Dynamic Objects diff --git a/unittests/class_cheatsheet.chai b/unittests/class_cheatsheet.chai new file mode 100644 index 00000000..2a6d92e1 --- /dev/null +++ b/unittests/class_cheatsheet.chai @@ -0,0 +1,103 @@ +// Test for class features documented in the cheatsheet (issue #591) + +// --- Class definition (preferred block syntax) --- +class Rectangle { + var width + var height + def Rectangle(w, h) { this.width = w; this.height = h; } + def Rectangle() { this.width = 0; this.height = 0; } + def area() { this.width * this.height; } +} + +auto r = Rectangle(3, 4) +assert_equal(12, r.area()) + +// --- Default constructor --- +auto r2 = Rectangle() +assert_equal(0, r2.area()) + +// --- Alternative open syntax --- +attr Circle::radius +def Circle::Circle(r) { this.radius = r; } +def Circle::circumference() { 2.0 * 3.14159 * this.radius; } + +auto c = Circle(5.0) +assert_equal(5.0, c.radius) + +// --- attr, auto, var all work for attributes --- +class AttrTest { + attr a + auto b + var c + def AttrTest() { this.a = 1; this.b = 2; this.c = 3; } +} + +auto at = AttrTest() +assert_equal(1, at.a) +assert_equal(2, at.b) +assert_equal(3, at.c) + +// --- Constructor guards --- +class Clamped { + var value + def Clamped(x) : x >= 0 { this.value = x; } + def Clamped(x) { this.value = 0; } +} + +assert_equal(5, Clamped(5).value) +assert_equal(0, Clamped(-3).value) + +// --- Method guards --- +class Abs { + var x + def Abs(v) { this.x = v; } + def get() : this.x >= 0 { this.x; } + def get() { -this.x; } +} + +assert_equal(5, Abs(5).get()) +assert_equal(3, Abs(-3).get()) + +// --- Operator overloading --- +class Vec2 { + var x + var y + def Vec2(x, y) { this.x = x; this.y = y; } + def `+`(other) { Vec2(this.x + other.x, this.y + other.y); } +} + +auto v = Vec2(1, 2) + Vec2(3, 4) +assert_equal(4, v.x) +assert_equal(6, v.y) + +// --- Cloning objects --- +auto r3 = Rectangle(10, 20) +auto r4 = clone(r3) +r4.width = 99 +assert_equal(10, r3.width) +assert_equal(99, r4.width) + +// --- Dynamic attributes --- +auto r5 = Rectangle(1, 1) +r5.color = "red" +assert_equal("red", r5.color) + +// --- Explicit mode --- +class Strict { + var x + def Strict() { + this.x = 0 + this.set_explicit(true) + } +} + +auto s = Strict() +assert_equal(0, s.x) +assert_equal(true, s.is_explicit()) + +try { + s.y = 10 + assert_equal(true, false) // should not reach here +} catch(e) { + // expected: cannot add dynamic attribute in explicit mode +} From 25c520ed81ae51d971c782ff4490abcf4f59b81f Mon Sep 17 00:00:00 2001 From: leftibot Date: Fri, 10 Apr 2026 11:31:59 -0600 Subject: [PATCH 2/2] Address review: remove issue number references from code Requested by @lefticus in PR #639 review. Co-Authored-By: Claude Opus 4.6 (1M context) --- unittests/class_cheatsheet.chai | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/class_cheatsheet.chai b/unittests/class_cheatsheet.chai index 2a6d92e1..476f1c49 100644 --- a/unittests/class_cheatsheet.chai +++ b/unittests/class_cheatsheet.chai @@ -1,4 +1,4 @@ -// Test for class features documented in the cheatsheet (issue #591) +// Test for class features documented in the cheatsheet // --- Class definition (preferred block syntax) --- class Rectangle {