16.2 TinyC 编译器

现在可以将 TinyC 前端和 TinyC 后端整合起来了。新建一个空的 tinyc 目录,然后 cd 到此目录,之后新建一个 sources 目录,然后将以下 7 个文件放到 sources 目录下:

scanner.l , 词法分析文件,和上一节相同;

parser.y , 语法分析文件,和上一节相同;

pysim.py , Pcode 模拟器( python 程序),和第 4 章相同;

tio.c , 库函数文件,和上一章最后一节相同;

macro.inc , NASM 宏文件,和上一章最后一节相同;

tcc , 编译 TinyC 源程序的脚本文件;

pysimulate , 模拟运行 Pcode 的脚本文件。

然后在 tinyc 目录下新建一个脚本文件 build.sh ,内容如下:

  1. mkdir -p release
  2. flex sources/scanner.l
  3. bison -vdty sources/parser.y
  4. gcc -o release/tcc-frontend lex.yy.c y.tab.c
  5. rm -f y.* lex.*
  6. gcc -m32 -c -o tio.o sources/tio.c
  7. ar -crv release/libtio.a tio.o > /dev/null
  8. rm -f tio.o
  9. cp sources/macro.inc sources/pysim.py sources/tcc sources/pysimulate release/
  10. chmod u+x release/tcc release/pysimulate
  11. export PATH=$PATH:$PWD/release
  12. echo "export PATH=\$PATH:$PWD/release" >> ~/.bashrc

在终端输入 source build.sh 将编译生成 TinyC 前端 tcc-frontend 、库文件 libtio.a ,并放在 release 目录下,同时将 macro.inc, pysim.py, pysimulate, tcc 这四个文件拷贝至 release 目录,最后,将 release 目录输出到 PATH 环境变量中。现在,在终端输入 tcc filename.c 就可以利用 TinyC 编译成可执行程序了,而输入 pysimulate filename.asm -da 则可以用 Pcode 模拟器单步调试中间代码 Pcode 了。

让我们来测试一下第一章的示例代码 test.c 吧,将其放在当前目录,然后在终端输入 tcc test.c ,将生成一个 test-c-build 目录,此目录中包含了中间代码文件 test.asm 、函数定义宏文件 test.inc 、目标文件 test.o 、最终的可执行文件 test 。可以输入 test-c-build/test 来运行可执行文件,也可以输入 pysimulate test-c-build/test.asm -da 用 Pcode 模拟器单步调试中间代码。

脚本文件 tcc 首先调用 tcc-frontend 将输入文件(假设为 test.c )编译为 test.asm 和 test.inc ,然后调用 nasm ,将 test.asm 、 test.inc 和 macro.inc 三个文件一起汇编成 test.o ,最后调用 ld 将 test.o 和 libtio.a 一起链接为最终的可执行程序 test 。 tcc 的内容如下:

  1. #!/usr/bin/env bash
  2.  
  3. if [ $# != 1 ];
  4. then
  5. echo "Usage: $0 <filename>"
  6. exit 1
  7. fi
  8.  
  9. if ! [ -f $1 ];
  10. then
  11. echo "Error: File $1 does NOT exists."
  12. exit 1
  13. fi
  14.  
  15. tccdir=$(dirname $0)
  16. filename=${1%.*}
  17. fileext=${1##*.}
  18. objdir=$filename-$fileext-build
  19.  
  20. "$(dirname $0)/tcc-frontend" $1
  21. nasm -f elf32 -P"$tccdir/macro.inc" -P"$filename.inc" -o "$filename.o" "$filename.asm"
  22. ld -m elf_i386 -o "$filename" "$filename.o" -L"$tccdir" -ltio
  23. mkdir -p "$objdir"
  24. mv "$filename.asm" "$filename.inc" "$filename.o" "$filename" "$objdir/"

脚本文件 pysimulate 将调用 python 和 pysim.py 文件,模拟运行输入的 Pcode 文件,其内容如下:

  1. #!/usr/bin/env bash
  2.  
  3. if [[ ($# != 1) && ($# != 2) ]];
  4. then
  5. echo "Usage: $0 <filename> [-da]"
  6. exit 1
  7. fi
  8.  
  9. if ! [ -f $1 ];
  10. then
  11. echo "Error: File $1 does NOT exists."
  12. exit 1
  13. fi
  14.  
  15. python "$(dirname $0)/pysim.py" $1 $2

下面来测试一下第 14 章最后的测试文件包 samples.zip ,将其解包至 samples 目录,再在当前目录新建一个脚本文件 testall.sh ,内容如下:

  1. for src in $(ls samples/*.c)
  2. do
  3. filename=${src%.*}
  4. fileext=${src##*.}
  5. filenakedname=${filename##*/}
  6. objdir=$filename-$fileext-build
  7.  
  8. clear
  9. echo build \"$src\" and run
  10. echo
  11. tcc "$src"
  12. "$objdir/$filenakedname"
  13. echo
  14. echo press any key to continue...
  15. read -n 1
  16. done

最后在终端输入 bash testall.sh 将对所有文件进行编译、运行。

至此 TinyC 编译器全部完成。

第 16 章完