语法
对于某些应用,我们所完成的简单的词类识别也许足够用了;而另一些应用需要识别特殊的标记序列并执行适当的动作。传统上,对这样的一套动作描述成为语法。
使用右箭头”->”意味着可以用一个新的符号取代一套特殊的标记。
例如:
subject ->noun\pronoun 指示一个新的符号subject是名词或代词。
词法分析程序和语法分析程序的通信
当一起使用lex扫描程序和yacc语法分析程序的时候,语法分析程序是比较高级别的例程。当他需要来自输入的标记时,就调用词法分析程序yylex()。然后,词法分析程序从头到尾扫描输入识别标记。他一找到对u语法分析程序有意义的标记就返回到语法分析程序,将返回标记的代码作为yylex()的值。
词法分析程序和语法分析程序必须对标记代码的内容达成一致。通过让yacc定义标记代码来解决这个问题。在我们的语法中,标记的词性是:NOUN,PRONOUN,VERB,ADVERB,ADJECTIVE,PREPOSITION和CONJUNCTION。yacc使用预处理程序#define将他们每一个都定义为小的整数。下面是一个示例:
#define NOUN 257
#define PRONOUN 258
#define VERB 258
#define ADVERB 260
#define ADJECTIVE 261
#define PREPOSITION 262
#define CONJUNCTION 263
输入的逻辑结束总是返回标记代码零。
下面这一段程序展示了新的词法分析程序的声明和规则段
名称为:forth.lex
%{
#include "y.tab.h" /* 来自语法分析程序的标记代码 */
#define LOOKUP 0 /* 默认情况 - 不是一个定义的单词类型 */
int state;
%}
%%
\n { state = LOOKUP; }
\.\n { state = LOOKUP;
return 0;
}
^verb { state = VERB; }
^adj { state = ADJECTIVE; }
^adv { state = ADVERB; }
^noun { state = NOUN; }
^prep { state = PREPOSITION;}
^pron { state = PRONOUN; }
^conj { state = CONJUNCTION; }
[a-zA-Z]+ {
if(state != LOOKUP){
add_word(state,yytext);
}else{
switch(lookup_word(yytext)){
case VERB:
return(VERB);
case ADJECTIVE:
return(ADJECTIVE);
case ADVERB:
return(ADVERB);
case NOUN:
return(NOUN);
case PRONOUN:
return(PRONOUN);
case CONJUNCTION:
return(CONJUNCTION);
default:
printf("%s: do not recognize!\n",yytext);
}
}
}
. ;
%%
struct word{
char *word_name;
int word_type;
struct word *next;
};
struct word *word_list;
extern void *malloc();
int add_word(int type,char *word)
{
struct word *wp;
if(lookup_word(word) != LOOKUP){
printf("!! warning: word %s already defined\n",word);
return 0;
}
wp = (struct word *)malloc(sizeof(struct word));
wp->next = word_list;
wp->word_name = (char *)malloc(strlen(word)+1);
strcpy(wp->word_name,word);
wp->word_type = type;
word_list = wp;
return 1;
}
int lookup_word(char *word)
{
struct word *wp = word_list;
for(;wp;wp = wp->next){
if(strcmp(wp->word_name,word) == 0)
return wp->word_type;
}
return LOOKUP;
}
int yywrap()
{
return 1;
}
他和之前的词法分析程序有一下几个区别:
1:词法分析程序中使用的词性名字改变为与词法分析程序中的标记名字相一致
2:添加return语句将所识别的单词的标记代码传递给语法分析程序
3:词法分析程序中定义新单词的标记没有任何return语句,因为语法分析程序不“关心”它们。
其中,返回语句表明yylex()操作类似与协同程序。每次语法分析程序调用他时,
都在他停止的那一点进行处理。这样就允许我们渐进地检查和操作输入流。
同时还增加了一条规则来标记句子的结尾:
\.\n { state = LOOKUP;
return 0;
}
句号前面的反斜杠引用这个句号,所以这条规则与后跟一个换行的句号匹配。对词法分析程序所做的另一个改变是省略
目前语法分析程序中提供的main()例程。
yacc语法分析程序
下面程序介绍了yacc语法中的第一步。
forth.y
%{
/*
* 用于识别英文句子基本语法的词法分析程序
*/
#include <stdio.h>
%}
%token NOUN PRONOUN VERB ADVERB ADJECTIVE PREPOSITION CONJUNCTION
%%
sentence: subject VERB object { printf("Sentence is valid.\n"); }
;
subject: NOUN
| PRONOUN
;
object: NOUN
;
%%
extern FILE *yyin;
int main()
{
yyparse();
while(!feof(yyin)){
yyparse();
}
return 0;
}
yyerror(s)
char *s;
{
fprintf(stderr,"%s\n",s);
}
yacc语法分析程序的结构类似与lex语法分析程序的结构。使用%{ %}括起的部分为定一段,使用%% %%括起的为规则段,%token 表示采用8个标记,通常规定标记名都用大写字母,而语法分析程序中的其他名字大部分或完全是小写字母.
最终要的子程序main(),重复调用yyparse()函数知道词法分析程序的输入文件结束,例程yyparse()是由yacc生成的语法分析程序,当词法分析程序看到行的结尾处的句号时返回零标记,表示当前分析的输入已经完成。
规则段将实际语法描述为一套产生式规则或简称为规则。每条规则由符号”:”操作符左侧的一个名字,右侧的符号列表和动作代码以及指示规则结尾的分号组成。默认情况下,第一条规则是最高级别的规则。典型的简单的规则的右侧有一个符号。规则左侧的符号在其他规则中能像标记一样使用。
语法中使用特殊字符”|”,表示或;规则的动作部分由C块组成,以”{}”括起。因为sentence是最高层的符号,所以整个输入必须匹配sentence。当词法分析程序报告输入结束的时候,分析程序返回到他的调用程序。在该情况下就是主程序。随后对yyparse()的调用重置状态并再次开始处理。如果看到输入标记的”subject VERB object”列表,则示例打印一条消息,如果不匹配的话,则会调用yyerror()函数,识别特殊的规则error。可以提供错误恢复代码。尝试将分析程序返回到能够继续分析的状态。如果错误恢复失败,即没有错误恢复代码,yyparse()在发现错误后,返回调用程序。
下面是我们的Makefile程序:
all:
lex forth.lex
yacc -d forth.y
gcc -c lex.yy.c y.tab.c
gcc -o hello lex.yy.o y.tab.o -ll
clean:
rm lex.yy.o y.tab.o lex.yy.c y.tab.c y.tab.h hello
下面我们使用命令:
make
对文件进行编译,编译完成之后,我们运行该程序:
./hello
程序的输出结果,我们来测试一下,运行之后,我们输入下面的语句:
verb are
noun you man
you are man
下面是程序的输出结果:
下面一篇博客将会使用该代码进行扩展,扩展一个简单的小学英语语法分析程序。
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>
版权声明:本文为博主原创文章,未经博主允许不得转载。
分享到:
相关推荐
用lex,yacc语法词法分析器的例子,桌面计算器,测试文件为exprTest.txt
借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法分析借助Lex和Yacc进行词法语法...
项目中的词法分析使用了lex 。 运行环境:Ubuntu 14.04 Ubuntu 16.04 本编译器所支持的词法和语法请参考第二第三小节 解压压缩包 运行命令 unzip compiler.zip 进入文件夹运行命令 ./compiler test.cmm 其中test.cmm...
基于lex和yacc的词法分析器+语法分析器,可以在控制台生成语法分析树 要使用lex和yacc(或其GNU版本flex和bison)来创建一个可以在控制台生成语法分析树的词法分析器和语法分析器,你需要遵循以下步骤: 定义词法...
C语言编译器的Lex及Yacc词法及语法分析规则源码
目的:通过编写简单的YACC程序熟悉YACC词法分析工具的基本用法,及词法分析器的设计思路。编写包括赋值语句、if语句以及含有加合乘运算的表达式等语法结构的pascal子集语言的语法分析器。 功能:程序由两个模块LEX和...
词法分析器的作用是读取源程序生成词法单元,并过滤掉注释和空白。项目中的词法分析使用了lex 。
c语言编译器,用lex和yacc工具完成词法分析与语法分析并生成语法树
语法分析程序自动产生器yacc的使用方法.doc
详细全面介绍了lex和yacc,给初学者一个很好的教材。不但是你全面了解lex和yacc,而且给你很多例子。
lex yacc简单使用,lex词法分析器自动生成,yacc语法分析器自动生成
这是关于编译原理, 词法分析lex, 与语法分析yacc的相关文档,高清版,但是这只有该书的前5章 第一章 lex与yacc 第二章 lex使用 第三章 yacc使用 第四章 菜单生成与语言 第五章 SQL分析
LEX和YACC第二版中文版。LEX是词法分析工具,YACC是语法分析工具,使用这两个工具可以编写编译器等。
介绍了Windows下使用Yacc生成语法分析器,词法分析器。 特别适合入门。
词法分析与语法分析的原始文件扩展: ://www.quut.com/c/ANSI-C-grammar-l-1998.html和 实现了C语言除了struct和指针几乎所有的语法。 运行 环境要求:flex bison g ++ 11 python3 中间代码生成 Windows命令行输入:...
lex是词法分析和语法分析器,本来以为这东西只有c++的呢,没想到也有delphi的 大家可以学习一下lex算法,想搞编译器的可以学习下yacc 非常不错的东西
c语法分析器,采用bison2.1(yacc), flex(lex), 生成程序的语法树 分析单个文件,不支持预处理, 不解析预处理符号# bison,flex工具在上传包内,语法见cgrammar-new.y,词法见input.lex 另附相关说明,本代码采用vs...
使用yacc和lex编写文本分析器(IBM DeveloperWorks).相信对学习编译原理和yacc lex的朋友很有帮助。
lex词法分析器&yacc语法分析器,值的你花时间去看去学的一本书
lex与yacc的pascal代码实现,词法分析和语法分析,做编译器的好东西