xtype (halted)

Check-in [952ea87e40]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Begin Luaoop import as `xtype.class` submodule.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | import-luaoop
Files: files | file ages | folders
SHA3-256: 952ea87e40d6e15fbd3127b797924f7da8a7dfbe03592b2456e65119246c6689
User & Date: imagic 2024-10-05 21:43:26.640
Context
2024-10-05
22:15
Import and adapt the Luaoop documentation. ... (check-in: 71502bba7f user: imagic tags: import-luaoop)
21:43
Begin Luaoop import as `xtype.class` submodule. ... (check-in: 952ea87e40 user: imagic tags: import-luaoop)
21:29
Improve license embedding. ... (check-in: 4eed6cd5ea user: imagic tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Added examples/test_class.lua.




















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package.path = "src/?.lua;"..package.path
local xtype = require("xtype")
local class = require("xtype.class")

local function errcheck(perr, f, ...)
  local ok, err = pcall(f, ...)
  assert(not ok and not not err:find(perr))
end

local function countPairs(t)
  local count = 0
  for _ in pairs(t) do count = count+1 end
  return count
end

do -- test class/instance basics
  local A = class("A")
  local a = A()
  assert(xtype.is(A, class) and xtype.is(A, "xtype"))
  assert(xtype.is(a, A))
  assert((tostring(A):find("class<A>")))
  assert((tostring(a):find("instance<A>")))
end
do -- test constructor/destructor/GC
  local A = class("A")
  local record = {}
  function A:__construct(i) self.i = i; record[i] = true end
  function A:__destruct() record[self.i] = nil end
  local as = {}
  for i=1,3 do as[i] = A(i) end
  assert(countPairs(record) == 3)
  -- GC
  as = nil
  for i=1,2 do collectgarbage("collect") end
  assert(countPairs(record) == 0)
end
do -- test special methods: unary operator binding
  local A = class("A")
  function A:__construct(i) self.i = i end
  function A:__call(a,b) return self.i+a+b end
  function A:__tostring() return "A"..self.i end
  local a = A(5)
  assert(a(1,2) == 8)
  assert(tostring(a) == "A5")
end
do -- test multifunction / binary operator
  local vec2 = class("vec2")
  function vec2:__construct(x, y) self.x, self.y = x, y end
  xtype.op.add:define(function(a, b) return vec2(a.x+b.x, a.y+b.y) end, vec2, vec2)
  xtype.op.eq:define(function(a, b) return a.x == b.x and a.y == b.y end, vec2, vec2)
  assert(vec2(1,0) ~= vec2(0,1))
  assert(vec2(1,0)+vec2(0,1) == vec2(1,1))
end
do -- test inheritance
  -- define
  local A = class("A"); function A:test() return "A" end
  local B = class("B"); function B:test() return "B" end
  local C = class("C", A, B);
  local D = class("D", B, A);
  local E = class("E", A, B); function E:test() return "E" end
  function E:__construct() end
  -- test
  assert(C.test) -- check partial build: class inheritance
  local e = E()
  assert(xtype.of(E, A) and xtype.of(E, B))
  assert(xtype.is(e, A) and xtype.is(e, B))
  assert(E.xtype_name and E.__meta and not e.xtype_name and not e.__meta)
  assert(E.__construct and not e.__construct)
  assert(C():test() == "A"); assert(D():test() == "B")
  assert(e:test() == "E")
end
do -- test class re-build
  local A = class("A"); function A:A() return "A" end
  local B = class("B"); function B:B() return "B" end
  local C = class("C", A, B); function C:C() return self:A()..self:B() end
  local c = C()
  assert(c:C() == "AB")
  function A:A() return "A'" end
  function B:B() return "B'" end
  class.build(A); class.build(B); class.build(C)
  assert(c:C() == "A'B'")
end
do -- test class meta (rudimentary)
  local A = class("A")
  local a = setmetatable({}, class.meta(A))
  assert(xtype.is(a, A))
end
do -- test errors
  errcheck("class expected", class.build)
end
Added src/xtype/class.lua.




























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
-- MIT License (see LICENSE or src/xtype.lua)
-- Copyright (c) 2017 ImagicTheCat

local xtype = require "xtype"

local lua51 = string.find(_VERSION, "5.1")
local getmetatable, setmetatable, newproxy = getmetatable, setmetatable, newproxy

local function error_arg(index, expected)
  error("bad argument #"..index.." ("..expected.." expected)")
end

-- class type
local class_mt = {xtype = "xtype"}
local class = setmetatable(xtype.create("class", "xtype"), class_mt)

local function check_class(v, index)
  if not xtype.is(v, class) then error_arg(index, "class") end
end

local function instance_tostring(self)
  local xt = xtype.get(self)
  local mt = getmetatable(self)
  mt.__tostring = nil
  local str = string.gsub(tostring(self), "table:", "instance<"..xtype.name(xt)..">:", 1)
  mt.__tostring = instance_tostring
  return str
end

-- Build the class inheritance (not the instance inheritance).
local function class_build_inheritance(classdef)
  local build = {}
  for i=#classdef.xtype_stack,1,-1 do -- least specific, descending order
    local base = classdef.xtype_stack[i]
    if xtype.is(base, class) then
      -- inherit base class fields
      for k,v in pairs(base) do
        if not k:find("^xtype_") then build[k] = v end
      end
    end
  end
  return build
end

-- Build/re-build the class (class and instance inheritance).
-- Will add the `__meta` field to the class.
--
-- classdef: class
local function class_build(classdef)
  check_class(classdef, 1)
  -- build
  --- inheritance
  ---- class build
  local build = class_build_inheritance(classdef)
  ---- instance build
  local instance_build = {}
  for k,v in pairs(build) do -- inherit class build, everything but special fields
    if not k:find("^__") then instance_build[k] = v end
  end
  for k,v in pairs(classdef) do -- inherit class, everything but special fields
    if not k:find("^xtype_") and not k:find("^__") then
      instance_build[k] = v
    end
  end
  --- setup class inheritance
  getmetatable(classdef).__index = build
  --- init instance metatable
  if not classdef.__meta then
    classdef.__meta = {
      xtype = classdef,
      -- binary operators
      __add = xtype.op.add,
      __sub = xtype.op.sub,
      __mul = xtype.op.mul,
      __div = xtype.op.div,
      __mod = xtype.op.mod,
      __pow = xtype.op.pow,
      __concat = xtype.op.concat,
      __eq = xtype.op.eq,
      __lt = xtype.op.lt,
      __le = xtype.op.le,
      __idiv = xtype.op.idiv,
      __band = xtype.op.band,
      __bor = xtype.op.bor,
      __bxor = xtype.op.bxor,
      __shl = xtype.op.shl,
      __shr = xtype.op.shr
    }
  end
  --- update instance metatable
  classdef.__meta.__index = instance_build
  classdef.__meta.__call = classdef.__call
  classdef.__meta.__gc = classdef.__destruct
  classdef.__meta.__tostring = classdef.__tostring or instance_tostring
  classdef.__meta.__unm = classdef.__unm
  classdef.__meta.__len = classdef.__len
  classdef.__meta.__bnot = classdef.__bnot
end

-- Build the class if not already built.
-- return instance metatable
local function class_prebuild(classdef)
  if not classdef.__meta then class_build(classdef) end
  return classdef.__meta
end

local function proxy_gc(self)
  local mt = getmetatable(self)
  mt.destructor(mt.instance)
end

-- Create instance.
-- Will build the class if not already built.
--
-- classdef: class
-- ...: constructor arguments
-- return created instance
local function class_instantiate(classdef, ...)
  local meta = class_prebuild(classdef)
  -- create instance
  local instance = setmetatable({}, meta)
  -- setup destructor (Lua 5.1)
  if lua51 then
    local destructor = classdef.__destruct
    if destructor then
      local proxy = newproxy(true)
      local mt = getmetatable(proxy)
      mt.__gc = proxy_gc
      mt.destructor = destructor
      mt.instance = instance
      instance.__proxy_gc = proxy
    end
  end
  -- construct
  local constructor = classdef.__construct
  if constructor then constructor(instance, ...) end
  return instance
end

-- Create a new class.
-- Base types can be classes or other xtypes.
--
-- name: human-readable string (doesn't have to be unique)
-- ...: base types, ordered by descending proximity, to the least specific type
-- return created class (an xtype)
local function class_new(name, ...)
  local xt = xtype.create(name, ...)
  -- default print "class<type>: 0x..."
  local tostring_const = string.gsub(tostring(xt), "xtype", "class", 1)
  return setmetatable(xt, {
    xtype = class,
    __index = class_build_inheritance(xt),
    __call = class_instantiate,
    __tostring = function() return tostring_const end
  })
end

-- Get the class metatable applied to the instances.
-- Will build the class if not already built; useful to apply class behaviour
-- to a custom table.
--
-- classdef: class
-- return metatable
local function class_meta(classdef)
  return class_prebuild(classdef)
end

class.new = class_new
class.meta = class_meta
class.instantiate = class_instantiate
class.build = class_build
class_mt.__call = function(t, ...) return class_new(...) end

return class
Changes to test.lua.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
#!/usr/bin/env luajit
-- or Lua 5.1

-- config
local envs = {"luajit", "lua5.1", "lua5.2", "lua5.3", "lua5.4"}
local tests = {
  "examples/test_main.lua",
  "examples/test_multifunction.lua",
  "examples/test_cdata.lua",
  "examples/test_error.lua"

}
-- test
local errors = 0
for _, env in ipairs(envs) do
  for _, test in ipairs(tests) do
    local status = os.execute(env.." "..test)
    if status ~= 0 then print(env, test, "FAILED"); errors = errors+1 end









|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env luajit
-- or Lua 5.1

-- config
local envs = {"luajit", "lua5.1", "lua5.2", "lua5.3", "lua5.4"}
local tests = {
  "examples/test_main.lua",
  "examples/test_multifunction.lua",
  "examples/test_cdata.lua",
  "examples/test_error.lua",
  "examples/test_class.lua",
}
-- test
local errors = 0
for _, env in ipairs(envs) do
  for _, test in ipairs(tests) do
    local status = os.execute(env.." "..test)
    if status ~= 0 then print(env, test, "FAILED"); errors = errors+1 end