...

Source file src/github.com/emicklei/proto/message.go

Documentation: github.com/emicklei/proto

     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  
    24  package proto
    25  
    26  import (
    27  	"text/scanner"
    28  )
    29  
    30  // Message consists of a message name and a message body.
    31  type Message struct {
    32  	Position scanner.Position
    33  	Comment  *Comment
    34  	Name     string
    35  	IsExtend bool
    36  	Elements []Visitee
    37  	Parent   Visitee
    38  }
    39  
    40  func (m *Message) groupName() string {
    41  	if m.IsExtend {
    42  		return "extend"
    43  	}
    44  	return "message"
    45  }
    46  
    47  // parse expects ident { messageBody
    48  func (m *Message) parse(p *Parser) error {
    49  	_, tok, lit := p.nextIdentifier()
    50  	if tok != tIDENT {
    51  		if !isKeyword(tok) {
    52  			return p.unexpected(lit, m.groupName()+" identifier", m)
    53  		}
    54  	}
    55  	m.Name = lit
    56  	consumeCommentFor(p, m)
    57  	_, tok, lit = p.next()
    58  	if tok != tLEFTCURLY {
    59  		return p.unexpected(lit, m.groupName()+" opening {", m)
    60  	}
    61  	return parseMessageBody(p, m)
    62  }
    63  
    64  // parseMessageBody parses elements after {. It consumes the closing }
    65  func parseMessageBody(p *Parser, c elementContainer) error {
    66  	var (
    67  		pos scanner.Position
    68  		tok token
    69  		lit string
    70  	)
    71  	for {
    72  		pos, tok, lit = p.next()
    73  		switch {
    74  		case isComment(lit):
    75  			if com := mergeOrReturnComment(c.elements(), lit, pos); com != nil { // not merged?
    76  				c.addElement(com)
    77  			}
    78  		case tENUM == tok:
    79  			e := new(Enum)
    80  			e.Position = pos
    81  			e.Comment = c.takeLastComment(pos.Line - 1)
    82  			if err := e.parse(p); err != nil {
    83  				return err
    84  			}
    85  			c.addElement(e)
    86  		case tMESSAGE == tok:
    87  			msg := new(Message)
    88  			msg.Position = pos
    89  			msg.Comment = c.takeLastComment(pos.Line - 1)
    90  			if err := msg.parse(p); err != nil {
    91  				return err
    92  			}
    93  			c.addElement(msg)
    94  		case tOPTION == tok:
    95  			o := new(Option)
    96  			o.Position = pos
    97  			o.Comment = c.takeLastComment(pos.Line - 1)
    98  			if err := o.parse(p); err != nil {
    99  				return err
   100  			}
   101  			c.addElement(o)
   102  		case tONEOF == tok:
   103  			o := new(Oneof)
   104  			o.Position = pos
   105  			o.Comment = c.takeLastComment(pos.Line - 1)
   106  			if err := o.parse(p); err != nil {
   107  				return err
   108  			}
   109  			c.addElement(o)
   110  		case tMAP == tok:
   111  			f := newMapField()
   112  			f.Position = pos
   113  			f.Comment = c.takeLastComment(pos.Line - 1)
   114  			if err := f.parse(p); err != nil {
   115  				return err
   116  			}
   117  			c.addElement(f)
   118  		case tRESERVED == tok:
   119  			r := new(Reserved)
   120  			r.Position = pos
   121  			r.Comment = c.takeLastComment(pos.Line - 1)
   122  			if err := r.parse(p); err != nil {
   123  				return err
   124  			}
   125  			c.addElement(r)
   126  		// BEGIN proto2
   127  		case tOPTIONAL == tok || tREPEATED == tok || tREQUIRED == tok:
   128  			// look ahead
   129  			prevTok := tok
   130  			pos, tok, lit = p.next()
   131  			if tGROUP == tok {
   132  				g := new(Group)
   133  				g.Position = pos
   134  				g.Comment = c.takeLastComment(pos.Line - 1)
   135  				g.Optional = prevTok == tOPTIONAL
   136  				g.Repeated = prevTok == tREPEATED
   137  				g.Required = prevTok == tREQUIRED
   138  				if err := g.parse(p); err != nil {
   139  					return err
   140  				}
   141  				c.addElement(g)
   142  			} else {
   143  				// not a group, will be tFIELD
   144  				p.nextPut(pos, tok, lit)
   145  				f := newNormalField()
   146  				f.Type = lit
   147  				f.Position = pos
   148  				f.Comment = c.takeLastComment(pos.Line - 1)
   149  				f.Optional = prevTok == tOPTIONAL
   150  				f.Repeated = prevTok == tREPEATED
   151  				f.Required = prevTok == tREQUIRED
   152  				if err := f.parse(p); err != nil {
   153  					return err
   154  				}
   155  				c.addElement(f)
   156  			}
   157  		case tGROUP == tok:
   158  			g := new(Group)
   159  			g.Position = pos
   160  			g.Comment = c.takeLastComment(pos.Line - 1)
   161  			if err := g.parse(p); err != nil {
   162  				return err
   163  			}
   164  			c.addElement(g)
   165  		case tEXTENSIONS == tok:
   166  			e := new(Extensions)
   167  			e.Position = pos
   168  			e.Comment = c.takeLastComment(pos.Line - 1)
   169  			if err := e.parse(p); err != nil {
   170  				return err
   171  			}
   172  			c.addElement(e)
   173  		case tEXTEND == tok:
   174  			e := new(Message)
   175  			e.Position = pos
   176  			e.Comment = c.takeLastComment(pos.Line - 1)
   177  			e.IsExtend = true
   178  			if err := e.parse(p); err != nil {
   179  				return err
   180  			}
   181  			c.addElement(e)
   182  		// END proto2 only
   183  		case tRIGHTCURLY == tok || tEOF == tok:
   184  			goto done
   185  		case tSEMICOLON == tok:
   186  			maybeScanInlineComment(p, c)
   187  			// continue
   188  		default:
   189  			// tFIELD
   190  			p.nextPut(pos, tok, lit)
   191  			f := newNormalField()
   192  			f.Position = pos
   193  			f.Comment = c.takeLastComment(pos.Line - 1)
   194  			if err := f.parse(p); err != nil {
   195  				return err
   196  			}
   197  			c.addElement(f)
   198  		}
   199  	}
   200  done:
   201  	if tok != tRIGHTCURLY {
   202  		return p.unexpected(lit, "extend|message|group closing }", c)
   203  	}
   204  	return nil
   205  }
   206  
   207  // Accept dispatches the call to the visitor.
   208  func (m *Message) Accept(v Visitor) {
   209  	v.VisitMessage(m)
   210  }
   211  
   212  // addElement is part of elementContainer
   213  func (m *Message) addElement(v Visitee) {
   214  	v.parent(m)
   215  	m.Elements = append(m.Elements, v)
   216  }
   217  
   218  // elements is part of elementContainer
   219  func (m *Message) elements() []Visitee {
   220  	return m.Elements
   221  }
   222  
   223  func (m *Message) takeLastComment(expectedOnLine int) (last *Comment) {
   224  	last, m.Elements = takeLastCommentIfEndsOnLine(m.Elements, expectedOnLine)
   225  	return
   226  }
   227  
   228  // Doc is part of Documented
   229  func (m *Message) Doc() *Comment {
   230  	return m.Comment
   231  }
   232  
   233  func (m *Message) parent(v Visitee) { m.Parent = v }
   234  

View as plain text