一般来说,你的词法分析器应该产生一个 stream 的 structs,其中包含语言元素:运算符、标识符、关键字、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 词法分析器中都隐含定义。
此调试跟踪还记录了词法分析器跨词法模式的转换(许多词法分析器生成器具有多种模式,它们可以对语言的各个部分进行词法分析)。它在读取时显示源代码行。