Skip to content

Instantly share code, notes, and snippets.

@apg

apg/README.md

Created Dec 23, 2017
Embed
What would you like to do?
Prototype: using Go's ast tools to build a comment parsing contract engine.

Overview

$ go run law.go
package main

import "fmt"

// @require: not-empty: len(x) > 0
func Foo(x string) {
        if !(len(x) > 0) {
                panic("not-empty")
        }

        fmt.Printf("Hello, %s!\n", x)
}

func main() {
        Foo("")
}

Idea: go test supports -cover which will rewrite your source code, annotating it with line coverage statements that provide a way in which to report code coverage. A new tool that could augment the build and/or test tools for the purposes of enforcing contracts during debug, or testing would be really neat. Beyond this basic prototype, however, I'm not yet sure how to get there...

package main
import (
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
"strings"
)
const Program = `
package main
import "fmt"
// @require: not-empty: len(x) > 0
func Foo(x string) {
fmt.Printf("Hello, %s!\n", x)
}
func main() {
Foo("")
}
`
func main() {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "foo.go", []byte(Program), parser.ParseComments)
if err != nil {
fmt.Println(err)
return
}
for _, dec := range f.Decls {
if fdec, ok := dec.(*ast.FuncDecl); ok {
if fdec.Doc != nil {
for _, c := range fdec.Doc.List {
bits := strings.Split(c.Text, ":")
if len(bits) != 3 {
continue
}
if !strings.Contains(bits[0], "@require") {
continue
}
contractName := fmt.Sprintf("%q", strings.TrimSpace(bits[1]))
contractRawExpr := bits[2]
expr, err := parser.ParseExprFrom(fset, "foo.go", []byte(contractRawExpr), 0)
if err != nil {
fmt.Printf("Unable to parse contract expression: %s", err)
os.Exit(1)
}
ifs := &ast.IfStmt{
Cond: &ast.UnaryExpr{Op: token.NOT, X: expr},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.ExprStmt{
X: &ast.CallExpr{
Fun: ast.NewIdent("panic"),
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: contractName}},
},
},
},
},
}
newBody := []ast.Stmt{ifs}
fdec.Body.List = append(newBody, fdec.Body.List...)
}
}
}
}
printer.Fprint(os.Stdout, fset, f)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment