1// Copyright (c) 2017 Ernest Micklei 2// 3// MIT License 4// 5// Permission is hereby granted, free of charge, to any person obtaining 6// a copy of this software and associated documentation files (the 7// "Software"), to deal in the Software without restriction, including 8// without limitation the rights to use, copy, modify, merge, publish, 9// distribute, sublicense, and/or sell copies of the Software, and to 10// permit persons to whom the Software is furnished to do so, subject to 11// the following conditions: 12// 13// The above copyright notice and this permission notice shall be 14// included in all copies or substantial portions of the Software. 15// 16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 24package proto 25 26// Proto represents a .proto definition 27type Proto struct { 28 Filename string 29 Elements []Visitee 30} 31 32// Accept dispatches the call to the visitor. 33func (proto *Proto) Accept(v Visitor) { 34 // As Proto is not (yet) a Visitee, we enumerate its elements instead 35 //v.VisitProto(proto) 36 for _, each := range proto.Elements { 37 each.Accept(v) 38 } 39} 40 41// addElement is part of elementContainer 42func (proto *Proto) addElement(v Visitee) { 43 v.parent(proto) 44 proto.Elements = append(proto.Elements, v) 45} 46 47// elements is part of elementContainer 48func (proto *Proto) elements() []Visitee { 49 return proto.Elements 50} 51 52// takeLastComment is part of elementContainer 53// removes and returns the last element of the list if it is a Comment. 54func (proto *Proto) takeLastComment(expectedOnLine int) (last *Comment) { 55 last, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, expectedOnLine) 56 return 57} 58 59// parse parsers a complete .proto definition source. 60func (proto *Proto) parse(p *Parser) error { 61 for { 62 pos, tok, lit := p.next() 63 switch { 64 case isComment(lit): 65 if com := mergeOrReturnComment(proto.Elements, lit, pos); com != nil { // not merged? 66 proto.Elements = append(proto.Elements, com) 67 } 68 case tOPTION == tok: 69 o := new(Option) 70 o.Position = pos 71 o.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 72 if err := o.parse(p); err != nil { 73 return err 74 } 75 proto.addElement(o) 76 case tSYNTAX == tok: 77 s := new(Syntax) 78 s.Position = pos 79 s.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 80 if err := s.parse(p); err != nil { 81 return err 82 } 83 proto.addElement(s) 84 case tIMPORT == tok: 85 im := new(Import) 86 im.Position = pos 87 im.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 88 if err := im.parse(p); err != nil { 89 return err 90 } 91 proto.addElement(im) 92 case tENUM == tok: 93 enum := new(Enum) 94 enum.Position = pos 95 enum.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 96 if err := enum.parse(p); err != nil { 97 return err 98 } 99 proto.addElement(enum) 100 case tSERVICE == tok: 101 service := new(Service) 102 service.Position = pos 103 service.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 104 err := service.parse(p) 105 if err != nil { 106 return err 107 } 108 proto.addElement(service) 109 case tPACKAGE == tok: 110 pkg := new(Package) 111 pkg.Position = pos 112 pkg.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 113 if err := pkg.parse(p); err != nil { 114 return err 115 } 116 proto.addElement(pkg) 117 case tMESSAGE == tok: 118 msg := new(Message) 119 msg.Position = pos 120 msg.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 121 if err := msg.parse(p); err != nil { 122 return err 123 } 124 proto.addElement(msg) 125 // BEGIN proto2 126 case tEXTEND == tok: 127 msg := new(Message) 128 msg.Position = pos 129 msg.Comment, proto.Elements = takeLastCommentIfEndsOnLine(proto.Elements, pos.Line-1) 130 msg.IsExtend = true 131 if err := msg.parse(p); err != nil { 132 return err 133 } 134 proto.addElement(msg) 135 // END proto2 136 case tSEMICOLON == tok: 137 maybeScanInlineComment(p, proto) 138 // continue 139 case tEOF == tok: 140 goto done 141 default: 142 return p.unexpected(lit, ".proto element {comment|option|import|syntax|enum|service|package|message}", p) 143 } 144 } 145done: 146 return nil 147} 148 149func (proto *Proto) parent(v Visitee) {} 150 151// elementContainer unifies types that have elements. 152type elementContainer interface { 153 addElement(v Visitee) 154 elements() []Visitee 155 takeLastComment(expectedOnLine int) *Comment 156} 157