Skip to content

Commit

Permalink
Accept __lt and __le for > and >= operators.
Browse files Browse the repository at this point in the history
As in Lua, "A comparison a > b is translated to b < a and a >= b is translated
to b <= a."
  • Loading branch information
hishamhm committed Jun 14, 2024
1 parent 910375e commit 3dc753c
Show file tree
Hide file tree
Showing 4 changed files with 434 additions and 0 deletions.
205 changes: 205 additions & 0 deletions spec/metamethods/le_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
local util = require("spec.util")

describe("binary metamethod __le using <=", function()
it("can be set on a record", util.check([[
local type Rec = record
x: number
metamethod __call: function(Rec, string, number): string
metamethod __le: function(Rec, Rec): boolean
end
local rec_mt: metatable<Rec>
rec_mt = {
__call = function(self: Rec, s: string, n: number): string
return tostring(self.x + n) .. s
end,
__le = function(a: Rec, b: Rec): boolean
return a.x <= b.x
end
}
local r = setmetatable({ x = 10 } as Rec, rec_mt)
local s = setmetatable({ x = 20 } as Rec, rec_mt)
if r <= s then
print("yes")
end
]]))

it("can be used on a record prototype", util.check([[
local record A
value: number
metamethod __call: function(A, number): A
metamethod __le: function(A, A): boolean
end
local A_mt: metatable<A>
A_mt = {
__call = function(a: A, v: number): A
return setmetatable({value = v} as A, A_mt)
end,
__le = function(a: A, b: A): boolean
return a.value <= b.value
end,
}
A.value = 10
if A <= A then
print("wat!?")
end
]]))

it("can be used via the second argument", util.check([[
local type Rec = record
x: number
metamethod __le: function(number, Rec): Rec
end
local rec_mt: metatable<Rec>
rec_mt = {
__le = function(a: number, b: Rec): boolean
return a <= b.x
end
}
local s = setmetatable({ y = 20 } as Rec, rec_mt)
if 10 <= s then
print("yes")
end
]]))

it("preserves nominal type checking when resolving metamethods for operators", util.check_type_error([[
local type Temperature = record
n: number
metamethod __le: function(t1: Temperature, t2: Temperature): boolean
end
local type Date = record
n: number
metamethod __le: function(t1: Date, t2: Date): boolean
end
local temp2: Temperature = { n = 45 }
local birthday2 : Date = { n = 34 }
setmetatable(temp2, {
__le = function(t1: Temperature, t2: Temperature): boolean
return t1.n <= t2.n
end,
})
setmetatable(birthday2, {
__le = function(t1: Date, t2: Date): boolean
return t1.n <= t2.n
end,
})
if temp2 <= birthday2 then
print("wat")
end
]], {
{ y = 26, msg = "Date is not a Temperature" },
}))
end)

describe("binary metamethod __le using >=", function()
it("can be set on a record", util.check([[
local type Rec = record
x: number
metamethod __call: function(Rec, string, number): string
metamethod __le: function(Rec, Rec): boolean
end
local rec_mt: metatable<Rec>
rec_mt = {
__call = function(self: Rec, s: string, n: number): string
return tostring(self.x + n) .. s
end,
__le = function(a: Rec, b: Rec): boolean
return a.x <= b.x
end
}
local r = setmetatable({ x = 10 } as Rec, rec_mt)
local s = setmetatable({ x = 20 } as Rec, rec_mt)
if s >= r then
print("yes")
end
]]))

it("can be used on a record prototype", util.check([[
local record A
value: number
metamethod __call: function(A, number): A
metamethod __le: function(A, A): boolean
end
local A_mt: metatable<A>
A_mt = {
__call = function(a: A, v: number): A
return setmetatable({value = v} as A, A_mt)
end,
__le = function(a: A, b: A): boolean
return a.value <= b.value
end,
}
A.value = 10
if A >= A then
print("wat!?")
end
]]))

it("can be used via the second argument", util.check([[
local type Rec = record
x: number
metamethod __le: function(number, Rec): Rec
end
local rec_mt: metatable<Rec>
rec_mt = {
__le = function(a: number, b: Rec): boolean
return a <= b.x
end
}
local s = setmetatable({ y = 20 } as Rec, rec_mt)
if s >= 10 then
print("yes")
end
]]))

it("preserves nominal type checking when resolving metamethods for operators", util.check_type_error([[
local type Temperature = record
n: number
metamethod __le: function(t1: Temperature, t2: Temperature): boolean
end
local type Date = record
n: number
metamethod __le: function(t1: Date, t2: Date): boolean
end
local temp2: Temperature = { n = 45 }
local birthday2 : Date = { n = 34 }
setmetatable(temp2, {
__le = function(t1: Temperature, t2: Temperature): boolean
return t1.n <= t2.n
end,
})
setmetatable(birthday2, {
__le = function(t1: Date, t2: Date): boolean
return t1.n <= t2.n
end,
})
if birthday2 >= temp2 then
print("wat")
end
]], {
{ y = 26, msg = "Date is not a Temperature" },
}))
end)
Loading

0 comments on commit 3dc753c

Please sign in to comment.