falleuion 发布的文章

SetUnhandledExceptionFilter

调用SetUnhandledExceptionFilter设置回调后,程序崩溃不进入回调。
VC++2005 开始出于安全因素微软改变了 CRT 的行为。在以下情况下 CRT 不会通知被注册的 Unhandled Exception Filter

  • 调用了 abort() 并且设置 abort 的行为为 _CALL_REPORTFAULT(Release 版本默认使用此设置)
  • Security Checks失败时,具体来说就是检查到一些会引发安全问题的堆栈溢出时不会通知被注册的 Unhandled Exception Filter,会引发安全问题的堆栈溢出包括:覆盖了函数的返回值,覆盖了 Exception handler 的地址,覆盖了某些类型的参数。关于编译器的 Security Checks 的内容,详细参考:http://msdn.microsoft.com/en-us/library/Aa290051(注意,此文章谈到的是 Visual Studio .NET 2003,其中_set_security_error_handler函数在 VC++2005 以及以上版本已经无法使用)
  • 如果没有调用_set_invalid_parameter_handler设置 Invalid parameter handler 时,检查到了非法的参数

解决方案

CRT中调用SetUnhandledExceptionFilter(NULL);来取消调用崩溃处理回调,所以可以自己调用完SetUnhandledExceptionFilter()后再修改SetUnhandledExceptionFilter()的行为来禁止CRT调用。

x86 DisableSetUnhandledExceptionFilter

void DisableSetUnhandledExceptionFilter()
{
  void *addr = (void *)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
  if (!addr) {
    return;
  }

  unsigned char code[16];
  int size = 0;
  code[size++] = 0x33;
  code[size++] = 0xC0;
  code[size++] = 0xC2;
  code[size++] = 0x04;
  code[size++] = 0x00;

  DWORD dwOldFlag, dwTempFlag;
  VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
  WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
  VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
}

x64 DisableSetUnhandledExceptionFilter

void DisableSetUnhandledExceptionFilter()
{
#ifndef _WIN64
  system("cmd /K echo The following code only works for x64");
  exit(EXIT_FAILURE);
#endif  // _WIN64

  void *addr = (void *)GetProcAddress(LoadLibrary(L"kernel32.dll"), "SetUnhandledExceptionFilter");
  if (!addr) {
    return;
  }

  unsigned char code[16];
  int size = 0;
  code[size++] = 0x48;
  code[size++] = 0x31;
  code[size++] = 0xC0;
  code[size++] = 0xC3;

  DWORD dwOldFlag, dwTempFlag;
  VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
  WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
  VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
}

ref

https://blog.csdn.net/kevin3683/article/details/19040809

code example

#include <QWidget>

class GreyMaskWidget : public QWidget
{
    Q_OBJECT
public:
    GreyMaskWidget(QWidget *target, QWidget *parent = nullptr);
    ~GreyMaskWidget();

protected:
    virtual void paintEvent(QPaintEvent *event) override;
    virtual bool eventFilter(QObject *obj, QEvent *event) override;

private:
    QWidget *m_target;
};
#include <QPainter>
#include <QEvent>
#include <QPainterPath>
#include <QtMath>

GreyMaskWidget::GreyMaskWidget(QWidget *target, QWidget *parent)
    : QWidget(parent)
    , m_target(target)
{
    setAttribute(Qt::WA_TranslucentBackground);
    setWindowFlags(Qt::FramelessWindowHint | Qt::Window);

    target->installEventFilter(this);
}

GreyMaskWidget::~GreyMaskWidget()
{

}

void GreyMaskWidget::paintEvent(QPaintEvent *event)
{
    if (m_target == nullptr)
        return;

    QPainter painter(this);
    painter.setBrush(Qt::NoBrush);
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);

    QRect targetRect = m_target->frameGeometry();
    QRectF rect(targetRect.topLeft(), QSize(targetRect.width() + 20, targetRect.height() + 20));
    rect.moveTo(rect.topLeft() - QPoint(10, 10));
    path.addRoundedRect(rect, 8, 8);

    painter.setRenderHint(QPainter::Antialiasing, true);

    QColor color(0, 0, 0, 30);
    for (int i = 0; i < 10; i++)
    {
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        path.addRect(10 - i, 10 - i, targetRect.width() + i * 2, targetRect.height() + i * 2);
        color.setAlpha(150 - qSqrt(i) * 50);
        painter.setPen(color);
        painter.drawPath(path);
    }
    QWidget::paintEvent(event);
}

