【问题标题】:Output of Lexer词法分析器的输出
【发布时间】:2016-04-17 19:21:12
【问题描述】:

我目前正在编写一个编译器,我正处于 Lexer 阶段。

我知道词法分析器标记输入流。

但是,请考虑以下流:

int foo = 0;

词法分析器的输出应该是:Keywordletterletterletterequalsdigitsemicolon?然后解析器将 letter letter letter 减少为标识符?

【问题讨论】:

    标签: compiler-construction lexer


    【解决方案1】:

    一般来说,你的词法分析器应该产生一个 streamstructs,其中包含语言元素:运算符、标识符、关键字、cmets 等。这些结构应该用类型标记的词位,并携带与其所代表的词位类型相关的内容。

    为了实现良好的错误报告,最好每个词位都包含有关起始行和列、结束行和列(一些词位跨越多行)和原始源文件(有时解析器必须将包含的文件处理为以及主文件)。

    对于那些包含可变内容(数字、标识符等)的语言元素,结构体应该包含可变内容。

    对于编译或程序分析,词法分析器可以丢弃空格和 cmets。如果您打算解析/修改代码,则需要捕获 cmets。

    示例输出可能具有指导意义。 对于 OP 示例的变体:

    /* My test file */
    
    int foo
        = 0; // a declaration
    

    ... DMS 的 C 前端产生以下词法(这是一个调试输出,在设计复杂词法分析器时非常方便):

    C:\DMS\Domains\C\GCC4\Tools\Lexer\Source>run ../domainlexer C:\temp\test.c
    Lexer Stream Display 1.5.1
    Using encoding Unicode-UTF-8?ANSI +CRLF +1 /^I
    !! Lexer:ResetLexicalModeStack
    !! after Lexer:PushLexicalMode:
    Lexical Mode Stack:
    1 C
    File "C:/temp/test.c", line 1: /* My test file */
    File "C:/temp/test.c", line 2:
    File "C:/temp/test.c", line 3: int foo
    !! Lexer:GotoLexicalMode 2 CMain
    !! Lexeme @ Line 3 Col 1 ELine 3 ECol 4 Token 23: 'int' [VOID]=0000
      <<< PreComments:
    Comment 1 Type 1 Line 1 Column 1 `/* My test file */'
    !! Lexeme @ Line 3 Col 4 ELine 3 ECol 5 Token 2: whitespace [VOID]=0000
    !! Lexeme @ Line 3 Col 5 ELine 3 ECol 8 Token 210: IDENTIFIER [STRING]=`foo'
    File "C:/temp/test.c", line 4:     = 0; // a declaration
    !! Lexer:GotoLexicalMode 1 C
    !! Lexeme @ Line 3 Col 8 ELine 4 ECol 5 Token 2: whitespace [VOID]=0000
    !! Lexer:GotoLexicalMode 2 CMain
    !! Lexeme @ Line 4 Col 5 ELine 4 ECol 6 Token 113: '=' [VOID]=0000
    !! Lexeme @ Line 4 Col 6 ELine 4 ECol 7 Token 2: whitespace [VOID]=0000
    !! Lexeme @ Line 4 Col 7 ELine 4 ECol 8 Token 138: INT_LITERAL [NATURAL]=0
    File "C:/temp/test.c", line 5:
    !! Lexeme @ Line 4 Col 8 ELine 4 ECol 9 Token 98: ';' [VOID]=0000
      >>> PostComments:
    Comment 1 Type 2 Line 4 Column 10 `// a declaration'
    File "C:/temp/test.c", line 5:
    File "C:/temp/test.c", line 6:
    File "C:/temp/test.c", line 7:
    !! Lexer:GotoLexicalMode 1 C
    !! Lexeme @ Line 4 Col 26 ELine 7 ECol 1 Token 2: whitespace [VOID]=0000
    !! Lexeme @ Line 7 Col 1 ELine 7 ECol 1 Token 4: end_of_input_stream [VOID]=0000
    !! Lexer:GotoLexicalMode 2 CMain
    !! Lexeme @ Line 7 Col 1 ELine 7 ECol 1 Token 0: EndOfFile
    11 lexemes processed.
    0 lexical errors detected.
    
    C:\DMS\Domains\C\GCC4\Tools\Lexer\Source>
    

    主要输出是标记为 !! 的行,每行代表词法分析器生成的词位结构的内容。每个词位携带:

    • 源文件位置信息(对于主文件,在这种情况下为“test.c”,为了使调试输出更具可读性,不打印该信息)
    • “令牌编号”(词位类型)和人类可读的令牌名称(使调试更容易)
    • token携带的值类型:[VOID]表示“无”,[STRING]表示token携带字符串值,[NATURAL]表示携带整数值等。
    • precmets:标记前的注释。这对于经典的词法分析器来说是不寻常的,但如果试图转换源代码,这是必要的。你不能失去cmets!请注意,前置注释附加到 token;因为 cmets 在语义上没有意义,人们可以争论它们应该放在哪里。这是我们的特别选择。
    • postcomment:跟随属于它的令牌的评论。

    最后一个“令牌”EndOfFile 在每个 DMS 词法分析器中都隐含定义。

    此调试跟踪还记录了词法分析器跨词法模式的转换(许多词法分析器生成器具有多种模式,它们可以对语言的各个部分进行词法分析)。它在读取时显示源代码行。

    【讨论】:

    • 我只是喜欢那些反对者。打了就跑,没有任何评论为什么这不能准确回答 OP 的问题。
    【解决方案2】:

    将“字母”作为中间步骤并没有真正的好处——相反,“foo”可能应该是一个标识符。否则你也可以将int 理解为“字母字母”,这没有多大意义。

    【讨论】:

      【解决方案3】:

      一般情况没有简单的答案。

      通常,如果语言允许。语法越动态,令牌的解释更多地取决于解析器的内部状态,那么将解释置于解析器上可能会更容易。否则,词法分析器和解析器之间的通信可能会变得过于复杂。 (例如,考虑一种语言,其中 int 在一个位置是类型,在另一个位置是有效变量名,在第三种情况下是语言关键字)

      作为一个经验法则:让词法分析器完成所有使语法变得简单的工作,而不会在词法分析器和解析器之间造成额外的复杂性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-17
        • 1970-01-01
        • 2019-10-18
        • 1970-01-01
        • 2014-04-21
        • 2010-10-21
        • 2013-01-31
        • 1970-01-01
        相关资源
        最近更新 更多