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 "strconv" 28 "text/scanner" 29) 30 31// Enum definition consists of a name and an enum body. 32type Enum struct { 33 Position scanner.Position 34 Comment *Comment 35 Name string 36 Elements []Visitee 37} 38 39// Accept dispatches the call to the visitor. 40func (e *Enum) Accept(v Visitor) { 41 v.VisitEnum(e) 42} 43 44// Doc is part of Documented 45func (e *Enum) Doc() *Comment { 46 return e.Comment 47} 48 49// addElement is part of elementContainer 50func (e *Enum) addElement(v Visitee) { 51 e.Elements = append(e.Elements, v) 52} 53 54// elements is part of elementContainer 55func (e *Enum) elements() []Visitee { 56 return e.Elements 57} 58 59// takeLastComment is part of elementContainer 60// removes and returns the last element of the list if it is a Comment. 61func (e *Enum) takeLastComment() (last *Comment) { 62 last, e.Elements = takeLastComment(e.Elements) 63 return 64} 65 66func (e *Enum) parse(p *Parser) error { 67 pos, tok, lit := p.next() 68 if tok != tIDENT { 69 if !isKeyword(tok) { 70 return p.unexpected(lit, "enum identifier", e) 71 } 72 } 73 e.Name = lit 74 _, tok, lit = p.next() 75 if tok != tLEFTCURLY { 76 return p.unexpected(lit, "enum opening {", e) 77 } 78 for { 79 pos, tok, lit = p.next() 80 switch tok { 81 case tCOMMENT: 82 if com := mergeOrReturnComment(e.elements(), lit, pos); com != nil { // not merged? 83 e.Elements = append(e.Elements, com) 84 } 85 case tOPTION: 86 v := new(Option) 87 v.Position = pos 88 v.Comment = e.takeLastComment() 89 err := v.parse(p) 90 if err != nil { 91 return err 92 } 93 e.Elements = append(e.Elements, v) 94 case tRIGHTCURLY, tEOF: 95 goto done 96 case tSEMICOLON: 97 maybeScanInlineComment(p, e) 98 default: 99 p.nextPut(pos, tok, lit) 100 f := new(EnumField) 101 f.Position = pos 102 f.Comment = e.takeLastComment() 103 err := f.parse(p) 104 if err != nil { 105 return err 106 } 107 e.Elements = append(e.Elements, f) 108 } 109 } 110done: 111 if tok != tRIGHTCURLY { 112 return p.unexpected(lit, "enum closing }", e) 113 } 114 return nil 115} 116 117// EnumField is part of the body of an Enum. 118type EnumField struct { 119 Position scanner.Position 120 Comment *Comment 121 Name string 122 Integer int 123 ValueOption *Option 124 InlineComment *Comment 125} 126 127// Accept dispatches the call to the visitor. 128func (f *EnumField) Accept(v Visitor) { 129 v.VisitEnumField(f) 130} 131 132// inlineComment is part of commentInliner. 133func (f *EnumField) inlineComment(c *Comment) { 134 f.InlineComment = c 135} 136 137// Doc is part of Documented 138func (f *EnumField) Doc() *Comment { 139 return f.Comment 140} 141 142// columns returns printable source tokens 143func (f EnumField) columns() (cols []aligned) { 144 cols = append(cols, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Integer))) 145 if f.ValueOption != nil { 146 cols = append(cols, f.ValueOption.columns()...) 147 } 148 cols = append(cols, alignedSemicolon) 149 if f.InlineComment != nil { 150 cols = append(cols, f.InlineComment.alignedInlinePrefix(), notAligned(f.InlineComment.Message())) 151 } 152 return 153} 154 155func (f *EnumField) parse(p *Parser) error { 156 _, tok, lit := p.nextIdentifier() 157 if tok != tIDENT { 158 if !isKeyword(tok) { 159 return p.unexpected(lit, "enum field identifier", f) 160 } 161 } 162 f.Name = lit 163 pos, tok, lit := p.next() 164 if tok != tEQUALS { 165 return p.unexpected(lit, "enum field =", f) 166 } 167 i, err := p.nextInteger() 168 if err != nil { 169 return p.unexpected(lit, "enum field integer", f) 170 } 171 f.Integer = i 172 pos, tok, lit = p.next() 173 if tok == tLEFTSQUARE { 174 o := new(Option) 175 o.Position = pos 176 o.IsEmbedded = true 177 err := o.parse(p) 178 if err != nil { 179 return err 180 } 181 f.ValueOption = o 182 pos, tok, lit = p.next() 183 if tok != tRIGHTSQUARE { 184 return p.unexpected(lit, "option closing ]", f) 185 } 186 } 187 if tSEMICOLON == tok { 188 p.nextPut(pos, tok, lit) // put back this token for scanning inline comment 189 } 190 return nil 191} 192