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 26import ( 27 "text/scanner" 28) 29 30// Service defines a set of RPC calls. 31type Service struct { 32 Position scanner.Position 33 Comment *Comment 34 Name string 35 Elements []Visitee 36 Parent Visitee 37} 38 39// Accept dispatches the call to the visitor. 40func (s *Service) Accept(v Visitor) { 41 v.VisitService(s) 42} 43 44// Doc is part of Documented 45func (s *Service) Doc() *Comment { 46 return s.Comment 47} 48 49// addElement is part of elementContainer 50func (s *Service) addElement(v Visitee) { 51 v.parent(s) 52 s.Elements = append(s.Elements, v) 53} 54 55// elements is part of elementContainer 56func (s *Service) elements() []Visitee { 57 return s.Elements 58} 59 60// takeLastComment is part of elementContainer 61// removes and returns the last elements of the list if it is a Comment. 62func (s *Service) takeLastComment(expectedOnLine int) (last *Comment) { 63 last, s.Elements = takeLastCommentIfEndsOnLine(s.Elements, expectedOnLine) 64 return 65} 66 67// parse continues after reading "service" 68func (s *Service) parse(p *Parser) error { 69 pos, tok, lit := p.nextIdentifier() 70 if tok != tIDENT { 71 if !isKeyword(tok) { 72 return p.unexpected(lit, "service identifier", s) 73 } 74 } 75 s.Name = lit 76 pos, tok, lit = p.next() 77 if tok != tLEFTCURLY { 78 return p.unexpected(lit, "service opening {", s) 79 } 80 for { 81 pos, tok, lit = p.next() 82 switch tok { 83 case tCOMMENT: 84 if com := mergeOrReturnComment(s.Elements, lit, pos); com != nil { // not merged? 85 s.addElement(com) 86 } 87 case tOPTION: 88 opt := new(Option) 89 opt.Position = pos 90 opt.Comment, s.Elements = takeLastCommentIfEndsOnLine(s.elements(), pos.Line-1) 91 if err := opt.parse(p); err != nil { 92 return err 93 } 94 s.addElement(opt) 95 case tRPC: 96 rpc := new(RPC) 97 rpc.Position = pos 98 rpc.Comment, s.Elements = takeLastCommentIfEndsOnLine(s.Elements, pos.Line-1) 99 err := rpc.parse(p) 100 if err != nil { 101 return err 102 } 103 s.addElement(rpc) 104 maybeScanInlineComment(p, s) 105 case tSEMICOLON: 106 maybeScanInlineComment(p, s) 107 case tRIGHTCURLY: 108 goto done 109 default: 110 return p.unexpected(lit, "service comment|rpc", s) 111 } 112 } 113done: 114 return nil 115} 116 117func (s *Service) parent(v Visitee) { s.Parent = v } 118 119// RPC represents an rpc entry in a message. 120type RPC struct { 121 Position scanner.Position 122 Comment *Comment 123 Name string 124 RequestType string 125 StreamsRequest bool 126 ReturnsType string 127 StreamsReturns bool 128 Elements []Visitee 129 InlineComment *Comment 130 Parent Visitee 131 132 // Options field is DEPRECATED, use Elements instead. 133 Options []*Option 134} 135 136// Accept dispatches the call to the visitor. 137func (r *RPC) Accept(v Visitor) { 138 v.VisitRPC(r) 139} 140 141// Doc is part of Documented 142func (r *RPC) Doc() *Comment { 143 return r.Comment 144} 145 146// inlineComment is part of commentInliner. 147func (r *RPC) inlineComment(c *Comment) { 148 r.InlineComment = c 149} 150 151// parse continues after reading "rpc" 152func (r *RPC) parse(p *Parser) error { 153 pos, tok, lit := p.next() 154 if tok != tIDENT { 155 return p.unexpected(lit, "rpc method", r) 156 } 157 r.Name = lit 158 pos, tok, lit = p.next() 159 if tok != tLEFTPAREN { 160 return p.unexpected(lit, "rpc type opening (", r) 161 } 162 pos, tok, lit = p.nextTypeName() 163 if tSTREAM == tok { 164 r.StreamsRequest = true 165 pos, tok, lit = p.nextTypeName() 166 } 167 if tok != tIDENT { 168 return p.unexpected(lit, "rpc stream | request type", r) 169 } 170 r.RequestType = lit 171 pos, tok, lit = p.next() 172 if tok != tRIGHTPAREN { 173 return p.unexpected(lit, "rpc type closing )", r) 174 } 175 pos, tok, lit = p.next() 176 if tok != tRETURNS { 177 return p.unexpected(lit, "rpc returns", r) 178 } 179 pos, tok, lit = p.next() 180 if tok != tLEFTPAREN { 181 return p.unexpected(lit, "rpc type opening (", r) 182 } 183 pos, tok, lit = p.nextTypeName() 184 if tSTREAM == tok { 185 r.StreamsReturns = true 186 pos, tok, lit = p.nextTypeName() 187 } 188 if tok != tIDENT { 189 return p.unexpected(lit, "rpc stream | returns type", r) 190 } 191 r.ReturnsType = lit 192 pos, tok, lit = p.next() 193 if tok != tRIGHTPAREN { 194 return p.unexpected(lit, "rpc type closing )", r) 195 } 196 pos, tok, lit = p.next() 197 if tSEMICOLON == tok { 198 p.nextPut(pos, tok, lit) // allow for inline comment parsing 199 return nil 200 } 201 if tLEFTCURLY == tok { 202 // parse options 203 for { 204 pos, tok, lit = p.next() 205 if tRIGHTCURLY == tok { 206 break 207 } 208 if isComment(lit) { 209 if com := mergeOrReturnComment(r.elements(), lit, pos); com != nil { // not merged? 210 r.addElement(com) 211 continue 212 } 213 } 214 if tSEMICOLON == tok { 215 maybeScanInlineComment(p, r) 216 continue 217 } 218 if tOPTION == tok { 219 o := new(Option) 220 o.Position = pos 221 if err := o.parse(p); err != nil { 222 return err 223 } 224 r.addElement(o) 225 } 226 } 227 } 228 return nil 229} 230 231// addElement is part of elementContainer 232func (r *RPC) addElement(v Visitee) { 233 v.parent(r) 234 r.Elements = append(r.Elements, v) 235 // handle deprecated field 236 if option, ok := v.(*Option); ok { 237 r.Options = append(r.Options, option) 238 } 239} 240 241// elements is part of elementContainer 242func (r *RPC) elements() []Visitee { 243 return r.Elements 244} 245 246func (r *RPC) takeLastComment(expectedOnLine int) (last *Comment) { 247 last, r.Elements = takeLastCommentIfEndsOnLine(r.Elements, expectedOnLine) 248 return 249} 250 251func (r *RPC) parent(v Visitee) { r.Parent = v } 252