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) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-09 22:15:47 -06:00
parent 2eb3279c39
commit 28894373f7
2 changed files with 193 additions and 15 deletions

View File

@ -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

View File

@ -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
}