Arsenal

词法分析器
授权协议 MIT
开发语言 C/C++
所属分类 程序开发、 其他开发相关
软件类型 开源软件
地区 不详
投 递 者 督建柏
操作系统 Windows
开源组织
适用人群 未知
 软件概览

目标组件:

  • 可配置的词法分析器
  • 可配置的LR-Parser
  • Ray: 类C的中间语言
  • 汇编器
  • 相关设计,测试工具

已完成组件:

  • 可配置的词法分析器
  • 可配置的LR-Parser,支持SLR(1),LALR(1)
  • 文法设计工具
    1. BNF Compiler,生成SLR(1),LALR(1)分析表
    2. 内建错误处理
    3. 实时观测语法及其分析树
    4. 实时报告分析表,冲突,First Follow集合以及左递归,左因子

目录结构:

  1. ./Arsenal/Lex : 词法分析器;
  2. ./Arsenal/Parser : 语法分析器.
  3. ./Arsenal/Common : 公共库函数.
  4. ./Arsenal/Tools : 一些必要的工具,例如读取语法分析配置文件的工具等等.
  5. ./Tools/GrammarDesigner : 基于MFC的文法设计工具.
  6. ./Prj : 各种编译器的工程(除了VS2K8之外的工程仅是为了测试代码的兼容性)
  7. ./misc 一些测试用的文法以及工具和一部分语言的yacc版.
  8. ./binary/x86/dll/release : 编译后,生成的x86平台下的dll release版,其余binary下结构与其类似,
  9. ./temp/ 编译器生成的临时文件,例如obj等.
  • 其中,./Arsenal/可支持所有目标平台,./Tools下的GrammarDesigner,仅支持win x86 X64.本工程所有生成的二进制文件都放在./Arsenal/binary下。所有引用到MFC的DLL版本都需要安装vs2k8发行包:VS2008_Redistributable_Package

用法:

  1. 在Grammar Designer中,选项Tools->Generate->Template会根据当前词法和语法规则,自动生成一份调用Arsenal库的.c模板.

词法分析部分:

  1. 例子 :请参考./misc/grammar/中的例子.
  2. 技 术细节: 基于NFA的Pseudo-Parallel正则表达式引擎,支持正向预搜索,逆向预搜索,贪婪,非贪婪,循环等操作,支持SINGLELINE IGNORECASE模式,不支持MULTILINE,"^" "$"永远匹配整个输入的首尾,但是扩展了关键字"\B" "\E"以便匹配行首尾,"."在默认不匹配"\n",SINGLELINE下匹配所有字符。

语法分析部分:

  • 目 标: 在最开始我并没有想把这两样东西通用化,仅仅想做一个比较精简的基于递归下降技术的类C解释器(应该会去掉很多晦涩的C语法支持),但是在实践过程中,我 发现需要一些工具,例如便于实时设计和修正文法以及观测此文法接受的语言和其具体语法树(包括错误处理之后的)等等,因此才决定实现一个可配置的LR分析 器。另外,一个被常问到的问题是,自己写一个类似yacc的库有什么用处?我只能说,这个东西可以轻易的改装成任何我需要的东西,不论是对UI的友好程度 还是线程安全性以及其执行时的解释性质,至少很难拿yacc做个像样的设计工具出来。
  • 实现:
    1. 实现简单的说就是个LR理论的瘦封装,生成Action和Goto表,之后每个产生式注册一个语义动作,收集节点,自底向上最终生成一颗AST。
  • 错 误处理:我采用的是和yacc相当类似的一种技术--error token,首先error作为保留终结符,当有错误发生时,直接检索当前Action Table是否有可以移入error,如果有,则移入,否则,向上pop_stack,直到可以接受终结符error为止,此时parser状态进入 error状态,在此状态下任何非法的输入符号均被丢弃,直到移入了一个在error上合法的符号(这里并非是终结符,请看例子),此时恢复到正常状态。 如果任何错误发生时直到parser-stack空为止都不能接受终结符error,则分析失败,否则带error的产生式会被当作正常产生式处理,可以 在规约时处理这条错误产生式。例如A -> error ';';当有任何错误发生时,parser会移入error直到移入';'后才会恢复到正常状态。同样的道理, A -> error TERM; TERM -> ';';同样可以接受,实际上和上面的error -> ';'相同,只不过先做一次';'到TERM的规约动作,当然 A->error同样可以接受,只是错误状态被传递到A之后的符号,也可以在分析中清除Error状态,例如在某个ActionHandler中调 用PSR_ClearError(...);
  • 优先级,结合性:同样和yacc的准则类似,优先选择移入,每条产生式的优先级默认是其最右终结符号,也可以单独指定产生式的优先级以及结合性。例如:
  • %left '+', '-';

    %left '*', '/', '%';

    %right UMINUS;

    E
    -> E '+' E | E '-' E | E '*' E | E '/' E | E '%' E | '-' E %prec UMINUS | '(' E ')';
