blob: 0fd95efcb206cddbcbb75125e43ef15aace78fcb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.php.twig.editor.lexer;
import java.util.Objects;
import org.netbeans.spi.lexer.LexerInput;
import org.netbeans.spi.lexer.LexerRestartInfo;
import org.netbeans.modules.web.common.api.ByteStack;
@org.netbeans.api.annotations.common.SuppressWarnings({"SF_SWITCH_FALLTHROUGH", "URF_UNREAD_FIELD", "DLS_DEAD_LOCAL_STORE", "DM_DEFAULT_ENCODING"})
%%
%public
%class TwigTopColoringLexer
%type TwigTopTokenId
%function findNextToken
%unicode
%caseless
%char
%eofval{
if(input.readLength() > 0) {
// backup eof
input.backup(1);
//and return the text as error token
if (zzLexicalState == ST_BLOCK) {
return TwigTopTokenId.T_TWIG_BLOCK;
} else if (zzLexicalState == ST_VAR) {
return TwigTopTokenId.T_TWIG_VAR;
} else {
return TwigTopTokenId.T_HTML;
}
} else {
return null;
}
%eofval}
%{
private ByteStack stack = new ByteStack();
private LexerInput input;
private Lexing lexing;
private boolean probablyInDString;
private boolean probablyInSString;
private int curlyBalance;
public TwigTopColoringLexer(LexerRestartInfo info) {
this.input = info.input();
if(info.state() != null) {
//reset state
setState((LexerState) info.state());
this.lexing = ((LexerState) info.state()).lexing;
probablyInDString = ((LexerState) info.state()).probablyInDString;
probablyInSString = ((LexerState) info.state()).probablyInSString;
curlyBalance = ((LexerState) info.state()).curlyBalance;
} else {
zzState = zzLexicalState = YYINITIAL;
this.lexing = Lexing.NORMAL;
probablyInDString = false;
probablyInSString = false;
curlyBalance = 0;
stack.clear();
}
}
private enum Lexing {
NORMAL,
RAW,
VERBATIM;
}
public static final class LexerState {
final ByteStack stack;
/** the current state of the DFA */
final int zzState;
/** the current lexical state */
final int zzLexicalState;
private final Lexing lexing;
private final boolean probablyInDString;
private final boolean probablyInSString;
private final int curlyBalance;
LexerState(ByteStack stack, int zzState, int zzLexicalState, Lexing lexing, boolean probablyInDString, boolean probablyInSString, int curlyBalance) {
this.stack = stack;
this.zzState = zzState;
this.zzLexicalState = zzLexicalState;
this.lexing = lexing;
this.probablyInDString = probablyInDString;
this.probablyInSString = probablyInSString;
this.curlyBalance = curlyBalance;
}
@Override
public int hashCode() {
int hash = 3;
hash = 17 * hash + Objects.hashCode(this.stack);
hash = 17 * hash + this.zzState;
hash = 17 * hash + this.zzLexicalState;
hash = 17 * hash + Objects.hashCode(this.lexing);
hash = 17 * hash + (this.probablyInDString ? 1 : 0);
hash = 17 * hash + (this.probablyInSString ? 1 : 0);
hash = 17 * hash + this.curlyBalance;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final LexerState other = (LexerState) obj;
if (this.zzState != other.zzState) {
return false;
}
if (this.zzLexicalState != other.zzLexicalState) {
return false;
}
if (this.probablyInDString != other.probablyInDString) {
return false;
}
if (this.probablyInSString != other.probablyInSString) {
return false;
}
if (this.curlyBalance != other.curlyBalance) {
return false;
}
if (!Objects.equals(this.stack, other.stack)) {
return false;
}
if (this.lexing != other.lexing) {
return false;
}
return true;
}
}
public LexerState getState() {
return new LexerState(stack.copyOf(), zzState, zzLexicalState, lexing, probablyInDString, probablyInSString, curlyBalance);
}
public void setState(LexerState state) {
this.stack.copyFrom(state.stack);
this.zzState = state.zzState;
this.zzLexicalState = state.zzLexicalState;
this.lexing = state.lexing;
this.probablyInDString = state.probablyInDString;
this.probablyInSString = state.probablyInSString;
this.curlyBalance = state.curlyBalance;
}
protected int getZZLexicalState() {
return zzLexicalState;
}
protected void popState() {
yybegin(stack.pop());
}
protected void pushState(final int state) {
stack.push(getZZLexicalState());
yybegin(state);
}
// End user code
%}
WHITESPACE=[ \t\r\n]+
BLOCK_START="{%"
BLOCK_END="%}"
BLOCK_RAW_START="{%"[ \t]*"raw"[ \t]*"%}"
BLOCK_RAW_END="{%"[ \t]*"endraw"[ \t]*"%}"
BLOCK_VERBATIM_START="{%"[ \t]*"verbatim"[ \t]*"%}"
BLOCK_VERBATIM_END="{%"[ \t]*"endverbatim"[ \t]*"%}"
VAR_START="{{"
VAR_END="}}"
COMMENT_START="{#"
COMMENT_END=([^#] | #[^}])*"#}"
D_STRING_DELIM=\"
S_STRING_DELIM='
PRECEDED_STRING="\\\'"|"\\\""|"\\\\"
OPEN_CURLY="{"
CLOSE_CURLY="}"
%state ST_RAW_START
%state ST_RAW_END
%state ST_VERBATIM_START
%state ST_VERBATIM_END
%state ST_BLOCK
%state ST_VAR
%state ST_COMMENT
%state ST_HIGHLIGHTING_ERROR
%%
<YYINITIAL, ST_RAW_START, ST_RAW_END, ST_VERBATIM_START, ST_VERBATIM_END, ST_BLOCK, ST_VAR, ST_COMMENT>{WHITESPACE}+ {
}
<YYINITIAL> {
{BLOCK_RAW_START} {
if (lexing == Lexing.NORMAL) {
yypushback(yylength());
pushState(ST_RAW_START);
}
}
{BLOCK_RAW_END} {
if (lexing != Lexing.VERBATIM) {
int indexOfRawBlockStart = yytext().lastIndexOf("{%"); //NOI18N
yypushback(yylength() - indexOfRawBlockStart);
pushState(ST_RAW_END);
}
}
{BLOCK_VERBATIM_START} {
if (lexing == Lexing.NORMAL) {
yypushback(yylength());
pushState(ST_VERBATIM_START);
}
}
{BLOCK_VERBATIM_END} {
if (lexing != Lexing.RAW) {
int indexOfVerbatimBlockStart = yytext().lastIndexOf("{%"); //NOI18N
yypushback(yylength() - indexOfVerbatimBlockStart);
pushState(ST_VERBATIM_END);
}
}
{BLOCK_START} {
if (lexing == Lexing.NORMAL) {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_HTML;
}
pushState(ST_BLOCK);
return TwigTopTokenId.T_TWIG_BLOCK_START;
}
}
{COMMENT_START} {
if (lexing == Lexing.NORMAL) {
int textLength = yylength();
yypushback(2);
pushState(ST_COMMENT);
if (textLength > 2) {
return TwigTopTokenId.T_HTML;
}
}
}
{VAR_START} {
if (lexing == Lexing.NORMAL) {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_HTML;
}
pushState(ST_VAR);
curlyBalance = 0;
return TwigTopTokenId.T_TWIG_VAR_START;
}
}
. {}
}
<ST_RAW_START> {
{BLOCK_START} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_HTML;
}
lexing = Lexing.RAW;
return TwigTopTokenId.T_TWIG_BLOCK_START;
}
}
<ST_VERBATIM_START> {
{BLOCK_START} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_HTML;
}
lexing = Lexing.VERBATIM;
return TwigTopTokenId.T_TWIG_BLOCK_START;
}
}
<ST_RAW_START, ST_VERBATIM_START> {
{BLOCK_END} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_TWIG_BLOCK;
}
popState();
return TwigTopTokenId.T_TWIG_BLOCK_END;
}
. {}
}
<ST_RAW_END, ST_VERBATIM_END> {
{BLOCK_START} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_HTML;
}
lexing = Lexing.NORMAL;
return TwigTopTokenId.T_TWIG_BLOCK_START;
}
{BLOCK_END} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_TWIG_BLOCK;
}
popState();
return TwigTopTokenId.T_TWIG_BLOCK_END;
}
. {}
}
<ST_COMMENT> {
{COMMENT_END} {
popState();
return TwigTopTokenId.T_TWIG_COMMENT;
}
. {}
}
<ST_BLOCK> {
{BLOCK_END} {
if (yylength() > 2) {
yypushback(2);
return TwigTopTokenId.T_TWIG_BLOCK;
}
popState();
return TwigTopTokenId.T_TWIG_BLOCK_END;
}
. {}
}
<ST_VAR> {
{PRECEDED_STRING} {
}
{D_STRING_DELIM} {
if (!probablyInSString) {
probablyInDString = !probablyInDString;
}
}
{S_STRING_DELIM} {
if (!probablyInDString) {
probablyInSString = !probablyInSString;
}
}
{OPEN_CURLY} {
if (!probablyInDString && !probablyInSString) {
curlyBalance++;
}
}
{CLOSE_CURLY} {
if (!probablyInDString && !probablyInSString) {
curlyBalance--;
}
}
"}}}" { // {{{}}}
if (!probablyInDString && !probablyInSString) {
if (curlyBalance >= 0 && curlyBalance <= 2) {
curlyBalance--;
yypushback(2);
} else {
curlyBalance -= 3;
}
}
}
{VAR_END} {
if (!probablyInDString && !probablyInSString) {
if (yylength() > 2) {
if (curlyBalance == 0 || curlyBalance == 1) {
if (zzInput == YYEOF) {
yypushback(3);
} else {
yypushback(2);
}
return TwigTopTokenId.T_TWIG_VAR;
}
}
if (curlyBalance == 0) {
popState();
return TwigTopTokenId.T_TWIG_VAR_END;
} else if (curlyBalance == 1) {
// missing closing curly "}"
popState();
curlyBalance = 0;
return TwigTopTokenId.T_TWIG_VAR_END;
} else {
curlyBalance -= 2;
}
}
}
. {}
}
/* ============================================
Stay in this state until we find a whitespace.
After we find a whitespace we go the the prev state and try again from the next token.
============================================ */
<ST_HIGHLIGHTING_ERROR> {
. {
return TwigTopTokenId.T_TWIG_OTHER;
}
}
/* ============================================
This rule must be the last in the section!!
it should contain all the states.
============================================ */
<YYINITIAL, ST_RAW_START, ST_RAW_END, ST_VERBATIM_START, ST_VERBATIM_END, ST_BLOCK, ST_VAR, ST_COMMENT> {
. {
yypushback(yylength());
pushState(ST_HIGHLIGHTING_ERROR);
}
}