bool GreyMaskWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == m_target && event->type() == QEvent::Hide)
    {
        hide();
    }
    else if (obj == m_target && event->type() == QEvent::Show)
    {
        QRect rect = m_target->frameGeometry();
        QPoint topLeft = rect.topLeft();
        rect.moveTo(topLeft - QPoint(10, 10));
        rect.setWidth(rect.width() + 20);
        rect.setHeight(rect.height() + 20);
        setGeometry(rect);
        show();
    }
    else if (obj == m_target && event->type() == QEvent::Move)
    {
        QPoint targetPos = m_target->frameGeometry().topLeft() - QPoint(10, 10);
        move(targetPos);
    }
    else if (obj == m_target && event->type() == QEvent::Resize)
    {
        QPoint targetPos = m_target->frameGeometry().topLeft() - QPoint(10, 10);
        QRect targetRect(targetPos, m_target->size() + QSize(20, 20));
        setGeometry(targetRect);
    }
    return QWidget::eventFilter(obj, event);
}

how to use it.

ExampleDialog::ExampleDialog(QWidget *parent)
    : QDialog(parent)
{
    setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
    initUI();
    new GreyMaskWidget(this, this);  // <---
}

create a virtual machine which based on arm64 in qemu

dependency: UEFI -> https://releases.linaro.org/components/kernel/uefi-linaro/16.02/release/qemu64/

create a virtual disk

qemu-img create -f qcow2 virtual_disk.qcow2 70G

install system from iso files.

qemu-system-aarch64 -m 4096 -M virt -cpu cortex-a72 -smp 4 -bios QEMU_EFI.fd -device VGA -device nec-usb-xhci -device usb-mouse -device usb-kbd -drive if=none,file=virtual_disk.qcow2,id=hd0 -device virtio-blk-device,drive=hd0 -drive if=none,file=/path/to/operater_system_image_arm64.iso,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom

after installation, and using it by hostfwd

qemu-system-aarch64 -m 4096 -M virt -cpu cortex-a72 -smp 4 -bios QEMU_EFI.fd -device VGA -device nec-usb-xhci -device usb-mouse -device usb-kbd -drive if=none,file=uos.qcow2,id=hd0 -device virtio-blk-device,drive=hd0 -net nic -net user,hostfwd=tcp::2222-:22

install dependency of qt

in Debian for example
sudo apt-get install build-essential make cmake libxkbcommon-x11-dev libgl1-mesa-dev libglu1-mesa-dev libfontconfig1-dev libxcb-xfixes0-dev libxcb-util-dev

download the qt source from qt.io

./configure -prefix ~/qtbin/build -confirm-license -opensource -debug -nomake examples -nomake tests -c++std 17 -bundled-xcb-xinput -skip qtquickcontrols -skip qtquickcontrols2 -skip qtsensors -skip qtdoc -no-compile-examples

C++ Meta Templates Programming

C++ 果然是一个语言联邦...

Hello World

template <int n>
struct add {
    static constexpr int value = add<n - 1>::value + n;
};

template <>
struct add<1> {
    static constexpr int value = 1;
};

上面就是模板元编程一个类似Hello world 的示例. 调用的话就是

std::cout << add<5>::value;

实现了一个编译期的叠加的代码. 下面则是与之相同功能的Haskell代码

addFunc :: (Integral a) => a -> a
addFunc 0 = 1
addFunc x = x + addFunc (x - 1)

(其实, 这两个函数都有点儿问题, 输入是负数就gg. 为了展示起见, 不管它)
c++ 代码中第一个struct 中的是函数的实现, 第二个则是边界(其实两个都是实现). 然后通过递归一层一层的求解. 可以跟Haskell 代码进行类比.

Functional Programming

