编译原理课程实验 语法分析器
概述
你需要完成一个 PL/0 语言的编译器。这个实验分为若干部分。在这个部分中,你需要完成 PL/0 语言的语法分析器。
你应当已经正确完成了词法分析器。如果没有,请先完成词法分析器的实验。
词法分析器会向语法分析器提供词语流。你需要以合适的方式对词语流进行分析,并生成一颗语法树,或者宣告语法错误。
PL/0 语言的语法可以用以下上下文无关文法描述:
<程序>→<分程序>.
<分程序>→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
<常量说明部分> → CONST<常量定义>{ ,<常量定义>};
<常量定义> → <标识符>=<无符号整数>
<无符号整数> → <数字>{<数字>}
<变量说明部分> → VAR<标识符>{ ,<标识符>};
<标识符> → <字母>{<字母>|<数字>}
<过程说明部分> → <过程首部><分程序>;{<过程说明部分>}
<过程首部> → PROCEDURE <标识符>;
<语句> → <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空语句>
<赋值语句> → <标识符>:=<表达式>
<复合语句> → BEGIN<语句>{ ;<语句>} END
<条件> → <表达式><关系运算符><表达式>|ODD<表达式>
<表达式> → [+|-]<项>{<加减运算符><项>}
<项> → <因子>{<乘除运算符><因子>}
<因子> → <标识符>|<无符号整数>|(<表达式>)
<加减运算符> → +|-
<乘除运算符> → *|/
<关系运算符> → =|#|<|<=|>|>=
<条件语句> → IF<条件>THEN<语句>
<过程调用语句> → CALL<标识符>
<当型循环语句> → WHILE<条件>DO<语句>
<读语句> → READ(<标识符>{ ,<标识符>})
<写语句> → WRITE(<标识符>{,<标识符>})
<字母> → A|B|C…X|Y|Z
<数字> → 0|1|2…7|8|9
<空语句> → epsilon
上表中 epsilon 指ϵ,即空
特别的,PL/0 语言允许嵌套定义函数(即,上述文法第 8 行中,由过程说明部分推导出的分程序再次推导出过程说明部分),但嵌套不得超过三层。
例如
procedure f1;
procedure f2;
procedure f3;
begin
end;
begin
end;
begin
end;
begin end.
是允许的,而
procedure f1;
procedure f2;
procedure f3;
procedure f4;
begin
end;
begin
end;
begin
end;
begin
end;
begin end.
则是一个语法错误的程序。
Tips:请注意和
procedure f1;
begin
end;
procedure f2;
begin
end;
procedure f3;
begin
end;
的区别。
对 PL/0 程序进行语法分析后,如果语法正确,应当生成以 < 程序 > 为根节点的抽象语法树。
你应该给出的抽象语法树应当对应规范推导所生成的抽象语法树。这种情况下,对应的答案是唯一的。
我们建议你使用递归下降子程序法进行语法分析。
在本程序中,请忽略语义错误,诸如重复声明的变量、使用未声明的变量等。在语法分析阶段,这些程序暂且还是正确的。
请注意你编写程序的可扩展性。之后的实验需要你对你编写的语法分析程序进行一定的修改。
树的括号表示形式
根据下列三条规则生成树的括号表示形式
- 如果树T为空,那么它的括号表示形式为空
- 如果树T仅包含一个节点,那么它的括号表示形式为节点本身
- 如果树T由根节点R和若干子树T1,T2...Tn组成,那么其括号表示形式为R(T1的括号表示形式,T2的括号表示形式, ... ,Tn的括号表示形式)
例如
的括号表示形式为
3(5(6,2(7,4)),1(0,8))
括号表示形式中应当不含空格。
节点名称约定
为评测方便考虑,语法分析树的各节点应当遵循以下命名规则:
-
如果该节点是(,即左括号,该节点应当输出为 LP
-
如果该节点是),即右括号,该节点应当输出为 RP
-
如果该节点是,,即逗号,该节点应当输出为 COMMA
-
如果该节点是其他的叶子节点(对应词法分析器中的某一个词语),那么其名称为其本身
-
如果该节点不是一个叶子节点,则遵循下表进行替换输出
上下文无关文法中的节点 | 对应的替换词语 |
---|---|
程序 | PROGRAM |
分程序 | SUBPROG |
常量说明部分 | CONSTANTDECLARE |
常量定义 | CONSTANTDEFINE |
无符号整数 | < 这是一个叶子节点,用其本身替代 > |
变量说明部分 | VARIABLEDECLARE |
标识符 | < 这是一个叶子节点,用其本身替代 > |
过程说明部分 | PROCEDUREDECLARE |
过程首部 | PROCEDUREHEAD |
语句 | SENTENCE |
赋值语句 | ASSIGNMENT |
复合语句 | COMBINED |
条件 | CONDITION |
表达式 | EXPRESSION |
项 | ITEM |
因子 | FACTOR |
加减运算符 | < 这是一个叶子节点,用其本身替代 > |
乘除运算符 | < 这是一个叶子节点,用其本身替代 > |
关系运算符 | < 这是一个叶子节点,用其本身替代 > |
条件语句 | IFSENTENCE |
过程调用语句 | CALLSENTENCE |
当型循环语句 | WHILESENTENCE |
读语句 | READSENTENCE |
写语句 | WRITESENTENCE |
空语句 | EMPTY |
提交程序
考虑到对于编译器这样的大型工程将代码分布在多个文件中是一个很实用的做法,我们会采用多文件编译/评测。你需要提供一个构建脚本来告知我们的评测系统如何构建你的答案可执行文件。
你需要把编译所需的代码以及一个名为 build.sh 的构建脚本压缩在 answer.zip 压缩包中(压缩包不含子文件夹),文件结构应该类似下图
answer.zip
|--build.sh
|--source1.xxx
|--source2.xxx
...
上图中的 source 开头的文件是你的源代码,不必遵循 source 开头的命名格式,可以任意命名。其中,.xxx 是对应语言的后缀,如.cpp 对应 c++,.java 对应 java,.c 对应 C 语言。
在执行 build.sh 后,应该构造出一个名为 Main 的可执行文件(对于 C/C++)或可被 java Main 执行的 Java 类文件(对于 Java),区分大小写。
这里提供示例 build.sh。
下文中的 sourcex 对应你的源文件,可以任意更名/增加/删除。你也可以自行编写自己的 build.sh 脚本
C
#! /bin/bash
gcc -c source1.c -o source1.o -O2
gcc -c source2.c -o source2.o -O2
gcc source1.o source2.o -o Main -lm
C++
#! /bin/bash
g++ -c source1.cpp -o source1.o -O2
g++ -c source2.cpp -o source2.o -O2
g++ source1.o source2.o -o Main -lm
Java
#! /bin/bash
javac *.java
样例
样例 1
输入
CONST C=0;
VAR A,B;
BEGIN
READ(B,A);
A:=B+C;
WRITE(A);
END.
输出
PROGRAM(SUBPROG(CONSTANTDECLARE(CONST,CONSTANTDEFINE(C,=,0),;),VARIABLEDECLARE(VAR,A,COMMA,B,;),SENTENCE(COMBINED(BEGIN,SENTENCE(READSENTENCE(READ,LP,B,COMMA,A,RP)),;,SENTENCE(ASSIGNMENT(A,:=,EXPRESSION(ITEM(FACTOR(B)),+,ITEM(FACTOR(C))))),;,SENTENCE(WRITESENTENCE(WRITE,LP,A,RP)),;,SENTENCE(EMPTY),END))),.)
解释
一个比较适合人类阅读的,经过处理的输出序列是
PROGRAM(
SUBPROG(
CONSTANTDECLARE(
CONST,
CONSTANTDEFINE(
C,
=,
0
),
;
),
VARIABLEDECLARE(
VAR,
A,
COMMA,
B,
;
),
SENTENCE(
COMBINED(
BEGIN,
SENTENCE(
READSENTENCE(
READ,
LP,
B,
COMMA,
A,
RP
)
),
;,
SENTENCE(
ASSIGNMENT(
A,
:=,
EXPRESSION(
ITEM(
FACTOR(
B
)
),
+,
ITEM(
FACTOR(
C
)
)
)
)
),
;,
SENTENCE(
WRITESENTENCE(
WRITE,
LP,
A,
RP
)
),
;,
SENTENCE(
EMPTY
),
END
)
)
),
.
)
样例 2
输入
1=2=3=4=5
输出
Syntax Error
样例 3
输入
VAR A,B;
BEGIN
A : = B;
END.
输出
Lexical Error
样例 4
输入
VAR A,B;
BEGIN
READ(A);
B := A;
WRITE(B)
END.
PROCEDURE APPENDIX
BEGIN
;
END
输出
Syntax Error
main.cpp
#include <bits/stdc++.h>
#include "PLPLO.cpp"
//#include "PLLexiacl.cpp"
using namespace std;
void output(Tree *root){
cout<<root->str;
int m=root->child.size();
if(m!=0)
{
cout<<"(";
for(int i=0;i<m;i++){
output(root->child[i]);
if(i+1<m) cout<<",";
}
cout<<")";
}
}
int main(){
freopen("program.txt","r",stdin);
freopen("program_out.txt","w",stdout);
string str;
while(getline(cin,str)){
is_no(str);
change(str);
}
if(isno) cout<<"Lexical Error";
else{
// for(int j=0;j<result.size();j++) cout<<result[j].str<<endl;
Tree *root=Parser();
if(is_error==1) cout<<"Syntax Error";
else output(root);
}
}
PLLexiacl.cpp
#include <bits/stdc++.h>
using namespace std;
#define is_number(a) ((a)>='0'&&(a)<='9')
#define is_smallidentifier(a) ((a)>='a'&&(a)<='z')
#define is_bigidentifier(a) ((a)>='A'&&(a)<='Z')
#define is_twospecial(a) (((a)=='>'||(a)=='<'||(a)==':')?1:0)
bool isno=0;
struct Word{
string type;
string str;
Word(string t,string s):type(t),str(s){};
};
vector<string> a;
vector<Word> result;
/* bool is_number(char a){
return a>='0'&&a<='9';
}
bool is_smallidentifier(char a){
return a>='a'&&a<='z';
}
bool is_bigidentifier(char a){
return a>='A'&&a<='Z';
} */
/* bool is_twospecial(char a){
if(a=='>'||a=='<'||a==':') return 1;
else return 0;
} */
bool is_special(char a){
if(a=='='||a=='+'||a=='-'||a=='*'||a=='/'||a=='#')
return 1;
if(a==';'||a==','||a=='.'||a=='('||a==')')
return 1;
else return 0;
}
bool is_special2(string a){
if(a=="CONST"||a=="VAR"||a=="PROCEDURE"||a=="BEGIN"||a=="END"
||a=="ODD"||a=="IF"||a=="THEN"||a=="CALL"||a=="WHILE"||a=="DO"||a=="READ"||a=="WRITE")
return 1;
if(a==">="||a=="<="||a==":="||a==">"||a=="<") return 1;
else return 0;
}
void print(string s,bool is1,bool is2,int m){
if(is_special2(s)){
a.push_back(s);
result.emplace_back("Special",s);
s.clear();
is1=0;is2=0;
}
if(is1){
string st="NUMBER "+to_string(m);
a.push_back(st);
result.emplace_back("NUMBER",to_string(m));
s.clear(),is1=0;
}
if(is2){
string st="IDENTIFIER "+s;
a.push_back(st);
result.emplace_back("IDENTIFIER",s);
s.clear(),is2=0;
}
}
bool is_no(string s){
string str;
bool isnum=0,isdir=0;
for(int i=0;i<s.size();i++){
if(is_number(s[i])){
isnum=1;
str+=s[i];
}
else if(is_bigidentifier(s[i])) {
if(isnum) {
isno=1;
return 1;
}
isdir=1;
str+=s[i];
}
else if(is_smallidentifier(s[i])) {
if(isnum) {
isno=1;
return 1;
}
isdir=1;
str+=s[i]-'a'+'A';
}
else if(s[i]==':'&&s[i+1]!='=') {
isno=1;
str.clear(),isnum=0,isdir=0;
return 1;
}
else if(s[i]==' '||s[i]==' '){
str.clear(),isnum=0,isdir=0;
}
else if(is_special(s[i])||is_twospecial(s[i])) {
str.clear(),isnum=0,isdir=0;
}
else if(is_special2(str)) {
str.clear(),isnum=0,isdir=0;
}
else {
isno=1;
str.clear(),isnum=0,isdir=0;
return 1;
}
if(str.size()>10) {
isno=1;
return 1;
}
}
str.clear();
return 0;
}
void change(string s){
string str;
int l=s.size();
bool is1=0,is2=0;
int m=0;
for(int i=0;i<s.size();i++){
if((s[i]==' '||s[i]==' ')) {
if(is2) print(str,is1,is2,m),str.clear(),is2=0;
if(is1) print(str,is1,is2,m),str.clear(),is1=0,m=0;
}
if(is_special(s[i])){
if(is2) print(str,is1,is2,m),str.clear(),is2=0;
if(is1) print(str,is1,is2,m),str.clear(),is1=0,m=0;
string st;
st+=s[i];
a.push_back(st);
result.emplace_back("Special",st);
}
if(is_twospecial(s[i])){
if(is2)print(str,is1,is2,m),str.clear(),is2=0;
if(is1)print(str,is1,is2,m),str.clear(),is1=0,m=0;
if(s[i+1]=='=')
{
string st1;
st1+=s[i];
st1+=s[i+1];
a.push_back(st1);
result.emplace_back("Special",st1);
i++;
}
if((s[i]=='>'&&s[i+1]!='=')||(s[i]=='<'&&s[i+1]!='=')) {
string st2;
st2+=s[i];
a.push_back(st2);
result.emplace_back("Special",st2);
}
}
if(is_number(s[i])){
is1=1;
if(is2) {
str+=s[i];
is1=0;
}
else{
m*=10;
m+=s[i]-'0';
}
}
if(is_bigidentifier(s[i])) {
if(is1) print(str,is1,is2,m),str.clear(),is1=0;
is2=1;
str+=s[i];
}
if(is_smallidentifier(s[i])) {
if(is1) print(str,is1,is2,m),str.clear(),is1=0;
is2=1;
str+=s[i]-'a'+'A';
}
}
if(is2) print(str,is1,is2,m),str.clear(),is2=0;
if(is1) print(str,is1,is2,m),str.clear(),is1=0;
}
PLPLO.cpp
#include <bits/stdc++.h>
#include "PLLexiacl.cpp"
using namespace std;
class Tree{
public:
Tree* root;
vector<Tree *> child;
string str;
public:
Tree(string ss){
this->str=ss,this->root=NULL,this->child.clear();
}
Tree *newNode(string s){
Tree* Node=new Tree(s);
Node->root=this;
this->child.push_back(Node);
return Node;
}
};
Tree* Parser();
void Subprog(Tree *Node);
void Const(Tree *Node);
void Var(Tree *Node);
void Procedure(Tree *Node);
void ProcedureHead(Tree *Node);
void Sentence(Tree *Node);
void Assignment(Tree *Node);
//void Epsilon(Tree *Node);
void Combined(Tree *Node);
//void Writesentence(Tree *Node);
//void Readsentence(Tree *Node);
//void Callsentence(Tree *Node);
void Whilesectence(Tree *Node);
void Condition(Tree *Node);
void Expression(Tree *Node);
void Factor(Tree *Node);
void Item(Tree *Node);
void IF(Tree *Node);
void Empty(Tree *Node);
bool is_error=0;
int i=0;
int k=0;
Tree* Parser(){
if(result.size()==0) {
is_error=1;
return NULL;
}
Tree *ROOT=new Tree("PROGRAM");
Subprog(ROOT);
if(result[i].str=="."){
ROOT->newNode("."),i++;
}
else is_error=1;
if(i<result.size()){
is_error=1;
}
return ROOT;
}
void Subprog(Tree *Node){
Tree *newchild=Node->newNode("SUBPROG");
if(result[i].str=="CONST") Const(newchild);
if(result[i].str=="VAR") Var(newchild);
if(result[i].str=="PROCEDURE") Procedure(newchild);
Sentence(newchild);
}
void Const(Tree *Node){
Tree *thisNode=Node->newNode("CONSTANTDECLARE");
thisNode->newNode("CONST");
i++;
if(result[i].type=="IDENTIFIER"&&result[i+1].str=="="&&result[i+2].type=="NUMBER") {
Tree* newchild=thisNode->newNode("CONSTANTDEFINE");
newchild->newNode(result[i].str);
newchild->newNode(result[i+1].str);
newchild->newNode(result[i+2].str);
i+=3;
}
else{
is_error=1;
return;
}
while(result[i].str==","){
thisNode->newNode("COMMA");
i++;
if(result[i].type=="IDENTIFIER"&&result[i+1].str=="="&&result[i+2].type=="NUMBER") {
Tree* newchild=thisNode->newNode("CONSTANTDEFINE");
newchild->newNode(result[i].str);
newchild->newNode(result[i+1].str);
newchild->newNode(result[i+2].str);
i+=3;
}
else {
is_error=1;
return;
}
}
if(result[i].str==";") thisNode->newNode(";"),i++;
else {
is_error=1;
return;
}
}
void Var(Tree *Node){
Tree *thisNode=Node->newNode("VARIABLEDECLARE");
thisNode->newNode("VAR"),i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);
i++;
}
else{
is_error=1;
return;
}
while(result[i].str==","){
thisNode->newNode("COMMA");i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);
i++;
}
else{
is_error=1;
return;
}
}
if(result[i].str==";"){
thisNode->newNode(";"),i++;
}
else{
is_error=1;
return;
}
}
void ProcedureHead(Tree *Node){
Tree *Head=Node->newNode("PROCEDUREHEAD");
Head->newNode("PROCEDURE");i++;
if(result[i].type=="IDENTIFIER"){
Head->newNode(result[i].str);
i++;
}
else{
is_error=1;
return;
}
if(result[i].str==";"){
Head->newNode(";");
i++;
}
else{
is_error=1;
return;
}
}
void Procedure(Tree *Node){
Tree *thisNode=Node->newNode("PROCEDUREDECLARE");
k++;
if(k>3){
is_error=1;
return;
}
ProcedureHead(thisNode);
Subprog(thisNode);
k--;
if(result[i].str==";"){
thisNode->newNode(";");
i++;
}else{
is_error=1;
return;
}
while(result[i].str=="PROCEDURE"){
Procedure(thisNode);
}
}
void Sentence(Tree *Node){
Tree *newNode=Node->newNode("SENTENCE");
if(i+1<result.size()&&result[i+1].str==":=") Assignment(newNode);
else if(result[i].str=="IF") IF(newNode);
else if(result[i].str=="WHILE") Whilesectence(newNode);
else if(result[i].str=="CALL") Callsentence(newNode);
else if(result[i].str=="READ") Readsentence(newNode);
else if(result[i].str=="WRITE") Writesentence(newNode);
else if(result[i].str=="BEGIN") Combined(newNode) ;
else Empty(newNode);
}
void Empty(Tree *Node){
Node->newNode("EMPTY");
}
void Assignment(Tree *Node){
Tree *thisNode=Node->newNode("ASSIGNMENT");
if(result[i].type=="IDENTIFIER"&&result[i+1].str==":="){
thisNode->newNode(result[i].str);
thisNode->newNode(result[i+1].str),i+=2;
}
else{
is_error=1;
return;
}
Expression(thisNode);
}
void Expression(Tree *Node){
Tree *newchild=Node->newNode("EXPRESSION");
if(result[i].str=="+"||result[i].str=="-"){
newchild->newNode(result[i].str),i++;
}
Item(newchild);
while(result[i].str=="+"||result[i].str=="-"){
newchild->newNode(result[i].str),i++;
Item(newchild);
}
}
void Item(Tree *Node){
Tree *newchild=Node->newNode("ITEM");
Factor(newchild);
while(result[i].str=="*"||result[i].str=="/"){
newchild->newNode(result[i].str);
i++;
Factor(newchild);
}
}
void Factor(Tree *Node){
Tree *newchild=Node->newNode("FACTOR");
if(result[i].type=="IDENTIFIER"||result[i].type=="NUMBER"){
newchild->newNode(result[i].str),i++;
return;
}
else if(result[i].str=="("){
newchild->newNode("LP");
i++;
Expression(newchild);
if(result[i].str==")"){
newchild->newNode("RP");
i++;
return;
}
else{
is_error=1;
return;
}
}
else{
is_error=1;
return;
}
}
void Condition(Tree *Node){
Tree *child=Node->newNode("CONDITION");
if(result[i].str=="ODD"){
child->newNode("ODD");
i++;
Expression(child);
}
else{
Expression(child);
if(result[i].str=="="||result[i].str=="#"||result[i].str=="<"||result[i].str=="<="||result[i].str==">"||result[i].str==">="){
child->newNode(result[i].str);i++;
}
else{
is_error=1;
return;
}
Expression(child);
}
}
void IF(Tree* Node){
Tree *thisNode=Node->newNode("IFSENTENCE");
Tree *newchild=thisNode->newNode("IF");
i++;
Condition(thisNode);
if(result[i].str=="THEN"){
thisNode->newNode("THEN");
i++;
}
else{
is_error=1;
return;
}
Sentence(thisNode);
}
void Whilesectence(Tree *Node){
Tree *thisNode=Node->newNode("WHILESENTENCE");
Tree *newchild=thisNode->newNode("WHILE");
i++;
Condition(thisNode);
if(result[i].str=="DO"){
thisNode->newNode("DO");i++;
}
else{
is_error=1;
return;
}
Sentence(thisNode);
}
void Callsentence(Tree *Node){
Tree *thisNode=Node->newNode("CALLSENTENCE");
thisNode->newNode("CALL");
i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);
i++;
}
else{
is_error=1;
return;
}
}
void Readsentence(Tree* Node){
Tree *thisNode=Node->newNode("READSENTENCE");
thisNode->newNode("READ");
i++;
if(result[i].str=="("){
thisNode->newNode("LP");
i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);i++;
}
else {
is_error=1;
return;
}
while(result[i].str==","){
thisNode->newNode("COMMA"),i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);i++;
}
else {
is_error=1;
return;
}
}
if(result[i].str==")"){
thisNode->newNode("RP");i++;
}
else {
is_error=1;
return;
}
}
else {
is_error=1;
return;
}
}
void Writesentence(Tree *Node){
Tree *thisNode=Node->newNode("WRITESENTENCE");
thisNode->newNode("WRITE"),i++;
if(result[i].str=="("){
thisNode->newNode("LP");
i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);i++;
}
else {
is_error=1;
return;
}
while(result[i].str==","){
thisNode->newNode("COMMA"),i++;
if(result[i].type=="IDENTIFIER"){
thisNode->newNode(result[i].str);i++;
}
else {
is_error=1;
return;
}
}
if(result[i].str==")"){
thisNode->newNode("RP");i++;
}
else {
is_error=1;
return;
}
}
else {
is_error=1;
return;
}
}
void Combined(Tree *Node){
Tree *thisNode=Node->newNode("COMBINED");
thisNode->newNode("BEGIN");
i++;
Sentence(thisNode);
while(result[i].str==";"){
thisNode->newNode(";");
i++;
Sentence(thisNode);
}
if(result[i].str=="END"){
thisNode->newNode("END");
i++;
}
else{
is_error=1;
return;
}
}