c - 这个 Bison 语法有什么问题?

标签 c bison flex-lexer

我正在尝试构建 Bison 语法,但似乎遗漏了一些东西。我保持它非常基本,但我仍然遇到语法错误并且无法弄清楚原因:

这是我的 Bison 代码:

%{

#include <stdlib.h>
#include <stdio.h>

int yylex(void);
int yyerror(char *s);

%}

// Define the types flex could return
%union {
    long lval;
    char *sval;
}

// Define the terminal symbol token types
%token <sval> IDENT;
%token <lval> NUM;

%%

Program: 
    Def ';' 
    ;

Def: 
    IDENT '=' Lambda { printf("Successfully parsed file"); }
    ;

Lambda: 
    "fun" IDENT "->" "end"
    ;

%%

main() {
    yyparse();
    return 0;
}

int yyerror(char *s)
{
  extern int yylineno;  // defined and maintained in flex.flex
  extern char *yytext;  // defined and maintained in flex.flex

  printf("ERROR: %s at symbol \"%s\" on line %i", s, yytext, yylineno); 
  exit(2);
}

这是我的Flex 代码

%{
#include <stdlib.h>
#include "bison.tab.h"
%}

ID [A-Za-z][A-Za-z0-9]*
NUM [0-9][0-9]*
HEX [$][A-Fa-f0-9]+
COMM [/][/].*$

%%

fun|if|then|else|let|in|not|head|tail|and|end|isnum|islist|isfun    {
    printf("Scanning a keyword\n");
}


{ID}    {
    printf("Scanning an IDENT\n");
    yylval.sval =  strdup( yytext );
    return IDENT;
}

{NUM}   {
    printf("Scanning a NUM\n");
    /* Convert into long to loose leading zeros */
    char *ptr = NULL;
    long num = strtol(yytext, &ptr, 10);
    if( errno == ERANGE ) {
            printf("Number was to big");
            exit(1);
    }

    yylval.lval = num;
    return NUM;
}

{HEX}   {
    printf("Scanning a NUM\n");
    char *ptr = NULL;
    /* convert hex into decimal using offset 1 because of the $ */
    long num = strtol(&yytext[1], &ptr, 16);
    if( errno == ERANGE ) {
            printf("Number was to big");
            exit(1);
    }

    yylval.lval = num;
    return NUM;
}


";"|"="|"+"|"-"|"*"|"."|"<"|"="|"("|")"|"->" {
    printf("Scanning an operator\n");
}

[ \t\n]+ /* eat up whitespace */


{COMM}* /* eat up one-line comments */

.   {
    printf("Unrecognized character: %s at linenumber %d\n", yytext, yylineno );
    exit(1);
}

%%

这是我的Makefile:

all:    parser

parser: bison flex
    gcc bison.tab.c lex.yy.c -o parser -lfl

bison:  bison.y
    bison -d bison.y

flex:   flex.flex
    flex flex.flex

clean:
    rm bison.tab.h
    rm bison.tab.c
    rm lex.yy.c
    rm parser

一切都编译得很好,我在运行 make all 时没有收到任何错误。

这是我的测试文件

f = fun x -> end;

这是输出:

./parser < a0.0
Scanning an IDENT
Scanning an operator
Scanning a keyword
Scanning an IDENT
ERROR: syntax error at symbol "x" on line 1

因为 x 似乎被识别为 IDENT 规则应该是正确的,但我仍然遇到语法错误。

我觉得我错过了一些重要的东西,希望有人能帮助我。

提前致谢!

编辑:

我尝试去掉Lambda规则和测试文件中的IDENT,现在好像跑完了那行,但还是报错

错误:第 1 行符号“”处的语法错误

在 EOF 之后。

最佳答案

您的扫描器可以识别关键字(并打印出调试行,但请参见下文),但它不会向解析器报告任何内容。因此它们实际上被忽略了。

在您的 bison 定义文件中,您使用(例如)“fun”作为终端,但您没有为终端提供可在扫描仪中使用的名称。扫描器需要这个名称,因为它必须向解析器返回一个 token ID。

总而言之,您需要的是这样的东西:

在您的语法中,%% 之前:

token T_FUN "fun"
token T_IF "if"
token T_THEN "then"
 /* Etc. */

在您的扫描仪定义中:

fun { return T_FUN; }
if  { return T_IF; }
then { return T_THEN; }
 /* Etc. */

一些其他注意事项:

  1. 用于识别运算符的扫描器规则也无法返回任何内容,因此运算符也将被忽略。这显然是不可取的。 flex 和 bison 为单字符运算符提供了一种更简单的解决方案,即让字符成为它自己的标记 id。这避免了必须创建 token 名称。在解析器中,一个单引号字符代表一个token-id,它的值为该字符;这与 双引号 字符串完全不同,后者是已声明 token 名称的别名。所以你可以这样做:

    "=" { return '='; }
     /* Etc. */
    

    但一次完成所有单字符标记更容易:

    [;+*.<=()-]  { return yytext[0]; }
    

    最后使用默认规则更容易:

    . { return yytext[0]; }
    

    这将通过向解析器返回未知标记 ID 来处理无法识别的字符,这将导致语法错误。

    这不适用于“->”,因为它不是单个字符标记,必须以与关键字相同的方式处理。

  2. 如果您在创建扫描器时使用 -d 标志,Flex 将自动生成调试输出。这比插入您自己的调试打印输出要容易得多,因为您只需删除 -d 选项即可将其关闭。 (如果您不想更改 makefile 中的 flex 调用,则可以使用 %option debug。)它也更好,因为它提供了一致的信息,包括位置信息。

    <
  3. 一些小问题:

    • 模式[0-9][0-9]*可以更容易地写成[0-9]+
    • 注释模式 "//".* 不需要在末尾使用 $ 前瞻,因为 .* 将始终匹配最长的非换行符序列;因此,第一个不匹配的字符必须是换行符或 EOF。如果模式以 EOF 终止,则 $ 前瞻将不匹配,如果文件以没有换行符的注释结尾,则会导致奇怪的错误。
    • 使用 {COMM}* 没有意义,因为注释模式与终止注释的换行符不匹配,所以不可能有两个连续的注释匹配。但是不管怎么说,匹配一个注释和后面的换行后,flex会继续匹配后面的注释,所以{COMM}就足够了。 (就个人而言,我不会使用 COMM 缩写;恕我直言,它对可读性毫无帮助。)

关于c - 这个 Bison 语法有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29749452/

相关文章:

c - 如何使用带有 PGI 编译器的 C/OpenACC 声明全局动态数组

C程序、堆栈计算器

c++ - 来自字符串 vector 的 Bison 输入

c - 制作 : flex: Command not found

regex - 字符类中的 Flex 错误负范围

char总是使用flex改变

c - Linux 上的 GetTcpTable 等效项

c - 结构成员不可全局访问,为什么会这样?

compiler-construction - 将属性从 flex 返回给 bison

c++ - 使用机器学习自动更正自定义句子的应用程序 : how to begin?