package compile
import (
"strconv"
"strings"
"github.com/freeeve/gochickpeas/gql/internal/ast"
)
func decorCanon(p *ast .Pattern , where ast .Expr , anchorVar , groupVar string ) string {
var sb strings .Builder
sub := map [string ]string {anchorVar : "\x01" , groupVar : "\x02" }
if !canonPattern (&sb , p , sub ) {
return ""
}
sb .WriteByte ('|' )
if where != nil && !canonExpr (&sb , where , sub ) {
return ""
}
return sb .String ()
}
func canonAtom(sb *strings .Builder , s string ) {
sb .WriteString (strconv .Itoa (len (s )))
sb .WriteByte (':' )
sb .WriteString (s )
}
func canonAtoms(sb *strings .Builder , ss []string ) {
sb .WriteString (strconv .Itoa (len (ss )))
sb .WriteByte ('[' )
for _ , s := range ss {
canonAtom (sb , s )
}
sb .WriteByte (']' )
}
func canonVar(sb *strings .Builder , v string , sub map [string ]string ) {
if m , ok := sub [v ]; ok {
sb .WriteString (m )
return
}
sb .WriteByte ('v' )
canonAtom (sb , v )
}
func canonPattern(sb *strings .Builder , p *ast .Pattern , sub map [string ]string ) bool {
if !canonNode (sb , &p .Start , sub ) {
return false
}
for i := range p .Hops {
h := &p .Hops [i ]
if h .Rel .Length != nil {
return false
}
if h .Rel .Where != nil {
return false
}
sb .WriteByte ('-' )
sb .WriteByte (byte ('0' + int (h .Rel .Dir )))
canonVar (sb , h .Rel .Var , sub )
canonAtoms (sb , h .Rel .Types )
if len (h .Rel .Props ) > 0 || len (h .Rel .PropExprs ) > 0 {
return false
}
sb .WriteByte ('-' )
if !canonNode (sb , &h .Node , sub ) {
return false
}
}
return true
}
func canonNode(sb *strings .Builder , n *ast .NodePat , sub map [string ]string ) bool {
sb .WriteByte ('(' )
canonVar (sb , n .Var , sub )
if n .LabelExpr != nil {
if !canonLabelExpr (sb , n .LabelExpr ) {
return false
}
} else {
sb .WriteByte (':' )
canonAtoms (sb , n .Labels )
}
if len (n .PropExprs ) > 0 {
return false
}
if n .Where != nil {
return false
}
for _ , pe := range n .Props {
sb .WriteByte ('{' )
canonAtom (sb , pe .Key )
sb .WriteByte (':' )
canonLiteral (sb , pe .Val )
sb .WriteByte ('}' )
}
sb .WriteByte (')' )
return true
}
func canonLabelExpr(sb *strings .Builder , e *ast .LabelExpr ) bool {
switch e .Kind {
case ast .LabelName :
sb .WriteString (":n(" )
canonAtom (sb , e .Name )
sb .WriteByte (')' )
case ast .LabelAnd , ast .LabelOr :
if e .Kind == ast .LabelAnd {
sb .WriteString (":and(" )
} else {
sb .WriteString (":or(" )
}
if !canonLabelExpr (sb , e .L ) || !canonLabelExpr (sb , e .R ) {
return false
}
sb .WriteByte (')' )
case ast .LabelWild :
sb .WriteString (":w" )
case ast .LabelNot :
sb .WriteString (":not(" )
if !canonLabelExpr (sb , e .L ) {
return false
}
sb .WriteByte (')' )
default :
return false
}
return true
}
func canonLiteral(sb *strings .Builder , l ast .Literal ) {
switch l .Kind {
case ast .LitInt :
sb .WriteByte ('i' )
sb .WriteString (strconv .FormatInt (l .I , 10 ))
case ast .LitFloat :
sb .WriteByte ('f' )
sb .WriteString (strconv .FormatFloat (l .F , 'b' , -1 , 64 ))
case ast .LitStr :
sb .WriteByte ('s' )
sb .WriteString (strconv .Quote (l .S ))
case ast .LitBool :
sb .WriteByte ('b' )
if l .B {
sb .WriteByte ('1' )
} else {
sb .WriteByte ('0' )
}
case ast .LitParam :
sb .WriteByte ('p' )
sb .WriteString (strconv .FormatUint (uint64 (l .P ), 10 ))
case ast .LitNamedParam :
sb .WriteString ("P(" )
canonAtom (sb , l .S )
sb .WriteByte (')' )
default :
sb .WriteByte ('n' )
}
}
func canonExpr(sb *strings .Builder , e ast .Expr , sub map [string ]string ) bool {
switch n := e .(type ) {
case *ast .Lit :
canonLiteral (sb , n .Value )
case *ast .Var :
canonVar (sb , n .Name , sub )
case *ast .Prop :
canonVar (sb , n .Var , sub )
sb .WriteByte ('.' )
canonAtom (sb , n .Key )
case *ast .PropOf :
sb .WriteString ("of(" )
if !canonExpr (sb , n .Base , sub ) {
return false
}
sb .WriteByte ('.' )
canonAtom (sb , n .Key )
sb .WriteByte (')' )
case *ast .Unary :
sb .WriteByte ('u' )
sb .WriteByte (byte ('0' + int (n .Op )))
if !canonExpr (sb , n .Expr , sub ) {
return false
}
case *ast .Binary :
sb .WriteString ("(b" )
sb .WriteString (strconv .Itoa (int (n .Op )))
if !canonExpr (sb , n .LHS , sub ) {
return false
}
sb .WriteByte (',' )
if !canonExpr (sb , n .RHS , sub ) {
return false
}
sb .WriteByte (')' )
case *ast .IsNull :
sb .WriteString ("isnull(" )
if n .Negated {
sb .WriteByte ('!' )
}
if !canonExpr (sb , n .Expr , sub ) {
return false
}
sb .WriteByte (')' )
case *ast .In :
sb .WriteString ("in(" )
if !canonExpr (sb , n .Expr , sub ) {
return false
}
sb .WriteByte (',' )
if !canonExpr (sb , n .List , sub ) {
return false
}
sb .WriteByte (')' )
case *ast .ListExpr :
sb .WriteString ("l(" )
for _ , el := range n .Elems {
if !canonExpr (sb , el , sub ) {
return false
}
sb .WriteByte (',' )
}
sb .WriteByte (')' )
case *ast .Func :
sb .WriteString ("fn(" )
canonAtom (sb , strings .ToLower (n .Name ))
if n .Distinct || n .Star {
return false
}
for _ , a := range n .Args {
sb .WriteByte (',' )
if !canonExpr (sb , a , sub ) {
return false
}
}
sb .WriteByte (')' )
case *ast .Case :
sb .WriteString ("case(" )
if n .Operand != nil {
if !canonExpr (sb , n .Operand , sub ) {
return false
}
}
for _ , w := range n .Whens {
sb .WriteString (";w" )
if !canonExpr (sb , w .Cond , sub ) {
return false
}
sb .WriteString (";t" )
if !canonExpr (sb , w .Result , sub ) {
return false
}
}
if n .Else != nil {
sb .WriteString (";e" )
if !canonExpr (sb , n .Else , sub ) {
return false
}
}
sb .WriteByte (')' )
case *ast .HasLabelExpr :
sb .WriteString ("has(" )
canonVar (sb , n .Var , sub )
if !canonLabelExpr (sb , n .Expr ) {
return false
}
sb .WriteByte (')' )
default :
return false
}
return true
}
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 .