这里,配置起来的 前两条产生式的优先级结合性是其最右终结符,也就是 '+' '-', 之后三条是 '*' '/' '%' ,在
E -> '-' E %prec UMINUS
中,其优先级结合性取自符号UMINUS,而最后一个
E->'(' E')'
的优先级为默认的,默认为0和无结合性。
  • 实例:先简单说下具体用法,因为采用的是表驱动的Shift-Reduce Parser,所以Parser的接口提供了一个名为PSR_AddToken的函数,每次调用处理一个词法值,直到移入它,此函数返回,当EOI被接受 后,此Parser状态为接受状态,所以实用中是词法分析器调用语法分析器。关于语法树的构建实际上是靠针对每条产生式注册一个相关的Action- Handler来完成的,详情请参考实例:./Arsenal/Tools/grammar_config.c,里面实现一个完整的基于Arsenal的 语法配置程序.一个完整的应用Arsenal的例子是./Tools/GrammarDesigner/,一个基于MFC的文法设计工具,可以用其观测大 部分文法的具体语法树。基于时间关系,文档也不可能写的相当详细,而且代码还处于不断的修改中,所以有人对其有兴趣的话可以联系我。
  • 错 误报告: 这是个不太好设计的部分,暂时权宜之计是在AR_Init中注册统一的error以及print handler,并由统一的AR_error和AR_printf调用,在AR_printf假设UI共享同一个终端,另外每个子模块例如lex_t被创 建出来时需要注册一个ioctx_t;例如:
  •  
    typedef void    (AR_STDCALL *AR_error_func_t)(int_t level, const wchar_t *msg, void *ctx);
    typedef void    (AR_STDCALL *AR_print_func_t)(const wchar_t *msg, void *ctx);


    typedef struct __arsenal_io_context_tag
    {
                    AR_error_func_t on_error
    ;
                    AR_print_func_t on_print
    ;
                   
    void                    *ctx;
    }arIOCtx_t;
在需要报告错误时,lex_t内部会调用例如:
AR_error_ctx(io_ctx, AR_ERR_FATAL, "error = %ls", err_msg);
等等,暂时限于我对此类软件的理解,只能如此设计。
  • 移植:
    1. 因为此类程序对系统调用几乎没有依赖,所以移植起来相对简单,这里比较棘 手的问题是在某些编译器下缺乏对于wchar_t以及相关CRT库函数的支持,所以所有被引用到的CRT函数已被隔离在common.h中,以AR_为前 缀,如果移植的平台编译器等不支持相关函数,在必要时可能要单独实现一套。未来可能会把所有IO部分的CRT都重写为独立的版本,因为类似sprintf 一族的函数缺乏统一标准,暂时是移植方面(甚至是编译器间)最大的麻烦。
    2. strconv.c提供了utf8到 (utf16&&utf32 非可变长标准)的相互转换,这块可以认为是平台无关的,wchar_t无论是配合win32下utf16的双字节或linux下utf32的4字节在本程 序中都不成为问题。此类程序和是字符集本身几乎没有关系,wchar_t换成unsigned int等等照样可以完成任务,此模块仅仅是为了读取一些配置文件时使用。
    3. thread.c中提供了靠CAS指令实现的spinlock,以提供Arsenal库在多线程环境下的安全初始化以及某些数据结构池的分配。
    4. 请留意基本数据类型的字长,例如int_t被定义为和目标平台字长相同,而并非32bit,而其它相关数值类型都由其后缀决定,例如int8_t int16_t int32_t int64_t等。wchar_t尤其相关编译器决定,所以永不少于16bit。
    5. 我 的主要开发工具是VS2K8,Arsenal由ANSI C编写,在VS2K8下可以完美的编译为x86,X64并通过测试,另外Prj目录中有VC6和BCB6的工程,在vs2K8之外都仅仅是编译通过,支持 gcc4.x版本,在ubuntu 8.04下编译通过,暂时没精力去做其它平台或者编译器的大量测试,而且,请注意,我不能保证最新代码完全可以在ubuntu下编译通过的,一般都是在核 心模块有变化的时候才会做一次兼容性测试,所以只能尽可能的保证方便的移植。
  • 一些问题:
    1. 这 种可配置的词法和语法分析器本质上都是通过模式构建一个状态机,就像不良的程序导致错误一样,这里有些不良的文法会导致它们生成死循环,很遗憾我没办法在 编译时检测它们,一个词法模式的例子是比如({name1}|{name2}|^)+这种模式导致的死循环,很难在对表达式做语法分析时检测并报告它们。 而语法分析器部分相对要好一些,但是存在一种可能,导致在用error 尝试容错的时候变成无穷reduce空Rule:首先尝试shift error,之后reduce Epsilon,但是shift Epsilon之后的状态并不能shift error,这里会弹出Epsilon,继续尝试shift error,这就是又一个死循环了。因为中间不一定存在多少reduce操作,所以几乎没办法知道如何从这个循环跳出去。
    2. 另一个被常 提及的问题是,例如如何描述C语言的声明和typedef等,例如typedef int int_t; int_t x;诸如此类,当type_specifier -> ID的会产生冲突的问题,这里的答案是没有办法,LALR(1)不具备这种能力,也许GLR是一种选择.但GLR的问题是错误报告和容错异常复杂,我还没 找到一种足够标准的做法。

