Можно ли создать очень разрешительную грамматику, используя Menhir?

Я пытаюсь разобрать некоторые фрагменты Verilog - меня в первую очередь интересует извлечение определений и экземпляров модулей.

В Verilog модуль определяется так:

module foo ( ... ) endmodule;

И модуль создается одним из двух возможных способов:

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

На данный момент меня интересует только имя определенного или созданного модуля; 'foo' в обоих случаях выше.

Учитывая эту грамматику менгира (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 };

Когда я пробую это в интерпретаторе менгира, он отлично работает, извлекая экземпляр модуля:

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

... и, конечно же, в реальном файле verilog будут идентификаторы до этих определений.

Я пытаюсь не полностью указывать грамматику Verilog, вместо этого я хочу создавать грамматику медленно и постепенно, чтобы в конечном итоге анализировать все больше и больше языка.

Если я добавлю IDENT в правило нежелательной почты, это решит описанную выше проблему, но тогда правило создания экземпляра модуля не будет работать, потому что теперь правило нежелательной почты захватывает IDENT.

Можно ли создать очень разрешающее правило, которое будет обходить вещи, которые я не хочу сопоставлять, или обычно требуется, чтобы вы создали полную грамматику, чтобы действительно сделать что-то подобное?

Можно ли создать правило, которое позволит мне сопоставить:

MODULE IDENT LPAREN stuff* RPAREN ENDMODULE

где "stuff*" изначально соответствует всему, кроме RPAREN?

Что-то типа :

stuff: 
  | !RPAREN { } ;

В прошлом я использовал парсеры PEG, которые допускали такие конструкции.


person aneccodeal    schedule 24.08.2012    source источник
comment
Просто любопытно, есть ли причина, по которой Verilog-Perl или грамматика Verilog ANTLR не подходят?   -  person    schedule 25.08.2012
comment
@ Adam12: ANTLR использует Java. Verilog-Perl может работать, но я думаю, что нам понадобится что-то более быстрое, чем Perl (для обработки многих тысяч файлов Verilog). В конечном итоге может перейти на C++ и Boost::spirit, поскольку синтаксические анализаторы PEG можно настроить для более легкого анализа с неполной грамматикой.   -  person aneccodeal    schedule 25.08.2012
comment
Если вы считаете, что PEG лучше подходят для этой работы (у меня нет опыта использования LR-грамматик для частичного/нечеткого разбора), вас может заинтересовать Aurochs, генератор синтаксического анализатора PEG на платформе OCaml.   -  person gasche    schedule 25.08.2012
comment
Это правда, что большинство документов ориентированы на Java, но есть цель C и цель C++ для ANTLR. Хотя у цели C есть некоторые проблемы с потреблением памяти, мои собственные тесты с грамматикой 1364-2005 показали производительность примерно в 40 раз выше, чем у цели Java.   -  person    schedule 25.08.2012
comment
@gasche: Спасибо, я использовал зубра в прошлом, однако я не уверен, что он будет достаточно быстрым для моих нужд (тысячи файлов для просмотра) - также не совсем уверен, как звонить из OCaml. Я взглянул на peg/leg и смог создать с его помощью грамматику PEG, которая, кажется, работает (она генерирует синтаксический анализатор C, поэтому он должен быть очень быстрым).   -  person aneccodeal    schedule 27.08.2012


Ответы (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, который должен быть довольно быстрым.

person aneccodeal    schedule 27.08.2012