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 }