diff --git a/Project.toml b/Project.toml index e8a657b81..2b4ee5f76 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Requires = "ae029012-a4dd-5104-9daa-d747884805df" @@ -41,6 +42,7 @@ LightXML = "0.8, 0.9" MLStyle = "0.4" MetaGraphs = "^0.6" Parameters = "0.11, 0.12" +PrettyTables = "0.9" Reexport = "0.2" Requires = "^1" StaticArrays = "0.12" diff --git a/src/categorical_algebra/CSetDataStructures.jl b/src/categorical_algebra/CSetDataStructures.jl index 3409c42ef..26f6b1d11 100644 --- a/src/categorical_algebra/CSetDataStructures.jl +++ b/src/categorical_algebra/CSetDataStructures.jl @@ -3,10 +3,13 @@ module CSetDataStructures export AbstractACSet, ACSet, AbstractCSet, CSet, Schema, FreeSchema, AbstractACSetType, ACSetType, AbstractCSetType, CSetType, - nparts, has_part, subpart, has_subpart, incident, add_part!, add_parts!, - copy_parts!, set_subpart!, set_subparts!, disjoint_union + tables, nparts, has_part, subpart, has_subpart, incident, + add_part!, add_parts!, copy_parts!, set_subpart!, set_subparts!, + disjoint_union using Compat: isnothing, only + +using PrettyTables: pretty_table using StructArrays using ...Theories: Schema, FreeSchema, dom, codom, @@ -193,8 +196,9 @@ function Base.copy(acs::T) where T <: ACSet T(map(copy, acs.tables), map(copy, acs.indices)) end -function Base.show(io::IO, acs::AbstractACSet{CD,AD,Ts}) where {CD,AD,Ts} - println(io, "ACSet(") +function Base.show(io::IO, acs::T) where {CD,AD,Ts,T<:AbstractACSet{CD,AD,Ts}} + print(io, T <: AbstractCSet ? "CSet" : "ACSet") + println(io, "(") join(io, vcat( [ " $ob = 1:$(nparts(acs,ob))" for ob in CD.ob ], [ " $data = $(Ts.parameters[i])" for (i,data) in enumerate(AD.data) ], @@ -206,21 +210,48 @@ function Base.show(io::IO, acs::AbstractACSet{CD,AD,Ts}) where {CD,AD,Ts} print(io, ")") end -function Base.show(io::IO, ::MIME"text/plain", acs::ACSet) - println(io, "ACSet:") - for (name, table) in pairs(acs.tables) - print(io, " $name table with $(length(table)) elements") +function Base.show(io::IO, ::MIME"text/plain", acs::T) where {T<:AbstractACSet} + print(io, T <: AbstractCSet ? "CSet" : "ACSet") + print(io, " with elements ") + join(io, ["$ob = 1:$(nparts(acs,ob))" for ob in keys(tables(acs))], ", ") + println(io) + for (ob, table) in pairs(tables(acs)) if !(eltype(table) <: EmptyTuple) - print(io, ":\n ") - join(io, map(string, table), "\n ") + # TODO: Set option `row_number_column_title=name` when next version of + # PrettyTables is released, instead of making new table. + table = StructArray((; ob => 1:nparts(acs,ob), fieldarrays(table)...)) + pretty_table(io, table, nosubheader=true) end - println(io) end end +function Base.show(io::IO, ::MIME"text/html", acs::T) where {T<:AbstractACSet} + println(io, "
") + print(io, "") + print(io, T <: AbstractCSet ? "CSet" : "ACSet") + print(io, " with elements ") + join(io, ["$ob = 1:$(nparts(acs,ob))" for ob in keys(tables(acs))], ", ") + println(io, "") + for (ob, table) in pairs(tables(acs)) + if !(eltype(table) <: EmptyTuple) + # TODO: Set option `row_number_column_title`. See above. + table = StructArray((; ob => 1:nparts(acs,ob), fieldarrays(table)...)) + pretty_table(io, table, backend=:html, standalone=false, nosubheader=true) + end + end + println(io, "
") +end + # Imperative interface ###################### +""" Tables defining a C-set. + +A named tuple with a table for each part type. To ensure consistency, do not +directly mutate these tables, especially when indexing is enabled! +""" +tables(acs::ACSet) = acs.tables + """ Number of parts of given type in a C-set. """ nparts(acs::ACSet, type) = length(acs.tables[type]) diff --git a/src/categorical_algebra/CSets.jl b/src/categorical_algebra/CSets.jl index ca9205d5b..0a6cd4b04 100644 --- a/src/categorical_algebra/CSets.jl +++ b/src/categorical_algebra/CSets.jl @@ -109,7 +109,7 @@ force(α::ACSetTransformation) = end end -finsets(X::ACSet) = map(table -> FinSet(length(table)), X.tables) +finsets(X::ACSet) = map(table -> FinSet(length(table)), tables(X)) # Limits and colimits ##################### diff --git a/test/categorical_algebra/CSetDataStructures.jl b/test/categorical_algebra/CSetDataStructures.jl index 454777858..30c1f75fa 100644 --- a/test/categorical_algebra/CSetDataStructures.jl +++ b/test/categorical_algebra/CSetDataStructures.jl @@ -18,6 +18,7 @@ const DDS = CSetType(TheoryDDS, index=[:Φ]) @test DDS <: CSet dds = DDS() +@test keys(tables(dds)) == (:X,) @test keys(dds.indices) == (:Φ,) @test nparts(dds, :X) == 0 @test add_part!(dds, :X) == 1 @@ -47,12 +48,18 @@ set_subpart!(dds, 1, :Φ, 1) # Pretty printing. s = sprint(show, dds) +@test startswith(s, "CSet") @test occursin("X = 1:3", s) @test occursin("Φ : X → X = ", s) s = sprint(show, MIME"text/plain"(), dds) -@test occursin("X table with 3 elements", s) -@test occursin("(Φ = 1,)", s) +@test startswith(s, "CSet") +@test occursin("X = 1:3", s) + +s = sprint(show, MIME"text/html"(), dds) +@test startswith(s, "
") +@test occursin("", s) +@test endswith(rstrip(s), "") # Error handling. @test_throws AssertionError add_part!(dds, :X, Φ=5) @@ -109,11 +116,12 @@ du = disjoint_union(d, d2) # Pretty printing of data attributes. s = sprint(show, d) +@test startswith(s, "ACSet") @test occursin("R = Int64", s) @test occursin("height : X → R = ", s) s = sprint(show, MIME"text/plain"(), d) -@test occursin("(parent = 4, height = 0)", s) +@test startswith(s, "ACSet") # Allow type inheritance for data attributes. d = Dendrogram{Number}()