Skip to content

Commit

Permalink
added basic packages data structure
Browse files Browse the repository at this point in the history
  • Loading branch information
olynch committed Feb 9, 2024
1 parent b145299 commit 79ecdf6
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["AlgebraicJulia Developers"]
version = "0.1.0"

[deps]
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
AlgebraicInterfaces = "23cfdc9f-0504-424a-be1f-4892b28e2f0c"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
Expand Down
187 changes: 187 additions & 0 deletions src/syntax/Packages.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
module Packages

using AbstractTrees

export Package, namespace, singleton, PACKAGE_ROOT, ■, PackageVar

struct Namespace{X}
values::Dict{Symbol, X}
end

struct Singleton{A}
value::A
end

struct Package{A}
content::Union{Singleton{A}, Namespace{Package{A}}}
end

content(p::Package) = getfield(p, :content)
issingleton(p::Package) = content(p) isa Singleton
singleton(x::A) where {A} = Package{A}(Singleton{A}(x))
isnamespace(p::Package) = content(p) isa Namespace
namespace(d::Dict{Symbol, Package{A}}) where {A} = Package{A}(Namespace{Package{A}}(d))
namespace(ps::Pair{Symbol, Package{A}}...) where {A} = namespace(Dict{Symbol, Package{A}}(ps...))

struct PackageIndexError <: Exception
package::Package
key::Symbol
end


struct PackageDerefError <: Exception
package::Package
end

function Base.getproperty(p::Package{A}, n::Symbol)::Package{A} where {A}
if isnamespace(p)
d = content(p).values
if haskey(d, n)
d[n]
else
throw(PackageIndexError(p, n))
end
else
throw(PackageIndexError(p, n))
end
end

Base.getindex(p::Package, n::Symbol) = getproperty(p, n)

function Base.getindex(p::Package{A})::A where {A}
if issingleton(p)
content(p).value
else
throw(PackageDerefError(p))
end
end

function Base.hasproperty(p::Package, n::Symbol)
if issingleton(p)
false
else
haskey(content(p).values, n)
end
end

function Base.propertynames(p::Package)
if issingleton(p)
Symbol[]
else
keys(content(p).values)
end
end

Base.keys(p) = Base.propertynames(p)

Base.valtype(p::Package{A}) where {A} = A

function Base.map(f, p::Package)::Package
if issingleton(p)
singleton(f(p[]))
else
d′ = Dict(map(pairs(content(p).values)) do (n, p′)
(n, map(f, p′))
end)
B = valtype(valtype(d′))
Package{B}(Namespace{Package{B}}(d′))
end
end

function flatten(p::Package{Package{A}})::Package{A} where {A}
if issingleton(p)
content(p).value
else
map(flatten, p)
end
end

struct PackageVar
content::Union{Nothing, Tuple{Symbol, PackageVar}}
end

PACKAGE_ROOT = PackageVar(nothing)
= PACKAGE_ROOT

content(v::PackageVar) = getfield(v, :content)
isroot(v::PackageVar) = isnothing(content(v))
isnested(v::PackageVar) = !isroot(v)

function Base.getproperty(v::PackageVar, n::Symbol)
if isroot(v)
PackageVar((n, v))
else
(n′, v′) = content(v)
PackageVar((n′, getproperty(v′, n)))
end
end

function Base.show(io::IO, v::PackageVar)
print(io, "")
while !isroot(v)
(n, v) = content(v)
print(io, ".", n)
end
end

function Base.haskey(p::Package, v::PackageVar)
if isroot(v)
issingleton(p)
else
(n, v′) = content(v)
hasproperty(p, n) && haskey(getproperty(p, n), v′)
end
end

struct PackageVarNotFound <: Exception
p::Package
v::PackageVar
end

function Base.showerror(io::IO, e::PackageVarNotFound)
println(io, "package variable ", e.v, " does not point to leaf in package")
println(io, e.p)
end

function Base.getindex(p::Package, v::PackageVar)
if isroot(v)
if issingleton(p)
p[]
else
throw(PackageVarNotFound(p, v))
end
else
(n, v′) = content(v)
if hasproperty(p, n)
getproperty(p, n)[v′]
else
throw(PackageVarNotFound(p, v))
end
end
end

function AbstractTrees.children(p::Package)
if issingleton(p)
()
else
pairs(content(p).values)
end
end

function AbstractTrees.printnode(io::IO, p::Package{A}; kw...) where {A}
if issingleton(p)
print(io, p[])
else
print(io, "Package{$A}")
end
end

function Base.show(io::IO, p::Package{A}) where {A}
if issingleton(p)
print(io, "singleton(", p[], ")::Package{$A}")
else
print_tree(io, p)
end
end

end
1 change: 1 addition & 0 deletions src/syntax/module.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Syntax
using Reexport

include("Scopes.jl")
include("Packages.jl")
include("ExprInterop.jl")
include("GATs.jl")
include("GATContexts.jl")
Expand Down
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
GATlab = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2"
StructEquality = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c"
Expand Down
24 changes: 24 additions & 0 deletions test/syntax/Packages.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module TestPackages

using GATlab.Syntax.Packages
using Test

p1 = namespace(:a => singleton(1), :b => namespace(:a => singleton(2), :c => singleton(3)))

@test p1.a isa Package
@test_throws Packages.PackageDerefError p1[]
@test p1.a[] == 1
@test p1.b.a isa Package
@test p1.b.a[] == 2

@test sprint(show, p1.a) == "singleton(1)::Package{Int64}"
@test sprint(show, p1.b) == "Package{Int64}\n├─ :a ⇒ 2\n└─ :c ⇒ 3\n"

@test== PACKAGE_ROOT
@test.a isa PackageVar
@test.a.b isa PackageVar
@test_throws Packages.PackageVarNotFound p1[■]
@test p1[■.a] == 1
@test p1[■.b.c] == 3

end

0 comments on commit 79ecdf6

Please sign in to comment.