Clang学习历程 编译过程-词法分析

前言

《编译原理》中提到

编译器的第一个步骤是词法分析(Lexical Analysis)或扫描。词法分析器读入组成源程序的字符流,并且将它们组织成为有意义的词素(lexeme)的序列。对于每个词素,词法分析产生如下形式的词法单元(token)作为输出:
<token-name,attribute-value>
token-name 是一个语法分析步骤要使用的抽象符号
attribute-value指向符号表中关于这个词法单元的条目

实验

int main(){
   
    @autoreleasepool {
   
        int initial = 8;
        int six = 6;
        NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
        int rank = initial + six;
        int position = initial + rank * 60;
        NSLog(@"%@ rank %d", site, position);
    }
    return 0;
}
## 使用手工编译的clang执行如下指令
## -fmodules               Enable the 'modules' language feature
## This will make any modules-enabled software libraries available as modules as well as introducing any modules-specific syntax.
## -E                      Only run the preprocessor
## 只运行预处理器
## -Xclang <arg>           Pass <arg> to the clang compiler
## -dump-tokens  -- man clang/ clang --help 都没不到
## 参考1
## http://clang.llvm.org/doxygen/namespaceclang_1_1driver_1_1options.html
## enum clang::driver::options::ClangFlags
## Flags specifically for clang options.

## 参考2
## Running the plugin
## Using the cc1 command line
## To run a plugin, the dynamic library containing the plugin registry # must be loaded via the -load command line option. This will load all plugins that are registered, and you can select the plugins to run by specifying the -plugin option. Additional parameters for the plugins can be passed with -plugin-arg-<plugin-name>.
## Note that those options must reach clang’s cc1 process. There are two ways to do so:

## grep -r "dump-tokens" src/llvm/tools/clang
## src/llvm/tools/clang/include/clang/Driver/CC1Options.td:def dump_tokens : Flag<["-"], "dump-tokens">,
## 根据上面的两个参考链接 + grep的结果确定-dump-tokens应该就是这么来的
## grep -r "dump_tokens" src/llvm/tools/clang
## src/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp:    case OPT_dump_tokens:
## static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
##                                   DiagnosticsEngine &Diags,
##                                   bool &IsHeaderFile) {
## ...
## case OPT_dump_tokens:
##      Opts.ProgramAction = frontend::DumpTokens; break;                                    
~% /opt/llvm/bin/clang -fmodules -E -Xclang -dump-tokens main.m

结果如下

annot_module_include '#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int initial = 8;
    '		Loc=<main.m:9:1>
int 'int'	 [StartOfLine]	Loc=<main.m:11:1>
identifier 'main'	 [LeadingSpace]	Loc=<main.m:11:5>
l_paren '('		Loc=<main.m:11:9>
int 'int'		Loc=<main.m:11:10>
identifier 'argc'	 [LeadingSpace]	Loc=<main.m:11:14>
comma ','		Loc=<main.m:11:18>
const 'const'	 [LeadingSpace]	Loc=<main.m:11:20>
char 'char'	 [LeadingSpace]	Loc=<main.m:11:26>
star '*'	 [LeadingSpace]	Loc=<main.m:11:31>
identifier 'argv'	 [LeadingSpace]	Loc=<main.m:11:33>
l_square '['		Loc=<main.m:11:37>
r_square ']'		Loc=<main.m:11:38>
r_paren ')'		Loc=<main.m:11:39>
l_brace '{'	 [LeadingSpace]	Loc=<main.m:11:41>
at '@'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:12:5>
identifier 'autoreleasepool'		Loc=<main.m:12:6>
l_brace '{'	 [LeadingSpace]	Loc=<main.m:12:22>
int 'int'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:13:9>
identifier 'initial'	 [LeadingSpace]	Loc=<main.m:13:13>
equal '='	 [LeadingSpace]	Loc=<main.m:13:21>
numeric_constant '8'	 [LeadingSpace]	Loc=<main.m:13:23>
semi ';'		Loc=<main.m:13:24>
int 'int'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:14:9>
identifier 'six'	 [LeadingSpace]	Loc=<main.m:14:13>
equal '='	 [LeadingSpace]	Loc=<main.m:14:17>
numeric_constant '6'	 [LeadingSpace]	Loc=<main.m:14:19>
semi ';'		Loc=<main.m:14:20>
identifier 'NSString'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:15:9>
star '*'		Loc=<main.m:15:17>
identifier 'site'	 [LeadingSpace]	Loc=<main.m:15:19>
equal '='	 [LeadingSpace]	Loc=<main.m:15:24>
l_square '['	 [LeadingSpace]	Loc=<main.m:15:26>
l_square '['		Loc=<main.m:15:27>
identifier 'NSString'		Loc=<main.m:15:28>
identifier 'alloc'	 [LeadingSpace]	Loc=<main.m:15:37>
r_square ']'		Loc=<main.m:15:42>
identifier 'initWithUTF8String'	 [LeadingSpace]	Loc=<main.m:15:44>
colon ':'		Loc=<main.m:15:62>
string_literal '"starming"'		Loc=<main.m:15:63>
r_square ']'		Loc=<main.m:15:73>
semi ';'		Loc=<main.m:15:74>
int 'int'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:16:9>
identifier 'rank'	 [LeadingSpace]	Loc=<main.m:16:13>
equal '='	 [LeadingSpace]	Loc=<main.m:16:18>
identifier 'initial'	 [LeadingSpace]	Loc=<main.m:16:20>
plus '+'	 [LeadingSpace]	Loc=<main.m:16:28>
identifier 'six'	 [LeadingSpace]	Loc=<main.m:16:30>
semi ';'		Loc=<main.m:16:33>
int 'int'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:17:9>
identifier 'position'	 [LeadingSpace]	Loc=<main.m:17:13>
equal '='	 [LeadingSpace]	Loc=<main.m:17:22>
identifier 'initial'	 [LeadingSpace]	Loc=<main.m:17:24>
plus '+'	 [LeadingSpace]	Loc=<main.m:17:32>
identifier 'rank'	 [LeadingSpace]	Loc=<main.m:17:34>
star '*'	 [LeadingSpace]	Loc=<main.m:17:39>
numeric_constant '60'	 [LeadingSpace]	Loc=<main.m:17:41>
semi ';'		Loc=<main.m:17:43>
identifier 'NSLog'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:18:9>
l_paren '('		Loc=<main.m:18:14>
at '@'		Loc=<main.m:18:15>
string_literal '"%@ rank %d"'		Loc=<main.m:18:16>
comma ','		Loc=<main.m:18:28>
identifier 'site'	 [LeadingSpace]	Loc=<main.m:18:30>
comma ','		Loc=<main.m:18:34>
identifier 'position'	 [LeadingSpace]	Loc=<main.m:18:36>
r_paren ')'		Loc=<main.m:18:44>
semi ';'		Loc=<main.m:18:45>
r_brace '}'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:19:5>
return 'return'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:20:5>
numeric_constant '0'	 [LeadingSpace]	Loc=<main.m:20:12>
semi ';'		Loc=<main.m:20:13>
r_brace '}'	 [StartOfLine]	Loc=<main.m:21:1>
eof ''		Loc=<main.m:21:2>
## 《编译原理》给的例子
position = initial + rate * 60
## 对应词法序列
<id,1> < = > <id,2> < + > <id,3> < * ><60>

