我正在寻找一种方法来将'#'标记为第一行的非空白,非注释字符(这与标准C ++预处理指令要求完全相同)。注意第一个非空白要求暗示#可以在空格和多行注释之前,例如(使用C ++预处理指令作为示例):
/* */ /* abc */ #define getit(x,y) #x x##y
和
/*
can be preceded by multiline comment spreading across >1 lines
123 */ /* abc */# /* */define xyz(a) #a
“#”之前和之后可以是跨越> 1行和空格的多行注释。其他'#'可以作为运算符出现在行中,因此作为行中的第一个有效字符是关键要求。
我们如何标记第一个有效的#字符?
我试过这个
FIRSTHASH: {getCharPositionInLine() == 0}? ('/*' .*? '*/' | [ \t\f])* '#';
但这是因为像这样的输入
的错误/* */other line
/* S*/ /*SS*/#
被错误地视为2个令牌(1个大评论+单个“#”)。即.*?
消耗2 2 */
错误地导致2行合并为1条评论。 (是否可以通过明确排除.*?
的内容替换多行注释中的*/
?)
答案 0 :(得分:0)
我在没有约束的情况下使用它,并在解析阶段或解析后进行检查。
将#'#'放在一起可能不符合语法。在其他地方,但它没有使解析无效=>将它移到后期,可以更容易地检测到它!
如果你真的想这么做的话,早期" (即解析后没有),在解析阶段进行。 乐兴并不依赖它(即不像字符串或注释),所以在lexing阶段没有任何意义。
这是C#中的一个示例。 它检查所有3个定义(前两个是好的,第三个不好)。
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