C语言语法高亮工具
本站将启动自己编写的语法高亮程序,该程序使用flex编写,只在Linux平台下测试,使用方法参考下文介绍。
遥想当年,第一次听说flex是学习编译原理时,当时问老师一个问题,结果老师将她的QQ号给我,后来给了我许多关于编译原理的资料,但是有许多名词不认识,像lex、flex、yacc、bison,等等。那个时候我陷入GNU/Linux中不能自拔,凡是跟它沾点边的东西,我都会去研究的。结果搞个简单的计算器,结果许多人不明白我在干什么,也不知道研究这些东西有什么用,这个结果让我感到十分无趣。——还不如认真分析LL、LR、SLR呢,这样或许在考试前我会大受欢迎。事实是我放弃了实践,全面应付考试,满足了虚荣心,结果编译原理课程设计时都没能力使用lex和yacc做。而在上机课堂中,我购买了一块ARM开发板,从此踏上了嵌入式这条不归路。
我尝试过许多的语法着色插件,但不太理想。后面一次偶然的机会,看到有博客说使用html语法来写死,这样到哪里都能正常显示代码颜色。由于很久没碰正则表达式、flex这些东西了,所以用了好几天功夫才有点入门。但又到投入到其它工作中,因此这个小东西是点到为止,不想太深入研究。
这个代码是从《纯C论坛·电子杂志》上看到的。不过作者使用了css,而我对css研究不深入(到建站很久以后才意识到css的重要性),而且作者在网上公开的源代码没法找到,因此只好使用另外一种方式来实现了。
程序其实不难,首先使用flex进行词法分析,分析各种类型的单词,比如C语言关键字、字符串、注释,等等,之后一一加上不同颜色。颜色方面,主要是参考大部分编译器(编辑器)对C语言的着色(如关键字为蓝色)。在本站logo制作的一些笔记中提到的测试例子,里面的注释显示失败,经检查,是由于HTML语法特性造成的,因为在HTML中,无论有多少个空格(包括t),都显示一个空格。解决方法是分析出注释中的空格,使用“ ”代替,另外,像“<”、“>”等等,也需要使用“<”(less than?)、“>”(great than?)代替。
程序已测bug:
- 如果源代码中最后一行为C++“//”风格的注释,但不能高亮该行,解决:源代码多加一行空行。这是由于“LINECOMMENT "//".*n”造成的。但是去掉“n”,则这种风格的注释又会无故多空出一行。
- 数字没有匹配,如果只使用程序中的NUMBER,则凡是数字都会着色,包括tmp1和tmp2这种非“数字”中的数字也会着色。干脆不着色了。
另外,我在这个基础上再修改一下正则表达式,为GNU/Linux下的配置文件及Makefile文件着色——因为Shell脚本用得不多,暂时不做。下面是这些代码的问题及bug:
- 配置文件中注释多使用井号“#”,这与root用户提示符“#”相同,而且两个“#”后通常会留一空格,因此无法区别,除非做两套代码。
- 像Makefile中常见的$(foo)等等,开始使用“.*”匹配,但是对于Makefile中的“($(DEBUG), y)”及Shell中常见的$(foo+i,(bar-i))等等匹配不理想,后来改用单词(WORD)区别,这样效果好一些。
- 对于Makefile中的target着色也不理想,像“all:”、"clean:"等等“伪目标”可以认为是关键字,但其它的如“foo.o:”等也是target,于是想到使用单词匹配,不过不知道如何匹配(以冒号(”:“)结尾的单词在lex中的正则表达式是什么?如果你知道,告诉我一声)。
使用方法:
$ lex foo.lex
$ gcc lex.yy.c -o code-color
$ ./code-color < you-code.c > you-code.html
这样,直接打开you-code.html,复制里面的代码到网页编辑器中就可以了。
由于能力有限且急用于实际中,因此匆匆完成,随着学习领域的增多及知识的加深,在以后应该会不断修改的。毕竟,现在这个小工具还能使用。
附上源代码:
%{
#include <stdio.h>
#define FALSE 0
#define TRUE 1
int yywrap();
%}
DIGIT [0-9]
XDIGIT [0-9a-fA-f]
ODIGIT [0-7]
HEX 0(x|X){XDIGIT}+
OCT 0{ODIGIT}+
DEC (0(.{DIGIT}+)?)|([1-9]{DIGIT}*(.{DIGIT}+)?)
NUMBER {HEX}|{OCT}|{DEC}
WORD [a-zA-Z_]+
WHITESPACE [t]+
NL r?n
STRING "[^"n]*"
CHAR '[^'n]*'
QUOTATION {STRING}|{CHAR}
KEYWORD "while"|"do"|"switch"|"case"|"break"|"default"|"continue"|"for"|"goto"|"if"|"else"|"return"|"typedef"|"sizeof"
TYPE1 "char"|"short"|"int"|"long"|"float"|"double"|"signed"|"unsigned"
TYPE2 "struct"|"union"|"enum"|"void"|"const"|"static"|"extern"|"register"|"auto"|"volatile"
TYPE {TYPE1}|{TYPE2}
PREWORD "#define"|"#include"|"#error"|"#if"|"#elif"|"#else"|"#ifdef"|"#endif"|"#ifndef"|"#undef"|"#line"|"#pragma"
LINECOMMENT "//".*n
%%
"<" {printf("<");}
">" {printf(">");}
t {printf(" ");}
" " {printf(" ");}
"&" {printf("&");}
{printf(" ");}
"/*" {
char c;
int done = FALSE;
printf("<span style="color:#008000">n");
ECHO;
do{
while ((c=input()) != '*')
{
if (c == 'n')
printf("<br/>n");
else if (c == ' ')
printf(" "); /* space in the comment */
else
putchar(c);
}
putchar(c);
while((c=input()) == '*')
putchar(c);
if (c=='n')
printf("<br/>n");
putchar(c);
if (c == '/')
{
done = TRUE;
printf("</span>n");
}
}while (!done);
}
{LINECOMMENT} {printf("<span style="color:#008000">%s</span><br/>n", yytext);}
{QUOTATION} {printf("<span style="color:#ff00ff">%s</span>", yytext);}
{KEYWORD}|{TYPE} {printf("<span style="color:#0000ff">%s</span>", yytext);}
{PREWORD} {printf("<span style="color:#0000ff">%s</span>", yytext);}
{NL} {printf("<br/>n");}
{WORD} {ECHO;}
{NUMBER} {ECHO;}
{WHITESPACE} {ECHO;}
%%
int main(void)
{
printf("<html>n");
printf("<head>n");
printf("</head>n");
printf("<body>n");
printf("<blockquote style="background-color:#f9f7ed">n");
yylex();
printf("</blockquote>n");
printf("</body>n");
printf("</html>n");
}
int yywrap()
{
return 1;
}
本文及源代码可自由使用而不用告知作者,当然,我作了修改也无需再公布出来。下面是一个关于log应用的例子:
#include <stdio.h>#include <stdlib.h> #include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h> /**< umask */#define MAXFILE 65535
int main(void)
{
pid_t pid,sid;
int i;
int fd;
int len;
char *buf = "This is a daemonn";
len = strlen(buf);
pid = fork();
if (pid < 0)
{
perror("fork error");
exit(1);
}
else if (pid > 0)
exit(0);
openlog("daemon_test", LOG_PID, LOG_DAEMON);
sid = setsid();
if (sid < 0)
{
syslog(LOG_ERR, "%sn", "setsid");
exit(1);
}
sid = chdir("/");
if (sid < 0)
{
syslog(LOG_ERR, "%sn", "chdir");
exit(1);
}
umask(0);
for (i=0; i<MAXFILE; i++)
close(i);
while (1)
{
fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND,0600);
if (fd < 0)
{
syslog(LOG_ERR, "open");
exit(1);
}
write(fd, buf, len+1);
close(fd);
sleep(10);
}
closelog();
return 0;
}
主要参考资料:
1、tinyfool的文章:基于Flex的c/c++代码加亮工具(源代码开放)(),该文章在05年发表,大部分网站都有转载,可惜源代码下载地址失效了。本程序以该文章代码为基础。
2、CU帖子:CU论坛C语言代码语法高亮工具()。
3、代码发芽网:
4、Flex、正则表达式,网上资料大把,不过都是基础的多,想深入需要慢慢搜索、研究。