## int position = initial + rank * 60;
int 'int'	 [StartOfLine] [LeadingSpace]	Loc=<main.m:17:9>
identifier 'position'	 [LeadingSpace]	Loc=<main.m:17:13>
equal '='	 [LeadingSpace]	Loc=<main.m:17:22>
identifier 'initial'	 [LeadingSpace]	Loc=<main.m:17:24>
plus '+'	 [LeadingSpace]	Loc=<main.m:17:32>
identifier 'rank'	 [LeadingSpace]	Loc=<main.m:17:34>
star '*'	 [LeadingSpace]	Loc=<main.m:17:39>
numeric_constant '60'	 [LeadingSpace]	Loc=<main.m:17:41>
semi ';'		Loc=<main.m:17:43>

## 可以获得每个 token 的类型,值还有类似 StartOfLine 的位置类型和 Loc=main.m:11:1 这个样的具体位置。
## 和《编译原理》有点不同,attribute-value没有指向符号表中关于这个词法单元的条目

实现

  1. 定义词元
  2. 遍历字符流 && 输出词元

案例

定义词元

入门教程中中的Kaleidoscope语言

//===----------------------------------------------------------------------===//
// Lexer
//===----------------------------------------------------------------------===//

// The lexer returns tokens [0-255] if it is an unknown character, otherwise one
// of these for known things.
// 字符流解析词元规则,要么是如下5种类型,要么返回对应的ASCII值
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();

  /// 判定是否是identifier 满足正则条件[a-zA-Z][a-zA-Z0-9]*
  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;
  }

  /// 判定是否是数字 满足正则条件 [0-9.]+
  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.
  /// 返回字符的ascii值
  int ThisChar = LastChar;
  LastChar = getchar();
  return ThisChar;
}

理论

正则表达式

正则表达式(Regular Expression,RE)是一种用来描述正则语言的更紧凑的表达方式。

每个正则表达式 r 定义(表示)一个语言,记为 L(r)。这个语言也是根据 r 的子表达式所表示的语言递归定义的。

Σ 是给定的有限字符集
ε 是空串(empty string)

归纳基础:

  1. ε是一个正则表达式,L(ε) = {ε},即该语言只包含空串
  2. 如果 a ∈ Σ,那么a是一个正则表达式,且L(a) = {a}。即这个语言仅包含一个长度为1的符号串a

归纳步骤:假设 r 和 s 都是正则表达式,表示的语言分别是L®和L(s)

  1. r|s 是一个正则表达式,L(r|s) = L(r)∪L(s)
  2. rs(rs 的连接)是一个正则表达式,L(rs)=L(r)L(s)
  3. r* 是一个正则表达式,L(r*) = (L(r))*
  4. (r)是一个正则表达式,L((r)) = L(r),表明表达式的两边加上括号并不影响表达式所表示的语言

运算符的优先级: * , 连接 , |
* 号代表字符可以不出现,也可以出现一次或者多次

有穷自动机

正则表达式描述的规则人容易理解,但是要解析字符串,还需要将其转化为计算机程序能理解的模型。

有穷自动机(Finite Automata,FA)是对一类处理系统建立的数学模型。这类系统具有一系列离散的输入输出信息有穷数目的内部状态

数学表达:

M = (S,Σ,δ,s0,F)

S: 有穷状态集
Σ: 字符表
δ: 转换函数
s0: 初始状态
F: 结束/可接受状态集

表示方法

有穷自动机可以用转换图来表示。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值