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-2018, Chris Fraire <cfraire@me.com>. 23 */ 24 25 package org.opengrok.indexer.analysis.powershell; 26 27 import java.io.IOException; 28 import java.util.Locale; 29 import java.util.Stack; 30 import java.util.regex.Matcher; 31 import org.opengrok.indexer.analysis.JFlexSymbolMatcher; 32 import org.opengrok.indexer.analysis.ScopeAction; 33 import org.opengrok.indexer.web.HtmlConsts; 34 %% 35 %public 36 %class PoshXref 37 %extends JFlexSymbolMatcher 38 %unicode 39 %ignorecase 40 %int 41 %char 42 %include ../CommonLexer.lexh 43 %include ../CommonXref.lexh 44 %{ 45 private final Stack<String> styleStack = new Stack<String>(); 46 47 @Override clearStack()48 protected void clearStack() { 49 super.clearStack(); 50 styleStack.clear(); 51 } 52 emitComplexVariable()53 private void emitComplexVariable() throws IOException { 54 String id = yytext().substring(2, yylength() - 1); 55 onNonSymbolMatched("${", yychar); 56 onFilteredSymbolMatched(id, yychar, Consts.poshkwd, false); 57 onNonSymbolMatched("}", yychar); 58 } 59 emitSimpleVariable()60 private void emitSimpleVariable() throws IOException { 61 String id = yytext().substring(1); 62 onNonSymbolMatched("$", yychar); 63 onFilteredSymbolMatched(id, yychar, Consts.poshkwd, false); 64 } 65 pushSpan(int newState,String className)66 public void pushSpan(int newState, String className) throws IOException { 67 onDisjointSpanChanged(className, yychar); 68 yypush(newState); 69 styleStack.push(className); 70 } 71 72 @Override yypop()73 public void yypop() throws IOException { 74 onDisjointSpanChanged(null, yychar); 75 super.yypop(); 76 styleStack.pop(); 77 78 if (!styleStack.empty()) { 79 String style = styleStack.peek(); 80 onDisjointSpanChanged(style, yychar); 81 } 82 } 83 chkLOC()84 protected void chkLOC() { 85 switch (yystate()) { 86 case COMMENT: 87 case SCOMMENT: 88 break; 89 default: 90 phLOC(); 91 break; 92 } 93 } 94 %} 95 96 File = {FNameChar}+ "." ([a-zA-Z0-9]+) 97 98 /* 99 * Differs from {FPath} in that the path segments are only constrained to be 100 * {FNameChar}. 101 */ 102 AnyFPath = "/"? {FNameChar}+ ("/" {FNameChar}+)+ 103 104 /* 105 * States: 106 * STRING - double-quoted string, ex: "hello, world!" 107 * QSTRING - single-quoted string, ex: 'hello, world!' 108 * COMMENT - multiple-line comment. 109 * SCOMMENT - single-line comment, ex: # this is a comment 110 * SUBSHELL - commands executed in a sub-shell, 111 * example 1: (echo $header; cat file.txt) 112 * HERESTRING - here-string, example: cat @" ... "@ 113 * HEREQSTRING - here-string, example: cat @' ... '@ 114 * DATATYPE - bracketed .NET datatype specification 115 * DOTSYNTAX - await possible dot syntax -- e.g. property or methods 116 */ 117 %state STRING COMMENT SCOMMENT QSTRING SUBSHELL HERESTRING HEREQSTRING 118 %state DATATYPE DOTSYNTAX 119 120 %include ../Common.lexh 121 %include ../CommonURI.lexh 122 %include ../CommonPath.lexh 123 %include Powershell.lexh 124 %% 125 126 <STRING>{ 127 {ComplexVariable} { 128 chkLOC(); 129 emitComplexVariable(); 130 } 131 {SimpleVariable} { 132 chkLOC(); 133 emitSimpleVariable(); 134 } 135 } 136 137 <YYINITIAL>{ 138 \{ { chkLOC(); onScopeChanged(ScopeAction.INC, yytext(), yychar); } 139 \} { chkLOC(); onScopeChanged(ScopeAction.DEC, yytext(), yychar); } 140 \; { chkLOC(); onScopeChanged(ScopeAction.END, yytext(), yychar); } 141 } 142 143 <YYINITIAL, SUBSHELL> { 144 ^ {Label} { 145 chkLOC(); 146 String capture = yytext(); 147 onLabelMatched(capture, yychar, capture.substring(1)); 148 } 149 {Break} | 150 {Continue} { 151 chkLOC(); 152 String capture = yytext(); 153 Matcher m = PoshUtils.GOTO_LABEL.matcher(capture); 154 if (!m.find()) { 155 onNonSymbolMatched(capture, yychar); 156 } else { 157 String control = m.group(1); 158 String space = m.group(2); 159 String label = m.group(3); 160 onFilteredSymbolMatched(control, yychar, Consts.poshkwd, false); 161 onNonSymbolMatched(space, yychar); 162 onLabelDefMatched(label, yychar); 163 } 164 } 165 166 {DataType} { 167 chkLOC(); 168 yypushback(yylength()); 169 pushSpan(DATATYPE, null); 170 } 171 } 172 173 <YYINITIAL, SUBSHELL, DOTSYNTAX> { 174 {ComplexVariable} { 175 chkLOC(); 176 emitComplexVariable(); 177 if (yystate() != DOTSYNTAX) pushSpan(DOTSYNTAX, null); 178 } 179 {SimpleVariable} { 180 chkLOC(); 181 emitSimpleVariable(); 182 if (yystate() != DOTSYNTAX) pushSpan(DOTSYNTAX, null); 183 } 184 } 185 186 <YYINITIAL, SUBSHELL> { 187 {Operator} { 188 chkLOC(); 189 String capture = yytext(); 190 if (Consts.poshkwd.contains(capture.toLowerCase(Locale.ROOT))) { 191 onKeywordMatched(capture, yychar); 192 } else { 193 String sigil = capture.substring(0, 1); 194 String id = capture.substring(1); 195 onNonSymbolMatched(sigil, yychar); 196 onFilteredSymbolMatched(id, yychar, Consts.poshkwd, false); 197 } 198 } 199 200 {Number} { 201 chkLOC(); 202 String lastClassName = getDisjointSpanClassName(); 203 onDisjointSpanChanged(HtmlConsts.NUMBER_CLASS, yychar); 204 onNonSymbolMatched(yytext(), yychar); 205 onDisjointSpanChanged(lastClassName, yychar); 206 } 207 208 \" { 209 chkLOC(); 210 pushSpan(STRING, HtmlConsts.STRING_CLASS); 211 onNonSymbolMatched(yytext(), yychar); 212 } 213 \' { 214 chkLOC(); 215 pushSpan(QSTRING, HtmlConsts.STRING_CLASS); 216 onNonSymbolMatched(yytext(), yychar); 217 } 218 219 \# { 220 pushSpan(SCOMMENT, HtmlConsts.COMMENT_CLASS); 221 onNonSymbolMatched(yytext(), yychar); 222 } 223 \<\# { 224 pushSpan(COMMENT, HtmlConsts.COMMENT_CLASS); 225 onNonSymbolMatched(yytext(), yychar); 226 } 227 228 \@\" { 229 chkLOC(); 230 pushSpan(HERESTRING, HtmlConsts.STRING_CLASS); 231 onNonSymbolMatched(yytext(), yychar); 232 } 233 \@\' { 234 chkLOC(); 235 pushSpan(HEREQSTRING, HtmlConsts.STRING_CLASS); 236 onNonSymbolMatched(yytext(), yychar); 237 } 238 } 239 240 <DOTSYNTAX> { 241 "." { 242 chkLOC(); 243 onNonSymbolMatched(yytext(), yychar); 244 } 245 246 [^] { 247 yypushback(yylength()); 248 yypop(); 249 } 250 } 251 252 <YYINITIAL, SUBSHELL, DATATYPE, DOTSYNTAX> { 253 {Identifier} { 254 chkLOC(); 255 String id = yytext(); 256 onFilteredSymbolMatched(id, yychar, Consts.poshkwd, false); 257 } 258 } 259 260 <DATATYPE> { 261 "]" { 262 chkLOC(); 263 yypushback(yylength()); 264 yypop(); 265 } 266 } 267 268 <STRING> { 269 [`][\"\$`] | 270 \"\" { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 271 272 \$? \" { 273 chkLOC(); 274 onNonSymbolMatched(yytext(), yychar); 275 yypop(); 276 } 277 } 278 279 <STRING, HERESTRING> { 280 "$(" { 281 chkLOC(); 282 pushSpan(SUBSHELL, null); 283 onNonSymbolMatched(yytext(), yychar); 284 } 285 } 286 287 <QSTRING> { 288 \'\' { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 289 \' { 290 chkLOC(); 291 onNonSymbolMatched(yytext(), yychar); 292 yypop(); 293 } 294 } 295 296 <COMMENT> { 297 \#\> { 298 onNonSymbolMatched(yytext(), yychar); 299 yypop(); 300 } 301 } 302 303 <SCOMMENT> { 304 {WhspChar}*{EOL} { 305 yypop(); 306 onEndOfLineMatched(yytext(), yychar); 307 } 308 } 309 310 <SUBSHELL> { 311 \) { 312 chkLOC(); 313 onNonSymbolMatched(yytext(), yychar); 314 yypop(); 315 } 316 } 317 318 <HERESTRING> { 319 // Match escaped dollar sign of variable 320 // (eg. `$var) so it does not turn into web-link. 321 "`$" { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 322 323 {SimpleVariable} { 324 chkLOC(); 325 emitSimpleVariable(); 326 } 327 328 {ComplexVariable} { 329 chkLOC(); 330 emitComplexVariable(); 331 } 332 ^ \"\@ { 333 chkLOC(); 334 onNonSymbolMatched(yytext(), yychar); 335 yypop(); 336 } 337 } 338 339 <HEREQSTRING> { 340 ^ "'@" { 341 chkLOC(); 342 onNonSymbolMatched(yytext(), yychar); 343 yypop(); 344 } 345 } 346 347 <YYINITIAL, SUBSHELL> { 348 /* Don't enter new state if special character is escaped. */ 349 [`][`\(\)\{\}\"\'\$\#\\] { 350 chkLOC(); 351 onNonSymbolMatched(yytext(), yychar); 352 } 353 354 /* $# should not start a comment. */ 355 "$#" { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 356 357 \$ ? \( { 358 chkLOC(); 359 pushSpan(SUBSHELL, null); 360 onNonSymbolMatched(yytext(), yychar); 361 } 362 } 363 364 <YYINITIAL, SUBSHELL, STRING, SCOMMENT, QSTRING> { 365 {File} { 366 chkLOC(); 367 String path = yytext(); 368 onFilelikeMatched(path, yychar); 369 } 370 371 {AnyFPath} { 372 chkLOC(); 373 onPathlikeMatched(yytext(), '/', false, yychar); 374 } 375 } 376 377 <YYINITIAL, DATATYPE, SUBSHELL, STRING, COMMENT, SCOMMENT, QSTRING, HERESTRING, 378 HEREQSTRING> { 379 380 {WhspChar}*{EOL} { onEndOfLineMatched(yytext(), yychar); } 381 [[\s]--[\n]] { onNonSymbolMatched(yytext(), yychar); } 382 [^\n] { chkLOC(); onNonSymbolMatched(yytext(), yychar); } 383 } 384 385 <STRING, SCOMMENT, QSTRING> { 386 {FNameChar}+ "@" {FNameChar}+ "." {FNameChar}+ 387 { 388 chkLOC(); 389 onEmailAddressMatched(yytext(), yychar); 390 } 391 } 392 393 <STRING, SCOMMENT> { 394 {BrowseableURI} { 395 chkLOC(); 396 onUriMatched(yytext(), yychar); 397 } 398 } 399 400 <QSTRING> { 401 {BrowseableURI} { 402 chkLOC(); 403 onUriMatched(yytext(), yychar, PoshUtils.STRINGLITERAL_APOS_DELIMITER); 404 } 405 } 406 407 <COMMENT> { 408 {BrowseableURI} \>? { 409 onUriMatched(yytext(), yychar, PoshUtils.MAYBE_END_MULTILINE_COMMENT); 410 } 411 } 412