mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-06-26 20:39:02 +08:00
Remove conditional operator registration (op_exists_for_base_type check) since users could add underlying operators later, and the runtime check was expensive. Operators that fail on the underlying type now error at call time instead of being absent. Add compound assignment operators (*=, +=, -=, /=, %=, <<=, >>=, &=, |=, ^=) via Strong_Typedef_Compound_Assign_Op which computes the base operation and stores the result back in __value. Requested by @lefticus in PR #680 review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
201 lines
4.8 KiB
ChaiScript
201 lines
4.8 KiB
ChaiScript
// Strong typedef: using Type = int creates a distinct type
|
|
using Meters = int
|
|
|
|
def measure(Meters m) {
|
|
return m
|
|
}
|
|
|
|
// Constructing a strong typedef value should work
|
|
var m = Meters(42)
|
|
|
|
// Calling with the typedef'd value should succeed
|
|
measure(m)
|
|
|
|
// Calling with a plain int should fail (strong typedef)
|
|
try {
|
|
measure(42)
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: type mismatch because int is not Meters
|
|
}
|
|
|
|
// Multiple strong typedefs from the same base type should be distinct
|
|
using Seconds = int
|
|
|
|
def wait(Seconds s) {
|
|
return s
|
|
}
|
|
|
|
var s = Seconds(10)
|
|
wait(s)
|
|
|
|
// Meters and Seconds should not be interchangeable
|
|
try {
|
|
wait(m)
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: Meters is not Seconds
|
|
}
|
|
|
|
try {
|
|
measure(s)
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: Seconds is not Meters
|
|
}
|
|
|
|
// to_underlying should return the base value
|
|
assert_equal(to_underlying(m), 42)
|
|
assert_equal(to_underlying(s), 10)
|
|
|
|
// to_underlying result should be a plain value, not a strong typedef
|
|
def takes_int(int i) {
|
|
return i
|
|
}
|
|
assert_equal(takes_int(to_underlying(m)), 42)
|
|
|
|
// --- Arithmetic operators: strongly typed ---
|
|
var m2 = Meters(8)
|
|
var m_sum = m + m2
|
|
assert_equal(to_underlying(m_sum), 50)
|
|
measure(m_sum)
|
|
|
|
var m_diff = m - m2
|
|
assert_equal(to_underlying(m_diff), 34)
|
|
|
|
var m_prod = Meters(3) * Meters(4)
|
|
assert_equal(to_underlying(m_prod), 12)
|
|
|
|
var m_quot = Meters(20) / Meters(5)
|
|
assert_equal(to_underlying(m_quot), 4)
|
|
|
|
var m_rem = Meters(17) % Meters(5)
|
|
assert_equal(to_underlying(m_rem), 2)
|
|
|
|
// Arithmetic result is strongly typed, not plain int
|
|
try {
|
|
takes_int(m_sum)
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: m_sum is Meters, not int
|
|
}
|
|
|
|
// --- Comparison operators ---
|
|
assert_equal(Meters(5) == Meters(5), true)
|
|
assert_equal(Meters(5) != Meters(3), true)
|
|
assert_equal(Meters(3) < Meters(5), true)
|
|
assert_equal(Meters(5) > Meters(3), true)
|
|
assert_equal(Meters(5) <= Meters(5), true)
|
|
assert_equal(Meters(3) >= Meters(3), true)
|
|
assert_equal(Meters(3) >= Meters(5), false)
|
|
|
|
// --- Bitwise and shift operators ---
|
|
assert_equal(to_underlying(Meters(6) & Meters(3)), 2)
|
|
assert_equal(to_underlying(Meters(6) | Meters(3)), 7)
|
|
assert_equal(to_underlying(Meters(6) ^ Meters(3)), 5)
|
|
assert_equal(to_underlying(Meters(5) << Meters(2)), 20)
|
|
assert_equal(to_underlying(Meters(12) >> Meters(1)), 6)
|
|
|
|
// Bitwise results are strongly typed
|
|
try {
|
|
takes_int(Meters(6) & Meters(3))
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: result is Meters, not int
|
|
}
|
|
|
|
// --- Strong typedef over string ---
|
|
using StrongString = string
|
|
|
|
var ss1 = StrongString("hello")
|
|
var ss2 = StrongString(" world")
|
|
var ss_cat = ss1 + ss2
|
|
assert_equal(to_underlying(ss_cat), "hello world")
|
|
|
|
// StrongString + StrongString -> StrongString (strongly typed)
|
|
def takes_strong_string(StrongString ss) {
|
|
return ss
|
|
}
|
|
takes_strong_string(ss_cat)
|
|
|
|
// Operators not supported by the underlying type error at call time
|
|
try {
|
|
var bad = ss1 * ss2
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: underlying string has no * operator
|
|
}
|
|
try {
|
|
var bad = ss1 - ss2
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: underlying string has no - operator
|
|
}
|
|
try {
|
|
var bad = ss1 / ss2
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: underlying string has no / operator
|
|
}
|
|
try {
|
|
var bad = ss1 % ss2
|
|
assert_equal(true, false)
|
|
} catch(e) {
|
|
// Expected: underlying string has no % operator
|
|
}
|
|
|
|
// Comparison on StrongString
|
|
assert_equal(StrongString("abc") < StrongString("def"), true)
|
|
assert_equal(StrongString("abc") == StrongString("abc"), true)
|
|
assert_equal(StrongString("abc") != StrongString("def"), true)
|
|
assert_equal(StrongString("def") > StrongString("abc"), true)
|
|
assert_equal(StrongString("abc") <= StrongString("abc"), true)
|
|
assert_equal(StrongString("def") >= StrongString("abc"), true)
|
|
|
|
// --- User-defined extensions on strong typedefs ---
|
|
def first_char(StrongString ss) {
|
|
return to_string(to_underlying(ss)[0])
|
|
}
|
|
assert_equal(first_char(StrongString("hello")), "h")
|
|
|
|
def double_meters(Meters m) {
|
|
return Meters(to_underlying(m) * 2)
|
|
}
|
|
assert_equal(to_underlying(double_meters(Meters(21))), 42)
|
|
|
|
// User-defined operator extension
|
|
def `[]`(StrongString ss, int offset) {
|
|
return to_string(to_underlying(ss)[offset])
|
|
}
|
|
assert_equal(StrongString("hello")[1], "e")
|
|
|
|
// --- Compound assignment operators ---
|
|
var m3 = Meters(10)
|
|
m3 += Meters(5)
|
|
assert_equal(to_underlying(m3), 15)
|
|
measure(m3)
|
|
|
|
m3 -= Meters(3)
|
|
assert_equal(to_underlying(m3), 12)
|
|
|
|
m3 *= Meters(2)
|
|
assert_equal(to_underlying(m3), 24)
|
|
|
|
m3 /= Meters(4)
|
|
assert_equal(to_underlying(m3), 6)
|
|
|
|
m3 %= Meters(4)
|
|
assert_equal(to_underlying(m3), 2)
|
|
|
|
// Compound assignment result is still the strong typedef
|
|
var m4 = Meters(10)
|
|
m4 += Meters(5)
|
|
assert_equal(to_underlying(m4), 15)
|
|
measure(m4)
|
|
|
|
// Compound assignment on StrongString
|
|
var ss3 = StrongString("hello")
|
|
ss3 += StrongString(" world")
|
|
assert_equal(to_underlying(ss3), "hello world")
|
|
takes_strong_string(ss3)
|