最近在写一个 IEC 61131-3 中的结构化文本 (ST) 到 C 语言的 transpiler,遇到一个小小的麻烦,调试时,用户看到的是 ST 代码,而调试器看到的 C 语言的 debug symbols,怎么把两者对应起来呢。最开始我想的是自己来实现映射关系,理论上是可行的,但是有些额外的工作要做。
是否有更简单的办法呢?我想其它 transpiler 应该有同样的需求。为此研究了一下 Vala 的做法,Vala 语言也是把自己翻译成 C 语言,再用 gcc 来编译的。
先写一个小程序(t.vala)
void main () {print ("hello, world\n");
}
valac -v -g t.vala
编译时加一个-v 参数,我们可以看到,valac 先把 t.vala 转换成 t.vala.c,再调用 cc 去编译生成 t 。
Loaded package `/usr/local/share/vala-0.56/vapi/glib-2.0.vapi'
Loaded package `/usr/local/share/vala-0.56/vapi/gobject-2.0.vapi'
cc -g -o '/Users/jim/work/lab/lang/vala-0.56.1/compiler/demo/t' '/Users/jim/work/lab/lang/vala-0.56.1/compiler/demo/t.vala.c' -I/usr/local/Cellar/libffi/3.3_3/include -I/usr/local/Cellar/glib/2.68.4/include -I/usr/local/Cellar/glib/2.68.4/include/glib-2.0 -I/usr/local/Cellar/glib/2.68.4/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/pcre/8.45/include -L/usr/local/Cellar/glib/2.68.4/lib -L/usr/local/opt/gettext/lib -lgobject-2.0 -lglib-2.0 -lintl
我们可以看到,虽然可执行文件 t 是由 t.vala.c 生成的,但是调试器里看到源码仍然是原始的 t.vala。这个是怎么做到的呢?
那个得先看看 t.vala.c 长什么样的了。但是 t.vala.c 是个临时文件,编译完后就被删除了。开始想了一个土的办法,在 vala_ccode_compiler_compile 里的 585 行,把删除临时文件的代码注释掉,就拿到了 t.vala.c 。后来发现可以直接生成 C 代码:
valac -g -c t.vala
t.vala.c 的内容:
/* t.vala.c generated by valac 0.56.1, the Vala compiler* generated from t.vala, do not modify */#include static void _vala_main (void);static void
_vala_main (void)
{
#line 2 "t.vala"g_print ("hello, world\n");
#line 14 "t.vala.c"
}int
main (int argc,char ** argv)
{
#line 1 "t.vala"_vala_main ();
#line 1 "t.vala"return 0;
#line 25 "t.vala.c"
}
原来是通过 #line 宏指令实现的,#line 指令并不陌生,在 bison/yacc/flex 生成的代码里经常见到。这里有 line 指令详细的解释。