Share
Embed
ゼロから作る Protocol Buffer のパーサーとレキサー / Writing Protocol Buffer Parser/Lexer in Go from scratch
Go Conference 2022 Spring Online の発表資料です。
https://gocon.jp/2022spring/sessions/b3-l/
元になった CFP はこちらです: https://yoheimuta.hatenablog.com/entry/2022/05/04/111034
詳しい情報ページのリンク:
- https://github.com/yoheimuta/go-protoparser
- https://github.com/yoheimuta/protolint
- https://www.amazon.co.jp/コンパイラ―原理・技法・ツール-Information-Computing-V-エイホ/dp/478191229X
- https://www.amazon.co.jp/オブジェクト指向における再利用のためのデザインパターン-エリック-ガンマ/dp/4797311126
yoheimuta
April 23, 2022
Transcript
91 slides
-
Yohei Yoshimuta θϩ͔Β࡞Δ Protocol Buffer ͷ ύʔαʔͱϨΩαʔ Go Conference 23
April 2022 ϋογϡλά: #gocon #goconB -
ࣗݾհʢॴଐʣ ύϥϨϧ: • Ϣʔβʔͷ 70 % ͕ Z ੈ •
1 ͷ௨࣌ؒฏۉ 3 ࣌ؒ Go ͷར༻࣮: • WebSocket: github.com/kuiperbelt/kuiperbelt • σʔλసૹ: Cloud DataFlow Ͱ MySQL ͔Β BigQuery -
σʔλߏͷෆҰக ଟ͘ͷϓϩάϥϛϯά՝ɺ͋ΔσʔλΛผͷߏΛ࣋ͬͨσʔλʹஔ͖ ͑Δ͜ͱͰ͋Δ • ςΩετΛߦʹׂ • όΠφϦͷγϦΞϥΠζɾσγϦΞϥΠζ • ਖ਼نදݱ •
ϨΩαʔ • ύʔαʔ ग़య: Lexical Scanning in Go: https://talks.golang.org/2011/lex.slide -
ϨΩαʔʢࣈ۟ղੳثʣ จࣈετϦʔϜΛτʔΫϯʹஔ͖͑Δ enum MachineState { option allow_alias = true ;
STOPPED = 0 ; RUNNING = 1 ; } enum MachineState { option allow_alias = true; STOPPED = 0; RUNNING = 1; } -
ϨΩαʔʢࣈ۟ղੳثʣ จࣈετϦʔϜΛτʔΫϯʹஔ͖͑Δ enum MachineState { option allow_alias = true ;
STOPPED = 0 ; RUNNING = 1 ; } enum MachineState { option allow_alias = true; STOPPED = 0; RUNNING = 1; } -
ύʔαʔʢߏจղੳثʣ τʔΫϯΛߏͷσʔλʹஔ͖͑Δ enum MachineState { option allow_alias = true ;
STOPPED = 0 ; RUNNING = 1 ; } Enum Option Field Field Name = MachineState Name = allow_alias Value = true Name =RUNNING Value = 0 Name =STOPPED Value = 1 -
ύʔαʔʢߏจղੳثʣ τʔΫϯΛߏͷσʔλʹஔ͖͑Δ enum MachineState { option allow_alias = true ;
STOPPED = 0 ; RUNNING = 1 ; } Enum Option Field Field Name = MachineState Name = allow_alias Value = true Name =RUNNING Value = 0 Name =STOPPED Value = 1 -
ࠓ͢͜ͱ • ύʔαʔͷϢʔεέʔε • ύʔαʔΛͳͥࣗ࡞͠Α͏ͱࢥͬͨͷ͔ • Ͳ͏͍͏ઃܭɾΞϓϩʔνΛऔͬͨͷ͔ • ࣮ৄࡉ •
Ԡ༻తͳ՝ • ϨΩαʔͷόοϑΝઃܭ • நߏจͷΠϯλʔϑΣʔεઃܭ -
ϨΩαʔͱύʔαʔॏཁͳߏཁૉ • ίϯύΠϥ ίϯύΠϥͷϑϩϯτΤϯυ ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -
ίϯύΠϥͷϑϩϯτΤϯυ ϨΩαʔͱύʔαʔॏཁͳߏཁૉ • ίϯύΠϥ • ੩తղੳ ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -
ίϯύΠϥͷϑϩϯτΤϯυ ϨΩαʔͱύʔαʔॏཁͳߏཁૉ • ίϯύΠϥ • ੩తղੳ • ίʔυੜ ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -
ίϯύΠϥͷϑϩϯτΤϯυ ϨΩαʔͱύʔαʔॏཁͳߏཁૉ • ίϯύΠϥ • ੩తղੳ • ίʔυੜ • σʔλϑΝΠϧ
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -
Protocol Buffer εΩʔϚ Protocol Buffers Google ͕։ൃͨ͠ݴޠɾϓϥοτϑΥʔϜʹґଘ͠ͳ͍ɺߏ Խ͞ΕͨσʔλΛγϦΞϥΠζ͢ΔΈ XML
JSON ͷΑ͏ʹ͑Δ͕ɺΑΓখ͘͞ɾૣ͘ɾ؆୯ ཉ͍͠σʔλͷߏʢεΩʔϚʣΛࣄલʹఆ͓ٛͯ͘͠ message Person { required string name = 1; required int32 id = 2; optional string email = 3; } ग़య: Protocol Bu ff ers | Google Developers: https://developers.google.com/protocol-bu ff ers -
ϨΩαʔɺύʔαʔɺΞφϥΠ βʔͷׂΛ໌֬ʹͯ͠ɺઃܭ ͨ͠͠ go-protoparser ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Protocol Buffer ? ? ? ? ? ? ? ? ? ? ? ? ? ? go-protoparser protolint -
ࠓ Protocol Buffer εΩʔϚͷύʔαʔʹযΛͯΔ syntax = "proto3"; package tutorial; message
Outer { // A message is an aggregate a set of typed fi elds. message Inner { // Level 2 int64 ival = 1; } repeated Inner inner_message = 2; EnumAllowingAlias enum_ fi eld =3; map<int32, string> my_map = 4; } -
Proto ܕ εΩʔϚ࣍ͷจ๏نଇʹै͏: • syntax จ͔Β͡·Δ • ͦͷޙɺimport จɺpackage จɺoption
จɺmessage จɺenum จɺ service จɺemptyStatement จͷͲΕ͔Λ܁Γฦ͢ // Proto represents a protocol buffer de fi nition. type Proto struct { Syntax *Synta x ProtoBody []interface{} } -
Proto ܕ syntax = "proto3"; package tutorial; message Outer {
message Inner { // Level 2 int64 ival = 1; } repeated Inner inner_message = 2; EnumAllowingAlias enum_ fi eld =3; map<int32, string> my_map = 4; } Proto Syntax Package Message Message Field Field MapField Field ProtoBody -
Parser io.Reader Lexer Proto ύʔαʔͷॳظԽ ύʔαʔϨΩαʔʹґଘ͢Δ ϨΩαʔจࣈετϦʔϜʹґଘ͢Δ // Parse parses
a Protocol Buffer fi le. func Parse(input io.Reader) (*parser.Proto, error) { p := parser.NewParser( lexer.NewLexer( input, ), ) return p.ParseProto() } -
ࣈ۟ղੳʹΑͬͯಘΒΕͨΞΠςϜ ࣈ۟ղੳͷ݁Ռ 2 ͭͷϑΟʔϧυͰද͢ • ܕ: ྫ͑ Number • :
ྫ͑ “100” // Lexer is a tokenizer. type Lexer struct { Token Token // Token is the lexical type. Text string // Text is the lexical value. It has a cool name "lexeme." -
Token ܕ τʔΫϯͨͩͷ Integer ఆ iota Λͬͯɺͯ͢ͷτʔΫϯΛྻڍ͢Δ type Token int
// Token represents a lexical type. const ( // The result of Scan is one of these tokens. TILLEGAL Token = iota // Special tokens TEO F TIDENT // Identi fi ers -
TINTLIT // Literals TFLOATLI T TBOOLLI T TSTRLI T //
Misc characters TSEMICOLON // ; TCOLON // : TEQUALS // = TQUOTE // " or ' ... TSYNTAX // Keywords TPACKAG E TMESSAG E ... -
ϨΩαʔͷΤϯτϦʔؔ ύʔαʔ lexer.Next() ͷݺͼग़͠ͱ lexer ͷΞΠςϜͷࢀরɺΛ܁Γ ฦ͠ߦ͏ // Next scans
the read buffer. func (lex *Lexer) Next() { var err error lex.Token, lex.Text, lex.Pos, err = lex.scan() if err != nil { log.Printf(`lexer encountered the error "%v"`, err) } } -
bufio.Reader Lexer จࣈετϦʔϜΛ rune ୯ҐͰૢ࡞ ͢Δ ͦͷͨΊʹɺLexer io.Reader Λ
bufio.Reader Ͱϥοϓ͍ͯ͠Δ bufio.Reader όοϑΝϦϯάͱςΩετ I/O ͷͨΊͷϔϧύʔؔΛఏڙͯ͘͠ΕΔ // Lexer is a tokenizer. type Lexer struct { ... r *bu fi o.Reade r } // NewLexer creates a new lexer. func NewLexer(r io.Reader) *Lexer { l := &Lexer{ r: bu fi o.NewReader(r), } return l } -
read ؔ var eof = rune(0) func (l *Lexer) read()
rune { ch, _, err := l.r.ReadRune() if err != nil { return eo f } return c h } -
peek ؔ func (l *Lexer) peek() rune { ch :=
l.read() l.r.UnreadRune() return c h } -
࣮ࡍʹࣈ۟ղੳΛߦ͏ؔ εςʔτϚγϯ switch จΛ ࣮ͬͯͰ͖Δ • ࠷ॳͷ rune Λ peek
͢Δ func (l *Lexer) scan() (Token, string, error) { ch := l.peek() -
࣮ࡍʹࣈ۟ղੳΛߦ͏ؔ εςʔτϚγϯ switch จΛ ࣮ͬͯͰ͖Δ • ࠷ॳͷ rune Λ peek
͢Δ • ࣍ͷΞΫγϣϯΛܾΊΔ func (l *Lexer) scan() (Token, string, error) { ch := l.peek() switch { case unicode.IsSpace(ch): case ch == eof: case isQuote(ch): -
࣮ࡍʹࣈ۟ղੳΛߦ͏ؔ εςʔτϚγϯ switch จΛ ࣮ͬͯͰ͖Δ • ࠷ॳͷ rune Λ peek
͢Δ • ࣍ͷΞΫγϣϯΛܾΊΔ • ͦͷΞΫγϣϯΛ࣮ߦ͢Δ func (l *Lexer) scan() (Token, string, error) { ch := l.peek() switch { case unicode.IsSpace(ch): l.read() return l.scan() case ch == eof: return TEOF, "", nil case isQuote(ch): lit, err := l.scanStrLit() if err != nil { return TILLEGAL, "", err } return TSTRLIT, lit, nil -
ࣝผࢠΛಡΈऔΔ // ident = letter { letter | decimalDigit |
"_" } func (l *Lexer) scanIdent() string { ident := string(l.read()) for { next := l.peek() switch { case isLetter(next), isDecimalDigit(next), next == '_': ident += string(s.read()) default: return iden t } -
จࣈͱࣈΛಡΈऔΔ // letter = "A" … "Z" | "a" …
"z" func isLetter(r rune) bool { // ref. https://en.wikipedia.org/wiki/List_of_Unicode_characters#Basic_Latin if r < 'A' { return false } if r > 'z' { return false } if r > 'Z' && r < 'a' { return false } return true } // decimalDigit = "0" … "9" func isDecimalDigit(r rune) bool { return '0' <= r && r <= '9 ' } -
σϞ: εΩʔϚΛࣈ۟ղੳ “Lex” ͢Δ import ( "fmt" "os" "github.com/yoheimuta/go-protoparser/v4/lexer" )
func main() { lex := lexer.NewLexer(os.Stdin) for !lex.IsEOF() { lex.Next() fmt.Println("[", lex.Text, "]") } } -
σϞ: εΩʔϚΛࣈ۟ղੳ “Lex” ͢Δ import ( "fmt" "os" "github.com/yoheimuta/go-protoparser/v4/lexer" )
func main() { lex := lexer.NewLexer(os.Stdin) for !lex.IsEOF() { lex.Next() fmt.Println("[", lex.Text, "]") } } -
ύʔεΛ͡ΊΔ ࠷ॳʹ syntax จΛύʔε͠ ͯɺͦͷޙʹ body ͦΕͧΕΛผͷؔʹ͚ͯ ͓͘ͱϢχοτςετ͕ॻ͖ ͘͢ͳΔ //
ParseProto parses the proto. // proto = syntax { import | package | option | topLevelDef | emptyStatement } func (p *Parser) ParseProto() (*Proto, error) { syntax, err := p.ParseSyntax() if err != nil { return nil, err } protoBody, err := p.parseProtoBody() if err != nil { return nil, err } return &Proto{ Syntax: syntax, ProtoBody: protoBody, }, nil } -
Syntax Protocol Buffer εΩʔϚʹ 2 ͭͷόʔδϣϯ͕͋Δ • syntax = "proto3"
• syntax = "proto2" // Syntax is used to de fi ne the protobuf version. type Syntax struct { ProtobufVersion string } -
func (p *Parser) ParseSyntax() (*Syntax, error) { p.lex.Next() if p.lex.Token
!= lexer.TSYNTAX { return nil, p.unexpected("syntax") // unexpected is a helper function to report the error } p.lex.Next() if p.lex.Token != lexer.TEQUALS { return nil, p.unexpected("=") } p.lex.Next() if p.lex.Token != lexer.TQUOTE { return nil, p.unexpected("quote") } p.lex.Next() if p.lex.Text != "proto3" && p.lex.Text != "proto2" { return nil, p.unexpected("proto3 or proto2") } version := p.lex.Tex t p.lex.Next() if p.lex.Token != lexer.TQUOTE { return nil, p.unexpected("quote") } p.lex.Next() if p.lex.Token != lexer.TSEMICOLON { return nil, p.unexpected(";") } return &Syntax{ ProtobufVersion: version }, nil } -
parseProtoBody 1 ͭτʔΫϯΛઌಡΈ͢Δ // protoBody = { import | package
| option | topLevelDef | emptyStatement } // topLevelDef = message | enum | service | extend func (p *Parser) parseProtoBody() ([]interface{}, error) { var protoBody []interface{} for { if p.IsEOF() { return protoBody, nil } var stmt interface{} p.lex.Next() token := p.lex.Toke n switch token { -
parseProtoBody 1 ͭτʔΫϯΛઌಡΈ͢Δ εςʔτ͕֬ఆͨ͠ΒɺͦΕ ʹରԠ͢ΔؔΛݺͿ case scanner.TIMPORT: importValue, err :=
p.ParseImport() if err != nil { return nil, er r } stmt = importValu e case scanner.TPACKAGE: packageValue, err := p.ParsePackage() if err != nil { return nil, er r } stmt = packageValue -
parseProtoBody 1 ͭτʔΫϯΛઌಡΈ͢Δ εςʔτ͕֬ఆͨ͠ΒɺͦΕ ʹରԠ͢ΔؔΛݺͿ case scanner.TOPTION: option, err :=
p.ParseOption() if err != nil { return nil, er r } stmt = optio n case scanner.TMESSAGE: message, err := p.ParseMessage() if err != nil { return nil, er r } stmt = message -
parseProtoBody 1 ͭτʔΫϯΛઌಡΈ͢Δ εςʔτ͕֬ఆͨ͠ΒɺͦΕ ʹରԠ͢ΔؔΛݺͿ case scanner.TENUM: enum, err :=
p.ParseEnum() if err != nil { return nil, er r } stmt = enu m case scanner.TSERVICE: service, err := p.ParseService() if err != nil { return nil, er r } stmt = service -
4 ͭͷεςοϓͰεΩʔϚΛύʔε͢Δ 1. ܁Γฦ͠ࣈ۟ղੳ “Lex” ͢Δ 2. ઌಡΈ “Lookahead” ͢Δ
3. εςʔτΛભҠ͢Δ 4. நߏจͷϊʔυΛΈཱͯΔ -
parseProtoBody ͦΕͧΕͷจʹରԠ͢Δϊʔ υ͕ἧͬͨΒ Proto ߏମʹ ֨ೲ͢Δ case scanner.TEXTEND: extend, err
:= p.ParseExtend() if err != nil { return nil, er r } stmt = exten d } protoBody = append(protoBody, extend) return &Proto{ Syntax: syntax, ProtoBody: protoBody, }, nil -
σϞ: εΩʔϚΛύʔε͢Δ syntax = "proto3"; package tutorial; message Outer {
message Inner { // Level 2 int64 ival = 1; } repeated Inner inner_message = 2; EnumAllowingAlias enum_ fi eld =3; map<int32, string> my_map = 4; } -
σϞ: εΩʔϚΛύʔε͢Δ syntax = "proto3"; package tutorial; message Outer {
message Inner { // Level 2 int64 ival = 1; } repeated Inner inner_message = 2; EnumAllowingAlias enum_ fi eld =3; map<int32, string> my_map = 4; } -
࠷ॳʹެ։ͨ͠ go-protoparser ʹ 2 ͭͷ͕͋ͬͨ: 1. όοΫτϥοΩϯά͕Ͱ͖ͳ͍ͱίʔυ͕ෳࡶʹͳΓ͕ͪ • ϨΩαʔʹόοϑΝػߏΛ࣋ͨͤͯɺઌಡΈ͕ؒҧͬͯͨΒ࠷ॳ
͔ΒΓͤΔΑ͏ʹͨ͠ɻৄࡉϦϙδτϦΛࢀর 2. நߏจ͕͍ͮΒ͍ • ੩తղੳثΛΧελϚΠζ͢Δͱ͖ʹେࣄͳࢹ • ࠓͪ͜ΒΛৄ͘͠ݟ͍ͯ͘ -
நߏจͷΠϯλʔϑΣʔεͷ Ϧϯλʔ ProtoBody Λࠪͯ͠ɺཁૉͷܕΛಛఆ͔ͯ͠ΒϦϯτ ϧʔϧΛద༻͢Δඞཁ͕͋Δ // Proto represents a protocol
buffer de fi nition. type Proto struct { Syntax *Synta x // ProtoBody is a slice of sum type consisted of *Import, *Package, *Option, *Message, *Enum, *Service, *Extend and *EmptyStatement. ProtoBody []interface{} } -
ܕ Switch ܕ Switch Ͱܕ Assertion Λྻ ʹࢦఆ͢Δ͜ͱ͕Ͱ͖Δ • ϘΠϥʔϓϨʔτ͕ଟ͍
• ؒҧ͍͑͢ for _, s := range src { switch t := s.(type) { case *parser.Import: case *parser.Package: case *parser.Option: case *parser.Message: -
Visitor ύλʔϯ σβΠϯύλʔϯͷҰͭ • Visitor ύλʔϯɺଟ͘ͷΫϥε ͔ΒΔΦϒδΣΫτͰߏ͞Ε ͨߏ͕͋ͬͯɺͦΕʹର͢Δ৽ ͍͠ॲཧΛఆ͍ٛͨ͠ͱ͖ʹ͏ Proto
Syntax Package Message Message Field Field MapField Field ProtoBody -
Visitor ύλʔϯ σβΠϯύλʔϯͷҰͭ • Visitor ύλʔϯɺଟ͘ͷΫϥε ͔ΒΔΦϒδΣΫτͰߏ͞Ε ͨߏ͕͋ͬͯɺͦΕʹର͢Δ৽ ͍͠ॲཧΛఆ͍ٛͨ͠ͱ͖ʹ͏ •
Visitor ΦϒδΣΫτߏͷཁૉ ʹର͢ΔॲཧΛද͢ Visitor VisitSyntax(Syntax) VisitMessage(Message) LintingVisitor VisitSyntax(Syntax) VisitMessage(Message) FormatingVisitor VisitSyntax(Syntax) VisitMessage(Message) Visitee Accept(Visitor) Syntax Accept(Visitor) Message Accept(Visitor) -
Visitor ύλʔϯΛಋೖ͢Δ 4 εςοϓ 1. ύʔαʔͰ Visitor ΠϯλʔϑΣʔεΛఆٛ͢Δ Visitor VisitSyntax(Syntax)
VisitMessage(Message) LintingVisitor VisitSyntax(Syntax) VisitMessage(Message) FormatingVisitor VisitSyntax(Syntax) VisitMessage(Message) Visitee Accept(Visitor) Syntax Accept(Visitor) Message Accept(Visitor) -
Visitor ΠϯλʔϑΣʔε Ϧϯλʔ͜ͷ Visitor Λ࣮͢Δ͜ͱʹͳΔ // Visitor is for dispatching
Protocol Buffer elements. type Visitor interface { VisitComment(*Comment) VisitEnum(*Enum) (next bool) VisitField(*Field) (next bool) VisitImport(*Import) (next bool) VisitMessage(*Message) (next bool) VisitPackage(*Package) (next bool) VisitSyntax(*Syntax) (next bool) -
Visitor ύλʔϯΛಋೖ͢Δ 4 εςοϓ 1. ύʔαʔͰ Visitor ΠϯλʔϑΣʔεΛఆٛ͢Δ 2. ύʔαʔͰ
Visitee ΠϯλʔϑΣʔεΛఆٛ͢Δ Visitor VisitSyntax(Syntax) VisitMessage(Message) LintingVisitor VisitSyntax(Syntax) VisitMessage(Message) FormatingVisitor VisitSyntax(Syntax) VisitMessage(Message) Visitee Accept(Visitor) Syntax Accept(Visitor) Message Accept(Visitor) -
Visitee ΠϯλʔϑΣʔε நߏจ͜ͷ Visitee Λ࣮͢Δ͜ͱʹͳΔ // Visitee is implemented by
all Protocol Buffer elements. type Visitee interface { Accept(v Visitor) } // Proto represents a protocol buffer de fi nition. type Proto struct { Syntax *Synta x // ProtoBody is a slice of sum type consisted of *Import, *Package, *Option, *Message, *Enum, *Service, *Extend and *EmptyStatement. ProtoBody []Visite e } -
Visitor ύλʔϯΛಋೖ͢Δ 4 εςοϓ 1. ύʔαʔͰ Visitor ΠϯλʔϑΣʔεΛఆٛ͢Δ 2. ύʔαʔͰ
Visitee ΠϯλʔϑΣʔεΛఆٛ͢Δ 3. நߏจ͕ Visitee ΠϯλʔϑΣʔεΛ࣮͢Δ Visitor VisitSyntax(Syntax) VisitMessage(Message) LintingVisitor VisitSyntax(Syntax) VisitMessage(Message) FormatingVisitor VisitSyntax(Syntax) VisitMessage(Message) Visitee Accept(Visitor) Syntax Accept(Visitor) Message Accept(Visitor) -
Visitee ࣮ ϧʔτϊʔυʢProtoʣͷ Accept ؔ ͕ɺϦϯλʔʹΑͬͯݺΕΔ Accept ؔΛ௨ͯ͠ Visitor ͕ൖ͞
ΕΔ // Accept dispatches the call to the visitor. func (p *Proto) Accept(v Visitor) { p.Syntax.Accept(v) for _, body := range p.ProtoBody { body.Accept(v) } } -
// Accept dispatches the call to the visitor. func (s
*Syntax) Accept(v Visitor) { if !v.VisitSyntax(s) { return } for _, comment := range s.Comments { comment.Accept(v) } } ͦΕͧΕͷϊʔυରԠ͢Δ Visit ؔ ΛݺͿ Ҿ͖ଓ͖ɺAccept ؔΛ௨ͯ͠ Visitor ͕ൖ͞ΕΔ Visitee ࣮ -
Visitor ύλʔϯΛಋೖ͢Δ 4 εςοϓ 1. ύʔαʔͰ Visitor ΠϯλʔϑΣʔεΛఆٛ͢Δ 2. ύʔαʔͰ
Visitee ΠϯλʔϑΣʔεΛఆٛ͢Δ 3. நߏจ͕ Visitee ΠϯλʔϑΣʔεΛ࣮͢Δ 4. ੩తղੳثʢex. Ϧϯλʔʣ͕ Visitor Πϯλʔ ϑΣʔεΛ࣮͢Δ Visitor VisitSyntax(Syntax) VisitMessage(Message) LintingVisitor VisitSyntax(Syntax) VisitMessage(Message) FormatingVisitor VisitSyntax(Syntax) VisitMessage(Message) Visitee Accept(Visitor) Syntax Accept(Visitor) Message Accept(Visitor) -
// LintingVisitor represents a visitor representing various lint operations. type
LintingVisitor struct{} func (v * LintingVisitor) VisitSyntax(s *parser.Syntax) bool { if s.ProtobufVersion != 3 { v.AddFailuref(s.Meta.Pos, "Syntax should be 3 but was %q.", s.ProtobufVersion) } return false } -
// LintingVisitor represents a visitor representing various lint operations. type
LintingVisitor struct{} func (v * LintingVisitor) VisitSyntax(s *parser.Syntax) bool { if s.ProtobufVersion != 3 { v.AddFailuref(s.Meta.Pos, "Syntax should be 3 but was %q.", s.ProtobufVersion) } return false } func (v * LintingVisitor) VisitMessage(message *parser.Message) bool { name := message.MessageNam e if !strs.IsUpperCamelCase(name) { expected := strs.ToUpperCamelCase(name) v.AddFailuref(message.Meta.Pos, "Message name %q must be UpperCamelCase like %q", name, expected) } return true } -
Visit! proto := parser.Parse(reader) linter := LintingVisitor{} proto.Accept(linter) formatter :=
FormatingVisitor{} proto.Accept(formatter) -
Visit! proto := parser.Parse(reader) linter := LintingVisitor{} proto.Accept(linter) formatter :=
FormatingVisitor{} proto.Accept(formatter) -
Thank you @yoheimuta [email protected] The Go gopher was designed by
Renée French. Illustrations by tottie.