1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2017, Chris Fraire <cfraire@me.com>. 23 */ 24 25 package org.opengrok.indexer.analysis.kotlin; 26 27 import java.io.IOException; 28 import org.opengrok.indexer.analysis.JFlexSymbolMatcher; 29 import org.opengrok.indexer.analysis.ScopeAction; 30 import org.opengrok.indexer.analysis.EmphasisHint; 31 import org.opengrok.indexer.util.StringUtils; 32 import org.opengrok.indexer.web.HtmlConsts; 33 %% 34 %public 35 %class KotlinXref 36 %extends JFlexSymbolMatcher 37 %unicode 38 %ignorecase 39 %int 40 %char 41 %include ../CommonLexer.lexh 42 %include ../CommonXref.lexh 43 %{ 44 /* Must match {WhspChar}+ regex */ 45 private final static String WHITE_SPACE = "[ \\t\\f]+"; 46 47 private int nestedComment; 48 49 @Override reset()50 public void reset() { 51 super.reset(); 52 nestedComment = 0; 53 } 54 55 @Override yypop()56 public void yypop() throws IOException { 57 onDisjointSpanChanged(null, yychar); 58 super.yypop(); 59 } 60 chkLOC()61 protected void chkLOC() { 62 switch (yystate()) { 63 case COMMENT: 64 case SCOMMENT: 65 case KDOC: 66 break; 67 default: 68 phLOC(); 69 break; 70 } 71 } 72 %} 73 74 File = [a-zA-Z]{FNameChar}* "." ([Jj][Aa][Vv][Aa] | 75 [Pp][Rr][Oo][Pp][Ee][Rr][Tt][Ii][Ee][Ss] | [Pp][Rr][Oo][Pp][Ss] | 76 [Xx][Mm][Ll] | [Cc][Oo][Nn][Ff] | [Tt][Xx][Tt] | [Hh][Tt][Mm][Ll]? | 77 [Ii][Nn][Ii] | [Jj][Nn][Ll][Pp] | [Jj][Aa][Dd] | [Dd][Ii][Ff][Ff] | 78 [Pp][Aa][Tt][Cc][Hh]) 79 80 KdocWithClassArg = "@throws" | "@exception" 81 KdocWithParamNameArg = "@param" 82 83 ClassName = ({Identifier} ".")* {Identifier} 84 ParamName = {Identifier} | "<" {Identifier} ">" 85 86 %state STRING COMMENT SCOMMENT QSTRING KDOC TSTRING 87 88 %include ../Common.lexh 89 %include ../CommonURI.lexh 90 %include ../CommonPath.lexh 91 %include Kotlin.lexh 92 %% 93 <YYINITIAL>{ 94 \{ { chkLOC(); onScopeChanged(ScopeAction.INC, yytext(), yychar); } 95 \} { chkLOC(); onScopeChanged(ScopeAction.DEC, yytext(), yychar); } 96 \; { chkLOC(); onScopeChanged(ScopeAction.END, yytext(), yychar); } 97 98 {Identifier} { 99 chkLOC(); 100 String id = yytext(); 101 onFilteredSymbolMatched(id, yychar, Consts.kwd); 102 } 103 104 "<" ({File}|{FPath}) ">" { 105 chkLOC(); 106 onNonSymbolMatched("<", yychar); 107 String path = yytext(); 108 path = path.substring(1, path.length() - 1); 109 onFilelikeMatched(path, yychar + 1); 110 onNonSymbolMatched(">", yychar + 1 + path.length()); 111 } 112 113 {Number} { 114 chkLOC(); 115 onDisjointSpanChanged(HtmlConsts.NUMBER_CLASS, yychar); 116 onNonSymbolMatched(yytext(), yychar); 117 onDisjointSpanChanged(null, yychar); 118 } 119 120 \" { 121 chkLOC(); 122 yypush(STRING); 123 onDisjointSpanChanged(HtmlConsts.STRING_CLASS, yychar); 124 onNonSymbolMatched(yytext(), yychar); 125 } 126 \' { 127 chkLOC(); 128 yypush(QSTRING); 129 onDisjointSpanChanged(HtmlConsts.STRING_CLASS, yychar); 130 onNonSymbolMatched(yytext(), yychar); 131 } 132 \"\"\" { 133 chkLOC(); 134 yypush(TSTRING); 135 onDisjointSpanChanged(HtmlConsts.STRING_CLASS, yychar); 136 onNonSymbolMatched(yytext(), yychar); 137 } 138 "/**" / [^/] { 139 if (nestedComment++ == 0) { 140 yypush(KDOC); 141 onDisjointSpanChanged(HtmlConsts.COMMENT_CLASS, yychar); 142 } 143 onNonSymbolMatched(yytext(), yychar); 144 } 145 "//" { 146 yypush(SCOMMENT); 147 onDisjointSpanChanged(HtmlConsts.COMMENT_CLASS, yychar); 148 onNonSymbolMatched(yytext(), yychar); 149 } 150 } 151 152 <STRING> { 153 \\[\"\$\\] { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 154 \" { 155 chkLOC(); 156 onNonSymbolMatched(yytext(), yychar); 157 yypop(); 158 } 159 } 160 161 <QSTRING> { 162 \\[\'\\] | 163 \' {WhspChar}+ \' { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 164 \' { 165 chkLOC(); 166 onNonSymbolMatched(yytext(), yychar); 167 yypop(); 168 } 169 } 170 171 <TSTRING> { 172 /* 173 * "raw string ... doesn't support backslash escaping" 174 */ 175 \"\"\" { 176 chkLOC(); 177 onNonSymbolMatched(yytext(), yychar); 178 yypop(); 179 } 180 } 181 182 <STRING, TSTRING> { 183 /* 184 * TODO : support template expressions inside curly brackets 185 */ 186 \$ {Identifier} { 187 chkLOC(); 188 String capture = yytext(); 189 String sigil = capture.substring(0, 1); 190 String id = capture.substring(1); 191 onNonSymbolMatched(sigil, yychar); 192 onDisjointSpanChanged(null, yychar); 193 onFilteredSymbolMatched(id, yychar, Consts.kwd); 194 onDisjointSpanChanged(HtmlConsts.STRING_CLASS, yychar); 195 } 196 {WhspChar}*{EOL} { 197 onDisjointSpanChanged(null, yychar); 198 onEndOfLineMatched(yytext(), yychar); 199 onDisjointSpanChanged(HtmlConsts.STRING_CLASS, yychar); 200 } 201 } 202 203 <YYINITIAL, COMMENT, KDOC> { 204 "/*" { 205 if (nestedComment++ == 0) { 206 yypush(COMMENT); 207 onDisjointSpanChanged(HtmlConsts.COMMENT_CLASS, yychar); 208 } 209 onNonSymbolMatched(yytext(), yychar); 210 } 211 } 212 213 <COMMENT, KDOC> { 214 "*/" { 215 onNonSymbolMatched(yytext(), yychar); 216 if (--nestedComment == 0) { 217 yypop(); 218 } 219 } 220 {WhspChar}*{EOL} { 221 onDisjointSpanChanged(null, yychar); 222 onEndOfLineMatched(yytext(), yychar); 223 onDisjointSpanChanged(HtmlConsts.COMMENT_CLASS, yychar); 224 } 225 } 226 227 <KDOC> { 228 {KdocWithParamNameArg} {WhspChar}+ {ParamName} | 229 {KdocWithClassArg} {WhspChar}+ {ClassName} { 230 String text = yytext(); 231 String[] tokens = text.split(WHITE_SPACE, 2); 232 onNonSymbolMatched(tokens[0], EmphasisHint.STRONG, yychar); 233 onNonSymbolMatched(text.substring(tokens[0].length(), text.length() - 234 tokens[1].length()), yychar); 235 onNonSymbolMatched(tokens[1], EmphasisHint.EM, yychar); 236 } 237 "@" {Identifier} { 238 onNonSymbolMatched(yytext(), EmphasisHint.STRONG, yychar); 239 } 240 } 241 242 <SCOMMENT> { 243 {WhspChar}*{EOL} { 244 yypop(); 245 onEndOfLineMatched(yytext(), yychar); 246 } 247 } 248 249 250 <YYINITIAL, STRING, COMMENT, SCOMMENT, QSTRING, KDOC, TSTRING> { 251 {WhspChar}*{EOL} { onEndOfLineMatched(yytext(), yychar); } 252 [[\s]--[\n]] { onNonSymbolMatched(yytext(), yychar); } 253 [^\n] { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 254 } 255 256 <STRING, COMMENT, SCOMMENT, QSTRING, TSTRING, KDOC> { 257 {FPath} { 258 chkLOC(); 259 onPathlikeMatched(yytext(), '/', false, yychar); 260 } 261 262 {File} 263 { 264 chkLOC(); 265 String path = yytext(); 266 onFilelikeMatched(path, yychar); 267 } 268 269 {FNameChar}+ "@" {FNameChar}+ "." {FNameChar}+ 270 { 271 chkLOC(); 272 onEmailAddressMatched(yytext(), yychar); 273 } 274 } 275 276 <STRING, SCOMMENT, QSTRING, TSTRING> { 277 {BrowseableURI} { 278 chkLOC(); 279 onUriMatched(yytext(), yychar); 280 } 281 } 282 283 <COMMENT, KDOC> { 284 {BrowseableURI} { 285 onUriMatched(yytext(), yychar, StringUtils.END_C_COMMENT); 286 } 287 } 288