Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InterTypes #75

Merged
merged 14 commits into from
Nov 2, 2023
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ Manifest.toml
.DS_Store
.vscode
.ipynb_checkpoints
test/.CondaPkg
test/intertypes/__pycache__
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ version = "0.2.9"

[deps]
AlgebraicInterfaces = "23cfdc9f-0504-424a-be1f-4892b28e2f0c"
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
CompTime = "0fb5dd42-039a-4ca4-a1d7-89a96eae6d39"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Permutations = "2ae35dd2-176d-5d53-8349-f30d82d94d4f"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/ACSets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include("PreimageCaches.jl")
include("Columns.jl")
include("ColumnImplementations.jl")
include("Schemas.jl")
include("intertypes/InterTypes.jl")
include("ACSetInterface.jl")
include("DenseACSets.jl")
include("serialization/Serialization.jl")
Expand All @@ -17,6 +18,7 @@ include("NautyInterface.jl")

@reexport using .ColumnImplementations: AttrVar
@reexport using .Schemas
@reexport using .InterTypes
@reexport using .ACSetInterface
@reexport using .DenseACSets
@reexport using .ACSetSerialization
Expand Down
11 changes: 10 additions & 1 deletion src/Schemas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module Schemas

export Schema, TypeLevelSchema, BasicSchema, TypeLevelBasicSchema, typelevel,
objects, attrtypes, attrtype_instantiation, homs, attrs, arrows, dom, codom,
ob, hom, attrtype, attr, dom_nums, codom_nums, adom_nums, acodom_nums, types
ob, hom, attrtype, attr, dom_nums, codom_nums, adom_nums, acodom_nums, types,
TypedSchema

using StructEquality
import AlgebraicInterfaces: dom, codom, ob, hom, attr, attrtype
Expand Down Expand Up @@ -250,4 +251,12 @@ codom_nums(s) = Tuple(findfirst(objects(s) .== c) for (_,_,c) in homs(s))
adom_nums(s) = Tuple(findfirst(objects(s) .== d) for (_,d,_) in attrs(s))
acodom_nums(s) = Tuple(findfirst(attrtypes(s) .== c) for (_,_,c) in attrs(s))

# Typed Schemas
###############

struct TypedSchema{Name, T}
schema::BasicSchema{Name}
types::Dict{Symbol, T}
end

end
60 changes: 60 additions & 0 deletions src/intertypes/InterTypes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module InterTypes
export InterType, InterTypeDecl, Binary, intertype, as_intertypes

using MLStyle
using ..Schemas

struct Field{T}
name::Symbol
type::T
end

Base.nameof(field::Field) = field.name

struct Variant{T}
tag::Symbol
fields::Vector{Field{T}}
end

Base.nameof(variant::Variant) = variant.tag

@data InterType begin
I32
U32
I64
U64
F64
Boolean
Str
Sym
Binary
List(elemtype::InterType)
Map(keytype::InterType, valuetype::InterType)
Record(fields::Vector{Field{InterType}})
Sum(variants::Vector{Variant{InterType}})
ACSetInterType(schema::TypedSchema{InterType})
Annot(desc::String, type::InterType)
TypeRef(to::Symbol)
end

@data InterTypeDecl begin
Alias(name::Symbol, type::InterType)
SumType(name::Symbol, variants::Vector{Variant{InterType}})
Struct(name::Symbol, fields::Vector{Field{InterType}})
SchemaDecl(name::Symbol, schema::TypedSchema{Symbol, InterType})

Check warning on line 44 in src/intertypes/InterTypes.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/InterTypes.jl#L44

Added line #L44 was not covered by tests
NamedACSetType(name::Symbol, schemaname::Symbol)
end

Base.nameof(decl::InterTypeDecl) = @match decl begin
Alias(name, _) => name

Check warning on line 49 in src/intertypes/InterTypes.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/InterTypes.jl#L49

Added line #L49 was not covered by tests
SumType(name, _) => name
Struct(name, _) => name
end

function intertype end

include("json.jl")
include("julia.jl")
include("python.jl")

end
237 changes: 237 additions & 0 deletions src/intertypes/json.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
export intertype_to_jsonschema, jsonwrite, jsonread, parse_intertype, toexpr, @intertype_decls, as_intertypes

