包含的内容:
词法分析
先分类,例如关键字、数值、字符、变量名/函数名、符号、运算符(符号和运算符可能有重叠)等,再进行语法分析,得出变量/函数/类/结构体的声明与定义、指针、赋值、判断、循环、函数。
之后对其上下文和指针在栈进行处理。
这个解析过程是使用正则进行大部分判断,可以把输入都当作一个个单词来考虑,例如int
关键字正则就是int\s
,函数式的判断例如void add(int a, int b) {}
使用正则是void\s[^(_|[a-b]){1}a-bA-Z_0-9]*\([^\)]*\)\{[^\}]*\}
而数值要注意可以根据开头或者结尾区分二进制、四进制、八进制、十进制、十六进制等,也要根据符号位区分正数、负数,根据小数点区分整数、浮点数。
词法和语法分析其实与AST语法树生成有紧密联系,只不过同时这种分析也要用于编辑器ide本身的即时提示和纠错。
AST语法树生成
一般是先中间后两边的树结构,例如a=1的赋值语法,父节点就是=,左节点是a,右节点是1,同理扩展到函数定义,void main(int num) {/函数体/},中间是函数名,左边是参数值,右边是函数体,且挂靠孙子节点为返回值类型。
语法优化
生成机器码
要点:
- 寄存器的分配,通过变量-图的着色算法RCG(Register Conflict Graph)来整理适用范围、冲突从而决定寄存器的分配,高级点的系统有16~32个寄存器,低级系统只有8个寄存器。
- 指令码的生成(查CPU手册)
- 特殊处理,如循环需要考虑具体的机器码生成,由于同一条指令最多只能使用3个寄存器,因此会出现对寄存器的预分配导致循环体内的代码顺序与实际处理后的顺序不一致的情况(会出现例如代码赋值是在真假判断里,而生成后是在判断外面,内存失去控制)。
参考文章:
编译器把代码转化为机器码的过程
编译原理:抽象语法树是怎么变成机器码的-编译原理(龙书)
代码片段
/// @file compiler.h
#pragma once
#include "../include/stdafx.h"
#ifndef META_COMPILE_H
#define META_COMPILE_H
/*
Kyeword: unsigned, signed, char, short, int, float, double, long, void, struct, union, static, __ignore_typecheck__, return ,include, sizeof, if, else, while, for, do, break, continue, switch, case, default, goto, typedef, const, exterun, restrict
Operator: +, -, *, >, <, ^, %, !, =, ~, |, &, (, [, ',', ., ?(We also allow some to join i.e >>, <<, ***)
Number: if (c >= '0' && c <= '9')
Symbol: {, }, :, ;, #, \, ), ]
Identifier: A-Z, a-z, followed by 0-9 and "_" underscrolls (Think of variable names, function names, structure names)
*/
struct token {
int type;
int flags;
union {
char cval;
const char* sval;
unsigned int inum;
unsigned long lnum;
unsigned long long llnum;
void* any;
};
// 为真时代表两个token之间有空格,例如* a作为token *意味着有空格应该被作为token "a"
bool whitespcae;
const char* between_brackets;
};
struct buffer {
}
struct lex_process_function {
}
struct lex_process {
struct pos pos;
struct std::vector<token>* token_vec;
struct compile_process* compiler;
int current_expression_count;
strct buffer* parentheses_uffer;
struct lex_process_function* function;
void* private;
}
enum compile_process_result {
COMPILE_PROCESS_SUCCESS,
COMPILE_PROCESS_FAILED
};
struct compile_process {
int flags;
struct compile_process_input_file {
std::ifstream* i_file;
const std::string* abs_path;
} cfile;
std::ofstream* o_file;
};
int compile_file(const std::string, const std::string, int);
#endif