最近在做llvm相关的一些东西,llvm官网上有些很好的文档和教程,翻译下再加工分享给有兴趣的朋友们。
⊙1.1 Kaleidoscope语言
⊙1.2 词法分析器
⊙1.3完整代码
1.1Kaleidoscope介绍
Kaleidoscope直译为万花筒,为本教程的语言的简称;Kaleidoscope在这里我们定义为一种过程语言,允许您定义函数、ifelse条件、标准库函数等;在本教程中我们逐步扩展万花筒,让其支持 if/then/else 结构、for 循环、用户定义的运算符、JIT使用简单的命令行界面、调试信息。
我们目的做一个简单的语言,所以万花筒语言只支持64位浮点类型,所以所有定义的数值都是隐式双精度的,并且该语言不需要类型声明。
下方example为我们实现的万花筒语言来计算Fibonacci numbers:
# Compute the x'th fibonacci number.
def fib(x)
if x < 3 then
1
else
fib(x-1)+fib(x-2)
# This expression will compute the 40th number.
fib(40)
下方example 展示了使用万花筒语言调用标准库函数,您可以在使用函数之前使用“extern”关键字来定义它。
extern sin(arg);
extern cos(arg);
extern atan2(arg1 arg2);
atan2(sin(.4), cos(42))
接下来我们深入的学习如何实现万花筒语言。
1.2词法分析器
词法分析器(Lexer)的任务是将输入的字符序列分割成一系列的词素(tokens),这些词素是编程语言语法的基本单位,如关键字、标识符、常量、运算符等。
在万花筒语言中根据上方的介绍,我们需要定义一些Token,首先如果我们去解析已经知的token,例如extern与def,可以判断出这个是我们需要解析的,我们将其定义为 tok_def 、tok_extern,除此之外未知的identifier进行定义,例如a,b,c12,这种“在语言中暂时没意义的都被定义为tok_identifier”。
在对于一些数字的时候,定义为tok_number。
对于一些加减乘除,我们直接先返回其ascii值进行返回。
了解了需求后,定义了如下的Token
enum Token {
tok_eof = -1,
// commands
tok_def = -2,
tok_extern = -3,
// primary
tok_identifier = -4,
tok_number = -5,
};
static std::string IdentifierStr;
static double NumVal;
我们要实现一个程序,每次输入完值后就能输出解析出来token的类别,我们要考虑到要忽略掉输入字符串的空格。
static int LastChar = ' ';
while (isspace(LastChar))
{
LastChar = getchar();
}
判断获得的getchar是否以字母来开头,如果是的情况下判定是否为def或者extern然后返回相应的token值,否则的情况,就会返回tok_identifier,实现代码如下
//必须以字母开头
if (isalpha(LastChar))
{
IdentifierStr = LastChar;
while (isalnum((LastChar = getchar())))
IdentifierStr += LastChar;
if (IdentifierStr == "def")
return tok_def;
if (IdentifierStr == "extern")
return tok_extern;
return tok_identifier;
}
然后再判断开头是否为数字,或者包含,这个直接返回token为tok_number。
if (isdigit(LastChar) || LastChar == '.')
{ // Number: [0-9.]+
std::string NumStr;
do
{
NumStr += LastChar;
LastChar = getchar();
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), 0);
return tok_number;
}
然后处理结尾换行符等。
if (LastChar == '#')
{
// Comment until end of line.
do
LastChar = getchar();
while (LastChar != EOF && LastChar != 'n' && LastChar != 'r');
if (LastChar != EOF)
return gettok();
}
1.3完整代码
using namespace std;
enum Token
{
tok_eof = -1,
// commands
tok_def = -2,
tok_extern = -3,
// primary
tok_identifier = -4,
tok_number = -5,
};
static std::string IdentifierStr; // Filled in if tok_identifier
static double NumVal; // Filled in if tok_number
/// gettok - Return the next token from standard input.
static int gettok()
{
static int LastChar = ' ';
// Skip any whitespace.
while (isspace(LastChar))
{
LastChar = getchar();
}
//必须以字母开头
if (isalpha(LastChar))
{
// identifier: [a-zA-Z][a-zA-Z0-9]*
IdentifierStr = LastChar;
while (isalnum((LastChar = getchar())))
IdentifierStr += LastChar;
if (IdentifierStr == "def")
return tok_def;
if (IdentifierStr == "extern")
return tok_extern;
return tok_identifier;
}
if (isdigit(LastChar) || LastChar == '.')
{ // Number: [0-9.]+
std::string NumStr;
do
{
NumStr += LastChar;
LastChar = getchar();
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), 0);
return tok_number;
}
if (LastChar == '#')
{
// Comment until end of line.
do
LastChar = getchar();
while (LastChar != EOF && LastChar != 'n' && LastChar != 'r');
if (LastChar != EOF)
return gettok();
}
// Check for end of file. Don't eat the EOF.
if (LastChar == EOF)
return tok_eof;
// Otherwise, just return the character as its ascii value.
int ThisChar = LastChar;
LastChar = getchar();
return ThisChar;
}
int main(){
while (true)
{
int tok= gettok();
std::cout << "got token:" << tok << endl;
}
}
编译完运行,就可以输出token数值了
学习爬虫和逆向相关知识可以关注我朋友:
我是BestToYou,分享工作或日常学习中关于二进制逆向和分析的一些思路和一些自己闲暇时刻调试的一些程序,文中若有错误的地方,恳请大家联系我批评指正。
原文始发于微信公众号(二进制科学):第 1 章:Kaleidoscope和词法分析器
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论