Skip to content

Commit

Permalink
Merge pull request #42 from AstroSnail/add-annotations
Browse files Browse the repository at this point in the history
Add annotations
  • Loading branch information
aiverson authored Dec 10, 2023
2 parents d1f6d81 + 368f7a7 commit a52dbbb
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 1 deletion.
13 changes: 13 additions & 0 deletions derivers.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
---@alias RecordDeriveInfo { kind: string, params: string[], params_types: Type[] }
---@alias UnitDeriveInfo { kind: string }
---@alias EnumDeriveInfo { name: string, variants: { [number]: string, [string]: { type: string, info: RecordDeriveInfo | UnitDeriveInfo } } }

---@class (exact) Deriver
---@field record fun(t: Record, info: RecordDeriveInfo)
---@field enum fun(t: Enum, info: EnumDeriveInfo)

local derive_print = function(...) end -- can make this call derive_print(...) if you want to debug

---@type Deriver
local eq = {
record = function(t, info)
local kind = info.kind
Expand Down Expand Up @@ -68,6 +77,7 @@ local eq = {
end,
}

---@type Deriver
local is = {
enum = function(t, info)
local idx = t.__index or {}
Expand Down Expand Up @@ -155,6 +165,7 @@ end
return compiled(pretty_print_or_tostring)
end

---@type Deriver
local pretty_print = {
record = function(t, info)
local idx = t.__index or {}
Expand Down Expand Up @@ -208,6 +219,7 @@ local pretty_print = {
end,
}

---@type Deriver
local unwrap = {
record = function(t, info)
local idx = t.__index or {}
Expand Down Expand Up @@ -276,6 +288,7 @@ local unwrap = {
end,
}

---@type Deriver
local as = {
enum = function(t, info)
t:derive(unwrap)
Expand Down
89 changes: 88 additions & 1 deletion terms-generators.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@
-- (likewise for array, and foreign given the same value_check function;
-- foreign values are constructed elsewhere)

---@class Type
---@field value_check ValueCheckFn
---@alias ValueCheckFn fun(val: Type): boolean

---@class Value
---@field kind string

-- TODO: are generic annotations powerful enough to describe this function?
-- worked around at the bottom of this file
local function new_self(fn)
return function(...)
return fn({}, ...)
end
end

---@param mt table
---@return ValueCheckFn
local function metatable_equality(mt)
if type(mt) ~= "table" then
error("trying to define metatable equality to something that isn't a metatable (possible typo?)")
Expand All @@ -27,6 +38,11 @@ local function metatable_equality(mt)
end
end

---@alias ParamsWithTypes (string | Type)[]

---@param params_with_types ParamsWithTypes
---@return string[] params
---@return Type[] params_types
local function parse_params_with_types(params_with_types)
-- params are odd entries of params_with_types
-- params_types are even
Expand All @@ -46,6 +62,10 @@ local function parse_params_with_types(params_with_types)
return params, params_types
end

---@param kind string
---@param params string[]
---@param params_types Type[]
---@return nil
local function validate_params_types(kind, params, params_types)
-- ensure there are at least as many param types as there are params
-- also ensure there is at least one param
Expand All @@ -68,6 +88,13 @@ local function validate_params_types(kind, params, params_types)
end
end

-- TODO: cons turns from a table to a callable table. how to conveniently annotate this?
---@param self table
---@param cons table
---@param kind string
---@param params_with_types ParamsWithTypes
---@return table cons
---@return RecordDeriveInfo derive_info
local function gen_record(self, cons, kind, params_with_types)
local params, params_types = parse_params_with_types(params_with_types)
validate_params_types(kind, params, params_types)
Expand Down Expand Up @@ -98,15 +125,27 @@ local function gen_record(self, cons, kind, params_with_types)
return cons, derive_info
end

---@class Record: Type
---@field derive fun(self: Record, deriver: Deriver)

---@param self table
---@param kind string
---@param params_with_types ParamsWithTypes
---@return Record self
local function define_record(self, kind, params_with_types)
local self, derive_info = gen_record(self, self, kind, params_with_types)
function self:derive(deriver)
return deriver.record(self, derive_info)
end
self.value_check = metatable_equality(self)
---@cast self Record
return self
end

---@param self table
---@param kind string
---@return Value val
---@return UnitDeriveInfo derive_info
local function gen_unit(self, kind)
local val = {
kind = kind,
Expand All @@ -118,6 +157,15 @@ local function gen_unit(self, kind)
return val, derive_info
end

---@class Enum: Type
---@field derive fun(self: Enum, deriver: Deriver)

---@alias Variants { [1]: string, [2]: ParamsWithTypes }[]

---@param self table
---@param name string
---@param variants Variants
---@return Enum self
local function define_enum(self, name, variants)
setmetatable(self, nil)
local derive_variants = {}
Expand Down Expand Up @@ -150,12 +198,19 @@ local function define_enum(self, name, variants)
return deriver.enum(self, derive_info)
end
self.value_check = metatable_equality(self)
---@cast self Enum
return self
end

---@class Foreign: Type

---@param self table
---@param value_check ValueCheckFn
---@return Foreign self
local function define_foreign(self, value_check)
setmetatable(self, nil)
self.value_check = value_check
---@cast self Foreign
return self
end

Expand Down Expand Up @@ -225,8 +280,14 @@ local function gen_map_fns(key_type, value_type)
return index, newindex
end

---@class Map: Type

-- TODO: memoize? otherwise LOTS of tables will be constructed,
-- through repeated calls to declare_map
---@param self table
---@param key_type Type
---@param value_type Type
---@return Map self
local function define_map(self, key_type, value_type)
if
type(key_type) ~= "table"
Expand All @@ -243,6 +304,7 @@ local function define_map(self, key_type, value_type)
self.__pairs = map_methods.pairs
-- NOTE: this isn't primitive equality; this type has a __eq metamethod!
self.value_check = metatable_equality(self)
---@cast self Map
return self
end

Expand Down Expand Up @@ -336,7 +398,7 @@ local function gen_array_fns(value_type)
error("key passed to index-assignment is out of bounds")
end
if value_type.value_check(value) ~= true then
p("array-index-assign", key_type, value_type)
p("array-index-assign", value_type)
p(value)
error("wrong value type passed to index-assignment")
end
Expand All @@ -348,7 +410,12 @@ local function gen_array_fns(value_type)
return index, newindex
end

---@class Array: Type

-- TODO: see define_map
---@param self table
---@param value_type Type
---@return Array self
local function define_array(self, value_type)
if type(value_type) ~= "table" or type(value_type.value_check) ~= "function" then
error("trying to set the value type to something that isn't a type (possible typo?)")
Expand All @@ -360,6 +427,7 @@ local function define_array(self, value_type)
self.__len = array_methods.len
-- NOTE: this isn't primitive equality; this type has a __eq metamethod!
self.value_check = metatable_equality(self)
---@cast self Array
return self
end

Expand All @@ -373,29 +441,48 @@ local type_mt = {
},
}

---@type ValueCheckFn
local function undefined_value_check(val)
error("trying to typecheck a value against a type that has been declared but not defined")
end

---@class UndefinedType: Type
---@field define_record fun(self: table, kind: string, params_with_types: ParamsWithTypes): Record
---@field define_enum fun(self: table, name: string, variants: Variants): Enum
---@field define_foreign fun(self: table, value_check: ValueCheckFn): Foreign
---@field define_map fun(self: table, key_type: Type, value_type: Type): Map
---@field define_array fun(self: table, value_type: Type): Array

---@param self table
---@return UndefinedType self
local function define_type(self)
setmetatable(self, type_mt)
self.value_check = undefined_value_check
---@cast self UndefinedType
return self
end

---@param typename string
---@return Foreign
local function gen_builtin(typename)
return define_foreign({}, function(val)
return type(val) == typename
end)
end

return {
---@type fun(kind: string, params_with_types: ParamsWithTypes): Record
declare_record = new_self(define_record),
---@type fun(name: string, variants: Variants): Enum
declare_enum = new_self(define_enum),
-- Make sure the function you pass to this returns true, not just a truthy value
---@type fun(value_check: ValueCheckFn): Foreign
declare_foreign = new_self(define_foreign),
---@type fun(key_type: Type, value_type: Type): Map
declare_map = new_self(define_map),
---@type fun(value_type: Type): Array
declare_array = new_self(define_array),
---@type fun(): UndefinedType
declare_type = new_self(define_type),
metatable_equality = metatable_equality,
builtin_number = gen_builtin("number"),
Expand Down

0 comments on commit a52dbbb

Please sign in to comment.