148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
|
|
// Copyright 2026 The Go Authors. All rights reserved.
|
|||
|
|
// Use of this source code is governed by a BSD-style
|
|||
|
|
// license that can be found in the LICENSE file.
|
|||
|
|
|
|||
|
|
package dfa
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"maps"
|
|||
|
|
"slices"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// A Semilattice describes a bounded semilattice over Elem.
|
|||
|
|
// That is, a partial order over values of type Elem, with a binary
|
|||
|
|
// Merge operator and an identity element.
|
|||
|
|
//
|
|||
|
|
// This is typically implemented by a stateless type, and acts as a factory for
|
|||
|
|
// lattice elements.
|
|||
|
|
type Semilattice[Elem any] interface {
|
|||
|
|
// Ident returns the identity element of this lattice, that is the unit of
|
|||
|
|
// the Merge operation.
|
|||
|
|
Ident() Elem
|
|||
|
|
|
|||
|
|
// Equals returns whether a and b are the same element.
|
|||
|
|
Equals(a, b Elem) bool
|
|||
|
|
|
|||
|
|
// Merge combines two lattice values, such as the two possible values of a
|
|||
|
|
// variable at the end of an if/else statement.
|
|||
|
|
//
|
|||
|
|
// Merge must satisfy the following identities, where we use ∧ for Merge, =
|
|||
|
|
// for Equals, and 𝟏 for Ident:
|
|||
|
|
//
|
|||
|
|
// - Associativity: x ∧ (y ∧ z) = (x ∧ y) ∧ z
|
|||
|
|
// - Commutativity: x ∧ y = y ∧ x
|
|||
|
|
// - Idempotency: x ∧ x = x
|
|||
|
|
// - Identity: x ∧ 𝟏 = x
|
|||
|
|
Merge(a, b Elem) Elem
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// A MapLattice implements [Semilattice][map[Key]Elem]. The values in the map
|
|||
|
|
// are themselves defined by [Semilattice] L.
|
|||
|
|
//
|
|||
|
|
// Any elements missing from the map are implicitly L's identity element, and
|
|||
|
|
// L's identity element never appears as a value in the map.
|
|||
|
|
//
|
|||
|
|
// For densely numbered keys, consider using [DenseMapLattice] instead.
|
|||
|
|
type MapLattice[Key comparable, Elem any, L Semilattice[Elem]] struct {
|
|||
|
|
l L
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (m MapLattice[Key, Elem, L]) Ident() map[Key]Elem {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (m MapLattice[Key, Elem, L]) Equals(a, b map[Key]Elem) bool {
|
|||
|
|
return maps.EqualFunc(a, b, m.l.Equals)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (m MapLattice[Key, Elem, L]) Merge(a, b map[Key]Elem) map[Key]Elem {
|
|||
|
|
if len(a) == 0 {
|
|||
|
|
return b
|
|||
|
|
} else if len(b) == 0 {
|
|||
|
|
return a
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// We need to consider the union of keys in a and b.
|
|||
|
|
out := make(map[Key]Elem)
|
|||
|
|
id := m.l.Ident()
|
|||
|
|
for k, av := range a {
|
|||
|
|
bv, ok := b[k]
|
|||
|
|
if !ok {
|
|||
|
|
// Because Merge(x, Ident()) == x, we can skip calling L.Merge.
|
|||
|
|
out[k] = av
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
w := m.l.Merge(av, bv)
|
|||
|
|
if m.l.Equals(w, id) {
|
|||
|
|
// In a semilattice, Merge(x, y) = Ident is only possible when x ==
|
|||
|
|
// Ident and y == Ident.
|
|||
|
|
panic(fmt.Sprintf(
|
|||
|
|
"%T is not a semilattice: Merge(%v, %v) returned Ident for non-Ident arguments",
|
|||
|
|
m.l, av, bv))
|
|||
|
|
}
|
|||
|
|
out[k] = w
|
|||
|
|
}
|
|||
|
|
// We considered keys that are only in a, and in both a and b. Now we just
|
|||
|
|
// need to handle keys that are only in b.
|
|||
|
|
for k, v2 := range b {
|
|||
|
|
if _, ok := a[k]; !ok {
|
|||
|
|
out[k] = v2
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return out
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// A DenseMapLattice implements [Semilattice][[]Elem]. It is like a [MapLattice]
|
|||
|
|
// that is indexed by integers. The values in the map are themselves defined by
|
|||
|
|
// [Semilattice] L.
|
|||
|
|
//
|
|||
|
|
// Unlike [MapLattice], L's identity element may appear as a value in the map,
|
|||
|
|
// to allow for gaps in the numbering of keys when the identity element is
|
|||
|
|
// Elem's zero value.
|
|||
|
|
type DenseMapLattice[Elem any, L Semilattice[Elem]] struct {
|
|||
|
|
l L
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s DenseMapLattice[Elem, L]) Ident() []Elem {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s DenseMapLattice[Elem, L]) Equals(a, b []Elem) bool {
|
|||
|
|
nmin := min(len(a), len(b))
|
|||
|
|
ident := s.l.Ident()
|
|||
|
|
|
|||
|
|
// Check that up to nmin, all elements in a and b match. If one of a or b
|
|||
|
|
// is longer, then its tail nmin:nmax must only contain identity elements.
|
|||
|
|
return slices.EqualFunc(a[:nmin], b[:nmin], s.l.Equals) &&
|
|||
|
|
!slices.ContainsFunc(a[nmin:], func(e Elem) bool {
|
|||
|
|
return !s.l.Equals(e, ident)
|
|||
|
|
}) &&
|
|||
|
|
!slices.ContainsFunc(b[nmin:], func(e Elem) bool {
|
|||
|
|
return !s.l.Equals(e, ident)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s DenseMapLattice[Elem, L]) Merge(a, b []Elem) []Elem {
|
|||
|
|
if len(a) == 0 {
|
|||
|
|
return b
|
|||
|
|
} else if len(b) == 0 {
|
|||
|
|
return a
|
|||
|
|
}
|
|||
|
|
out := make([]Elem, max(len(a), len(b)))
|
|||
|
|
for k := range max(len(a), len(b)) {
|
|||
|
|
av := s.l.Ident()
|
|||
|
|
bv := s.l.Ident()
|
|||
|
|
if k < len(a) {
|
|||
|
|
av = a[k]
|
|||
|
|
}
|
|||
|
|
if k < len(b) {
|
|||
|
|
bv = b[k]
|
|||
|
|
}
|
|||
|
|
out[k] = s.l.Merge(av, bv)
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|