您不需要实际解析代码来计算行号,将其标记化就足够了。
算法可能如下所示:
int lastLine = -1;
int lines = 0;
for each token {
if (isCode(token) && lastLine != token.line) {
++lines;
lastLine = token.line;
}
}
在标记化过程中您需要收集的唯一信息是:
- 它是什么类型的标记(操作符、标识符、注释...)您实际上不需要在这里非常精确,因为您只需要区分“非代码标记”(cmets)和“代码令牌”(其他)
- 令牌出现在文件中的哪一行。
关于如何标记化,这是你自己想办法,但是为这样一个简单的案例手写标记器应该不难。你可以使用flex,但这可能是多余的。
编辑
我已经提到了“标记化”,让我快速为您描述一下:
标记化是编译的第一阶段。分词的输入是文本(多行程序),输出是一个“令牌”序列,如:具有某种意义的符号。例如下面的程序:
#include "something.h"
/*
This is my program.
It is quite useless.
*/
int main() {
return something(2+3); // this is equal to 5
}
可能看起来像:
PreprocessorDirective("include")
StringLiteral("something.h")
PreprocessorDirectiveEnd
MultiLineComment(...)
Keyword(INT)
Identifier("main")
Symbol(LeftParen)
Symbol(RightParen)
Symbol(LeftBrace)
Keyword(RETURN)
Identifier("something")
Symbol(LeftParen)
NumericLiteral(2)
Operator(PLUS)
NumericLiteral(3)
Symbol(RightParen)
Symbol(Semicolon)
SingleLineComment(" this is equal to 5")
Symbol(RightBrace)
等等。
根据其类型,令牌可能附加任意元数据(即符号类型、运算符类型、标识符文本,或者可能是找到令牌的行号)。
然后将此类标记流馈送到解析器,解析器使用根据这些标记编写的语法生成规则,例如,构建语法树。
做一个完整的解析器可以给你一个完整的代码语法树是具有挑战性的,尤其是在我们谈论的是 C++ 时。但是,标记化(或“词法分析”或“词法分析”)更容易,尤其是。当您不关心太多细节时,您应该能够使用Finite state machine 自己编写标记器。
关于如何实际使用输出来计算代码行数(即至少“代码”标记的行,即除注释之外的任何标记) - 请参阅我之前描述的算法。