在野牛(或与此相关的yacc)中,是否有语法定义的顺序?

时间:2018-09-26 22:20:39

标签: grammar bison yacc

我在Bisone文件中具有以下语法:

export class PostProvider {
  items: any = [];

  constructor(private db: AngularFireDatabase) {  }

  getPosts(following){
    following = Object.keys(following)

    for(let i = 0; i < following.length; i++){    
      this.db.list('posts/' + following[i]).valueChanges().subscribe(data => {
        for(let i = 0; i < data.length; i++){
          this.items.push(data[i]);
        }
      })
    }

    return this.items;
  }
}

当我尝试解析以下字符串item : "ITEM" t_name t_type v_storage t_prefix t_tag ';' ; t_name : [$_A-Za-z][$_A-Z0-9a-z]* ; t_type : "BYTE" | "WORD" | "LONG" | "QUAD" ; v_storage : %empty | "TYPEDEF" ; t_prefix : %empty | "PREFIX" t_name ; t_tag : %empty | "TAG" t_name ; 时,得到一个意外的'TYPEDEF'并且接受了“;”。是否需要做一些事情以允许指定任何顺序?我希望有一个简单的解决方案。否则,我需要做更多的工作。

1 个答案:

答案 0 :(得分:1)

不可能告诉野牛(或yacc)顺序无关紧要。规则是严格命令的。

因此,您有两种选择:

  1. 列出所有可能的订单。如果这样做,请注意由可选产品引起的歧义。您实际上需要列出所有订单和子集。呈指数级增长。

  2. 仅接受任何组件列表作为列表。这将接受重复的成分,因此,如果您需要的话,您需要在语义动作中加以捕捉。

第二个选项几乎总是您想要的那个。实现通常很简单,因为您将需要将组件存储在某个位置。只要该位置具有唯一值(例如NULL),表示“尚未设置”,则只需在设置该值之前对其进行测试。例如而不是问题中的那个):

%{
   #include <stdbool>
   enum Type { 
     TYPE_DEFAULT = 0, TYPE_BYTE, TYPE_WORD, TYPE_LONG, TYPE_QUAD
   };
   typedef struct Item Item;
   struct Item {
     const char *name;
     enum Type   type;
     int         storage; /* 0: unset, 1: TYPEDEF */
     const char *prefix;
     const char *tag;
  };
  // ...
  // Relies on the fact that NULL and 0 are converted to boolean 
  // false. Returns true if it's ok to do the set (i.e. thing
  // wasn't set).
  bool check_dup(bool already_set, const char* thing) {
    if (already_set) 
      fprintf(stderr, "Duplicate %s ignored at line %d\n", thing, yylineno);
    return !already_set;
  }
%}

%union {
   const char *str;
   Item  *item;
   // ...
}

%type <item> item item-def
%token <str> NAME STRING

%%
/* Many of the actions below depend on $$ having been set to $1.
 * If you use a template which doesn't provide that guarantee, you
 * will have to add $$ = $1; to some actions.
 */
item: item-def { /* Do whatever is necessary to finalise $1 */ }
item-def
    : "ITEM" NAME
               { $$ = calloc(1, sizeof *$$); $$->name = $2; }
    | item-def "BYTE"
               { if (check_dup($$->type, "type") $$->type = TYPE_BYTE; }
    | item-def "WORD"
               { if (check_dup($$->type, "type") $$->type = TYPE_WORD; }
    | item-def "LONG"
               { if (check_dup($$->type, "type") $$->type = TYPE_LONG; }
    | item-def "QUAD"
               { if (check_dup($$->type, "type") $$->type = TYPE_QUAD; }
    | item-def "TYPEDEF"
               { if (check_dup($$->storage, "storage") $$->storage = 1; }
    | item-def "PREFIX" STRING
               { if (check_dup($$->prefix, "prefix") $$->prefix = $3; }
    | item-def "TAG" STRING
               { if (check_dup($$->tag, "tag") $$->tag = $3; }

您可以将所有这些item-def作品分成类似以下内容:

item-def: "ITEM" NAME   { /* ... */ }
        | item-def item-option
item-option: type | storage | prefix | tag

但是在操作中,您需要获得item对象,这不是选项生产的一部分。您可以使用Bison功能来做到这一点,该功能可让您查看解析器堆栈:

prefix: "PREFIX" STRING { if (check_dup($<item>0->prefix, "prefix")
                            $<item>0->prefix = $2; }

在这种情况下,$0prefix之前的内容,item-option之前的内容是item-def。请参见《野牛手册》中this section的结尾,该手册将这种做法描述为“危险”。它还需要您明确指定标签,因为bison不会进行必要的语法分析来验证$0的使用,从而可以识别其类型。