函数式编程比较重要的一点就是函数没有副作用也不会改变装填, 即多次调用同一个函数, 只要输入相同它的输出不会改变.
使用函数式编程解决问题时,常常先取一个合适的集合, 然后对这个集合进行变形, 让数据通过过滤条件, 最后得到正确的数据.

模板元编程便和函数式编程部分类似, 因为模板会在编译时期进行推倒.我们就可以使用推到展开和递归的手法来达到部分和函数式编程类似功能的函数. 上面的代码就是一个不错的例子.

当然了, 如果直接与纯的函数式编程语言如Haskell, 相比较还是有一些东西没有的. 比如不全调用啥的, 可能也没有高阶函数吧, 还没了解过...这使得c++ 代码写起来不是那么容易理解. 不过是很好玩的一个东西.

编译期插入排序

integral_constantinteger_sequence 分别已经在c++11 和 c++14 标准中出现定义, 那就不用自己写了.2333
integral_constant -> <type_traits>
integer_sequence -> <utility>

Haskell

下面是函数式编程的代码实现, 大概能够体现编程的思想

insert :: (Ord a) => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys)
    | x < y = x:y:ys
    | otherwise = y:insert x ys

insertSort :: (Ord a) => [a] -> [a]
insertSort [] = []
insertSort (x:xs) = insert x $ insertSort xs

C++ 版

把一些实现放到test.hpp中, 因为标准库中有integer_sequence

#ifndef TEST_HPP_
#define TEST_HPP_

#include <utility>
#include <type_traits>

// alias
template <int ... values>
using int_sequence = std::integer_sequence<int, values...>;
// 当然也可以重命名为
// template <long long ... values>
// using dlong_sequence = std::integer_sequence<long long, values...>;
// 不过我还没试过,233333



// 类似Haskell 中 `s:[]`
template <int V, typename int_sequence>
struct push_front;

template <int V, int ... values>
struct push_front<V, int_sequence<values...>> {
    using type = int_sequence<V, values...>;
};

template <int V, typename T>
using push_front_t = typename push_front<V, T>::type;


// if-else 表达式,
template <bool Cond, typename Then, typename Else>
struct conditional;

template <typename Then, typename Else>
struct conditional<true, Then, Else> { using type = Then; };

template <typename Then, typename Else>
struct conditional<false, Then, Else> { using type = Else; };

template <bool Cond, typename Then, typename Else>
using conditional_t = typename conditional<Cond, Then, Else>::type;



// insert func
template <template <int, int> typename Compare, int x, typename int_sequence>
struct insert;

template <template <int, int> typename Compare, int x>
struct insert<Compare, x, int_sequence<>> {
    using type = int_sequence<x>;
};

template <template <int, int> typename Compare, int x, int head, int ...tail>
struct insert<Compare, x, int_sequence<head, tail...>> {
    using type = typename conditional<Compare<x, head>::value,
          int_sequence<x, head, tail...>,
          typename push_front<head, typename insert<Compare, x, int_sequence<tail...>>::type>::type>::type;
};



// insert sort func
template <template <int, int> typename Compare, typename int_sequence>
struct insertion_sort;

template <template <int, int> typename Compare>
struct insertion_sort<Compare, int_sequence<>> {
    using type = int_sequence<>;
};

template <template <int, int> typename Compare, int head, int ...tail>
struct insertion_sort<Compare, int_sequence<head, tail...>> {
    using type = typename insert<Compare, head, typename insertion_sort<Compare, int_sequence<tail...>>::type>::type;
};

#endif

调用它

#include <iostream>
#include "test.hpp"

template <int a, int b>
struct lessThan {
    static const bool value = a < b;
};


// print func
template <int head>
void print(int_sequence<head> seq) {
    std::cout << head << std::endl;
}

template <int head, int ... tail>
void print(int_sequence<head, tail ...> seq) {
    std::cout << head << ' ';
    print(int_sequence<tail ...>());
}

int main(void)
{
    using s = int_sequence<4, 2, 7, 5, 3, 21>;
    print(typename insertion_sort<lessThan, s>::type());

    return 0;
}

编译的时候最好打开c++17 选项

g++ -std=c++17

