对一行的第一个非空白非注释字符进行标记的ANTLR4

时间:2014-04-04 17:38:55

标签: antlr4

我正在寻找一种方法来将'#'标记为第一行的非空白,非注释字符(这与标准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条评论。 (是否可以通过明确排除.*?的内容替换多行注释中的*/?)

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