我正在使用 Intellij IDEA Grammar-Kit 插件创建一种语言,并且希望块是基于缩进的,而不是基于大括号的。到目前为止,我发现这样做的唯一方法是使用
https://github.com/kandeshvari/idea-nim/blob/master/src/org/dmitrigb/ideanim/parser/中的
<<indent ...>>
函数ParserUtil.java.
我已将该链接中的代码最小化为:
package org.intellij.sdk.language;
import com.intellij.lang.LighterASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.lang.impl.PsiBuilderImpl;
import com.intellij.lang.parser.GeneratedParserUtilBase;
import com.intellij.openapi.util.Key;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.containers.IntIntHashMap;
// https://github.com/kandeshvari/idea-nim/blob/master/src/org/dmitrigb/ideanim/parser/ParserUtil.java
public class SPPParserUtil extends GeneratedParserUtilBase {
private static class ParserState {
enum PrimaryMode {
NORMAL, TYPE_DEF, TYPE_DESC
}
private PsiBuilder builder;
private int currentIndent = 0;
private int pragmaCount = 0;
private PrimaryMode primaryMode = PrimaryMode.NORMAL;
private int semiStmtListCount = 0;
private IntIntHashMap tokIndentCache = new IntIntHashMap();
ParserState(PsiBuilder builder) {
this.builder = builder;
}
private String getPrecedingWhiteSpace() {
int wsOffset = 0;
while (builder.rawLookup(wsOffset - 1) == TokenType.WHITE_SPACE)
--wsOffset;
int wsStart = builder.rawTokenTypeStart(wsOffset);
return builder.getOriginalText().subSequence(wsStart, builder.getCurrentOffset()).toString();
}
int getTokenIndent() {
int tokStart = builder.getCurrentOffset();
if (tokIndentCache.containsKey(tokStart))
return tokIndentCache.get(tokStart);
int indent = -1;
String ws = getPrecedingWhiteSpace();
int nlPos = ws.lastIndexOf('\n');
if (nlPos != -1)
indent = ws.length() - nlPos - 1;
tokIndentCache.put(tokStart, indent);
return indent;
}
}
private static Key<ParserState> parserStateKey = new Key<>("parser-state");
private static ParserState getParserState(PsiBuilder builder) {
return builder.getUserData(parserStateKey);
}
public static boolean indented(PsiBuilder builder, int level, Parser parser) {
ParserState state = getParserState(builder);
int tokIndent = state.getTokenIndent();
if (tokIndent > state.currentIndent) {
int prevIndent = state.currentIndent;
state.currentIndent = tokIndent;
boolean result = parser.parse(builder, level + 1);
state.currentIndent = prevIndent;
return result;
}
return false;
}
}
另存为
SPPParserUtil.java
这是我目前最小的 BNF:
{
parserUtilClass='org.intellij.sdk.language.SPPParserUtil'
tokens=[
TK_DOT = '.'
TK_ARROW = '->'
TK_STAR = '*'
TK_COLON = ':'
KW_FILE = 'file'
KW_IMPORT = 'import'
TK_EOL = 'regexp:[\n|\r]*'
TK_IDENTIFIERS = 'regexp:[a-zA-Z_][a-zA-Z0-9_]*(,\s+[a-zA-Z_][a-zA-Z0-9_]*)*'
TK_SCOPED_IDENTIFIER = 'regexp:[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*'
TK_SPACE = 'regexp:\s+'
]
}
root ::= Program
Program ::=
ProgramContents <<eof>>
ProgramContents ::=
FileDefinition ImportBlock? TK_EOL
FileDefinition ::=
KW_FILE TK_SPACE ScopedIdentifier TK_EOL
ImportLocation ::=
TK_DOT* ScopedIdentifier
ImportDefinition ::=
ImportLocation TK_SPACE TK_ARROW TK_SPACE (Identifiers | TK_STAR) TK_EOL
ImportBlock ::=
KW_IMPORT TK_COLON <<indented ImportDefinition+>>
ScopedIdentifier ::=
TK_SCOPED_IDENTIFIER
Identifiers ::=
TK_IDENTIFIERS
这里是一些应该通过的示例代码:
file src.classes.class1
import:
src.classes.class2 -> class2, e2
src.classes.class3 -> class3, e3
src.classes.class4 -> *
但是,每当我尝试在我的 BNF 中使用
<<indent ...>>
时,总是会出现错误:所描述的错误是 ':' expected, got ':'
,在“导入”之后的冒号上。基本上<<indent ...>>
之前的任何东西似乎都被<<indent ...>>
消耗了。我将如何更改 BNF / ParseUtil
的使用来解决这个问题?