error

在使用clangvs 编译的时候,对于print 这个模板,都会抛出类似下面的错误.

"print": ambiguous call to overloaded function

表示在进行模板推到的时候,最后仅剩一个元素的时候,不仅匹配了print(int_sequence<1,>) 也匹配上了print(int_sequence<1>) 这两个不同的匹配项.产生出了二义化.做出下面的更改, 让它们都能编译通过.

// 不特化一个元素了,直接重载以空元素为参数的`print`
void print(int_sequence<> seq) {
    std::cout << std::endl;
}

template <int head, int ... tail>
void print(int_sequence<head, tail...> seq) {
    std::cout << head << ' ';
    print(int_sequence<tail...>());
}

上述的代码其实没有什么卵用, 2333 除了好玩...

参考资料

模板代码来源 -> https://blog.csdn.net/huanghongxun/article/details/85065406

Flex

Flex是一个生成词法分析器的工具,它可以利用正则表达式来生成匹配相应字符串的C语言代码,其语法格式基本同Lex相同。

格式

LEX的源文件由三个部份组成,每个部分之间用顶行的 `%%' 分割,其格式如下:

定义部份

%%

规则部份

%%

用户附加C语言部份
手册->Flex, version 2.5
怎么用就不赘述了....

主要就是用这个词法生成器,给输入的源文件中的每一个关键字打上tag,以便后面的语法分析器能够识别.

GNU Bison

GNU bison 是属于 GNU 项目的一个语法分析器生成器。Bison 把一个关于“向前查看 从左到右 最右 \`LALR' 上下文无关文法的描述转化成可以分析该文法的 C 或 C++ 程序。它也可以为二义文法生成 “通用的 从左到右 最右 \`(GLR)' 语法分析器。
这里主要使用的就是普通的LALR...

手册->Bison 3.0.5

格式

与flex的格式大致相同...

声明部分
%%
语法规则
%%
C语言附加部分

源码

flex 部分

/* filename -> scanner.l */
%option noyywrap yylineno

%{
#include <stdio.h>   
#include <stdlib.h>
#include <string.h>
#include "parser.tab.h"
int old_status;
void yyerror(char *s, ...);

%}

%x COMMENT

%%
    /* 下面都是正则表达式 */
int         { return INT; }
float       { return FLOAT; }
double      { return DOUBLE; }
auto        { return AUTO; }
break       { return BREAK; }
case        { return CASE; }
const       { return CONST; }
else        { return ELSE; }
for         { return FOR; }
if          { return IF; }
long        { return LONG; }
return      { return RETURN; }
short       { return SHORT; }
signed      { return SIGNED; }
char        { return CHAR; }
unsigned    { return UNSIGNED; }
    
    /* user variable */
[a-zA-Z][a-zA-Z0-9_]*       { yylval.strval = strdup(yytext); return IDENTITY; }
    /* integer numbers */
-?[0-9]+                    { yylval.intval = atoi(yytext); return INT_NUMBER; }
-?[0-9]+\.[0-9]+            { yylval.floatval = atof(yytext); return FLOAT_NUMBER; }
    /* real numbers */
\"(\\.|\"\"|[^"\n"])*\"     { yylval.strval = strdup(yytext); return STRING; }
    /* C-type strings */
\"(\\.|[^"\n])*$            { yyerror("Unterminated string %s", yytext); }

    /* operators */
[-+&~|^/%*(),.;!]       { return yytext[0]; }
"&&"                    { return AND; }
"||"                    { return OR; }
"="                     { return ASSIGN; }

    /* comments */
"//".*;
"/*"                    { old_status = YY_START; BEGIN COMMENT; }
<COMMENT>"*/"           { BEGIN old_status; }
    /* space & tab */
[ \t\n]
    /* prevent flex jam */
.           { yyerror("something goes wrong...\n"); }

%%

Bison 部分

 /* filename -> parser.y */
%require "3.0.4"

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void yyerror(char *s, ...);
int yylex();
%}

%debug

%locations

%union {
    int intval;
    double floatval;
    char *strval;
    int subtok;
}

    /* token 关键字 */
%token INT
%token FLOAT
%token DOUBLE
%token AUTO
%token BREAK
%token CASE
%token CONST
%token ELSE
%token FOR
%token IF
%token LONG
%token RETURN
%token SHORT
%token SIGNED
%token CHAR
%token UNSIGNED
%token <strval> IDENTITY
%token <intval> INT_NUMBER
%token <floatval> FLOAT_NUMBER
%token <strval> STRING

%left OR
%left AND
%left ASSIGN
%left '!'
%left '+' '-'
%left '*' '/' '%'

%type <intval> decl_stmt bin_expr factor expr sentence id_list assign_stmt

%start statement

%%

/* 语法规则 */
statement: sentence ';'         { printf("STMT\n"); }
    | statement sentence ';'
    ;

sentence: decl_stmt     { $$ = $1; }
    | assign_stmt       { $$ = $1; }
    | /* empty rule */  { $$ = 0; }
    ;


decl_stmt: type_dec id_list { printf("stmt_decl\n"); }
    | type_dec assign_stmt  { printf("stmt_decl & assignment\n"); }
    ;

id_list: IDENTITY           { printf("id: %s\n", $1); $$ = $1; }
    | id_list ',' IDENTITY  { printf("id: %s\n", $3); $$ = $3; }
    ;

assign_stmt: IDENTITY ASSIGN expr    { printf("id: %s\nASSIGNMENT\n", $1); }
    ;

expr: factor    { $$ = $1; }
    | bin_expr  { $$ = $1; }
    ;

factor: INT_NUMBER      { printf("VALUE: %d\n", $1); $$ = $1; }
    | FLOAT_NUMBER      { printf("VALUE: %d\n", $1); $$ = $1; }
    | IDENTITY          { printf("VALUE: %s\n", $1); $$ = $1; }
    ;

bin_expr: expr '+' expr     { printf("PLUS.\n"); }
    | expr '-' expr         { printf("SUB.\n"); }
    | expr '*' expr         { printf("MUL.\n"); }
    | expr '/' expr         { printf("DIV.\n"); }
    ;

type_dec: INT       { printf("TYPE:INT\n"); }
    | FLOAT         { printf("TYPE:FLOAT\n"); }
    | SHORT         { printf("TYPE:SHORT\n"); }
    | LONG          { printf("TYPE:LONG\n"); }
    | UNSIGNED LONG { printf("TYPE:UNSIGNED\n"); }
    ;


%%

int main(int argc, const char *args[])
{
    /* 将注释去掉就能看到stack具体是怎么工作的.. */
    /* yydebug = 1; */

    extern FILE *yyin;
    if(argc > 1 && (yyin = fopen(args[1], "r")) == NULL) {
        fprintf(stderr, "can not open %s\n", args[1]);
        exit(1);
    }
    if(yyparse()) {
        exit(-1);
    }
    
    return 0;
}

void yyerror(char *s, ...)
{
    extern int yylineno;

    va_list ap;
    va_start(ap, s);

    fprintf(stderr, "%d: error: ", yylineno);
    vfprintf(stderr, s, ap);
    fprintf(stderr, "\n");
}

当然还有Makefile

LEX = flex
YYAC = bison
CC = gcc

all: test

test: parser.tab.o scanner.o
        $(CC) -o $@ parser.tab.o scanner.o

parser.tab.c parser.tab.h: parser.y
        $(YYAC) -vd parser.y

scanner.c: scanner.l
        $(LEX) -o $@ $<

scanner.o: scanner.c parser.tab.h

.PYHONY: clean

clean:
        -@ rm parser.tab.c parser.tab.h scanner.c parser.output *.o

上面的bison代码中语法部分仅仅只写了赋值语句和声明语句的语法解析规则.连错误处理都没有,23333
也没有生成中间树..但这不重要..
打印的输出是逆波兰式`RPN'..

