From 79ecdf637a41d5f77a8f1fd66883d55332b30c1b Mon Sep 17 00:00:00 2001 From: Owen Lynch Date: Thu, 8 Feb 2024 17:40:04 -0800 Subject: [PATCH] added basic packages data structure --- Project.toml | 1 + src/syntax/Packages.jl | 187 ++++++++++++++++++++++++++++++++++++++++ src/syntax/module.jl | 1 + test/Project.toml | 1 + test/syntax/Packages.jl | 24 ++++++ 5 files changed, 214 insertions(+) create mode 100644 src/syntax/Packages.jl create mode 100644 test/syntax/Packages.jl diff --git a/Project.toml b/Project.toml index 1e59b305..830e8b68 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/src/syntax/Packages.jl b/src/syntax/Packages.jl new file mode 100644 index 00000000..f525bffb --- /dev/null +++ b/src/syntax/Packages.jl @@ -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 diff --git a/src/syntax/module.jl b/src/syntax/module.jl index 5c4b7f55..0db4a953 100644 --- a/src/syntax/module.jl +++ b/src/syntax/module.jl @@ -3,6 +3,7 @@ module Syntax using Reexport include("Scopes.jl") +include("Packages.jl") include("ExprInterop.jl") include("GATs.jl") include("GATContexts.jl") diff --git a/test/Project.toml b/test/Project.toml index b7561128..f3b20b76 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -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" diff --git a/test/syntax/Packages.jl b/test/syntax/Packages.jl new file mode 100644 index 00000000..464b3237 --- /dev/null +++ b/test/syntax/Packages.jl @@ -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