Initial QSfera import
This commit is contained in:
+439
@@ -0,0 +1,439 @@
|
||||
// Copyright 2016 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Compare returns an integer indicating whether two AST values are less than,
|
||||
// equal to, or greater than each other.
|
||||
//
|
||||
// If a is less than b, the return value is negative. If a is greater than b,
|
||||
// the return value is positive. If a is equal to b, the return value is zero.
|
||||
//
|
||||
// Different types are never equal to each other. For comparison purposes, types
|
||||
// are sorted as follows:
|
||||
//
|
||||
// nil < Null < Boolean < Number < String < Var < Ref < Array < Object < Set <
|
||||
// ArrayComprehension < ObjectComprehension < SetComprehension < Expr < SomeDecl
|
||||
// < With < Body < Rule < Import < Package < Module.
|
||||
//
|
||||
// Arrays and Refs are equal if and only if both a and b have the same length
|
||||
// and all corresponding elements are equal. If one element is not equal, the
|
||||
// return value is the same as for the first differing element. If all elements
|
||||
// are equal but a and b have different lengths, the shorter is considered less
|
||||
// than the other.
|
||||
//
|
||||
// Objects are considered equal if and only if both a and b have the same sorted
|
||||
// (key, value) pairs and are of the same length. Other comparisons are
|
||||
// consistent but not defined.
|
||||
//
|
||||
// Sets are considered equal if and only if the symmetric difference of a and b
|
||||
// is empty.
|
||||
// Other comparisons are consistent but not defined.
|
||||
func Compare(a, b any) int {
|
||||
|
||||
if t, ok := a.(*Term); ok {
|
||||
if t == nil {
|
||||
a = nil
|
||||
} else {
|
||||
a = t.Value
|
||||
}
|
||||
}
|
||||
|
||||
if t, ok := b.(*Term); ok {
|
||||
if t == nil {
|
||||
b = nil
|
||||
} else {
|
||||
b = t.Value
|
||||
}
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if b == nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
sortA := sortOrder(a)
|
||||
sortB := sortOrder(b)
|
||||
|
||||
if sortA < sortB {
|
||||
return -1
|
||||
} else if sortB < sortA {
|
||||
return 1
|
||||
}
|
||||
|
||||
switch a := a.(type) {
|
||||
case Null:
|
||||
return 0
|
||||
case Boolean:
|
||||
if a == b.(Boolean) {
|
||||
return 0
|
||||
}
|
||||
if !a {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case Number:
|
||||
return NumberCompare(a, b.(Number))
|
||||
case String:
|
||||
b := b.(String)
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case *TemplateString:
|
||||
b := b.(*TemplateString)
|
||||
return a.Compare(b)
|
||||
case Var:
|
||||
return VarCompare(a, b.(Var))
|
||||
case Ref:
|
||||
return termSliceCompare(a, b.(Ref))
|
||||
case *Array:
|
||||
b := b.(*Array)
|
||||
return termSliceCompare(a.elems, b.elems)
|
||||
case *lazyObj:
|
||||
return Compare(a.force(), b)
|
||||
case *object:
|
||||
if x, ok := b.(*lazyObj); ok {
|
||||
b = x.force()
|
||||
}
|
||||
return a.Compare(b.(*object))
|
||||
case Set:
|
||||
return a.Compare(b.(Set))
|
||||
case *ArrayComprehension:
|
||||
b := b.(*ArrayComprehension)
|
||||
if cmp := Compare(a.Term, b.Term); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return a.Body.Compare(b.Body)
|
||||
case *ObjectComprehension:
|
||||
b := b.(*ObjectComprehension)
|
||||
if cmp := Compare(a.Key, b.Key); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
if cmp := Compare(a.Value, b.Value); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return a.Body.Compare(b.Body)
|
||||
case *SetComprehension:
|
||||
b := b.(*SetComprehension)
|
||||
if cmp := Compare(a.Term, b.Term); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return a.Body.Compare(b.Body)
|
||||
case Call:
|
||||
return termSliceCompare(a, b.(Call))
|
||||
case *Expr:
|
||||
return a.Compare(b.(*Expr))
|
||||
case *SomeDecl:
|
||||
return a.Compare(b.(*SomeDecl))
|
||||
case *Every:
|
||||
return a.Compare(b.(*Every))
|
||||
case *With:
|
||||
return a.Compare(b.(*With))
|
||||
case Body:
|
||||
return a.Compare(b.(Body))
|
||||
case *Head:
|
||||
return a.Compare(b.(*Head))
|
||||
case *Rule:
|
||||
return a.Compare(b.(*Rule))
|
||||
case Args:
|
||||
return termSliceCompare(a, b.(Args))
|
||||
case *Import:
|
||||
return a.Compare(b.(*Import))
|
||||
case *Package:
|
||||
return a.Compare(b.(*Package))
|
||||
case *Annotations:
|
||||
return a.Compare(b.(*Annotations))
|
||||
case *Module:
|
||||
return a.Compare(b.(*Module))
|
||||
}
|
||||
panic(fmt.Sprintf("illegal value: %T", a))
|
||||
}
|
||||
|
||||
type termSlice []*Term
|
||||
|
||||
func (s termSlice) Less(i, j int) bool { return Compare(s[i].Value, s[j].Value) < 0 }
|
||||
func (s termSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s termSlice) Len() int { return len(s) }
|
||||
|
||||
func sortOrder(x any) int {
|
||||
switch x.(type) {
|
||||
case Null:
|
||||
return 0
|
||||
case Boolean:
|
||||
return 1
|
||||
case Number:
|
||||
return 2
|
||||
case String:
|
||||
return 3
|
||||
case *TemplateString:
|
||||
return 4
|
||||
case Var:
|
||||
return 5
|
||||
case Ref:
|
||||
return 6
|
||||
case *Array:
|
||||
return 7
|
||||
case Object:
|
||||
return 8
|
||||
case Set:
|
||||
return 9
|
||||
case *ArrayComprehension:
|
||||
return 10
|
||||
case *ObjectComprehension:
|
||||
return 11
|
||||
case *SetComprehension:
|
||||
return 12
|
||||
case Call:
|
||||
return 13
|
||||
case Args:
|
||||
return 14
|
||||
case *Expr:
|
||||
return 100
|
||||
case *SomeDecl:
|
||||
return 101
|
||||
case *Every:
|
||||
return 102
|
||||
case *With:
|
||||
return 110
|
||||
case *Head:
|
||||
return 120
|
||||
case Body:
|
||||
return 200
|
||||
case *Rule:
|
||||
return 1000
|
||||
case *Import:
|
||||
return 1001
|
||||
case *Package:
|
||||
return 1002
|
||||
case *Annotations:
|
||||
return 1003
|
||||
case *Module:
|
||||
return 10000
|
||||
}
|
||||
panic(fmt.Sprintf("illegal value: %T", x))
|
||||
}
|
||||
|
||||
func importsCompare(a, b []*Import) int {
|
||||
minLen := min(len(b), len(a))
|
||||
for i := range minLen {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func annotationsCompare(a, b []*Annotations) int {
|
||||
minLen := min(len(b), len(a))
|
||||
for i := range minLen {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func rulesCompare(a, b []*Rule) int {
|
||||
minLen := min(len(b), len(a))
|
||||
for i := range minLen {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func termSliceCompare(a, b []*Term) int {
|
||||
minLen := min(len(b), len(a))
|
||||
for i := range minLen {
|
||||
if cmp := Compare(a[i], b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
} else if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func withSliceCompare(a, b []*With) int {
|
||||
minLen := min(len(b), len(a))
|
||||
for i := range minLen {
|
||||
if cmp := Compare(a[i], b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
} else if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func VarCompare(a, b Var) int {
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func TermValueCompare(a, b *Term) int {
|
||||
return a.Value.Compare(b.Value)
|
||||
}
|
||||
|
||||
func TermValueEqual(a, b *Term) bool {
|
||||
return ValueEqual(a.Value, b.Value)
|
||||
}
|
||||
|
||||
func ValueEqual(a, b Value) bool {
|
||||
switch v := a.(type) {
|
||||
case Null:
|
||||
return v.Equal(b)
|
||||
case Boolean:
|
||||
return v.Equal(b)
|
||||
case Number:
|
||||
return v.Equal(b)
|
||||
case String:
|
||||
return v.Equal(b)
|
||||
case Var:
|
||||
return v.Equal(b)
|
||||
case Ref:
|
||||
return v.Equal(b)
|
||||
case *Array:
|
||||
return v.Equal(b)
|
||||
case *TemplateString:
|
||||
return v.Equal(b)
|
||||
}
|
||||
|
||||
return a.Compare(b) == 0
|
||||
}
|
||||
|
||||
func RefCompare(a, b Ref) int {
|
||||
return termSliceCompare(a, b)
|
||||
}
|
||||
|
||||
func RefEqual(a, b Ref) bool {
|
||||
return termSliceEqual(a, b)
|
||||
}
|
||||
|
||||
func NumberCompare(x, y Number) int {
|
||||
xs, ys := string(x), string(y)
|
||||
|
||||
var xIsF, yIsF bool
|
||||
|
||||
// Treat "1" and "1.0", "1.00", etc as "1"
|
||||
if strings.Contains(xs, ".") {
|
||||
if tx := strings.TrimRight(xs, ".0"); tx != xs {
|
||||
// Still a float after trimming?
|
||||
xIsF = strings.Contains(tx, ".")
|
||||
xs = tx
|
||||
}
|
||||
}
|
||||
if strings.Contains(ys, ".") {
|
||||
if ty := strings.TrimRight(ys, ".0"); ty != ys {
|
||||
yIsF = strings.Contains(ty, ".")
|
||||
ys = ty
|
||||
}
|
||||
}
|
||||
if xs == ys {
|
||||
return 0
|
||||
}
|
||||
|
||||
var xi, yi int64
|
||||
var xf, yf float64
|
||||
var xiOK, yiOK, xfOK, yfOK bool
|
||||
|
||||
if xi, xiOK = x.Int64(); xiOK {
|
||||
if yi, yiOK = y.Int64(); yiOK {
|
||||
return cmp.Compare(xi, yi)
|
||||
}
|
||||
}
|
||||
|
||||
if xIsF && yIsF {
|
||||
if xf, xfOK = x.Float64(); xfOK {
|
||||
if yf, yfOK = y.Float64(); yfOK {
|
||||
if xf == yf {
|
||||
return 0
|
||||
}
|
||||
// could still be "equal" depending on precision, so we continue?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var a *big.Rat
|
||||
fa, ok := new(big.Float).SetString(string(x))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
if fa.IsInt() {
|
||||
if i, _ := fa.Int64(); i == 0 {
|
||||
a = new(big.Rat).SetInt64(0)
|
||||
}
|
||||
}
|
||||
if a == nil {
|
||||
a, ok = new(big.Rat).SetString(string(x))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
}
|
||||
|
||||
var b *big.Rat
|
||||
fb, ok := new(big.Float).SetString(string(y))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
if fb.IsInt() {
|
||||
if i, _ := fb.Int64(); i == 0 {
|
||||
b = new(big.Rat).SetInt64(0)
|
||||
}
|
||||
}
|
||||
if b == nil {
|
||||
b, ok = new(big.Rat).SetString(string(y))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
}
|
||||
|
||||
return a.Cmp(b)
|
||||
}
|
||||
Reference in New Issue
Block a user