mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Groovy: parsing of single quote string literals with slash before line feed
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -133,12 +133,12 @@ mIDENT_NOBUCKS = {mLETTER} ({mLETTER} | {mDIGIT})*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
mSTRING_NL = {mONE_NL}
|
||||
mSTRING_ESC = \\ [^]
|
||||
mSTRING_ESC = \\ [^] | \\ ({WHITE_SPACE})+ (\n|\r)
|
||||
mREGEX_ESC = \\ n | \\ r | \\ t | \\ b | \\ f | "\\" "\\" | \\ "$" | \\ \" | \\ \'
|
||||
| "\\""u"{mHEX_DIGIT}{4}
|
||||
| "\\" [0..3] ([0..7] ([0..7])?)?
|
||||
| "\\" [4..7] ([0..7])?
|
||||
| "\\" {mONE_NL}
|
||||
| "\\" ({WHITE_SPACE})* {mONE_NL}
|
||||
|
||||
/// Regexes ////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -160,7 +160,7 @@ mDOLLAR_SLASHY_LITERAL = {mDOLLAR_SLASHY_LITERAL_3}
|
||||
mSINGLE_QUOTED_STRING_BEGIN = "\'" ( {mSTRING_ESC}
|
||||
| "\""
|
||||
| [^\\\'\r\n]
|
||||
| "$" )*
|
||||
| "$")*
|
||||
mSINGLE_QUOTED_STRING = {mSINGLE_QUOTED_STRING_BEGIN} \'
|
||||
mTRIPLE_QUOTED_STRING = "\'\'\'" ({mSTRING_ESC}
|
||||
| \"
|
||||
|
||||
+22
-7
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
|
||||
import com.intellij.psi.impl.source.tree.LeafElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
|
||||
@@ -32,6 +33,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
@@ -43,10 +45,10 @@ import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
|
||||
*/
|
||||
public class GrLiteralImpl extends GrExpressionImpl implements GrLiteral, PsiLanguageInjectionHost {
|
||||
|
||||
private static final Function<GrLiteralImpl,PsiType> TYPE_CALCULATOR = new Function<GrLiteralImpl, PsiType>() {
|
||||
private static final Function<GrLiteralImpl,PsiType> TYPE_CALCULATOR = new NullableFunction<GrLiteralImpl, PsiType>() {
|
||||
@Override
|
||||
public PsiType fun(GrLiteralImpl grLiteral) {
|
||||
IElementType elemType = grLiteral.getFirstChild().getNode().getElementType();
|
||||
IElementType elemType = getLiteralType(grLiteral);
|
||||
return TypesUtil.getPsiType(grLiteral, elemType);
|
||||
}
|
||||
};
|
||||
@@ -60,7 +62,7 @@ public class GrLiteralImpl extends GrExpressionImpl implements GrLiteral, PsiLan
|
||||
}
|
||||
|
||||
public PsiType getType() {
|
||||
if (getFirstChild().getNode().getElementType() == kNULL) return PsiType.NULL;
|
||||
if (getLiteralType(this) == kNULL) return PsiType.NULL;
|
||||
return GroovyPsiManager.getInstance(getProject()).getType(this, TYPE_CALCULATOR);
|
||||
}
|
||||
|
||||
@@ -114,7 +116,9 @@ public class GrLiteralImpl extends GrExpressionImpl implements GrLiteral, PsiLan
|
||||
if (text.endsWith("'")) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
return StringUtil.unescapeStringCharacters(text);
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, true);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
else if (elemType == mGSTRING_LITERAL) {
|
||||
if (!text.startsWith("\"")) return null;
|
||||
@@ -124,12 +128,23 @@ public class GrLiteralImpl extends GrExpressionImpl implements GrLiteral, PsiLan
|
||||
else {
|
||||
text = StringUtil.trimEnd(text.substring(1), "\"");
|
||||
}
|
||||
return StringUtil.unescapeStringCharacters(text);
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, true);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
else if (elemType == mREGEX_LITERAL) {
|
||||
return StringUtil.trimStart(StringUtil.trimEnd(text, "/"), "/");
|
||||
text = StringUtil.trimStart(StringUtil.trimEnd(text, "/"), "/");
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, false);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
return null; //todo
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IElementType getLiteralType(GrLiteral literal) {
|
||||
PsiElement firstChild = literal.getFirstChild();
|
||||
assert firstChild != null;
|
||||
return firstChild.getNode().getElementType();
|
||||
}
|
||||
|
||||
public boolean isStringLiteral() {
|
||||
|
||||
@@ -349,4 +349,145 @@ public class GrStringUtil {
|
||||
if (text.startsWith(DOUBLE_QUOTES)) return DOUBLE_QUOTES;
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl#parseStringCharacters(String, StringBuilder, int[])
|
||||
*/
|
||||
public static boolean parseStringCharacters(@NotNull String chars,
|
||||
@NotNull StringBuilder outChars,
|
||||
@Nullable int[] sourceOffsets,
|
||||
final boolean strictBackSlash) {
|
||||
assert sourceOffsets == null || sourceOffsets.length == chars.length()+1;
|
||||
if (chars.indexOf('\\') < 0) {
|
||||
outChars.append(chars);
|
||||
if (sourceOffsets != null) {
|
||||
for (int i = 0; i < sourceOffsets.length; i++) {
|
||||
sourceOffsets[i] = i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int index = 0;
|
||||
final int outOffset = outChars.length();
|
||||
while (index < chars.length()) {
|
||||
char c = chars.charAt(index++);
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length()-outOffset] = index - 1;
|
||||
sourceOffsets[outChars.length() + 1 -outOffset] = index;
|
||||
}
|
||||
if (c != '\\') {
|
||||
outChars.append(c);
|
||||
continue;
|
||||
}
|
||||
if (index == chars.length()) return false;
|
||||
c = chars.charAt(index++);
|
||||
switch (c) {
|
||||
case'b':
|
||||
outChars.append('\b');
|
||||
break;
|
||||
|
||||
case't':
|
||||
outChars.append('\t');
|
||||
break;
|
||||
|
||||
case'n':
|
||||
outChars.append('\n');
|
||||
break;
|
||||
|
||||
case'f':
|
||||
outChars.append('\f');
|
||||
break;
|
||||
|
||||
case'r':
|
||||
outChars.append('\r');
|
||||
break;
|
||||
|
||||
case'"':
|
||||
outChars.append('"');
|
||||
break;
|
||||
|
||||
case'\'':
|
||||
outChars.append('\'');
|
||||
break;
|
||||
|
||||
case'\\':
|
||||
outChars.append('\\');
|
||||
break;
|
||||
|
||||
case'0':
|
||||
case'1':
|
||||
case'2':
|
||||
case'3':
|
||||
case'4':
|
||||
case'5':
|
||||
case'6':
|
||||
case'7':
|
||||
char startC = c;
|
||||
int v = (int)c - '0';
|
||||
if (index < chars.length()) {
|
||||
c = chars.charAt(index++);
|
||||
if ('0' <= c && c <= '7') {
|
||||
v <<= 3;
|
||||
v += c - '0';
|
||||
if (startC <= '3' && index < chars.length()) {
|
||||
c = chars.charAt(index++);
|
||||
if ('0' <= c && c <= '7') {
|
||||
v <<= 3;
|
||||
v += c - '0';
|
||||
}
|
||||
else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
index--;
|
||||
}
|
||||
}
|
||||
outChars.append((char)v);
|
||||
break;
|
||||
|
||||
case'u':
|
||||
// uuuuu1234 is valid too
|
||||
while (index != chars.length() && chars.charAt(index) == 'u') {
|
||||
index++;
|
||||
}
|
||||
if (index + 4 <= chars.length()) {
|
||||
try {
|
||||
int code = Integer.parseInt(chars.substring(index, index + 4), 16);
|
||||
//line separators are invalid here
|
||||
if (code == 0x000a || code == 0x000d) return false;
|
||||
c = chars.charAt(index);
|
||||
if (c == '+' || c == '-') return false;
|
||||
outChars.append((char)code);
|
||||
index += 4;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
int newIndex = index;
|
||||
while (chars.charAt(newIndex) == ' ') newIndex++;
|
||||
if (chars.charAt(newIndex) == '\n') {
|
||||
index = newIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strictBackSlash) {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length()-outOffset] = index;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -286,4 +286,19 @@ public class ExpressionsParsingTest extends GroovyParsingTestCase {
|
||||
void testpath$stringMethodCall1() {doTest()}
|
||||
void testpath$stringMethodCall2() {doTest()}
|
||||
void testpath$stringMethodCall3() {doTest()}
|
||||
|
||||
void testSpacesInStringAfterSlash() {
|
||||
checkParsing '''
|
||||
print 'abc \\ \ncde' ''', '''
|
||||
Groovy script
|
||||
PsiElement(new line)(\'\\n\')
|
||||
Call expression
|
||||
Reference expression
|
||||
PsiElement(identifier)(\'print\')
|
||||
PsiWhiteSpace(\' \')
|
||||
Command arguments
|
||||
Literal
|
||||
PsiElement(string)(\'\'abc \\ \\ncde\'\')
|
||||
PsiWhiteSpace(\' \')'''
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user