3

我一直在使用正则表达式来检查一堆 Verilog 文件并提取某些语句。目前,正则表达式对此很好,但是,我开始意识到需要一个真正的解析器来处理嵌套结构,所以我正在研究 ocamllex/ocamlyacc。我想首先复制我在正则表达式实现中得到的内容,然后慢慢地在语法中添加更多内容。

现在我主要对提取模块声明和实例感兴趣。为了让这个问题更简短,让我们只看一下模块声明。

在 Verilog 中,模块声明如下所示:

module modmame ( ...other statements ) endmodule;

我当前的正则表达式实现只是检查是否有一个使用特定名称声明的模块(检查我感兴趣的名称列表 - 我不需要找到所有模块声明,只是具有特定名称的模块声明)。所以基本上,我得到了我想要解析的 Verilog 文件的每一行并进行这样的匹配(带有 Pythonish 和 Rubyish 元素的伪 OCaml):

foreach file in list_of_files:
  let found_mods = Hashtbl.create 17;
  open file 
  foreach line in file:
    foreach modname in modlist
    let mod_patt=  Str.regexp ("module"^space^"+"^modname^"\\("^space^"+\\|(\\)") in 
    try
      Str.search_forward (mod_patt) line 0
      found_mods[file] = modname; (* map filename to modname *)
    with Not_found -> ()

这很好用。模块声明可以出现在 Verilog 文件中的任何位置;我只是想知道该文件是否包含该特定声明,我不在乎该文件中可能还有什么。

我第一次尝试将其转换为 ocamllex/ocamlyacc:

verLexer.mll:

rule lex = parse
  | [' ' '\n' '\t']               { lex lexbuf }
  | ['0'-'9']+ as s               { INT(int_of_string s) }
  | '('                           { LPAREN }
  | ')'                           { RPAREN }
  | "module"                      { MODULE }
  | ['A'-'Z''a'-'z''0'-'9''_']+ as s  { IDENT(s) }
  | _                             { lex lexbuf }
  | eof 

verParser.mly:

%{ type expr =  Module of expr | Ident of string | Int of int %}

%token <int> INT
%token <string> IDENT
%token  LPAREN RPAREN MODULE EOF

%start expr1
%type <expr> expr1

%%

expr:   
| MODULE IDENT LPAREN    { Module( Ident $2) };

expr1:   
| expr EOF { $1 };

然后在 REPL 中尝试一下:

# #use "verLexer.ml" ;; 
# #use "verParser.ml" ;; 
# expr1 lex (Lexing.from_string "module foo (" ) ;;
- : expr = Module (Ident "foo")

太好了,它有效!

然而,一个真正的 Verilog 文件将不止包含一个模块声明:

# expr1 lex (Lexing.from_string "//comment\nmodule foo ( \nstuff" ) ;;
Exception: Failure "lexing: empty token".

我并不真正关心该模块定义之前或之后出现的内容,有没有办法只提取语法的那部分以确定Verilog文件包含'module foo('语句?是的,我意识到正则表达式是工作正常,但是,如上所述,我计划慢慢地发展这个语法并添加更多元素,正则表达式将开始分解。

编辑:我在 lex 规则中添加了一个 match any char:

      | _                             { lex lexbuf }

认为它会跳过迄今为止不匹配的任何字符,但这似乎不起作用:

 # expr1 lex (Lexing.from_string "fof\n module foo (\n" ) ;;
 Exception: Parsing.Parse_error.
4

2 回答 2

4

ocamlyacc第一个广告分钟:您应该考虑使用 François Pottier 的Menhir代替,它就像“yacc,升级版”,在所有方面都更好(更易读的语法,更强大的构造,更容易调试......),但仍然非常相似。它当然可以与ocamllex.

您的expr1规则只允许以规则开始和结束expr。您应该放大它以允许“东西”之前或之后expr。就像是:

junk:
| junk LPAREN
| junk RPAREN
| junk INT
| junk IDENT

expr1:
| junk expr junk EOF

请注意,此语法不允许module标记出现在junk节中。这样做会有点问题,因为它会使语法模棱两可(您要查找的结构可能嵌入到expror中junk)。如果您可能module在您正在查看的表单之外有一个标记,您应该考虑更改词法分析器以module ident (在单个标记中捕获整个感兴趣的结构,以便可以从语法中原子地匹配它。然而,从长远来看,拥有更细粒度的令牌可能会更好。

于 2012-08-22T05:35:48.700 回答
3

正如@gasche 所建议的那样,我尝试了menhir,并且已经获得了更好的结果。我将 verLexer.ml 更改为:

{
  open VerParser
}
rule lex = parse
  | [' ' '\n' '\t']               { lex lexbuf }
  | ['0'-'9']+ as s               { INT(int_of_string s) }
  | '('                           { LPAREN }
  | ')'                           { RPAREN }
  | "module"                      { MODULE }
  | ['A'-'Z''a'-'z''0'-'9''_']+ as s  { IDENT(s) }
  | _  as c                       { lex lexbuf }
  | eof                           { EOF }

并将 verParser.mly 更改为:

%{ type expr =  Module of expr | Ident of string | Int of int
           |Lparen | Rparen  | Junk %}

%token <int> INT
%token <string> IDENT
%token  LPAREN RPAREN MODULE EOF

%start expr1
%type <expr> expr1


%%

expr:
  | MODULE IDENT LPAREN    { Module( Ident $2) };

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

expr1:
| junk* expr junk* EOF { $2 };

这里的关键是 menhir 允许使用“*”对规则进行参数化,如上一行中我在规则中使用“垃圾*”表示匹配垃圾 0 次或更多次。ocamlyacc 似乎不允许这样做。

现在,当我在 REPL 中尝试它时,我得到:

# #use "verParser.ml" ;;
# #use "verLexer.ml" ;;
# expr1 lex (Lexing.from_string "module foo ( " ) ;;
- : expr = Module (Ident "foo")
# expr1 lex (Lexing.from_string "some module foo ( " ) ;;
- : expr = Module (Ident "foo")
# expr1 lex (Lexing.from_string "some module foo (\nbar " ) ;;
- : expr = Module (Ident "foo")
# expr1 lex (Lexing.from_string "some module foo (\n//comment " ) ;;
- : expr = Module (Ident "foo")
# expr1 lex (Lexing.from_string "some module fot foo (\n//comment " ) ;;
Exception: Error.
# expr1 lex (Lexing.from_string "some module foo (\n//comment " ) ;;

这似乎完全按照我的意愿工作。

于 2012-08-22T21:02:28.873 回答