package pattern import ( "fmt" "go/ast" "go/token" "reflect" ) type Pattern struct { Root Node // Relevant contains instances of ast.Node that could potentially // initiate a successful match of the pattern. Relevant []reflect.Type } func MustParse(s string) Pattern { p := &Parser{AllowTypeInfo: true} pat, err := p.Parse(s) if err != nil { panic(err) } return pat } func roots(node Node) []reflect.Type { switch node := node.(type) { case Or: var out []reflect.Type for _, el := range node.Nodes { out = append(out, roots(el)...) } return out case Not: return roots(node.Node) case Binding: return roots(node.Node) case Nil, nil: // this branch is reached via bindings return allTypes default: Ts, ok := nodeToASTTypes[reflect.TypeOf(node)] if !ok { panic(fmt.Sprintf("internal error: unhandled type %T", node)) } return Ts } } var allTypes = []reflect.Type{ reflect.TypeOf((*ast.RangeStmt)(nil)), reflect.TypeOf((*ast.AssignStmt)(nil)), reflect.TypeOf((*ast.IndexExpr)(nil)), reflect.TypeOf((*ast.Ident)(nil)), reflect.TypeOf((*ast.ValueSpec)(nil)), reflect.TypeOf((*ast.GenDecl)(nil)), reflect.TypeOf((*ast.BinaryExpr)(nil)), reflect.TypeOf((*ast.ForStmt)(nil)), reflect.TypeOf((*ast.ArrayType)(nil)), reflect.TypeOf((*ast.DeferStmt)(nil)), reflect.TypeOf((*ast.MapType)(nil)), reflect.TypeOf((*ast.ReturnStmt)(nil)), reflect.TypeOf((*ast.SliceExpr)(nil)), reflect.TypeOf((*ast.StarExpr)(nil)), reflect.TypeOf((*ast.UnaryExpr)(nil)), reflect.TypeOf((*ast.SendStmt)(nil)), reflect.TypeOf((*ast.SelectStmt)(nil)), reflect.TypeOf((*ast.ImportSpec)(nil)), reflect.TypeOf((*ast.IfStmt)(nil)), reflect.TypeOf((*ast.GoStmt)(nil)), reflect.TypeOf((*ast.Field)(nil)), reflect.TypeOf((*ast.SelectorExpr)(nil)), reflect.TypeOf((*ast.StructType)(nil)), reflect.TypeOf((*ast.KeyValueExpr)(nil)), reflect.TypeOf((*ast.FuncType)(nil)), reflect.TypeOf((*ast.FuncLit)(nil)), reflect.TypeOf((*ast.FuncDecl)(nil)), reflect.TypeOf((*ast.ChanType)(nil)), reflect.TypeOf((*ast.CallExpr)(nil)), reflect.TypeOf((*ast.CaseClause)(nil)), reflect.TypeOf((*ast.CommClause)(nil)), reflect.TypeOf((*ast.CompositeLit)(nil)), reflect.TypeOf((*ast.EmptyStmt)(nil)), reflect.TypeOf((*ast.SwitchStmt)(nil)), reflect.TypeOf((*ast.TypeSwitchStmt)(nil)), reflect.TypeOf((*ast.TypeAssertExpr)(nil)), reflect.TypeOf((*ast.TypeSpec)(nil)), reflect.TypeOf((*ast.InterfaceType)(nil)), reflect.TypeOf((*ast.BranchStmt)(nil)), reflect.TypeOf((*ast.IncDecStmt)(nil)), reflect.TypeOf((*ast.BasicLit)(nil)), } var nodeToASTTypes = map[reflect.Type][]reflect.Type{ reflect.TypeOf(String("")): nil, reflect.TypeOf(Token(0)): nil, reflect.TypeOf(List{}): {reflect.TypeOf((*ast.BlockStmt)(nil)), reflect.TypeOf((*ast.FieldList)(nil))}, reflect.TypeOf(Builtin{}): {reflect.TypeOf((*ast.Ident)(nil))}, reflect.TypeOf(Object{}): {reflect.TypeOf((*ast.Ident)(nil))}, reflect.TypeOf(Function{}): {reflect.TypeOf((*ast.Ident)(nil)), reflect.TypeOf((*ast.SelectorExpr)(nil))}, reflect.TypeOf(Any{}): allTypes, reflect.TypeOf(RangeStmt{}): {reflect.TypeOf((*ast.RangeStmt)(nil))}, reflect.TypeOf(AssignStmt{}): {reflect.TypeOf((*ast.AssignStmt)(nil))}, reflect.TypeOf(IndexExpr{}): {reflect.TypeOf((*ast.IndexExpr)(nil))}, reflect.TypeOf(Ident{}): {reflect.TypeOf((*ast.Ident)(nil))}, reflect.TypeOf(ValueSpec{}): {reflect.TypeOf((*ast.ValueSpec)(nil))}, reflect.TypeOf(GenDecl{}): {reflect.TypeOf((*ast.GenDecl)(nil))}, reflect.TypeOf(BinaryExpr{}): {reflect.TypeOf((*ast.BinaryExpr)(nil))}, reflect.TypeOf(ForStmt{}): {reflect.TypeOf((*ast.ForStmt)(nil))}, reflect.TypeOf(ArrayType{}): {reflect.TypeOf((*ast.ArrayType)(nil))}, reflect.TypeOf(DeferStmt{}): {reflect.TypeOf((*ast.DeferStmt)(nil))}, reflect.TypeOf(MapType{}): {reflect.TypeOf((*ast.MapType)(nil))}, reflect.TypeOf(ReturnStmt{}): {reflect.TypeOf((*ast.ReturnStmt)(nil))}, reflect.TypeOf(SliceExpr{}): {reflect.TypeOf((*ast.SliceExpr)(nil))}, reflect.TypeOf(StarExpr{}): {reflect.TypeOf((*ast.StarExpr)(nil))}, reflect.TypeOf(UnaryExpr{}): {reflect.TypeOf((*ast.UnaryExpr)(nil))}, reflect.TypeOf(SendStmt{}): {reflect.TypeOf((*ast.SendStmt)(nil))}, reflect.TypeOf(SelectStmt{}): {reflect.TypeOf((*ast.SelectStmt)(nil))}, reflect.TypeOf(ImportSpec{}): {reflect.TypeOf((*ast.ImportSpec)(nil))}, reflect.TypeOf(IfStmt{}): {reflect.TypeOf((*ast.IfStmt)(nil))}, reflect.TypeOf(GoStmt{}): {reflect.TypeOf((*ast.GoStmt)(nil))}, reflect.TypeOf(Field{}): {reflect.TypeOf((*ast.Field)(nil))}, reflect.TypeOf(SelectorExpr{}): {reflect.TypeOf((*ast.SelectorExpr)(nil))}, reflect.TypeOf(StructType{}): {reflect.TypeOf((*ast.StructType)(nil))}, reflect.TypeOf(KeyValueExpr{}): {reflect.TypeOf((*ast.KeyValueExpr)(nil))}, reflect.TypeOf(FuncType{}): {reflect.TypeOf((*ast.FuncType)(nil))}, reflect.TypeOf(FuncLit{}): {reflect.TypeOf((*ast.FuncLit)(nil))}, reflect.TypeOf(FuncDecl{}): {reflect.TypeOf((*ast.FuncDecl)(nil))}, reflect.TypeOf(ChanType{}): {reflect.TypeOf((*ast.ChanType)(nil))}, reflect.TypeOf(CallExpr{}): {reflect.TypeOf((*ast.CallExpr)(nil))}, reflect.TypeOf(CaseClause{}): {reflect.TypeOf((*ast.CaseClause)(nil))}, reflect.TypeOf(CommClause{}): {reflect.TypeOf((*ast.CommClause)(nil))}, reflect.TypeOf(CompositeLit{}): {reflect.TypeOf((*ast.CompositeLit)(nil))}, reflect.TypeOf(EmptyStmt{}): {reflect.TypeOf((*ast.EmptyStmt)(nil))}, reflect.TypeOf(SwitchStmt{}): {reflect.TypeOf((*ast.SwitchStmt)(nil))}, reflect.TypeOf(TypeSwitchStmt{}): {reflect.TypeOf((*ast.TypeSwitchStmt)(nil))}, reflect.TypeOf(TypeAssertExpr{}): {reflect.TypeOf((*ast.TypeAssertExpr)(nil))}, reflect.TypeOf(TypeSpec{}): {reflect.TypeOf((*ast.TypeSpec)(nil))}, reflect.TypeOf(InterfaceType{}): {reflect.TypeOf((*ast.InterfaceType)(nil))}, reflect.TypeOf(BranchStmt{}): {reflect.TypeOf((*ast.BranchStmt)(nil))}, reflect.TypeOf(IncDecStmt{}): {reflect.TypeOf((*ast.IncDecStmt)(nil))}, reflect.TypeOf(BasicLit{}): {reflect.TypeOf((*ast.BasicLit)(nil))}, reflect.TypeOf(IntegerLiteral{}): {reflect.TypeOf((*ast.BasicLit)(nil)), reflect.TypeOf((*ast.UnaryExpr)(nil))}, reflect.TypeOf(TrulyConstantExpression{}): allTypes, // this is an over-approximation, which is fine } var requiresTypeInfo = map[string]bool{ "Function": true, "Builtin": true, "Object": true, } type Parser struct { // Allow nodes that rely on type information AllowTypeInfo bool lex *lexer cur item last *item items chan item } func (p *Parser) Parse(s string) (Pattern, error) { p.cur = item{} p.last = nil p.items = nil fset := token.NewFileSet() p.lex = &lexer{ f: fset.AddFile("", -1, len(s)), input: s, items: make(chan item), } go p.lex.run() p.items = p.lex.items root, err := p.node() if err != nil { // drain lexer if parsing failed for range p.lex.items { } return Pattern{}, err } if item := <-p.lex.items; item.typ != itemEOF { return Pattern{}, fmt.Errorf("unexpected token %s after end of pattern", item.typ) } return Pattern{ Root: root, Relevant: roots(root), }, nil } func (p *Parser) next() item { if p.last != nil { n := *p.last p.last = nil return n } var ok bool p.cur, ok = <-p.items if !ok { p.cur = item{typ: eof} } return p.cur } func (p *Parser) rewind() { p.last = &p.cur } func (p *Parser) peek() item { n := p.next() p.rewind() return n } func (p *Parser) accept(typ itemType) (item, bool) { n := p.next() if n.typ == typ { return n, true } p.rewind() return item{}, false } func (p *Parser) unexpectedToken(valid string) error { if p.cur.typ == itemError { return fmt.Errorf("error lexing input: %s", p.cur.val) } var got string switch p.cur.typ { case itemTypeName, itemVariable, itemString: got = p.cur.val default: got = "'" + p.cur.typ.String() + "'" } pos := p.lex.f.Position(token.Pos(p.cur.pos)) return fmt.Errorf("%s: expected %s, found %s", pos, valid, got) } func (p *Parser) node() (Node, error) { if _, ok := p.accept(itemLeftParen); !ok { return nil, p.unexpectedToken("'('") } typ, ok := p.accept(itemTypeName) if !ok { return nil, p.unexpectedToken("Node type") } var objs []Node for { if _, ok := p.accept(itemRightParen); ok { break } else { p.rewind() obj, err := p.object() if err != nil { return nil, err } objs = append(objs, obj) } } return p.populateNode(typ.val, objs) } func populateNode(typ string, objs []Node, allowTypeInfo bool) (Node, error) { T, ok := structNodes[typ] if !ok { return nil, fmt.Errorf("unknown node %s", typ) } if !allowTypeInfo && requiresTypeInfo[typ] { return nil, fmt.Errorf("Node %s requires type information", typ) } pv := reflect.New(T) v := pv.Elem() if v.NumField() == 1 { f := v.Field(0) if f.Type().Kind() == reflect.Slice { // Variadic node f.Set(reflect.AppendSlice(f, reflect.ValueOf(objs))) return v.Interface().(Node), nil } } if len(objs) != v.NumField() { return nil, fmt.Errorf("tried to initialize node %s with %d values, expected %d", typ, len(objs), v.NumField()) } for i := 0; i < v.NumField(); i++ { f := v.Field(i) if f.Kind() == reflect.String { if obj, ok := objs[i].(String); ok { f.Set(reflect.ValueOf(string(obj))) } else { return nil, fmt.Errorf("first argument of (Binding name node) must be string, but got %s", objs[i]) } } else { f.Set(reflect.ValueOf(objs[i])) } } return v.Interface().(Node), nil } func (p *Parser) populateNode(typ string, objs []Node) (Node, error) { return populateNode(typ, objs, p.AllowTypeInfo) } var structNodes = map[string]reflect.Type{ "Any": reflect.TypeOf(Any{}), "Ellipsis": reflect.TypeOf(Ellipsis{}), "List": reflect.TypeOf(List{}), "Binding": reflect.TypeOf(Binding{}), "RangeStmt": reflect.TypeOf(RangeStmt{}), "AssignStmt": reflect.TypeOf(AssignStmt{}), "IndexExpr": reflect.TypeOf(IndexExpr{}), "Ident": reflect.TypeOf(Ident{}), "Builtin": reflect.TypeOf(Builtin{}), "ValueSpec": reflect.TypeOf(ValueSpec{}), "GenDecl": reflect.TypeOf(GenDecl{}), "BinaryExpr": reflect.TypeOf(BinaryExpr{}), "ForStmt": reflect.TypeOf(ForStmt{}), "ArrayType": reflect.TypeOf(ArrayType{}), "DeferStmt": reflect.TypeOf(DeferStmt{}), "MapType": reflect.TypeOf(MapType{}), "ReturnStmt": reflect.TypeOf(ReturnStmt{}), "SliceExpr": reflect.TypeOf(SliceExpr{}), "StarExpr": reflect.TypeOf(StarExpr{}), "UnaryExpr": reflect.TypeOf(UnaryExpr{}), "SendStmt": reflect.TypeOf(SendStmt{}), "SelectStmt": reflect.TypeOf(SelectStmt{}), "ImportSpec": reflect.TypeOf(ImportSpec{}), "IfStmt": reflect.TypeOf(IfStmt{}), "GoStmt": reflect.TypeOf(GoStmt{}), "Field": reflect.TypeOf(Field{}), "SelectorExpr": reflect.TypeOf(SelectorExpr{}), "StructType": reflect.TypeOf(StructType{}), "KeyValueExpr": reflect.TypeOf(KeyValueExpr{}), "FuncType": reflect.TypeOf(FuncType{}), "FuncLit": reflect.TypeOf(FuncLit{}), "FuncDecl": reflect.TypeOf(FuncDecl{}), "ChanType": reflect.TypeOf(ChanType{}), "CallExpr": reflect.TypeOf(CallExpr{}), "CaseClause": reflect.TypeOf(CaseClause{}), "CommClause": reflect.TypeOf(CommClause{}), "CompositeLit": reflect.TypeOf(CompositeLit{}), "EmptyStmt": reflect.TypeOf(EmptyStmt{}), "SwitchStmt": reflect.TypeOf(SwitchStmt{}), "TypeSwitchStmt": reflect.TypeOf(TypeSwitchStmt{}), "TypeAssertExpr": reflect.TypeOf(TypeAssertExpr{}), "TypeSpec": reflect.TypeOf(TypeSpec{}), "InterfaceType": reflect.TypeOf(InterfaceType{}), "BranchStmt": reflect.TypeOf(BranchStmt{}), "IncDecStmt": reflect.TypeOf(IncDecStmt{}), "BasicLit": reflect.TypeOf(BasicLit{}), "Object": reflect.TypeOf(Object{}), "Function": reflect.TypeOf(Function{}), "Or": reflect.TypeOf(Or{}), "Not": reflect.TypeOf(Not{}), "IntegerLiteral": reflect.TypeOf(IntegerLiteral{}), "TrulyConstantExpression": reflect.TypeOf(TrulyConstantExpression{}), } func (p *Parser) object() (Node, error) { n := p.next() switch n.typ { case itemLeftParen: p.rewind() node, err := p.node() if err != nil { return node, err } if p.peek().typ == itemColon { p.next() tail, err := p.object() if err != nil { return node, err } return List{Head: node, Tail: tail}, nil } return node, nil case itemLeftBracket: p.rewind() return p.array() case itemVariable: v := n if v.val == "nil" { return Nil{}, nil } var b Binding if _, ok := p.accept(itemAt); ok { o, err := p.node() if err != nil { return nil, err } b = Binding{ Name: v.val, Node: o, } } else { p.rewind() b = Binding{Name: v.val} } if p.peek().typ == itemColon { p.next() tail, err := p.object() if err != nil { return b, err } return List{Head: b, Tail: tail}, nil } return b, nil case itemBlank: if p.peek().typ == itemColon { p.next() tail, err := p.object() if err != nil { return Any{}, err } return List{Head: Any{}, Tail: tail}, nil } return Any{}, nil case itemString: return String(n.val), nil default: return nil, p.unexpectedToken("object") } } func (p *Parser) array() (Node, error) { if _, ok := p.accept(itemLeftBracket); !ok { return nil, p.unexpectedToken("'['") } var objs []Node for { if _, ok := p.accept(itemRightBracket); ok { break } else { p.rewind() obj, err := p.object() if err != nil { return nil, err } objs = append(objs, obj) } } tail := List{} for i := len(objs) - 1; i >= 0; i-- { l := List{ Head: objs[i], Tail: tail, } tail = l } return tail, nil } /* Node ::= itemLeftParen itemTypeName Object* itemRightParen Object ::= Node | Array | Binding | itemVariable | itemBlank | itemString Array := itemLeftBracket Object* itemRightBracket Array := Object itemColon Object Binding ::= itemVariable itemAt Node */