mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
RegExp: support MySQL syntax extension
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -111,4 +111,9 @@ public enum RegExpCapability {
|
||||
* Allow \x9 in addition to \x09 (ruby)
|
||||
*/
|
||||
ONE_HEX_CHAR_ESCAPE,
|
||||
|
||||
/**
|
||||
* MySQL character classes [=c=] [.class.] [:<:] [:>:]
|
||||
*/
|
||||
MYSQL_BRACKET_EXPRESSIONS,
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public interface RegExpElementTypes {
|
||||
IElementType NAMED_GROUP_REF = new RegExpElementType("NAMED_GROUP_REF");
|
||||
IElementType PY_COND_REF = new RegExpElementType("PY_COND_REF");
|
||||
IElementType POSIX_BRACKET_EXPRESSION = new RegExpElementType("POSIX_BRACKET_EXPRESSION");
|
||||
IElementType MYSQL_CHAR_EXPRESSION = new RegExpElementType("MYSQL_CHAR_EXPRESSION");
|
||||
IElementType MYSQL_CHAR_EQ_EXPRESSION = new RegExpElementType("MYSQL_CHAR_EQ_EXPRESSION");
|
||||
IElementType NUMBER = new RegExpElementType("NUMBER");
|
||||
|
||||
TokenSet ATOMS = TokenSet.create(CLOSURE, BOUNDARY, SIMPLE_CLASS, CLASS, CHAR, GROUP, PROPERTY, BACKREF, NAMED_GROUP_REF,
|
||||
|
||||
@@ -256,6 +256,12 @@ public class RegExpParser implements PsiParser, LightPsiParser {
|
||||
else if (token == RegExpTT.BRACKET_EXPRESSION_BEGIN) {
|
||||
parseBracketExpression(builder);
|
||||
}
|
||||
else if (token == RegExpTT.MYSQL_CHAR_BEGIN) {
|
||||
parseMysqlCharExpression(builder);
|
||||
}
|
||||
else if (token == RegExpTT.MYSQL_CHAR_EQ_BEGIN) {
|
||||
parseMysqlCharEqExpression(builder);
|
||||
}
|
||||
else if (RegExpTT.CHARACTERS.contains(token) || token == RegExpTT.NAMED_CHARACTER) {
|
||||
parseCharacterRange(builder);
|
||||
}
|
||||
@@ -285,6 +291,27 @@ public class RegExpParser implements PsiParser, LightPsiParser {
|
||||
marker.done(RegExpElementTypes.POSIX_BRACKET_EXPRESSION);
|
||||
}
|
||||
|
||||
private static void parseMysqlCharExpression(PsiBuilder builder) {
|
||||
final PsiBuilder.Marker marker = builder.mark();
|
||||
builder.advanceLexer();
|
||||
if (builder.getTokenType() == RegExpTT.NAME) {
|
||||
builder.advanceLexer();
|
||||
}
|
||||
else {
|
||||
checkMatches(builder, RegExpTT.CHARACTER, "Character or MySQL character name expected");
|
||||
}
|
||||
checkMatches(builder, RegExpTT.MYSQL_CHAR_END, "Unclosed MySQL character expression");
|
||||
marker.done(RegExpElementTypes.MYSQL_CHAR_EXPRESSION);
|
||||
}
|
||||
|
||||
private static void parseMysqlCharEqExpression(PsiBuilder builder) {
|
||||
final PsiBuilder.Marker marker = builder.mark();
|
||||
builder.advanceLexer();
|
||||
checkMatches(builder, RegExpTT.CHARACTER, "Character expected");
|
||||
checkMatches(builder, RegExpTT.MYSQL_CHAR_EQ_END, "Unclosed MySQL character equivalence class");
|
||||
marker.done(RegExpElementTypes.MYSQL_CHAR_EQ_EXPRESSION);
|
||||
}
|
||||
|
||||
private void parseCharacterRange(PsiBuilder builder) {
|
||||
final PsiBuilder.Marker rangeMarker = builder.mark();
|
||||
parseCharacter(builder);
|
||||
|
||||
@@ -48,6 +48,14 @@ public interface RegExpTT {
|
||||
IElementType BRACKET_EXPRESSION_BEGIN = new RegExpElementType("BRACKET_EXPRESSION_BEGIN");
|
||||
/** ":]" */
|
||||
IElementType BRACKET_EXPRESSION_END = new RegExpElementType("BRACKET_EXPRESSION_END");
|
||||
/** "[." */
|
||||
IElementType MYSQL_CHAR_BEGIN = new RegExpElementType("MYSQL_CHAR_BEGIN");
|
||||
/** ".]" */
|
||||
IElementType MYSQL_CHAR_END = new RegExpElementType("MYSQL_CHAR_END");
|
||||
/** "[=" */
|
||||
IElementType MYSQL_CHAR_EQ_BEGIN = new RegExpElementType("MYSQL_CHAR_EQ_BEGIN");
|
||||
/** "=]" */
|
||||
IElementType MYSQL_CHAR_EQ_END = new RegExpElementType("MYSQL_CHAR_EQ_END");
|
||||
|
||||
/** "(" */
|
||||
IElementType GROUP_BEGIN = new RegExpElementType("GROUP_BEGIN");
|
||||
|
||||
@@ -41,6 +41,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
||||
private boolean allowTransformationEscapes;
|
||||
private boolean allowExtendedUnicodeCharacter;
|
||||
private boolean allowOneHexCharEscape;
|
||||
private boolean allowMysqlBracketExpressions;
|
||||
private int maxOctal = 0777;
|
||||
private int minOctalDigits = 1;
|
||||
|
||||
@@ -59,6 +60,7 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
||||
this.allowEmptyCharacterClass = capabilities.contains(ALLOW_EMPTY_CHARACTER_CLASS);
|
||||
this.allowPosixBracketExpressions = capabilities.contains(POSIX_BRACKET_EXPRESSIONS);
|
||||
this.allowTransformationEscapes = capabilities.contains(TRANSFORMATION_ESCAPES);
|
||||
this.allowMysqlBracketExpressions = capabilities.contains(MYSQL_BRACKET_EXPRESSIONS);
|
||||
if (capabilities.contains(MAX_OCTAL_177)) {
|
||||
maxOctal = 0177;
|
||||
}
|
||||
@@ -113,6 +115,8 @@ import static org.intellij.lang.regexp.RegExpCapability.*;
|
||||
%xstate PY_NAMED_GROUP_REF
|
||||
%xstate PY_COND_REF
|
||||
%xstate BRACKET_EXPRESSION
|
||||
%xstate MYSQL_CHAR_EXPRESSION
|
||||
%xstate MYSQL_CHAR_EQ_EXPRESSION
|
||||
%xstate EMBRACED_HEX
|
||||
|
||||
DOT="."
|
||||
@@ -126,6 +130,7 @@ RBRACKET="]"
|
||||
ESCAPE="\\"
|
||||
NAME=[:letter:]([:letter:]|_|-|" "|"("|")"|[:digit:])*
|
||||
GROUP_NAME=[:letter:]([:letter:]|_|-|" "|[:digit:])*
|
||||
MYSQL_CHAR_NAME=[:letter:](-|[:letter:])*[:digit:]?
|
||||
ANY=[^]
|
||||
|
||||
META1 = {ESCAPE} | {LBRACKET}
|
||||
@@ -322,6 +327,7 @@ HEX_CHAR=[0-9a-fA-F]
|
||||
{ESCAPE} {RBRACKET} { yybegin(CLASS2); return allowEmptyCharacterClass ? RegExpTT.ESC_CHARACTER : RegExpTT.REDUNDANT_ESCAPE; }
|
||||
{RBRACKET} { if (allowEmptyCharacterClass) { yypopstate(); return RegExpTT.CLASS_END; } yybegin(CLASS2); return RegExpTT.CHARACTER; }
|
||||
{LBRACKET} / ":" { yybegin(CLASS2); if (allowPosixBracketExpressions) { yypushback(1); } else if (allowNestedCharacterClasses) { yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN; } else { return RegExpTT.CHARACTER; } }
|
||||
{LBRACKET} / [.=] { yybegin(CLASS2); if (allowMysqlBracketExpressions) { yypushback(1); } else if (allowNestedCharacterClasses) { yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN; } else { return RegExpTT.CHARACTER; } }
|
||||
{LBRACKET} / "^" { yybegin(CLASS2); if (allowNestedCharacterClasses) { yypushstate(NEGATED_CLASS); return RegExpTT.CLASS_BEGIN; } return RegExpTT.CHARACTER; }
|
||||
{LBRACKET} { yybegin(CLASS2); if (allowNestedCharacterClasses) { yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN; } return RegExpTT.CHARACTER; }
|
||||
[\n\b\t\r\f ] { if (commentMode) return com.intellij.psi.TokenType.WHITE_SPACE; yypushback(1); yybegin(CLASS2); }
|
||||
@@ -329,9 +335,16 @@ HEX_CHAR=[0-9a-fA-F]
|
||||
}
|
||||
|
||||
<CLASS2> {
|
||||
{LBRACKET} ":" { if (allowPosixBracketExpressions) {
|
||||
{LBRACKET} [:=.] { char c = yycharat(1);
|
||||
if (allowPosixBracketExpressions && c == ':') {
|
||||
yybegin(BRACKET_EXPRESSION);
|
||||
return RegExpTT.BRACKET_EXPRESSION_BEGIN;
|
||||
} else if (allowMysqlBracketExpressions && c == '=') {
|
||||
yybegin(MYSQL_CHAR_EQ_EXPRESSION);
|
||||
return RegExpTT.MYSQL_CHAR_EQ_BEGIN;
|
||||
} else if (allowMysqlBracketExpressions && c == '.') {
|
||||
yybegin(MYSQL_CHAR_EXPRESSION);
|
||||
return RegExpTT.MYSQL_CHAR_BEGIN;
|
||||
} else {
|
||||
yypushback(1);
|
||||
return allowNestedCharacterClasses ? RegExpTT.CLASS_BEGIN : RegExpTT.CHARACTER;
|
||||
@@ -351,6 +364,21 @@ HEX_CHAR=[0-9a-fA-F]
|
||||
"^" { return RegExpTT.CARET; }
|
||||
{NAME} { return RegExpTT.NAME; }
|
||||
":" {RBRACKET} { yybegin(CLASS2); return RegExpTT.BRACKET_EXPRESSION_END; }
|
||||
[<>] { return allowMysqlBracketExpressions ? RegExpTT.NAME : RegExpTT.BAD_CHARACTER; }
|
||||
{ANY} { return RegExpTT.BAD_CHARACTER; }
|
||||
}
|
||||
|
||||
<MYSQL_CHAR_EXPRESSION> {
|
||||
{MYSQL_CHAR_NAME} { return RegExpTT.NAME; }
|
||||
"." {RBRACKET} { yybegin(CLASS2); return RegExpTT.MYSQL_CHAR_END; }
|
||||
{ANY} / "." {RBRACKET} { return RegExpTT.CHARACTER; }
|
||||
{ANY} { return RegExpTT.BAD_CHARACTER; }
|
||||
}
|
||||
|
||||
<MYSQL_CHAR_EQ_EXPRESSION> {
|
||||
{NAME} { return RegExpTT.NAME; }
|
||||
"=" {RBRACKET} { yybegin(CLASS2); return RegExpTT.MYSQL_CHAR_EQ_END; }
|
||||
{ANY} / "=" {RBRACKET} { return RegExpTT.CHARACTER; }
|
||||
{ANY} { return RegExpTT.BAD_CHARACTER; }
|
||||
}
|
||||
|
||||
|
||||
@@ -418,7 +418,7 @@ public final class RegExpAnnotator extends RegExpElementVisitor implements Annot
|
||||
@Override
|
||||
public void visitPosixBracketExpression(RegExpPosixBracketExpression posixBracketExpression) {
|
||||
final String className = posixBracketExpression.getClassName();
|
||||
if (!POSIX_CHARACTER_CLASSES.contains(className)) {
|
||||
if (!POSIX_CHARACTER_CLASSES.contains(className) && !"<".equals(className) && !">".equals(className)) {
|
||||
final ASTNode node = posixBracketExpression.getNode().findChildByType(RegExpTT.NAME);
|
||||
if (node != null) {
|
||||
final Annotation annotation = myHolder.createErrorAnnotation(node, "Unknown POSIX character class");
|
||||
|
||||
@@ -132,6 +132,24 @@ public class RegExpLexerTest extends LexerTestCase {
|
||||
"CLASS_END (']')", lexer);
|
||||
}
|
||||
|
||||
public void testMysqlCharExpressions() {
|
||||
final RegExpLexer lexer = new RegExpLexer(EnumSet.of(MYSQL_BRACKET_EXPRESSIONS));
|
||||
doTest("[[.~.][.tilda.][.NUL.][.plus-sign.]]", "CLASS_BEGIN ('[')\n" +
|
||||
"MYSQL_CHAR_BEGIN ('[.')\nCHARACTER ('~')\nMYSQL_CHAR_END ('.]')\n" +
|
||||
"MYSQL_CHAR_BEGIN ('[.')\nNAME ('tilda')\nMYSQL_CHAR_END ('.]')\n" +
|
||||
"MYSQL_CHAR_BEGIN ('[.')\nNAME ('NUL')\nMYSQL_CHAR_END ('.]')\n" +
|
||||
"MYSQL_CHAR_BEGIN ('[.')\nNAME ('plus-sign')\nMYSQL_CHAR_END ('.]')\n" +
|
||||
"CLASS_END (']')", lexer);
|
||||
}
|
||||
|
||||
public void testMysqlCharEqExpressions() {
|
||||
final RegExpLexer lexer = new RegExpLexer(EnumSet.of(MYSQL_BRACKET_EXPRESSIONS));
|
||||
doTest("[[=.=][=c=]]", "CLASS_BEGIN ('[')\n" +
|
||||
"MYSQL_CHAR_EQ_BEGIN ('[=')\nCHARACTER ('.')\nMYSQL_CHAR_EQ_END ('=]')\n" +
|
||||
"MYSQL_CHAR_EQ_BEGIN ('[=')\nCHARACTER ('c')\nMYSQL_CHAR_EQ_END ('=]')\n" +
|
||||
"CLASS_END (']')", lexer);
|
||||
}
|
||||
|
||||
/**
|
||||
* \\177 is the maximum valid octal character under Ruby.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user