package flowclass import ( "bytes" "fmt" "idio.link/go/netaddr/v2" "idio.link/go/parser" ) /* root ::= -> dstcs ::= | {, ..., } dstc ::= : srcs ::= dsts ::= pfxs ::= | {, ..., } pfx ::= [.[...]][/] oct ::= 0-255 port ::= 0-65535 len ::= 0-32 ipps ::= | {, ..., } ippi ::= 0 | [(, ..., )] ipp ::= icmp | tcp | udp | 1-255 */ /************************** Root **************************/ type fcRoot struct{} func (t fcRoot) Shift(sm *parser.SM, e rune) error { switch e { case ' ', '\n': return nil } last := len(sm.Top().Nodes) - 1 if last == -1 { return sm.Push(fcSrcs{}).Shift(sm, e) } token := sm.Top().Nodes[last].ASTNode switch token.(type) { case fcSrcs: buf := new(bytes.Buffer) buf.WriteRune(e) sm.Push(fcArrow{val: buf}) case fcArrow: return sm.Push(fcDsts{}).Shift(sm, e) case fcDsts: buf := new(bytes.Buffer) buf.WriteRune(e) sm.Push(fcIPPDelim{val: buf}) case fcIPPDelim: return sm.Push(fcIPPs{}).Shift(sm, e) case fcIPPs: return nil default: return fmt.Errorf("unexpected state: %+v; event: %v", sm, e) } return nil } func (t fcRoot) Coalesce(nodes []*parser.CSTNode) any { nodes[2].Coalesce().([]*DstSock), fc := &FlowClass{ srcs: nodes[0].Coalesce().(map[string]*netaddr.NetAddr), dsts: nodes[2].Coalesce().([]*DstSock), } if len(nodes) == 5 { fc.ipps = nodes[4].Coalesce().(map[string]IPP) } return fc } func (t fcRoot) Value() *bytes.Buffer { return nil } /************************** Srcs **************************/ type fcSrcs struct{} func (t fcSrcs) Shift(sm *parser.SM, e rune) error { if len(sm.Top().Nodes) == 0 { return sm.Push(fcPfxs{}).Shift(sm, e) } return parser.NewUnrecognized("sources", e) } func (t fcSrcs) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcSrcs) Value() *bytes.Buffer { return nil } /************************* Arrow **************************/ type fcArrow struct { val *bytes.Buffer } func (t fcArrow) Shift(sm *parser.SM, e rune) error { switch e { case '-', '>': t.val.WriteRune(e) default: if string(t.val.Bytes()) != "->" { return fmt.Errorf("arrow: unexpected input: '%s'", t.val.Bytes()) } return parser.NewUnrecognized("arrow", e) } return nil } func (_ fcArrow) Coalesce(_ []*parser.CSTNode) any { return nil } func (t fcArrow) Value() *bytes.Buffer { return t.val } /*********************** DstSocks *************************/ type fcDstSocks struct{} func (t fcDstSocks) Shift(sm *parser.SM, e rune) error { if len(sm.Top().Nodes) == 0 { return sm.Push(fcPfxs{}).Shift(sm, e) } return parser.NewUnrecognized("destinations", e) } func (t fcDstSocks) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcDstSocks) Value() *bytes.Buffer { return nil } /********************** DstSockSet ************************/ type fcDstSockSet struct{} func (t fcDstSockSet) Shift(sm *parser.SM, e rune) error { next := fcSet[fcDstSock, *netaddr.NetAddr]{ newToken: func() fcDstSock { return fcDstSock{} }, } if len(sm.Top().Nodes) == 0 { return sm.Push(next).Shift(sm, e) } return parser.NewUnrecognized("prefix set", e) } func (t fcDstSockSet) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcDstSockSet) Value() *bytes.Buffer { return nil } type fcDstSock struct{} /************************** Dsts **************************/ type fcDsts struct{} func (t fcDsts) Shift(sm *parser.SM, e rune) error { if len(sm.Top().Nodes) == 0 { return sm.Push(fcPfxs{}).Shift(sm, e) } return parser.NewUnrecognized("destinations", e) } func (t fcDsts) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcDsts) Value() *bytes.Buffer { return nil }