是否有可能使用Menhir创建一个非常宽松的语法?

时间:2012-08-24 16:21:59

标签: parsing ocaml peg menhir

我正在尝试解析Verilog的一些零碎 - 我主要对提取模块定义和实例化感兴趣。

在verilog中,模块定义如下:

module foo ( ... ) endmodule;

模块以两种不同的方式之一实例化:

foo fooinst ( ... );
foo #( ...list of params... ) fooinst ( .... );

此时我只对找到已定义或实例化模块的名称感兴趣;在上述两种情况下都是'foo'。

鉴于这个menhir语法(verParser.mly):

%{ 

  type expr =   Module of expr 
           | ModInst of expr
           | Ident of string 
           | Int of int
           | Lparen 
           | Rparen  
           | Junk 
           | ExprList of expr list

%}

%token <string> INT
%token <string> IDENT
%token  LPAREN RPAREN MODULE TICK OTHER HASH EOF



%start expr2
%type <expr> mod_expr
%type <expr> expr1
%type <expr list> expr2

%%


mod_expr:
  | MODULE IDENT LPAREN    { Module ( Ident $2) }
  | IDENT IDENT LPAREN     { ModInst ( Ident $1) }
  | IDENT HASH  LPAREN     { ModInst ( Ident $1) };

junk: 
  |  LPAREN {  }
  |  RPAREN {  }
  |  HASH { } 
  |  INT {  };

expr1:
  | junk* mod_expr junk* { $2 } ;

expr2: 
  | expr1* EOF { $1 };

当我在menhir解释器中尝试这个时,它可以很好地提取模块实例:

MODULE IDENT LPAREN
ACCEPT
[expr2:
  [list(expr1):
    [expr1:
      [list(junk):]
      [mod_expr: MODULE IDENT LPAREN]
      [list(junk):]
    ]
    [list(expr1):]
  ]
  EOF
]

它适用于单个模块实例化:

IDENT IDENT LPAREN
ACCEPT
[expr2:
  [list(expr1):
    [expr1:
      [list(junk):]
      [mod_expr: IDENT IDENT LPAREN]
      [list(junk):]
    ]
    [list(expr1):]
  ]
  EOF
]

但是,当然,如果在任何这些之前出现IDENT,它将拒绝:

IDENT MODULE IDENT LPAREN IDENT IDENT LPAREN
REJECT

...当然,在这些defs之前,实际的verilog文件中会有标识符。

我试图不必完全指定一个Verilog语法,而是我想慢慢地逐步构建语法,最终解析越来越多的语言。

如果我将IDENT添加到垃圾规则中,那就解决了上面的问题,但是模块实例化规则不起作用,因为现在垃圾规则正在捕获IDENT。

是否有可能创建一个非常宽松的规则来绕过我不想匹配的东西,或者通常要求你必须创建一个完整的语法才能真正做到这样的事情?

是否可以创建一个让我匹配的规则:

MODULE IDENT LPAREN stuff* RPAREN ENDMODULE

其中“stuff *”最初匹配除RPAREN之外的所有内容?

类似的东西:

stuff: 
  | !RPAREN { } ;

我过去曾使用PEG解析器,这样可以使用类似的结构。

1 个答案:

答案 0 :(得分:1)

我认为PEG更适合于宽容,非详尽的语法。看看peg/leg,并且能够很快地将一个腿部语法拼凑起来,做我需要做的事情:

start   = ( comment | mod_match | char)

line    = < (( '\n' '\r'* ) | ( '\r' '\n'* )) > { lines++;  chars += yyleng; }
module_decl =    module  modnm:ident lparen ( !rparen . )* rparen   {  chars += yyleng; printf("Module    decl: <%s>\n",yytext);}
module_inst = modinstname:ident ident lparen { chars += yyleng; printf("Module Inst: <%s>\n",yytext);}
         |modinstname:ident hash lparen { chars += yyleng; printf("Module Inst: <%s>\n",yytext);} 

mod_match = ( module_decl | module_inst ) 
module     =  'module' ws                { modules++;    chars +=yyleng; printf("Module: <%s>\n", yytext);  } 
endmodule  = 'endmodule' ws              { endmodules++; chars +=yyleng; printf("EndModule: <%s>\n", yytext); } 

kwd = (module|endmodule)
ident   = !kwd<[a-zA-z][a-zA-Z0-9_]+>-    { words++;  chars += yyleng;  printf("Ident: <%s>\n", yytext);  }

char    = .                 { chars++; }
lparen  =  '(' - 
rparen  =  ')' - 
hash    =  '#' 

- =  ( space | comment )*
ws = space+
space = ' ' | '\t' | EOL
comment = '//' ( !EOL .)* EOL
          | '/*' ( !'*/' .)* '*/' 
EOF = !.
EOL = '\r\n' | '\n' | '\r' 

Aurochs也可能是一个选项,但我担心Aurochs生成的解析器的速度和内存使用情况。 peg / leg在C中生成一个解析器应该非常快。