C语言语法高亮工具 | 迟思堂工作室
A-A+

C语言语法高亮工具

2014-08-30 18:10 GNU/Linux程序 暂无评论 阅读 1,534 次

本站将启动自己编写的语法高亮程序,该程序使用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),都显示一个空格。解决方法是分析出注释中的空格,使用“&nbsp;”代替,另外,像“<”、“>”等等,也需要使用“&lt”(less than?)、“&gt”(great than?)代替。

程序已测bug:

  1. 如果源代码中最后一行为C++“//”风格的注释,但不能高亮该行,解决:源代码多加一行空行。这是由于“LINECOMMENT "//".*n”造成的。但是去掉“n”,则这种风格的注释又会无故多空出一行。
  2. 数字没有匹配,如果只使用程序中的NUMBER,则凡是数字都会着色,包括tmp1和tmp2这种非“数字”中的数字也会着色。干脆不着色了。

另外,我在这个基础上再修改一下正则表达式,为GNU/Linux下的配置文件及Makefile文件着色——因为Shell脚本用得不多,暂时不做。下面是这些代码的问题及bug:

  1. 配置文件中注释多使用井号“#”,这与root用户提示符“#”相同,而且两个“#”后通常会留一空格,因此无法区别,除非做两套代码。
  2. 像Makefile中常见的$(foo)等等,开始使用“.*”匹配,但是对于Makefile中的“($(DEBUG), y)”及Shell中常见的$(foo+i,(bar-i))等等匹配不理想,后来改用单词(WORD)区别,这样效果好一些。
  3. 对于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("&lt;");}

">" {printf("&gt;");}

t {printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");}

" " {printf("&nbsp;");}

"&" {printf("&amp;");}

&nbsp; {printf("&nbsp;");}

 

"/*" {

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("&nbsp;"); /* 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、正则表达式,网上资料大把,不过都是基础的多,想深入需要慢慢搜索、研究。



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate



标签:

给我留言