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  }