• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package parser.elements
18 
19 import lexer.Token
20 import lexer.TokenCategory
21 import lexer.TokenGrammar
22 import parser.config
23 import parser.elements.declarations.*
24 import parser.peekPreviousToken
25 import parser.peekToken
26 import writer.tokenValues
27 import writer.warn
28 import java.text.ParseException
29 
30 //an entry consists of: doc block, annotationParsers, declarationParser (code sig)
31 //EntryParser contains:
32 //- docParser
33 //  * description
34 //  * docAnnotationParsers
35 //    - tag (token)
36 //    - arg?
37 //    - description
38 //- annotationParsers
39 //  * name
40 //  * value
41 //- declarationParser
42 //  * name
43 //  * members [CompoundDeclarationParser]: type, name, tokens, typedef [CompoundMemberDeclaration]
44 //  * members [EnumDeclarationParser]: name, value?
45 //  * type [EnumDeclarationParser]
46 //  * type [TypedefDeclarationParser]
47 //  * extendsName [InterfaceDeclarationParser]
48 //  * extendsVersion [InterfaceDeclarationParser]
49 //  * prefix [MethodDeclarationParser]
50 //  * params [MethodDeclarationParser]: ArgEntry: type, name
51 //  * returns [[MethodDeclarationParser]: ArgEntry: type, name
52 
53 class IllegalEntryException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
54 
55 /**
56  * An entry inclused the doc comment block, code docAnnotationParsers, and method signature/interface
57  */
58 class EntryParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
59 
60     //doc description, summary, and doc annottions
61     lateinit var docParser: DocParser
62     //annotation
63     val annotationParsers = mutableListOf<AnnotationParser>()
64     //declaration - properties depend on declarationParser subclass
65     lateinit var declarationParser: AbstractDeclarationParser
66 
67     init {
68         parseTokens(scanTokens(iter))
69         if (shouldResetIterator) resetIterator(iter)
70     }
71 
72     //start at doc_start, collect until end of declaration
scanTokensnull73     override fun scanTokens(iter: ListIterator<Token>): List<Token> {
74         //queue up doc_start
75         //ignore any empty lines that start the doc block
76         while (peekPreviousToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) iter.previous()
77         //if called after the doc_start was found
78         if (peekPreviousToken(iter)?.identifier == TokenGrammar.DOC_START) iter.previous()
79 
80         val tokens = mutableListOf<Token>()
81         //collect doc block /** ... */
82         tokens += scanDocTokens(iter)
83         //collect annotations and declaration, nested to semicolon
84         tokens += scanDeclarationTokens(iter)
85         return tokens
86     }
87 
parseTokensnull88     override fun parseTokens(tokens: List<Token>) {
89         val iter = tokens.listIterator()
90 
91         /*
92          * doc comment block
93          */
94         do {
95             assert(peekToken(iter)!!.identifier == TokenGrammar.DOC_START)
96             this.docParser = DocParser(iter) //increments iterator
97             assert(peekPreviousToken(iter)!!.identifier == TokenGrammar.DOC_END)
98 
99             //if there's consecutive doc blocks, use the last one found in warning mode, otherwise error
100             if (peekToken(iter)?.identifier != TokenGrammar.DOC_START) {
101                 break //good to go
102             } else {
103                 val msg = "Found consecutive doc block after: ${this.docParser.description}"
104                 if (config.warnOnly) {
105                     warn("${msg}\nUsing last found doc block.")
106                 } else {
107                     throw ParseException(msg, this.indexStart)
108                 }
109             }
110         } while (true)
111 
112         /*
113          * annotations (optional)
114          */
115         while (iter.hasNext() && peekToken(iter)!!.identifier == TokenGrammar.AT) {
116             iter.next()
117             if (peekToken(iter)?.category == TokenCategory.Annotation) {
118                 this.annotationParsers.add(AnnotationParser(iter)) //increments iterator
119             } else {
120                 throw ParseException("Unknown annotation tag: ${peekToken(iter)?.value}", this.indexStart)
121             }
122         }
123 
124         /*
125          * declaration
126          */
127         val token = peekToken(iter) ?: throw ParseException("No declaration body available", this.indexStart)
128 
129         //check we're not at an annotation
130         assert(token.identifier != TokenGrammar.AT && token.category != TokenCategory.Annotation)
131 
132         /*
133          * known bad starts for a declaration
134          */
135 
136         if (token.identifier == TokenGrammar.DOC_START) {
137             throw ParseException("Bad doc block location:\n${tokenValues(tokens)}", this.indexStart)
138         } else if (token.identifier == TokenGrammar.PACKAGE) {
139             //usually this means they've used a doc block for the license
140             throw IllegalEntryException("Don't document the package declaration") //handled in EntryCollectionParser
141         } else if (token.category != TokenCategory.Word
142                 && token.category != TokenCategory.TypeDef
143                 && token.category != TokenCategory.Keyword) {
144             //sanity check - skip entry or bail
145             throw IllegalEntryException("Invalid start for entry declaration: '${token.value}'\n" +
146                     "tokens: ${tokenValues(tokens)}")
147             //throw ParseException("Invalid start for entry declaration: ${token}\ntoken: ${token.value}\n${tokenValues(tokens)}", this.indexStart)
148         }
149 
150         this.declarationParser = when (token.identifier) {
151             TokenGrammar.INTERFACE -> {
152                 this.shouldResetIterator = true
153                 InterfaceDeclarationParser(iter)
154             }
155             TokenGrammar.ENUM -> EnumDeclarationParser(iter)
156             TokenGrammar.TYPEDEF -> TypedefDeclarationParser(iter)
157             TokenGrammar.STRUCT, TokenGrammar.UNION -> CompoundDeclarationParser(iter)
158             else -> MethodDeclarationParser(iter)
159         }
160 
161     }
162 }