github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/matcher/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/ast" 7 "go/format" 8 "go/parser" 9 "io" 10 "os" 11 "strconv" 12 "strings" 13 ) 14 15 var template = ` 16 func () { 17 if _, νok := νidx[νkey]; νok { 18 νidx[νkey] = append(νidx[νkey], νvalue) 19 } else { 20 νidx[νkey] = []τvalue{νvalue} 21 } 22 } 23 ` 24 25 func main() { 26 t, err := parser.ParseExpr(template) 27 fmt.Println(err) 28 root := t.(*ast.FuncLit).Body.List[0] 29 30 var b bytes.Buffer 31 fmt.Fprintf(&b, "package x\nfunc match(p interface{}, varlist, typelist map[string][]*ast.Ident) bool{\n") 32 WriteMatch(&b, "p", root, false) 33 fmt.Fprintf(&b, "}\n") 34 35 formatted, err := format.Source(b.Bytes()) 36 fmt.Println(err) 37 if err != nil { 38 fmt.Println("ERRORED:", b.String()) 39 } 40 fmt.Println(string(formatted)) 41 42 ast.Print(nil, root) 43 } 44 45 func WriteMatch(w io.Writer, src string, e ast.Node, skip bool) { 46 dst := sanitize(src) 47 48 if !skip { 49 fmt.Fprintf(w, "\t%s, ok := %s.(%T);\n\tif !ok { return false }\n", dst, src, e) 50 } else { 51 dst = src 52 } 53 54 switch e := e.(type) { 55 case *ast.AssignStmt: 56 fmt.Fprintf(w, "\tif len(%s.Lhs) != %d || len(%s.Rhs) != %d { return false }\n", dst, len(e.Lhs), dst, len(e.Rhs)) 57 for idx := range e.Lhs { 58 WriteMatch(w, dst+".Lhs["+strconv.Itoa(idx)+"]", e.Lhs[idx], false) 59 } 60 for idx := range e.Rhs { 61 WriteMatch(w, dst+".Rhs["+strconv.Itoa(idx)+"]", e.Rhs[idx], false) 62 } 63 64 case *ast.CompositeLit: 65 WriteMatch(w, dst+".Type", e.Type, false) 66 fmt.Fprintf(w, "\tif len(%s.Elts) != %d { return false }\n", dst, len(e.Elts)) 67 for idx := range e.Elts { 68 WriteMatch(w, dst+".Elts["+strconv.Itoa(idx)+"]", e.Elts[idx], false) 69 } 70 case *ast.ArrayType: 71 WriteMatch(w, dst+".Elt", e.Elt, false) 72 case *ast.BasicLit: 73 // TODO: use proper constants for .Kind 74 fmt.Fprintf(w, "\tif %s.Kind != %d || %s.Value != %q { return false }\n", dst, e.Kind, dst, e.Value) 75 case *ast.Ident: 76 if strings.HasPrefix(e.Name, "ν") { 77 name := strings.TrimPrefix(e.Name, "ν") 78 fmt.Fprintf(w, "\tvarlist[%q] = append(varlist[%q], %s)\n", name, name, dst) 79 } else if strings.HasPrefix(e.Name, "τ") { 80 name := strings.TrimPrefix(e.Name, "τ") 81 fmt.Fprintf(w, "\ttypelist[%q] = append(typelist[%q], %s)\n", name, name, dst) 82 } else { 83 fmt.Fprintf(w, "\tif %q != %s.Name { return false }\n", e.Name, dst) 84 } 85 case *ast.ExprStmt: 86 WriteMatch(w, dst+".X", e.X, false) 87 case *ast.CallExpr: 88 WriteMatch(w, dst+".Fun", e.Fun, false) 89 fmt.Fprintf(w, "\tif len(%s.Args) != %d { return false }\n", dst, len(e.Args)) 90 for idx := range e.Args { 91 WriteMatch(w, dst+".Args["+strconv.Itoa(idx)+"]", e.Args[idx], false) 92 } 93 case *ast.IndexExpr: 94 WriteMatch(w, dst+".X", e.X, false) 95 WriteMatch(w, dst+".Index", e.Index, false) 96 case *ast.BinaryExpr: 97 // TODO: use proper constants for .Op 98 fmt.Fprintf(w, "\tif %s.Op != %d { return false }\n", dst, e.Op) 99 WriteMatch(w, dst+".X", e.X, false) 100 WriteMatch(w, dst+".Y", e.Y, false) 101 case *ast.ReturnStmt: 102 fmt.Fprintf(w, "\tif len(%s.Results) != %d { return false }\n", dst, len(e.Results)) 103 for idx := range e.Results { 104 WriteMatch(w, dst+".Results["+strconv.Itoa(idx)+"]", e.Results[idx], false) 105 } 106 case *ast.IfStmt: 107 WriteMatch(w, dst+".Init", e.Init, false) 108 WriteMatch(w, dst+".Cond", e.Cond, false) 109 WriteMatch(w, dst+".Body", e.Body, true) 110 WriteMatch(w, dst+".Else", e.Else, false) 111 case *ast.BlockStmt: 112 fmt.Fprintf(w, "\tif len(%s.List) != %d { return false }\n", dst, len(e.List)) 113 for idx := range e.List { 114 WriteMatch(w, dst+".List["+strconv.Itoa(idx)+"]", e.List[idx], false) 115 } 116 default: 117 fmt.Fprintf(os.Stderr, "unhandled %T\n", e) 118 } 119 } 120 121 func sanitize(s string) string { 122 return strings.Map(func(r rune) rune { 123 switch r { 124 case '.', '[', ']': 125 return '_' 126 default: 127 return r 128 } 129 }, s) 130 }