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, 2019, Chris Fraire <cfraire@me.com>. 22 */ 23 package org.opengrok.indexer.analysis.javascript; 24 25 import org.opengrok.indexer.analysis.JFlexJointLexer; 26 import org.opengrok.indexer.analysis.JFlexSymbolMatcher; 27 import org.opengrok.indexer.analysis.Resettable; 28 import org.opengrok.indexer.web.HtmlConsts; 29 30 import java.io.IOException; 31 import java.util.Stack; 32 33 /** 34 * Represents an abstract base class for JavaScript lexers. 35 */ 36 @SuppressWarnings("Duplicates") 37 public abstract class JavaScriptLexer extends JFlexSymbolMatcher 38 implements JFlexJointLexer, Resettable { 39 40 private ECMAScriptLexerData data; 41 42 /** 43 * Represents the stack of data if substitution is nested. 44 */ 45 private Stack<ECMAScriptLexerData> dataStack; 46 JavaScriptLexer()47 public JavaScriptLexer() { 48 data = new ECMAScriptLexerData(); 49 // dataStack is null to begin. 50 } 51 52 /** 53 * Resets the instance to an initial state. 54 */ 55 @Override reset()56 public void reset() { 57 super.reset(); 58 data = new ECMAScriptLexerData(); 59 if (dataStack != null) { 60 dataStack.clear(); 61 } 62 } 63 64 /** 65 * Calls {@link #phLOC()} if the yystate is not COMMENT or SCOMMENT. 66 */ chkLOC()67 public void chkLOC() { 68 if (yystate() != COMMENT() && yystate() != SCOMMENT()) { 69 phLOC(); 70 } 71 } 72 73 /** 74 * Resets the substitution brace counter to 1. 75 */ substitutionOp()76 protected void substitutionOp() { 77 data.nEndBrace = 1; 78 } 79 80 /** 81 * Determine if template substitution should end based on the first 82 * character of {@code capture}, and also recognizing tokens that increase 83 * the nesting level alternatively. 84 * <p> 85 * Calling this method has side effects to possibly modify 86 * {@code nEndBrace}. 87 * @return {@code true} if the substitution state does not end 88 */ notInTemplateOrSubstitutionDoesNotEnd(String capture)89 protected boolean notInTemplateOrSubstitutionDoesNotEnd(String capture) throws IOException { 90 if (data.nEndBrace <= 0) { 91 return true; 92 } 93 if (capture.startsWith("}")) { 94 if (--data.nEndBrace <= 0) { 95 int nRemaining = capture.length() - 1; 96 String opener = capture.substring(0, 1); 97 popData(); 98 yypop(); 99 disjointSpan(HtmlConsts.STRING_CLASS); 100 offer(opener); 101 if (nRemaining > 0) { 102 yypushback(nRemaining); 103 } 104 return false; 105 } 106 } 107 if (capture.startsWith("{")) { 108 ++data.nEndBrace; 109 } 110 return true; 111 } 112 pushData()113 protected void pushData() { 114 if (dataStack == null) { 115 dataStack = new Stack<>(); 116 } 117 dataStack.push(data); 118 data = new ECMAScriptLexerData(); 119 } 120 popData()121 private void popData() { 122 data = dataStack.pop(); 123 } 124 125 /** 126 * Subclasses must override to get the constant value created by JFlex to 127 * represent COMMENT. 128 */ COMMENT()129 protected abstract int COMMENT(); 130 131 /** 132 * Subclasses must override to get the constant value created by JFlex to 133 * represent SCOMMENT. 134 */ SCOMMENT()135 protected abstract int SCOMMENT(); 136 137 private static class ECMAScriptLexerData { 138 /** 139 * When interpolating inside `` with ${, the number of remaining '}' 140 * characters is stored. It starts at 1, and any nesting increases the 141 * value. 142 */ 143 int nEndBrace; 144 } 145 } 146