#P16968. [SGU418] Deducing Grammar
[SGU418] Deducing Grammar
题目描述
给定一份具有固定框架的 Pascal 自顶向下解析器程序。解析器的主体包含若干个“解析过程”(parsing routine),每个解析过程对应文法中的一个非终结符。你的任务是根据程序结构,机械地恢复出它所对应的 BNF 文法。
解析器整体结构如下:
Program Parser;
Procedure Skip; Forward;
Function Peek:Char; Forward;
Procedure Error; Forward;
<所有解析过程的 Forward 声明>
<所有解析过程的定义>
Var
St:String;
Pos:Integer;
...
其中解析过程定义形如:
Procedure <name>;
Begin
<segment>
<segment>
...
End;
每个过程体至少包含一个 segment。
1. 无条件段
无条件段是对另一个解析过程的调用:
<name>;
它在产生式中贡献非终结符 <name>。
2. 条件段
条件段具有如下形式:
If Peek=<character> Then Begin
Skip;
<segment>...
End Else If Peek=<character> Then Begin
Skip;
<segment>...
End Else If ...
End;
也可以只有一个 If 而没有 Else。
最后一个 End; 还可以写成:
End Else Error;
表示除前面列出的分支之外,其他情况都会失败。
每个 <character> 都是 ASCII 码在 之间、且不是单引号 ' 的单个字符,并用单引号包围,例如 'a'。
在每个条件分支中,一旦 Peek 匹配,就一定立即执行一次 Skip;,并且程序中只有这里会调用 Skip。
条件分支内部可以没有其他 segment,也可以继续嵌套条件段。
3. 标识符、大小写和空白
- 解析过程名只由英文字母组成,长度不超过 20;
- 名字大小写不敏感;
- 不同解析过程名字不同;
- 解析过程名不会与 Pascal 关键字或内部函数
Skip、Peek、Error重名; - 必须存在一个名为
Parse的解析过程; - 标识符和关键字的大小写可以任意;
- 除了两个单词之间必须至少有一个空白、单词内部以及字符串字面量内部不能插入空白外,其余位置可以任意加入或删除空白。
文法转换规则
请严格按照以下规则转换,而不要判断得到的文法是否真的与解析器接受的语言完全等价。
- 每个解析过程对应一个非终结符,输出时名字全部转成小写,例如过程
Parse对应<parse>; - 终结符集合为 ASCII 码 中除单引号外的字符;
- 对于一个解析过程,考虑其过程体中所有
If的所有可能求值方式;每一种不会调用Error的执行方式都对应一条产生式; - 即使某些执行方式实际上不可能发生(例如同一个条件链里出现重复的
Peek='x'),也必须把它们分别当作产生式处理; - 条件分支匹配一个字符并执行
Skip时,该字符成为一个终结符; - 无条件调用过程时,加入相应非终结符;
- 若条件链结尾没有
Else Error,则“所有条件均不满足、什么也不做”也是一个合法选择,因此会产生空串分支; - 相邻的终结符必须合并为一个字符串字面量,例如应输出
'AB',而不是'A''B'; - 同一非终结符的所有产生式按字典序排序;
- 所有非终结符对应的整行也按字典序排序;
- 开始符号为
Parse。
输入格式
输入是一份满足上述限制的 Pascal 解析器源代码。
输入文件大小不超过 字节,每个单词长度不超过 。
输出格式
输出底层文法的 BNF 形式。
每个解析过程输出一行,格式类似:
<name>::=production1|production2|...
输出中除每行末尾的换行符外不能出现任何空白字符。
保证正确输出总长度不超过 字节。
样例
Program Parser;
Procedure Skip; Forward;
Function Peek:Char; Forward;
Procedure Error; Forward;
Procedure Parse; Forward;
Procedure Addend; Forward;
Procedure Term; Forward;
Procedure Number; Forward;
Procedure Term;
Begin
If Peek='0' Then Begin
Skip;
Number;
End Else If Peek='1' Then Begin
Skip;
Number;
End Else If Peek='(' Then Begin
Skip;
Parse;
If Peek=')' Then Begin
Skip;
End Else Error;
End Else If Peek='P' Then Begin
Skip;
If Peek='I' Then Begin
Skip;
End Else If Peek='E' Then Begin
Skip;
End Else Error;
End Else Error;
End;
Procedure Number;
Begin
If Peek='0' Then Begin
Skip;
Number;
End Else If Peek='1' Then Begin
Skip;
Number;
End;
End;
Procedure Addend;
Begin
Term;
If Peek='*' Then Begin
Skip;
Addend;
End Else If Peek='/' Then Begin
Skip;
Addend;
End;
End;
Procedure Parse;
Begin
Addend;
If Peek='+' Then Begin
Skip;
Parse;
End Else If Peek='-' Then Begin
Skip;
Parse;
End;
End;
Var
St:String;
Pos:Integer;
Procedure Error;
Begin
WriteLn('NO');
Halt;
End;
Procedure Skip;
Begin
Inc(Pos);
If Pos>Length(St) Then Error;
End;
Function Peek:Char;
Begin
Peek:=St[Pos];
End;
Begin
ReadLn(St);
St:=St+'#';
Pos:=1;
Parse;
If Pos=Length(St) Then WriteLn('YES') Else WriteLn('NO');
End.
<addend>::=<term>|<term>'*'<addend>|<term>'/'<addend>
<number>::=|'0'<number>|'1'<number>
<parse>::=<addend>|<addend>'+'<parse>|<addend>'-'<parse>
<term>::='('<parse>')'|'0'<number>|'1'<number>|'PE'|'PI'