不打开debug模式

图先用老的吧,2333

3808135993.png

int a;
TYPE:INT
id: a
stmt_decl
STMT
int a, b, c;
TYPE:INT
id: a
id: b
id: c
stmt_decl
int a = b + 1;
TYPE:INT
VALUE: b
VALUE: 1
PLUS.
id: a
ASSIGNMENT
stmt_decl & assignment
float a, b, c;
TYPE:FLOAT
id: a
id: b
id: c
stmt_decl
unsigned long a, b, c;
TYPE:UNSIGNED
id: a
id: b
id: c
stmt_decl
unsigned long a = b + c;
TYPE:UNSIGNED
VALUE: b
VALUE: c
PLUS.
id: a
ASSIGNMENT
stmt_decl & assignment
int a, b, c + 1;
TYPE:INT
id: a
id: b
id: c
stmt_decl
7: error: syntax error

打开debug模式

Starting parse
Entering state 0
Reading a token: int a, b, c; 
Next token is token INT (1.1: )
Shifting token INT (1.1: )
Entering state 1
Reducing stack by rule 20 (line 95):
   $1 = token INT (1.1: )
TYPE:INT
-> $$ = nterm type_dec (1.1: )
Stack now 0
Entering state 11
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 17
Reading a token: Next token is token ',' (1.1: )
Reducing stack by rule 8 (line 73):
   $1 = token IDENTITY (1.1: )
