xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/javascript/JavaScriptLexer.java (revision 5d9f3aa0ca3da3a714233f987fa732f62c0965f6)
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