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	Elements []Visitee
29}
30
31// addElement is part of elementContainer
32func (proto *Proto) addElement(v Visitee) {
33	proto.Elements = append(proto.Elements, v)
34}
35
36// elements is part of elementContainer
37func (proto *Proto) elements() []Visitee {
38	return proto.Elements
39}
40
41// takeLastComment is part of elementContainer
42// removes and returns the last element of the list if it is a Comment.
43func (proto *Proto) takeLastComment() (last *Comment) {
44	last, proto.Elements = takeLastComment(proto.Elements)
45	return
46}
47
48// parse parsers a complete .proto definition source.
49func (proto *Proto) parse(p *Parser) error {
50	for {
51		pos, tok, lit := p.next()
52		switch {
53		case isComment(lit):
54			if com := mergeOrReturnComment(proto.Elements, lit, pos); com != nil { // not merged?
55				proto.Elements = append(proto.Elements, com)
56			}
57		case tOPTION == tok:
58			o := new(Option)
59			o.Position = pos
60			o.Comment, proto.Elements = takeLastComment(proto.Elements)
61			if err := o.parse(p); err != nil {
62				return err
63			}
64			proto.Elements = append(proto.Elements, o)
65		case tSYNTAX == tok:
66			s := new(Syntax)
67			s.Position = pos
68			s.Comment, proto.Elements = takeLastComment(proto.Elements)
69			if err := s.parse(p); err != nil {
70				return err
71			}
72			proto.Elements = append(proto.Elements, s)
73		case tIMPORT == tok:
74			im := new(Import)
75			im.Position = pos
76			im.Comment, proto.Elements = takeLastComment(proto.Elements)
77			if err := im.parse(p); err != nil {
78				return err
79			}
80			proto.Elements = append(proto.Elements, im)
81		case tENUM == tok:
82			enum := new(Enum)
83			enum.Position = pos
84			enum.Comment, proto.Elements = takeLastComment(proto.Elements)
85			if err := enum.parse(p); err != nil {
86				return err
87			}
88			proto.Elements = append(proto.Elements, enum)
89		case tSERVICE == tok:
90			service := new(Service)
91			service.Position = pos
92			service.Comment, proto.Elements = takeLastComment(proto.Elements)
93			err := service.parse(p)
94			if err != nil {
95				return err
96			}
97			proto.Elements = append(proto.Elements, service)
98		case tPACKAGE == tok:
99			pkg := new(Package)
100			pkg.Position = pos
101			pkg.Comment, proto.Elements = takeLastComment(proto.Elements)
102			if err := pkg.parse(p); err != nil {
103				return err
104			}
105			proto.Elements = append(proto.Elements, pkg)
106		case tMESSAGE == tok:
107			msg := new(Message)
108			msg.Position = pos
109			msg.Comment, proto.Elements = takeLastComment(proto.Elements)
110			if err := msg.parse(p); err != nil {
111				return err
112			}
113			proto.Elements = append(proto.Elements, msg)
114		// BEGIN proto2
115		case tEXTEND == tok:
116			msg := new(Message)
117			msg.Position = pos
118			msg.Comment, proto.Elements = takeLastComment(proto.Elements)
119			msg.IsExtend = true
120			if err := msg.parse(p); err != nil {
121				return err
122			}
123			proto.Elements = append(proto.Elements, msg)
124		// END proto2
125		case tSEMICOLON == tok:
126			maybeScanInlineComment(p, proto)
127			// continue
128		case tEOF == tok:
129			goto done
130		default:
131			return p.unexpected(lit, ".proto element {comment|option|import|syntax|enum|service|package|message}", p)
132		}
133	}
134done:
135	return nil
136}
137
138// elementContainer unifies types that have elements.
139type elementContainer interface {
140	addElement(v Visitee)
141	elements() []Visitee
142	takeLastComment() *Comment
143}
144