package plan
import (
"slices"
"github.com/freeeve/gochickpeas/gql/internal/ast"
)
func MentionsVar (e ast .Expr , name string ) bool {
out := map [string ]bool {}
collectAllVars (e , out )
return out [name ]
}
func collectAllVars(e ast .Expr , out map [string ]bool ) {
collectVars (e , out , nil )
}
func freeVarsOutside(e ast .Expr , allowed []string ) []string {
locals := make (map [string ]bool , len (allowed )+4 )
for _ , a := range allowed {
locals [a ] = true
}
out := map [string ]bool {}
collectVars (e , out , locals )
bad := make ([]string , 0 , len (out ))
for v := range out {
bad = append (bad , v )
}
slices .Sort (bad )
return bad
}
func collectVars(e ast .Expr , out , locals map [string ]bool ) {
ref := func (name string ) {
if locals == nil || !locals [name ] {
out [name ] = true
}
}
bound := func (fn func (), names ...string ) {
if locals == nil {
for _ , n := range names {
out [n ] = true
}
fn ()
return
}
var added []string
for _ , n := range names {
if !locals [n ] {
locals [n ] = true
added = append (added , n )
}
}
fn ()
for _ , n := range added {
delete (locals , n )
}
}
switch n := e .(type ) {
case nil :
return
case *ast .Var :
ref (n .Name )
case *ast .Prop :
ref (n .Var )
case *ast .MapProj :
ref (n .Var )
for _ , en := range n .Entries {
if en .Expr != nil {
collectVars (en .Expr , out , locals )
}
}
case *ast .HasLabelExpr :
ref (n .Var )
case *ast .Cost :
ref (n .From )
ref (n .To )
case *ast .Unary :
collectVars (n .Expr , out , locals )
case *ast .IsNull :
collectVars (n .Expr , out , locals )
case *ast .IsTruth :
collectVars (n .Expr , out , locals )
case *ast .IsTyped :
collectVars (n .Expr , out , locals )
case *ast .Binary :
collectVars (n .LHS , out , locals )
collectVars (n .RHS , out , locals )
case *ast .Func :
for _ , a := range n .Args {
collectVars (a , out , locals )
}
case *ast .ListExpr :
for _ , a := range n .Elems {
collectVars (a , out , locals )
}
case *ast .In :
collectVars (n .Expr , out , locals )
collectVars (n .List , out , locals )
case *ast .Case :
if n .Operand != nil {
collectVars (n .Operand , out , locals )
}
for _ , w := range n .Whens {
collectVars (w .Cond , out , locals )
collectVars (w .Result , out , locals )
}
if n .Else != nil {
collectVars (n .Else , out , locals )
}
case *ast .Exists :
bound (func () { collectVars (n .Where , out , locals ) }, patternVarNames (n .Pattern )...)
case *ast .CountSub :
bound (func () { collectVars (n .Where , out , locals ) }, patternVarNames (n .Pattern )...)
case *ast .PatternComp :
bound (func () {
collectVars (n .Where , out , locals )
collectVars (n .Proj , out , locals )
}, patternVarNames (n .Pattern )...)
case *ast .ListPred :
collectVars (n .List , out , locals )
bound (func () { collectVars (n .Pred , out , locals ) }, n .Var )
case *ast .Reduce :
collectVars (n .Init , out , locals )
collectVars (n .List , out , locals )
bound (func () { collectVars (n .Body , out , locals ) }, n .Acc , n .Var )
case *ast .ListComp :
collectVars (n .List , out , locals )
bound (func () {
if n .Filter != nil {
collectVars (n .Filter , out , locals )
}
if n .Map != nil {
collectVars (n .Map , out , locals )
}
}, n .Var )
case *ast .Index :
collectVars (n .Base , out , locals )
collectVars (n .Idx , out , locals )
case *ast .Slice :
collectVars (n .Base , out , locals )
if n .From != nil {
collectVars (n .From , out , locals )
}
if n .To != nil {
collectVars (n .To , out , locals )
}
case *ast .PropOf :
collectVars (n .Base , out , locals )
case *ast .MapLit :
for _ , f := range n .Fields {
collectVars (f .Val , out , locals )
}
}
}
func renameFree(e ast .Expr , from , to string ) ast .Expr {
rec := func (c ast .Expr ) ast .Expr { return renameFree (c , from , to ) }
shadowed := func (names ...string ) bool {
return slices .Contains (names , from )
}
switch n := e .(type ) {
case *ast .Var :
if n .Name == from {
return &ast .Var {Name : to }
}
case *ast .Prop :
if n .Var == from {
n .Var = to
}
case *ast .HasLabelExpr :
if n .Var == from {
n .Var = to
}
case *ast .MapProj :
if n .Var == from {
n .Var = to
}
for i := range n .Entries {
if n .Entries [i ].Kind == ast .MapProjField {
n .Entries [i ].Expr = rec (n .Entries [i ].Expr )
}
}
case *ast .Cost :
if n .From == from {
n .From = to
}
if n .To == from {
n .To = to
}
case *ast .Unary :
n .Expr = rec (n .Expr )
case *ast .IsNull :
n .Expr = rec (n .Expr )
case *ast .IsTruth :
n .Expr = rec (n .Expr )
case *ast .IsTyped :
n .Expr = rec (n .Expr )
case *ast .PropOf :
n .Base = rec (n .Base )
case *ast .Binary :
n .LHS = rec (n .LHS )
n .RHS = rec (n .RHS )
case *ast .In :
n .Expr = rec (n .Expr )
n .List = rec (n .List )
case *ast .Index :
n .Base = rec (n .Base )
n .Idx = rec (n .Idx )
case *ast .Func :
for i := range n .Args {
n .Args [i ] = rec (n .Args [i ])
}
case *ast .ListExpr :
for i := range n .Elems {
n .Elems [i ] = rec (n .Elems [i ])
}
case *ast .Case :
if n .Operand != nil {
n .Operand = rec (n .Operand )
}
for i := range n .Whens {
n .Whens [i ].Cond = rec (n .Whens [i ].Cond )
n .Whens [i ].Result = rec (n .Whens [i ].Result )
}
if n .Else != nil {
n .Else = rec (n .Else )
}
case *ast .Slice :
n .Base = rec (n .Base )
if n .From != nil {
n .From = rec (n .From )
}
if n .To != nil {
n .To = rec (n .To )
}
case *ast .MapLit :
for i := range n .Fields {
n .Fields [i ].Val = rec (n .Fields [i ].Val )
}
case *ast .ListPred :
n .List = rec (n .List )
if !shadowed (n .Var ) {
n .Pred = rec (n .Pred )
}
case *ast .ListComp :
n .List = rec (n .List )
if !shadowed (n .Var ) {
if n .Filter != nil {
n .Filter = rec (n .Filter )
}
if n .Map != nil {
n .Map = rec (n .Map )
}
}
case *ast .Reduce :
n .Init = rec (n .Init )
n .List = rec (n .List )
if !shadowed (n .Acc , n .Var ) {
n .Body = rec (n .Body )
}
case *ast .Exists :
if n .Where != nil && !shadowed (patternVarNames (n .Pattern )...) {
n .Where = rec (n .Where )
}
case *ast .CountSub :
if n .Where != nil && !shadowed (patternVarNames (n .Pattern )...) {
n .Where = rec (n .Where )
}
case *ast .PatternComp :
if !shadowed (patternVarNames (n .Pattern )...) {
if n .Where != nil {
n .Where = rec (n .Where )
}
n .Proj = rec (n .Proj )
}
}
return e
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .