ANTLR4标记行的第一个非空白非注释字符 我正在寻找一种方法来标记一个“是”,这是一个行的第一个非空白,非注释字符(这与标准C++预处理指令要求完全相同)。注意第一个非空白空间要求,它意味着可以用空白和多行注释(例如,使用C++预处理指令作为例子):
及 “#”的前面和后面可以是跨越>1行和空格的多行注释。其他“#”可以作为运算符出现在行中,因此作为行中的第一个有效字符是关键要求 我们如何标记第一个有效字符 我试过这个ANTLR4标记行的第一个非空白非注释字符 我正在寻找一种方法来标记一个“是”,这是一个行的第一个非空白,非注释字符(这与标准C++预处理指令要求完全相同)。注意第一个非空白空间要求,它意味着可以用空白和多行注释(例如,使用C++预处理指令作为例子):,antlr4,Antlr4,及 “#”的前面和后面可以是跨越>1行和空格的多行注释。其他“#”可以作为运算符出现在行中,因此作为行中的第一个有效字符是关键要求 我们如何标记第一个有效字符 我试过这个 FIRSTHASH: {getCharPositionInLine() == 0}? ('/*' .*? '*/' | [ \t\f])* '#'; 但这是错误的,因为这样的输入 /* */other line /* S*/ /*SS*/# 被错误地认为是2个标记(1个大注释+一个“#”)。i、 e.*?错误地消耗了两
FIRSTHASH: {getCharPositionInLine() == 0}? ('/*' .*? '*/' | [ \t\f])* '#';
但这是错误的,因为这样的输入
/* */other line
/* S*/ /*SS*/#
被错误地认为是2个标记(1个大注释+一个“#”)。i、 e.
*?
错误地消耗了两条*/
导致两行合并为一条注释。(是否可以将多行注释中的*?
替换为明确排除*/
?)的内容。我会在解析阶段甚至解析之后不受约束地对其进行检查
将“#”放在别处可能不符合语法,但不会使解析无效=>将其移动到更容易检测到的稍后阶段
如果您真的想“早”完成(即不是在解析之后),请在解析阶段完成。
词法分析不依赖于它(即与字符串或注释不同),因此在词法分析阶段进行词法分析没有任何意义
这是C#中的一个示例。
它检查所有3个定义(前两个正常,第三个不正常)
解析器:
parser grammar hashParse;
options { tokenVocab=hashLex; }
compileUnit
: (allKindOfStuff | preproc)*
EOF
;
preproc : PreProcStart .*? PreProcEnd
;
allKindOfStuff
: Identifier
| LParen
| RParen
;
样本:
/*
can be preceded by multiline comment spreading across >1 lines
123 */ /* abc */# /* */define xyz(a) #a
/* def */# /* */define xyz(a) #a
some other code // #
illegal #define a b
public class hashTest
{
public static void test()
{
var sample = File.ReadAllText(@"ANTLR\unrelated\hashTest.txt");
var sampleStream = new Antlr4.Runtime.AntlrInputStream(sample);
var lexer = new hashLex(input: sampleStream);
var tokenStream = new CommonTokenStream(tokenSource: lexer);
var parser = new hashParse(input: tokenStream);
var result = parser.compileUnit();
var visitor = new HashVisitor(tokenStream: tokenStream);
var visitResult = visitor.Visit(result);
}
}
public class HashVisitor : hashParseBaseVisitor<object>
{
private readonly CommonTokenStream tokenStream;
public HashVisitor(CommonTokenStream tokenStream)
{
this.tokenStream = tokenStream;
}
public override object VisitPreproc(hashParse.PreprocContext context)
{
;
var startSymbol = context.PreProcStart().Symbol;
var tokenIndex = startSymbol.TokenIndex;
var startLine = startSymbol.Line;
var previousTokens_reversed = tokenStream.GetTokens(0, tokenIndex - 1).Reverse();
var ok = true;
var allowedTypes = new[] { hashLex.RangeComment, hashLex.WS, };
foreach (var token in previousTokens_reversed)
{
if (token.Line < startLine)
break;
if (allowedTypes.Contains(token.Type) == false)
{
ok = false;
break;
}
;
}
if (!ok)
{
; // handle error
}
return base.VisitPreproc(context);
}
}
lexer grammar hashLex;
PreProcStart : Hash -> pushMode(PRE_PROC_MODE)
;
Identifier:
Identifier_
;
LParen : '(';
RParen : ')';
WS
: WS_-> channel(HIDDEN)
;
LineComment
: '//'
~('\r'|'\n')*
(LineBreak|EOF)
-> channel(HIDDEN)
;
RangeComment
: '/*'
.*?
'*/'
-> channel(HIDDEN)
;
mode PRE_PROC_MODE;
PreProcIdentifier : Identifier_;
PreProcHash : Hash;
PreProcEnd :
(EOF|LineBreak) -> popMode
;
PreProcWS : [ \t]+ -> channel(HIDDEN)
;
PreProcLParen : '(';
PreProcRParen : ')';
PreProcRangeComment
: '/*'
(~('\r' | '\n'))*?
'*/'
-> channel(HIDDEN)
;
fragment LineBreak
: '\r' '\n'?
| '\n'
;
fragment Identifier_:
[a-zA-Z]+
;
fragment Hash : '#'
;
fragment WS_
: [ \t\r\n]+
;
parser grammar hashParse;
options { tokenVocab=hashLex; }
compileUnit
: (allKindOfStuff | preproc)*
EOF
;
preproc : PreProcStart .*? PreProcEnd
;
allKindOfStuff
: Identifier
| LParen
| RParen
;
/*
can be preceded by multiline comment spreading across >1 lines
123 */ /* abc */# /* */define xyz(a) #a
/* def */# /* */define xyz(a) #a
some other code // #
illegal #define a b