| // Copyright 2020 CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package subsume |
| |
| // TODO: structural subsumption has not yet been implemented. |
| |
| import "cuelang.org/go/internal/core/adt" |
| |
| func (s *subsumer) subsumes(gt, lt adt.Conjunct) bool { |
| if gt == lt { |
| return true |
| } |
| |
| // First try evaluating at the value level. |
| x, _ := gt.Expr().(adt.Value) |
| y, _ := lt.Expr().(adt.Value) |
| if x == nil { |
| // Fall back to structural. |
| return s.structural(gt, lt) |
| } |
| if y == nil { |
| return false |
| } |
| |
| return s.values(x, y) |
| } |
| |
| func (s *subsumer) conjunct(gt, lt adt.Conjunct) bool { |
| return false |
| } |
| |
| func (s *subsumer) c(env *adt.Environment, x adt.Expr) adt.Conjunct { |
| return adt.MakeRootConjunct(env, x) |
| } |
| |
| func isBottomConjunct(c adt.Conjunct) bool { |
| b, _ := c.Expr().(*adt.Bottom) |
| return b != nil |
| } |
| |
| func (s *subsumer) node(env *adt.Environment, up int32) *adt.Vertex { |
| for ; up != 0; up-- { |
| env = env.Up |
| } |
| return env.Vertex |
| } |
| |
| func (s *subsumer) structural(a, b adt.Conjunct) bool { |
| if y, ok := b.Expr().(*adt.LetReference); ok { |
| return s.conjunct(a, s.c(b.Env, y.X)) |
| } |
| if isBottomConjunct(b) { |
| return true |
| } |
| |
| switch x := a.Expr().(type) { |
| case *adt.DisjunctionExpr: |
| |
| case *adt.StructLit: |
| case *adt.ListLit: |
| |
| case *adt.FieldReference: |
| if y, ok := b.Expr().(*adt.FieldReference); ok && x.Label == y.Label { |
| if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { |
| return true |
| } |
| } |
| |
| case *adt.LabelReference: |
| if y, ok := b.Expr().(*adt.LabelReference); ok { |
| if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { |
| return true |
| } |
| } |
| |
| case *adt.DynamicReference: |
| if y, ok := b.Expr().(*adt.FieldReference); ok { |
| if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { |
| return true |
| } |
| } |
| |
| case *adt.ImportReference: |
| if y, ok := b.Expr().(*adt.ImportReference); ok && |
| x.ImportPath == y.ImportPath { |
| return true |
| } |
| |
| case *adt.LetReference: |
| return s.conjunct(s.c(a.Env, x.X), b) |
| |
| case *adt.SelectorExpr: |
| if y, ok := a.Expr().(*adt.SelectorExpr); ok && |
| x.Sel == y.Sel && |
| s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) { |
| return true |
| } |
| |
| case *adt.IndexExpr: |
| if y, ok := b.Expr().(*adt.IndexExpr); ok && |
| s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) && |
| s.conjunct(s.c(a.Env, x.Index), s.c(b.Env, y.Index)) { |
| return true |
| } |
| |
| case *adt.SliceExpr: |
| if r, ok := b.Expr().(*adt.SliceExpr); ok && |
| s.conjunct(s.c(a.Env, x.X), s.c(b.Env, r.X)) && |
| s.conjunct(s.c(a.Env, x.Lo), s.c(b.Env, r.Lo)) && |
| s.conjunct(s.c(a.Env, x.Hi), s.c(b.Env, r.Hi)) { |
| return true |
| } |
| |
| case *adt.Interpolation: |
| switch y := b.Expr().(type) { |
| case *adt.String: |
| // Be conservative if not ground. |
| s.inexact = true |
| |
| case *adt.Interpolation: |
| // structural equivalence |
| if len(x.Parts) != len(y.Parts) { |
| return false |
| } |
| for i, p := range x.Parts { |
| if !s.conjunct(s.c(a.Env, p), s.c(b.Env, y.Parts[i])) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| case *adt.BoundExpr: |
| if y, ok := b.Expr().(*adt.BoundExpr); ok && x.Op == y.Op { |
| return s.conjunct(s.c(a.Env, x.Expr), s.c(b.Env, y.Expr)) |
| } |
| |
| case *adt.UnaryExpr: |
| if y, ok := b.Expr().(*adt.UnaryExpr); ok && x.Op == y.Op { |
| return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) |
| } |
| |
| case *adt.BinaryExpr: |
| if y, ok := b.Expr().(*adt.BinaryExpr); ok && x.Op == y.Op { |
| return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) && |
| s.conjunct(s.c(a.Env, x.Y), s.c(b.Env, y.Y)) |
| } |
| |
| case *adt.CallExpr: |
| if y, ok := b.Expr().(*adt.CallExpr); ok { |
| if len(x.Args) != len(y.Args) { |
| return false |
| } |
| for i, arg := range x.Args { |
| if !s.conjunct(s.c(a.Env, arg), s.c(b.Env, y.Args[i])) { |
| return false |
| } |
| } |
| return s.conjunct(s.c(a.Env, x.Fun), s.c(b.Env, y.Fun)) |
| } |
| } |
| return false |
| } |
| |
| func (s *subsumer) structLit( |
| ea *adt.Environment, sa *adt.StructLit, |
| eb *adt.Environment, sb *adt.StructLit) bool { |
| |
| // Create index of instance fields. |
| ca := newCollatedDecls() |
| ca.collate(ea, sa) |
| |
| if ca.yielders != nil || ca.dynamic != nil { |
| // TODO: we could do structural comparison of comprehensions |
| // in many cases. For instance, an if clause would subsume |
| // structurally if it subsumes any of the if clauses in sb. |
| s.inexact = true |
| return false |
| } |
| |
| cb := newCollatedDecls() |
| cb.collate(eb, sb) |
| |
| if ca.hasOptional && !s.IgnoreOptional { |
| // TODO: same argument here as for comprehensions. This could |
| // be made to work. |
| if ca.pattern != nil || ca.dynamic != nil { |
| s.inexact = true |
| return false |
| } |
| |
| // for f, b := range cb.fields { |
| // if !b.required || f.IsDef() { |
| // continue |
| // } |
| // name := ctx.LabelStr(b.Label) |
| // arg := &stringLit{x.baseValue, name, nil} |
| // u, _ := x.optionals.constraint(ctx, arg) |
| // if u != nil && !s.subsumes(u, b.v) { |
| // return false |
| // } |
| // } |
| |
| } |
| |
| return false |
| |
| } |
| |
| // collatedDecls is used to compute the structural subsumption of two |
| // struct literals. |
| type collatedDecls struct { |
| fields map[adt.Feature]field |
| yielders []adt.Yielder |
| pattern []*adt.BulkOptionalField |
| dynamic []*adt.DynamicField |
| values []adt.Expr |
| additional []*adt.Ellipsis |
| isOpen bool |
| hasOptional bool |
| } |
| |
| func newCollatedDecls() *collatedDecls { |
| return &collatedDecls{fields: map[adt.Feature]field{}} |
| } |
| |
| type field struct { |
| required bool |
| conjuncts []adt.Conjunct |
| } |
| |
| func (c *collatedDecls) collate(env *adt.Environment, s *adt.StructLit) { |
| for _, d := range s.Decls { |
| switch x := d.(type) { |
| case *adt.Field: |
| e := c.fields[x.Label] |
| e.required = true |
| e.conjuncts = append(e.conjuncts, adt.MakeRootConjunct(env, x)) |
| c.fields[x.Label] = e |
| |
| case *adt.OptionalField: |
| e := c.fields[x.Label] |
| e.conjuncts = append(e.conjuncts, adt.MakeRootConjunct(env, x)) |
| c.fields[x.Label] = e |
| c.hasOptional = true |
| |
| case *adt.BulkOptionalField: |
| c.pattern = append(c.pattern, x) |
| c.hasOptional = true |
| |
| case *adt.DynamicField: |
| c.dynamic = append(c.dynamic, x) |
| c.hasOptional = true |
| |
| case *adt.Ellipsis: |
| c.isOpen = true |
| c.additional = append(c.additional, x) |
| |
| case *adt.ForClause: |
| c.yielders = append(c.yielders, x) |
| |
| case *adt.IfClause: |
| c.yielders = append(c.yielders, x) |
| |
| case *adt.LetClause: |
| case *adt.ValueClause: |
| } |
| } |
| } |