+--
+-- Compile a datatype specification into a parse tree for evaluation later on
+--
+local cdt_cache = { }
+
+function compile_datatype(code)
+ local i
+ local pos = 0
+ local esc = false
+ local depth = 0
+ local stack = { }
+
+ for i = 1, #code+1 do
+ local byte = code:byte(i) or 44
+ if esc then
+ esc = false
+ elseif byte == 92 then
+ esc = true
+ elseif byte == 40 or byte == 44 then
+ if depth <= 0 then
+ if pos < i then
+ local label = code:sub(pos, i-1)
+ :gsub("\\(.)", "%1")
+ :gsub("^%s+", "")
+ :gsub("%s+$", "")
+
+ if #label > 0 and tonumber(label) then
+ stack[#stack+1] = tonumber(label)
+ elseif label:match("^'.+'$") or label:match('^".+"$') then
+ stack[#stack+1] = label:gsub("[\"'](.+)[\"']", "%1")
+ elseif type(datatypes[label]) == "function" then
+ stack[#stack+1] = datatypes[label]
+ stack[#stack+1] = { }
+ else
+ error("Datatype error, bad token %q" % label)
+ end
+ end
+ pos = i + 1
+ end
+ depth = depth + (byte == 40 and 1 or 0)
+ elseif byte == 41 then
+ depth = depth - 1
+ if depth <= 0 then
+ if type(stack[#stack-1]) ~= "function" then
+ error("Datatype error, argument list follows non-function")
+ end
+ stack[#stack] = compile_datatype(code:sub(pos, i-1))
+ pos = i + 1
+ end
+ end
+ end
+
+ return stack
+end
+
+function verify_datatype(dt, value)
+ if dt and #dt > 0 then
+ if not cdt_cache[dt] then
+ local c = compile_datatype(dt)
+ if c and type(c[1]) == "function" then
+ cdt_cache[dt] = c
+ else
+ error("Datatype error, not a function expression")
+ end
+ end
+ if cdt_cache[dt] then
+ return cdt_cache[dt][1](value, unpack(cdt_cache[dt][2]))
+ end
+ end
+ return true
+end
+