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.files 18 19 import lexer.Token 20 import lexer.TokenCategory 21 import lexer.TokenGrammar 22 import parser.elements.EntryCollectionParser 23 import parser.elements.EntryParser 24 import parser.elements.declarations.CompoundDeclarationParser 25 import parser.elements.declarations.EnumDeclarationParser 26 import parser.elements.declarations.TypedefDeclarationParser 27 import parser.peekPreviousToken 28 import parser.peekToken 29 import java.text.ParseException 30 31 /** 32 * Parses package info and all entries (determined by doc start/end tokens). 33 * Adds empty doc tokens for required types. 34 * This class shouldn't be instantiated on it's own. 35 */ 36 abstract class AbstractFileParser(tokens: List<Token>) { 37 38 private val packageInfo: PackageInfo by lazy { parsePackageInfo(tokens) } 39 40 abstract val name: String 41 val packageName: String get() = packageInfo.name 42 val packageVersion: Float get() = packageInfo.version 43 44 protected val entries: List<EntryParser> by lazy { 45 assert(tokens.isNotEmpty()) 46 //add empty docblocks 47 EntryCollectionParser(insertDocsForRequiredTypes(tokens)).entryParsers 48 } 49 50 val enums: List<EntryParser> by lazy { getEntriesByDeclarationParser<EnumDeclarationParser>() } 51 val typedefs: List<EntryParser> by lazy { getEntriesByDeclarationParser<TypedefDeclarationParser>() } 52 val structs: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.STRUCT) } 53 val unions: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.UNION) } 54 55 protected inline fun <reified T> getEntriesByDeclarationParser(): List<EntryParser> { 56 return entries.filter { it.declarationParser is T } 57 } 58 59 private fun getEntriesByCompoundDeclarationParser(identifier: TokenGrammar): List<EntryParser> { 60 return getEntriesByDeclarationParser<CompoundDeclarationParser>() 61 .filter { (it.declarationParser as CompoundDeclarationParser).type == identifier } 62 } 63 64 private val REQUIRED_DOC_TYPES = listOf( 65 TokenGrammar.INTERFACE, 66 TokenGrammar.ENUM, 67 TokenGrammar.STRUCT, 68 TokenGrammar.UNION, 69 TokenGrammar.TYPEDEF) 70 71 /** 72 * Insert doc block before the undocumented types we want to show up. 73 */ 74 private fun insertDocsForRequiredTypes(tokens: List<Token>): List<Token> { 75 val tokensCopy = mutableListOf<Token>() 76 val iter = tokens.listIterator() 77 var token: Token 78 var inDoc = false 79 80 while (iter.hasNext()) { 81 token = iter.next() 82 tokensCopy.add(token) 83 84 if (token.identifier == TokenGrammar.DOC_START) { 85 inDoc = true 86 continue 87 } else if (token.identifier == TokenGrammar.DOC_END) { 88 inDoc = false 89 continue 90 91 } else if (!inDoc && token.identifier in REQUIRED_DOC_TYPES) { 92 //make sure it's not a reference to a Generic: <name> 93 if (peekToken(iter)?.identifier == TokenGrammar.CHEVRON_CLOSE) { 94 continue 95 } 96 97 val idx = indexInsertionPointforDocTokens(tokensCopy) 98 if (idx != -1) { 99 val removedTokens = mutableListOf<Token>() 100 repeat(idx) { 101 removedTokens.add(tokensCopy.removeAt(tokensCopy.size-1)) 102 } 103 tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_START.value)) 104 tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_END.value)) 105 removedTokens.reversed().forEach { tokensCopy.add(it) } 106 } 107 } 108 } 109 return tokensCopy.toList() 110 } 111 112 /** 113 * @return -1 if documented, otherwise the index count backwards for where 114 * to begin insertion of doc tokens. 115 */ 116 private fun indexInsertionPointforDocTokens(tokens: List<Token>): Int { 117 val iter = tokens.reversed().listIterator() 118 var token: Token 119 var idx = 0 120 121 iter.next() //skip keyword token 122 while (iter.hasNext()) { 123 token = iter.next() 124 if (token.identifier == TokenGrammar.AT || token.category == TokenCategory.Annotation) { 125 idx++ 126 continue //skip annotations 127 } else { 128 return if (token.identifier == TokenGrammar.DOC_END) -1 else idx+1 129 } 130 } 131 throw ParseException("Empty token list", 0) 132 } 133 }