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),都显示一个空格。解决方法是分析出注释中的空格,使用“ ”代替,另外,像“<”、“>”等等,也需要使用“&lt”(less than?)、“&gt”(great than?)代替。
程序已测bug:

  1. 如果源代码中最后一行为C++“//”风格的注释,但不能高亮该行,解决:源代码多加一行空行。这是由于“LINECOMMENT “//“.*n”造成的。但是去掉“n”,则这种风格的注释又会无故多空出一行。
  2. 数字没有匹配,如果只使用程序中的NUMBER,则凡是数字都会着色,包括tmp1和tmp2这种非“数字”中的数字也会着色。干脆不着色了。
    另外,我在这个基础上再修改一下正则表达式,为GNU/Linux下的配置文件及Makefile文件着色——因为Shell脚本用得不多,暂时不做。下面是这些代码的问题及bug:
  3. 配置文件中注释多使用井号“#”,这与root用户提示符“#”相同,而且两个“#”后通常会留一空格,因此无法区别,除非做两套代码。
  4. 像Makefile中常见的$(foo)等等,开始使用“.*”匹配,但是对于Makefile中的“($(DEBUG), y)”及Shell中常见的$(foo+i,(bar-i))等等匹配不理想,后来改用单词(WORD)区别,这样效果好一些。
  5. 对于Makefile中的target着色也不理想,像“all:”、”clean:”等等“伪目标”可以认为是关键字,但其它的如“foo.o:”等也是target,于是想到使用单词匹配,不过不知道如何匹配(以冒号(”:“)结尾的单词在lex中的正则表达式是什么?如果你知道,告诉我一声)。
    使用方法:
    1
    2
    3
    $ lex foo.lex
    $ gcc lex.yy.c -o code-color
    $ ./code-color < you-code.c > you-code.html
    这样,直接打开you-code.html,复制里面的代码到网页编辑器中就可以了。 由于能力有限且急用于实际中,因此匆匆完成,随着学习领域的增多及知识的加深,在以后应该会不断修改的。毕竟,现在这个小工具还能使用。
    附上源代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
     /***********************************************
    * C语言代码语法高亮工具
    *编译方法:
    * $ flex code2html.lex
    * $ gcc lex.yy.c -o code2html
    *
    * 使用:
    * $ ./code2html < test.c > test.html
    * 将test.c生成test.html,打开test.html,
    * 复制内容到网页编辑器(即见即所得)中即可。
    * 并不是所有编辑器都支持。
    *
    * log & bug:
    1、注释中多个空格识别不出来。==>在代码中添加对注释空格的处理。
    2、连续2个(或多个)tab键识别不出来。==>暂时用空格代替。
    3、2011-04-21
    尝试不使用blockquote,直接使用div。
    修改显示方式,背景色为浅色,默认字体为黑色。
    添加Makefile,直接make即可生成code2html文件。
    4、2011-04-25
    数字着色问题未解决==>解决
    修改单词规则:WORD [a-zA-Z_]+{DIGIT}*,能识别tmp1,数字在前识别不到
    5、为解决数字着色问题,连带一个bug,就是头文件中“.”也被识别为“数字”中的小数点
    6、
    **********************************************/

    %{
    #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}+)?)*/
    DEC [0-9.]+
    NUMBER {HEX}|{OCT}|{DEC}
    WORD [a-zA-Z_]+{DIGIT}*

    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/>");
    putchar(c);
    if (c == '/')
    {
    done = TRUE;

    }
    }while (!done);
    printf("</span>");
    }

    {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} {printf("<span style=\"color:#ff0000\">%s</span>", yytext);}
    {WHITESPACE} {ECHO;}
    /*{WHITESPACE} {printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");} */
    %%

    int main(void)
    {
    printf("<html>\n");
    printf("<head>\n");
    printf("</head>\n");
    printf("<body>\n");

    #ifdef BACKGROUND_COLOR
    //printf("<blockquote style=\"background-color:#f9f7cc\">\n");
    printf("<div style=\"BORDER-RIGHT: #aaaaaa 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #aaaaaa 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #aaaaaa 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #aaaaaa 1px solid; BACKGROUND-COLOR: #dcdcdc\">\n");
    #else
    printf("<blockquote>\n");
    #endif
    //printf("<!--本语法高亮工具由Late Lee(http://www.latelee.org)使用lex编写-->\n");
    printf("\n<!--this tool is written by Late Lee(http://www.latelee.org) using lex.-->\n\n");

    yylex();

    #ifdef BACKGROUND_COLOR
    //printf("</blockquote>\n");
    printf("</div>\n");
    #else
    printf("</blockquote>\n");
    #endif
    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++)

        lose(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;

}

约八年后的PS:本文年代久远,且手动语法着色过于烦琐,已舍弃。
主要参考资料:
1、tinyfool的文章:基于Flex的c/c++代码加亮工具(源代码开放)(http://blog.csdn.net/tinydust/archive/2005/04/15/348946.aspx),该文章在05年发表,大部分网站都有转载,可惜源代码下载地址失效了。本程序以该文章代码为基础。
2、CU帖子:CU论坛C语言代码语法高亮工具(http://bbs.chinaunix.net/thread-879964-1-1.html)。
3、代码发芽网:http://fayaa.com/code/
4、Flex、正则表达式,网上资料大把,不过都是基础的多,想深入需要慢慢搜索、研究。