联系方式:

  1. Email:SolidusSnakeEx@gmail.com
  2. GTalk: SolidusSnakeEx@gmail.com
  3. QQ : 2070341
  • https://android-arsenal.com/

  • 离上班还有八分钟,期待下午有钱拿,期待晚上的冠军杯。 下午有钱了就可以把电话费交了,然后就可以开通网络,晚上才可以看球,否则,没有电视的日子,看球的很受伤。 Barcelona v Arsenal Champions League Round F Stade de France Wednesday, May 17, 2006 Kick-off: 7.45pm 2006-06-1802:45 the

  • Black Hat Arsenal USA 2018 — The w0w lineup After the huge success of Black Hat Arsenal USA 2017, @toolswatch has now announced the list of tools selected for Black Hat Arsenal USA 2018. This time the

 相关资料
  • 一个高级语言程序在计算机中一般以文件形式存在,文件是一堆字节的集合,而它要表达的含义显然不是一堆字节,最小单位是一个个词,因此编译一个程序,一开始的工作就是词法分析 龙书的词法分析部分,掺杂了很多自动机相关的东西,其实这些在计算理论有更详细的描述,在编译原理里面讲大概是希望能让零基础的人看懂,可惜这样一来内容就比较臃肿,而且好像也讲的不是很系统反而让人看糊涂,就好像算法导论里面讲NP一样,虽然没有

  • 2. 词法分析 Python程序由解析器读取。输入到解析器中的是由词法分析器生成的词符流。本章讲述词法分析器如何把一个文件拆分成词符。 Python程序的文本使用7比特ASCII字符集。 2.3版中新增:可以使用编码声明指出字符串字面值和注释使用一种不同于ASCII的编码。 为了和旧的版本兼容,如果发现8比特字符,Python只会给出警告。修正这些警告的方法是声明显式的编码,或者对非字符的二进制数

  • 上一篇文章讲到了状态机和词法分析的基本知识,这一节我们来分析Jsoup是如何进行词法分析的。 代码结构 先介绍以下parser包里的主要类: Parser Jsoup parser的入口facade,封装了常用的parse静态方法。可以设置maxErrors,用于收集错误记录,默认是0,即不收集。与之相关的类有ParseError,ParseErrorList。基于这个功能,我写了一个PageEr

  • 因为词法规则可以使用递归,所以词法解析器在技术上和语法解析器一样强大。那意味着我们甚至可以在词法分析器中匹配语法结构。或者,在另一个极端,我们可以把字符当作记号,使用语法分析器去把语法结构应用到字符流(这种被称为无扫描语法分析器)。这导致什么在词法分析器中匹配和什么在语法分析器中匹配的界线在哪里并不是很明显。幸运的是,有几条经验法则可以让我们做出判断: 在词法分析器中匹配和丢弃任何语法分析器根本不

  • 我一直在尝试用java编写一个简单的词法分析器。 File Token.java如下: Lexer如下:Lexer。JAVA 并且可以用Try.java测试如下: 说出输入。txt有 我期望的输出是 但我面临的问题是:它把每个数字都当作 而且它不能识别实数。我得到: 意外符号:'.' 为了达到预期的效果,需要做哪些改变?

  • 12.1. 概述 词法分析器用于读取各种格式的数据,这些数据可以具有灵活但可能非常复杂的结构。 关于"格式"的一个最好的例子就是 C++ 代码。 编译器的词法分析器必须理解 C++ 的各种可能的语言结构组合,以将它们翻译为某种二进制形式。 开发词法分析器的主要问题是所分析的数据的组成结构具有大量的规则。 例如,C++ 支持很多的语言结构,开发一个相应的词法分析器可能需要无数个 if 表达式来识别任