• 今天我们来继续上次的话题,聊一聊C语言词法中的constant:

    constant:
    floating-point-constant
    integer-constant
    enumeration-constant
    character-constant

    我们看到C语言中有四种constant,分别是浮点数常量,整数常量,枚举常量和字符常量。我们今天先来讲浮点数常量:

    floating-point-constant:
    fractional-constant exponent-part opt floating-suffix opt
    digit-sequence exponent-part floating-suffix opt
     
    fractional-constant:
    digit-sequence opt . digit-sequence
    digit-sequence .
     
    exponent-part:
    e sign opt digit-sequence
    E sign opt digit-sequence
     
    sign: one of
    + -
     
    floating-suffix: one of
    f l F L
     
    digit-sequence:
    digit
    digit-sequence digit

    flex里面我们可以用下面的正则表达式来描述这些词法:

    floating-point-constant        ({fractional-constant}{exponent-part}?{floating-suffix}?) |
                                   ({digit-sequence}{exponent-part}{floating-suffix}?)
    fractional-constant            ({digit-sequence}?"."{digit-sequence})|({digit-sequence}".")
    exponent-part                  [eE]{sign}?{digit-sequence}
    sign                           [+-]
    floating-suffix                [flFL]
    digit-sequence                 {digit}+

    看上去很复杂,其实很简单,和上面的词法是一一对应的。

    注:

    1、词法中的opt意思是optional,指前面的元素允许出现0次或1次,在正则表达式中等同于?的含义。
    2、浮点数常量可以没有整数部分,只有小数点和小数部分;也可以只有整数部分和小数点,没有小数部分。
    3、为了便于阅读,我把floating-point-constant的正则表达式拆成了两行。
    4、不知道什么是digit,请看上篇:C语言词法分析 二

  • 上一篇我们讲了keyword,在词法分析中,keyword是比较简单的部分,copy & paste就可以了。这一篇我们来讲identifier(标识符):

    identifier:
    nondigit
    identifier nondigit
    identifier digit

    nondigit: one of
    _ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

    digit: one of
    0 1 2 3 4 5 6 7 8 9

    换言之,标识符是以字符或下划线开头,后跟字符,数字或下划线的标记。在flex中这样定义:

    identifier      {nondigit}({nondigit}|{digit})*
    nondigit        [_a-zA-Z]
    digit           [0-9]

    %%
    {identifier}    return IDENTIFIER;
    %%

    在flex中,我们用正则表达式来表示词法。

    ======================================

    由于typedef的存在,有时我们可能需要从identifier中派生出typename来。GNU C就是这么实现的。

  • 俗话说,有梦想谁都了不起,我的梦想是写一个编译器。这一篇来讲C语言词法分析。

    词法分析器的任务,就是给语法分析器提供token。在编译原理中,token是指:

    Any nonreducible textual element in data that is being parsed. For example, the use in a program of a variable name, a reserved word, or an operator. Storing tokens as short codes shortens program files and speeds execution.

    中文翻译为:标记。参见 微软术语

    C语言词法如下:

    token:
    keyword
    identifier
    constant
    string-literal
    operator
    punctuator

    什么是keyword呢?

    keyword: one of
    auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

    对于每一个C语言的keyword,我们都需要返回一个唯一的token。这点和其他的元素不同。
    我用lex (flex)开发,所以实际代码应该是:

    %%
    auto         return AUTO;
    break        return BREAK;
    case         return CASE;
    char         return CHAR;
    const        return CONST;
    continue     return CONTINUE;
    default      return DEFAULT;
    do           return DO;
    double       return DOUBLE;
    else         return ELSE;
    enum         return ENUM;
    extern       return EXTERN;
    float        return FLOAT;
    for          return FOR;
    goto         return GOTO;
    if           return IF;
    int          return INT;
    long         return LONG;
    register     return REGISTER;
    return       return RETURN;
    short        return SHORT;
    signed       return SIGNED;
    sizeof       return SIZEOF;
    static       return STATIC;
    struct       return STRUCT;
    switch       return SWITCH;
    typedef      return TYPEDEF;
    union        return UNION;
    unsigned     return UNSIGNED;
    void         return VOID;
    volatile     return VOLATILE;
    while        return WHILE;
    %%

    限于篇幅,就先写到这里,后面几篇中我会介绍其他的一些token。

  • 在我用过的所有集成开发环境中,我个人认为:Visual Studio不一定是最好的,但是是最好用的。哎~~叹息一下Borland之塔的倒掉。

    在Linux环境下,就不同了。Visual Studio不用想了,地球人都知道是不可能的事。好用的只有Anjuta,KDevelop和Code::Blocks。本人偏爱Gnome,所以KDevelop排除。Anjuta很久很久以前用过,很好用,但是搭配的是GTK。要跨平台开发,非Code::Blocks+wxWidgets莫属。

    今天我们就来看看怎样用Code::Blocks和wxWidgets打造Linux下的跨平台集成开发环境。

    硬件环境:联想T61,英特尔酷睿2双核64位CPU T7300,4GB内存
    软件环境:Ubuntu 8.04.1 LTS

    安装步骤:
    1、俗话说兵马未动,粮草先行,我们先把编译环境,C库,C++库和传说中巨牛逼的Boost库装好,如下:

    sudo apt-get install build-essential libc6 libc6-dbg libc6-dev glibc-doc libstdc++6 libstdc++6-4.2-dbg libstdc++6-4.2-dev libstdc++6-4.2-doc libboost*

    2、最新版的Code::Blocks包括一个调试组件Valgrind,用来探测内存泄露的:

    sudo apt-get install valgrind

    3、然后我们来添加Code::Blocks最新版和wxWidgets的安装源:

    安装公钥:

    wget -q http://apt.wxwidgets.org/key.asc -O- | sudo apt-key add -
    wget -q http://lgp203.free.fr/public.key -O- | sudo apt-key add -

    修改软件源配置文件:

    sudo gedit /etc/apt/sources.list

    在末尾加上软件源:

    deb http://apt.wxwidgets.org/ hardy-wx main
    deb-src http://apt.wxwidgets.org/ hardy-wx main


    deb http://lgp203.free.fr/ubuntu/ hardy universe
    deb-src http://lgp203.free.fr/ubuntu/ hardy universe

    然后存盘,退出gedit。

    4、更新软件源:

    sudo apt-get update

    5、安装Code::Blocks最新版和wxWidgets:

    sudo apt-get install libwxbase2.8-0 libwxbase2.8-dbg libwxbase2.8-dev libwxgtk2.8-0 libwxgtk2.8-dbg libwxgtk2.8-dev wx2.8-doc wx2.8-examples wx2.8-headers wx2.8-i18n wx-common
    sudo apt-get install codeblocks codeblocks-contrib libcodeblocks0 libwxsmithlib0

    6、如果你有心研究wxWidgets架构,可以下载wxWidgets的源代码,各种平台都有哦:

    sudo apt-get source wxwidgets2.8

    然后我们就可以开始使用Code::Blocks了,应用程序->编程->Code::Blocks IDE。
    Code::Blocks和wxWidgets整合非常好,几乎不用做任何额外的配置。
  • Blogbus有一个很不错的功能“导出日志”,该功能可以将你所有的博客文章导出为XML格式以便备份和编辑。今天我们就来讲一讲导出日志的XML文件格式。

    首先我们把自己的博客导出,步骤如下:
    1、登录blogbus
    2、选择博客->博客设置->导入导出->点这里导出日志
    3、保存XML文件

    然后我们就可以分析XML文件格式了,用IE,Firefox或Visual Studio将备份文件打开,便可以看到它的结构:

    <BlogBusCom dtype="BlogData" SchemaVersion="1.1" Creator="BlogBus.Com BlogSystem V4.0">
    // 导出文件的数据类型,版本和出处
        <Description> // 博客描述
            <BlogName>博客名称,例如:孙楠的技术讲堂</BlogName>
            <DomainName>博客地址,例如:sunnantechnology.blogbus.com</DomainName>
            <ExportTime>导出的时间,格式为:2008-12-20 15:43:26</ExportTime>
        </Description> // 描述结束
        <Log>
    // 博客日志
            <Title>日志标题,例如:Blogbus导出日志的XML文件格式分析</Title>
            <Status>
    日志状态,控制隐藏和置顶,隐藏为0,正常为1,置顶为2</Status>
            <AllowComment>
    评论,Y为允许,N为不允许</AllowComment>
            <AllowPing>
    引用,Y为允许,N为不允许</AllowPing>
            <AllowLinks>
    自动链接,Y为允许,N为不允许</AllowLinks>
            <Writer>作者姓名,可以为空</Writer>
            <Sort>分类,为什么用Sort,Catalog不是更好?</Sort>
            <Content>日志内容,包括html格式</Content>
            <Excerpt>日志摘要,包括html格式</Excerpt>
            <Tags>关键词,关键词之间用空格分隔</Tags>
            <LogDate>日志时间,格式为:2008-04-17 13:40:00</LogDate>
            <Comments> // 这个标记做什么用?可以省略吗?
                <Comment>
    // 博客评论
                    <Email>评论者的邮件地址</Email>
                    <HomePage>评论者的主页地址</HomePage>
                    <CreateTime>评论时间,格式为:2008-12-19 21:45:48</CreateTime>
                    <NiceName>评论者的姓名</NiceName>
                    <CommentText>评论的内容和回复,无html格式</CommentText>
                </Comment>
    // 评论结束
                <Comment>...</Comment> // 更多评论
            </Comments>
    // 这个标记做什么用?可以省略吗?
        </Log> // 日志结束
        <Log>...</Log> // 更多日志
    </BlogBusCom> // 导出文件结束


    Blogbus的XML导出文件结构简单清晰,易读易维护,设计得不错。但是关于<Comments></Comments>这个标记,真的需要吗?

    1、首先不可能是排序用,日志和评论都以时间排序,而时间都已经有了相应的标记
    2、如果是要把评论按照每篇日志分开,似乎也没有必要,毕竟每个<Comment></Comment>都在<Log></Log>里面

    思来想去,我感觉比较可能的作用是,在批量处理评论的操作中,可以节省时间。操作数可以从评论条数,减少到日志条数。但是从算法来说,都是O(n),真正效果如何,就要实测了。

    另外:
    1、日志的顺序可能与博客上看到的顺序不同,但是不会影响导入后的结果。这是由XML的特性决定的。
    2、有了XML可以批量删除不必要的html标记,比如将>&nbsp;<批量替换为><,也可以把搬家时丢失的评论加进去,当然最重要的功能还是存档啦。