using OrderedCollections
using Base64
using MLStyle
import JSON3

struct JSONFormat
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract type SerialFormat end
Struct JSONFormat <: SerialFormat end

?
Suppose it doesn’t matter if no other Serial formats are on the radar.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, I also want s-expression and msgpack serialization

end

struct InterTypeConversionError <: Exception
expected::InterType
got::Any
end

function read(format, type::Type, x)
throw(InterTypeConversionError(intertype(type), x))

Check warning on line 17 in src/intertypes/json.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/json.jl#L16-L17

Added lines #L16 - L17 were not covered by tests
end

function joinwith(io::IO, f, xs, separator)
for x in xs[1:end-1]
f(io, x)
print(io, separator)
end
f(io, xs[end])
end

jsonwrite(x) = sprint(jsonwrite, x)
jsonwrite(io::IO, x) = write(io, JSONFormat(), x)
function jsonread(s::String, ::Type{T}) where {T}
json = JSON3.read(s)
read(JSONFormat(), T, json)
end

intertype(::Type{Int32}) = I32
read(::JSONFormat, ::Type{Int32}, s::Real) = Int32(s)
write(io::IO, ::JSONFormat, d::Int32) = print(io, d)

intertype(::Type{UInt32}) = U32
read(::JSONFormat, ::Type{UInt32}, s::Real) = UInt32(s)
write(io::IO, ::JSONFormat, d::UInt32) = print(io, d)

intertype(::Type{Int64}) = I64
read(::JSONFormat, ::Type{Int64}, s::String) = parse(Int64, s)
write(io::IO, ::JSONFormat, d::Int64) = print(io, "\"", d, "\"")

intertype(::Type{UInt64}) = U64
read(::JSONFormat, ::Type{UInt64}, s::String) = parse(UInt64, s)
write(io::IO, ::JSONFormat, d::UInt64) = print(io, "\"", d, "\"")

intertype(::Type{Float64}) = F64
read(::JSONFormat, ::Type{Float64}, s::Real) = Float64(s)
write(io::IO, ::JSONFormat, d::Float64) = print(io, d)

Check warning on line 53 in src/intertypes/json.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/json.jl#L51-L53

Added lines #L51 - L53 were not covered by tests

intertype(::Type{Bool}) = Boolean
read(::JSONFormat, ::Type{Bool}, s::Bool) = s
write(io::IO, ::JSONFormat, d::Bool) = print(io, d)

intertype(::Type{String}) = Str
read(::JSONFormat, ::Type{String}, s::String) = s
write(io::IO, ::JSONFormat, d::String) = JSON3.write(io, d)

intertype(::Type{Symbol}) = Sym
read(::JSONFormat, ::Type{Symbol}, s::String) = Symbol(s)
write(io::IO, ::JSONFormat, d::Symbol) = JSON3.write(io, string(d))

intertype(::Type{Vector{UInt8}}) = Binary
read(::JSONFormat, ::Type{Vector{UInt8}}, s::String) = base64decode(s)
function write(io::IO, ::JSONFormat, d::Vector{UInt8})
print(io, "\"")
Base.write(Base64EncodePipe(io), d)
print(io, "\"")
end

intertype(::Type{Vector{T}}) where {T} = List(intertype(T))
function read(format::JSONFormat, ::Type{Vector{T}}, s::JSON3.Array) where {T}
res = T[]
for elt in s
push!(res, read(format, T, elt))
end
res
end
function write(io::IO, format::JSONFormat, d::Vector{T}) where {T}
print(io, "[")
joinwith(io, (io, x) -> write(io, format, x), d, ",")
print(io, "]")
end

intertype(::Type{OrderedDict{K,V}}) where {K,V} = Map(intertype(K), intertype(V))
function read(format::JSONFormat, ::Type{OrderedDict{K, V}}, s::JSON3.Array) where {K, V}
res = OrderedDict{K, V}()
for elt in s
(;key, value) = read(format, NamedTuple{(:key, :value), Tuple{K, V}}, elt)
res[key] = value
end
res
end
function write(io::IO, format::JSONFormat, d::OrderedDict{K, V}) where {K, V}
print(io, "[")
joinwith(io, (io, x) -> write(io, format, (key=x[1], value=x[2])), collect(pairs(d)), ",")
print(io, "]")
end

