PS:部分图片是中科大保健老师的课上截屏过来的
1.一般编译器的结构
简单来说一个编译器的设计其工程规模是十分庞大的,从字符序列到目标代码其中的每一次转变都需要一个模块的作用
2.编译器自顶向下阶段划分
如果将我们常用的编译器看成一个黑盒,不管内部的实现:
如果要看到更细节的地方:
源程序需要经过前端(front end)产生中间表示,再经过后端(back end)去产生目标代码。
可以看出:前端处理的是和源程序相关的属性,后端处理的是具体的体系结构和目标机相关的属性
2.1前端构造
语义分析器有时也成为类型检查器
2.1.1词法分析器
将字符流读取进来转换为记号流,字符流就是源代码字符串,记号流则是在程序中将字符串中每一个有意义的部分打上记号(Token)。
对于这些有穷的记号集合我们有能力用程序描述出来,可以将每一个记号看作一个结构体或者类,比如如果使用C#语言去描述:
首先需要枚举出所有可能的类型名称,使用类型和语义的结构体来描述一个记号
namespace HylicCompiler
{
//描述类型
enum DescirbeType{
IF, //if
ELSE, //else
ELIF, //elif
LPAREN, //(
RPAREN, //)
IDENT, //标识符
GT, // >
ASSIGN, // =
STRING, //"xxxx"
SEMICOLON,// ;
INT // 10
}
/// <summary>
/// 记号结构体
/// 如果是"hello world" 则T=STRING lexeme="hello world"
/// 如果是 if 则T=IF lexeme=null
/// 如果是 标识符x 则T=IDENT lexeme="x"
/// </summary>
struct Token
{
DescirbeType T; //类型
string lexeme; //语义
}
}
定义好记号后,词法分析器的任务就开始了,它会一个个的去读取字符串中有意义的字串,并转化为记号,从而字串流就变为了记号流.
2.1.2 词法分析器的构造方法
词法分析器的构造有两种方法:
方法1.手工构造,代码量大,相对复杂,容易出错。但是目前非常流行GCC,LLVM
方法2.声明好规则交给一种机器,它会帮你生成词法分析器。 快捷,代码量少,难以控制细节
尝试实现过的一个Stack计算机的paser
Token类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HylicCompiler
{
//zen-code will be composed with following element
enum DescirbeType{
IF, // if
ELSE, // else
ELIF, // elif
LPAREN, // (
RPAREN, // )
IDENT, // identify
GT, // >
LT, // <
PLUS, // +
MINUS, // -
MULTIPLY, // *
DEVIDE, // /
ASSIGN, // =
STRING, // "xxxx"
SEMICOLON, // ;
INT, // 10
FLOAT, // 10.12
}
/// <summary>
/// 记号类
/// 如果是"hello world" 则T=STRING lexeme="hello world"
/// 如果是 if 则T=IF lexeme= null
/// 如果是 标识符x 则T=IDENT lexeme="x"
/// </summary>
class Token
{
public DescirbeType T; //类型
public string lexeme; //语义
public Token(DescirbeType t,string lexeme) {
this.T = t;
this.lexeme = lexeme;
}
//For Debug
public void PrintSelf()
{
if(lexeme!=null)
{
Console.WriteLine($"Type:{T.GetType()},Value:{lexeme}\n");
}
else
{
Console.WriteLine($"Type:{T.GetType()}\n");
}
}
}
}
Lexer类
namespace HylicCompiler
{
/// <summary>
/// List possible situation correspond with each Type
/// </summary>
sealed class Possible
{
public static string DIGITAL = "1234567890";
}
/// <summary>
/// 记录分析到的位置
/// </summary>
class Position
{
public int _index; //指针位置
public int _line; //行号
public int _column; //列号
public string _source; //来源
public string _txt; //文本内容
/// <summary>
/// 初始化位置指针
/// </summary>
/// <param name="index">指针位置</param>
/// <param name="line">行号</param>
/// <param name="column">列号</param>
/// <param name="source">字符序列来源</param>
/// <param name="txt">文本类</param>
public Position(int index,int line,int column,string source,string txt)
{
this._index = index;
this._line = line;
this._column = column;
this._source = source;
this._txt = txt;
}
/// <summary>
/// 返回一份位置的副本
/// </summary>
/// <returns></returns>
public Position GetDuplicate()
{
return new Position(this._index, this._line, this._column, this._source, this._txt);
}
/// <summary>
/// 前向匹配
/// </summary>
/// <param name="current_char"></param>
public void next(char current_char)
{
_index += 1;
_column += 1;
if (current_char == '\n') //遇到换行符,列号清零 行号加一
{
_column = 0;
_line += 1;
}
}
}
/// <summary>
/// 词法分析器-Craft
/// </summary>
class Lexer
{
private string _source; //来源
private string _text; //文本内容
private Position _pos; //位置
private char _current_char; //当前字符
private List<Token> tokens;
public Lexer(string source,string text)
{
_source = source;
_text = text;
_pos = new Position(-1, 0, -1, source, text);
_current_char =Char.MinValue;
this.next(); // 0 0 0
}
/// <summary>
/// 前向
/// </summary>
public void next()
{
this._pos.next(this._current_char);
if(_pos._index < _text.Length)
{
_current_char = _text[_pos._index];
}
else
{
_current_char = Char.MinValue;
}
}
public void PrintSelf()
{
Console.WriteLine(tokens.Count);
foreach(var t in tokens)
{
Console.Write($"[{t.T} {t.lexeme}] ");
}
Console.Write(" \n");
}
/// <summary>
/// Create Tokens From Code Text
/// </summary>
/// <param name="txt"></param>
/// <returns>Tokens</returns>
public List<Token> MakeTokens()
{
tokens = new List<Token>();
Console.WriteLine(_text);
while (_current_char!=Char.MinValue)
{
//pass if blank or tabs
if(_current_char == ' ' || _current_char == '\t')
{
this.next();
}
// current char exist in "1234567890" which means a Number(int or float) is coming
else if (Possible.DIGITAL.Contains(_current_char))
{
tokens.Add(this.MakeNumberToken());
}
else if(_current_char == '+')
{
tokens.Add(new Token(DescirbeType.PLUS, null));
this.next();
}
else if(_current_char == '-')
{
tokens.Add(new Token(DescirbeType.MINUS, null));
this.next();
}
else if(_current_char == '(')
{
tokens.Add(new Token(DescirbeType.LPAREN, null));
this.next();
}
else if(_current_char == ')')
{
tokens.Add(new Token(DescirbeType.RPAREN, null));
this.next();
}
else if (_current_char == '*')
{
tokens.Add(new Token(DescirbeType.MULTIPLY, null));
this.next();
}
else if (_current_char == '/')
{
tokens.Add(new Token(DescirbeType.DEVIDE, null));
this.next();
}
else
{
//report error position and report
Position ErrPos = _pos.GetDuplicate();
char ErrChar = _current_char;
IllegalCharError illegalCharError = new IllegalCharError(ErrPos, ErrPos, $"Illegal Char:{ErrChar}");
}
}
return tokens;
}
/// <summary>
/// Coming A int or A float Number
/// </summary>
/// <returns>A number Token</returns>
public Token MakeNumberToken()
{
string numstr = "";
int dot_count = 0;
//current char exist in "1234567890."
while(_current_char != char.MinValue &&
_current_char!=' ' &&
(Possible.DIGITAL+'.').Contains(_current_char) )
{
//match dotnet
if (_current_char == '.')
{
//judge occurrence number of dot.
//invalide if More than one
//add into numstr if zero
if (dot_count == 1)
break;
dot_count += 1;
numstr += '.';
}
//match char
else
{
numstr += _current_char;
}
this.next();
}
//dot_count =0 ==> int
if(dot_count == 0)
{
return new Token(DescirbeType.INT, numstr);
}
//dot_count =1 ==> float
else
{
return new Token(DescirbeType.FLOAT, numstr);
}
return default(Token);
}
}
}