id: a
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Next token is token ',' (1.1: )
Shifting token ',' (1.1: )
Entering state 27
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 32
Reducing stack by rule 9 (line 74):
   $1 = nterm id_list (1.1: )
   $2 = token ',' (1.1: )
   $3 = token IDENTITY (1.1: )
id: b
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Reading a token: Next token is token ',' (1.1: )
Shifting token ',' (1.1: )
Entering state 27
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 32
Reducing stack by rule 9 (line 74):
   $1 = nterm id_list (1.1: )
   $2 = token ',' (1.1: )
   $3 = token IDENTITY (1.1: )
id: c
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Reading a token: Next token is token ';' (1.1: )
Reducing stack by rule 6 (line 69):
   $1 = nterm type_dec (1.1: )
   $2 = nterm id_list (1.1: )
stmt_decl
-> $$ = nterm decl_stmt (1.1: )
Stack now 0
Entering state 9
Reducing stack by rule 3 (line 63):
   $1 = nterm decl_stmt (1.1: )
-> $$ = nterm sentence (1.1: )
Stack now 0
Entering state 8
Next token is token ';' (1.1: )
Shifting token ';' (1.1: )
Entering state 16
Reducing stack by rule 1 (line 59):
   $1 = nterm sentence (1.1: )
   $2 = token ';' (1.1: )
STMT
-> $$ = nterm statement (1.1: )
Stack now 0
Entering state 7
Reading a token: Now at end of input.
Shifting token $end (1.1: )
Entering state 14
Stack now 0 7 14
Cleanup: popping token $end (1.1: )
Cleanup: popping nterm statement (1.1: )

能够清楚的看到栈的状态....

CmakeList.txt

cmake在较新的版本已经可以直接使用了.之前的版本是不支持找flex & bison的,只能通过`add_custom_command' 来使用flex & bison...这非常的麻烦

通过看CMake的手册才知道了可以这样写....

cmake_minimum_required(VERSION 3.4.3)

project(tincompiler)

find_package(BISON)
find_package(FLEX)

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})

FIND_LIBRARY(LEX_LIB l)

FLEX_TARGET(scanner scanner.l   ${CMAKE_CURRENT_BINARY_DIR}/scanner.c)
BISON_TARGET(parser parser.y    ${CMAKE_CURRENT_BINARY_DIR}/parser.c)

ADD_FLEX_BISON_DEPENDENCY(scanner parser)

ADD_EXECUTABLE(tincompiler
        ${BISON_parser_OUTPUTS}
        ${FLEX_scanner_OUTPUTS})
如果使用CMake作为构建工具需要将 \`scanner.l' 里面的 \` #include "parser.tab.h" ' 改成 \` #include "parser.h" '

参考资料:

《flex & bison》(John Levine), O'Reilly Media
CMake Manual, FindFLEX
CMake Manual, FindBISON
《Compilers Principles, Techniques, & Tools》(龙书), (Alfred V. Aho / Monica S.Lam / Ravi Sethi / Jeffrey D. Ullman)