function intertype(::Type{T}) where {T<:Tuple}
types = T.parameters
Record(map(enumerate(types)) do (i, type)
Field{InterType}(Symbol("_", i), intertype(type))
end)
end
function read(format::JSONFormat, ::Type{T}, s::JSON3.Object) where {T<:Tuple}
keys = Tuple([Symbol("_", i) for i in 1:length(T.parameters)])
Tuple(read(format, NamedTuple{keys, T}, s))
end
function write(io::IO, format::JSONFormat, d::T) where {T<:Tuple}
keys = Tuple([Symbol("_", i) for i in 1:length(T.parameters)])
write(io, format, NamedTuple{keys, T}(d))
end

function intertype(::Type{NamedTuple{names, T}}) where {names, T<:Tuple}
types = T.parameters
Record([Field{InterType}(name, intertype(type)) for (name, type) in zip(names, (types))])
end
# TODO: comptime this
function read(format::JSONFormat, ::Type{NamedTuple{names, T}}, s::JSON3.Object) where {names, T<:Tuple}
keys(s) == Set(names) || error("wrong keys: expected $names got $(keys(s))")
vals = Any[]
for (name, type) in zip(names, T.parameters)
push!(vals, read(format, type, s[name]))
end
NamedTuple{names, T}(vals)
end
function write(io::IO, format::JSONFormat, d::NamedTuple{names, T}) where {names, T<:Tuple}
print(io, "{")
function writekv(io, kv::Pair{Symbol, T}) where {T}
(k, v) = kv
JSON3.write(io, k)
print(io, ":")
write(io, format, v)
end
joinwith(io, writekv, [pairs(d)...], ",")
print(io, "}")
end

const Object = OrderedDict{String, Any}

function fieldproperties(fields::Vector{Field{InterType}})
map(fields) do field
string(field.name) => intertype_to_jsonschema(field.type)
end
end

function intertype_to_jsonschema(type::InterType)
@match type begin
I32 => Object(
"type" => "integer",
"\$comment" => "I32",
"minimum" => typemin(Int32),
"maximum" => typemax(Int32)
)
U32 => Object(
"type" => "integer",
"\$comment" => "U32",
"minimum" => typemin(UInt32),
"maximum" => typemax(UInt32)
)
I64 => Object(
"type" => "string",
"\$comment" => "I64"
)
U64 => Object(
"type" => "string",
"\$comment" => "U64"
)
F64 => Object(

Check warning on line 174 in src/intertypes/json.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/json.jl#L174

Added line #L174 was not covered by tests
"type" => "number",
"\$comment" => "F64"
)
Boolean => Object(
"type" => "boolean",
"\$comment" => "Boolean"
)
Str => Object(
"type" => "string",
"\$comment" => "Str"
)
Sym => Object(
"type" => "string",
"\$comment" => "Sym"
)
Binary => Object(
"type" => "string",
"contentEncoding" => "base64",
"\$comment" => "Binary"
)
List(elemtype) => Object(
"type" => "array",
"items" => intertype_to_jsonschema(elemtype)
)
Map(keytype, valuetype) => Object(
"type" => "array",
"items" => Object(
"type" => "object",
"properties" => Object(
"key" => intertype_to_jsonschema(keytype),
"value" => intertype_to_jsonschema(valuetype)
)
)
)
Record(fields) => Object(
"type" => "object",
"properties" => Object(fieldproperties(fields)),
"required" => string.(nameof.(fields))
)
Sum(variants) => Object(
"oneOf" => Vector{Object}(map(variants) do variant
Object(
"type" => "object",
"properties" => Object(
"tag" => Object(
"const" => string(variant.tag)
),
fieldproperties(variant.fields)...
),
"required" => string.(nameof.(variant.fields))
)
end)
)
Annot(desc, innertype) => begin
innerschematype = intertype_to_jsonschema(innertype)
innerschematype["description"] = desc

Check warning on line 230 in src/intertypes/json.jl

View check run for this annotation

Codecov / codecov/patch

src/intertypes/json.jl#L228-L230

Added lines #L228 - L230 were not covered by tests
innerschematype
end
TypeRef(to) => Object(
"\$ref" => string(to)
)
end
end
Loading