PL0语言数组变量类型文法可定义为:

Var <数组变量名> ’(‘<下界>,<上界>’)’

其中<下界>,<上界>是常量名或无符号数

引用格式可为:C(1),c(a),C(a+b),C(b+C(1))

头文件修改:

1、增加冒号符号(增加colon,symnum+1)

/* 符号 */
enum symbol {nul,         ident,     number,     plus,      minus,times,       slash,     oddsym,     eql,       neq,lss,         leq,       gtr,        geq,       lparen,rparen,      comma,     semicolon,  period,    becomes,beginsym,    endsym,    ifsym,      thensym,   whilesym,writesym,    readsym,   dosym,      callsym,   constsym,varsym,      procsym, elsesym,    forsym,    tosym, colon
};
#define symnum 36

2、定义名字表中的数组类型(添加array)

enum object {constant,variable,procedur,array       //add
};

3、添加虚拟机代码(增加sta,lda,arrcheck,且fctnum+3)

enum fct {lit, opr, lod,sto, cal, inte,jmp, jpc, sta,lda, arrcheck
};
#define fctnum 11

4、修改名字表结构(增加数组上界startid)

/* 名字表结构 */
struct tablestruct
{char name[al];      /* 名字 */enum object kind;   /* 类型:const, var, array or procedure */int val;            /* 数值,仅const使用 */int level;          /* 所处层,仅const不使用 */int adr;            /* 地址,仅const不使用 */int size;           /* 需要分配的数据区空间, 仅procedure使用 */int startid;        /*数组的上界*/
};

源文件修改:

1、增加指令(sta,lda,arrcheck)

 /* 设置指令名称 */strcpy(&(mnemonic[lit][0]), "lit");strcpy(&(mnemonic[opr][0]), "opr");strcpy(&(mnemonic[lod][0]), "lod");strcpy(&(mnemonic[sto][0]), "sto");strcpy(&(mnemonic[cal][0]), "cal");strcpy(&(mnemonic[inte][0]), "int");strcpy(&(mnemonic[jmp][0]), "jmp");strcpy(&(mnemonic[jpc][0]), "jpc");strcpy(&(mnemonic[sta][0]), "sta");strcpy(&(mnemonic[lda][0]), "lda");strcpy(&(mnemonic[arrcheck][0]), "arrcheck");

2、修改冒号检测(365行附近)

if (ch == ':')      /* 检测赋值符号 */
{getchdo;if (ch == '='){sym = becomes;getchdo;}else{sym = colon;  /* 此处改为colon */}
}

3、增加array名字表输出(只需增加case array就行 ):620行附近

switch (table[i].kind)
{
case constant:...break;
case variable:...break;
case procedur:...break;
case array:printf("    %d arr     %s   ", i, table[i].name);printf("lev=%d addr=%d size=%d startid=%d\n", table[i].level, table[i].adr, table[i].size, table[i].startid);fprintf(fas, "    %d arr    %s   ", i, table[i].name);fprintf(fas, "lev=%d addr=%d size=%d startid=%d\n", table[i].level, table[i].adr, table[i].size, table[i].startid);break;
}

4、修改enter函数(增加array处理)

void enter(enum object k, int* ptx, int lev, int* pdx)
{(*ptx)++;strcpy(table[(*ptx)].name, id); /* 全局变量id中已存有当前名字的名字 */table[(*ptx)].kind = k;switch (k){case constant:  /* 常量名字 */if (num > amax){error(31);  /* 数越界 */num = 0;}table[(*ptx)].val = num;break;case variable:  /* 变量名字 */table[(*ptx)].level = lev;table[(*ptx)].adr = (*pdx);(*pdx)++;break;case array:table[(*ptx)].level = lev;table[(*ptx)].adr = (*pdx);break;case procedur:  /* 过程名字 */table[(*ptx)].level = lev;break;}
}

5、修改变量声明处理函数vardeclaration(增加数组处理)

int vardeclaration(int* ptx, int lev, int* pdx)
{if (sym == ident){// 填写名字表并改变堆栈帧计数器int startid = 0, endid = 0;enter(variable, ptx, lev, pdx);//填写名字表(*pdx)++;getsymdo;if (sym == lparen) {getsymdo;switch (sym) {case ident:startid = table[position(id, *ptx)].val;break;case number:startid = num;break;case plus:getsymdo;startid = num;break;case minus:getsymdo;startid = -num;break;}table[(*ptx)].adr = table[(*ptx)].adr - startid;table[(*ptx)].startid = startid;getsymdo;if (sym != colon) {error(30);}else {getsymdo;switch (sym) {case ident:endid = table[position(id, *ptx)].val;table[(*ptx)].size = endid - startid + 1;break;case number:endid = num + 1;table[(*ptx)].size = endid - startid + 1;break;case plus:getsymdo;endid = num + 1;table[(*ptx)].size = endid - startid + 1;break;case minus:getsymdo;endid = -num + 1;table[(*ptx)].size = endid - startid + 1;break;}table[(*ptx)].kind = array;(*pdx) = (*pdx) + endid - startid + 1;   //计算数组中的各自dxgetsymdo;getsymdo;}}}else{error(4);}return 0;
}

6、数组的赋值处理(修改statement函数的外层【if (sym == ident) 】):840行附近


if (sym == ident)   /* 准备按照赋值语句处理 */
{i = position(id, *ptx);if (i == 0){error(11);  /* 变量未找到 */}else{if(table[i].kind != variable){error(12);  /* 赋值语句格式错误 */i = 0;}else{getsymdo;if(sym == becomes){getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev); /* 处理赋值符号右侧表达式 */if (i != 0){/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */gendo(sto, lev - table[i].level, table[i].adr);}}else if (sym == lparen) {      // 数组啦...getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev);// parseExpression将产生一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值//在expression最后已经有这个了:nextSym();  //一个)getsymdo;  //一个:=//后面和var赋值相同,除了最后生成的语句getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev);// parseExpression将产生一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值gendo(sta, lev - table[i].level, table[i].adr);}else{error(13);  /* 没有检测到赋值符号 */}}}//if (i == 0)
}

7、数组的read操作(修改statement的【if (sym == readsym)】内的第一个else里的都...while语句):900行附近

do {getsymdo;if (sym == ident){i = position(id, *ptx); /* 查找要读的变量 */}else{i=0;}if (i == 0){error(35);  /* read()中应是声明过的变量名 */}else{if (table[i].kind == constant || table[i].kind == procedur) {error(32);     // read()中的标识符不是变量}else {getsymdo;if (sym != lparen) {         //非数组,即变量gendo(opr, 0, 16);gen(sto, lev - table[i].level, table[i].adr);}else {     //TODO 数组getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);int ltmp = lev - table[i].level;int adrtmp = table[i].adr;gendo(arrcheck, table[i].startid, table[i].size);gendo(jpc, 0, 0);gendo(opr, 0, 16);gendo(sta, ltmp, adrtmp);getsymdo;}}}} while (sym == comma); /* 一条read语句可读多个变量 */

8、修改factor函数(增加case array)

switch (table[i].kind)
{
case constant:  /* 名字为常量 */gendo(lit, 0, table[i].val);    /* 直接把常量的值入栈 */break;
case variable:  /* 名字为变量 */gendo(lod, lev-table[i].level, table[i].adr);   /* 找到变量地址并将其值入栈 */break;
case procedur:  /* 名字为过程 */error(21);  /* 不能为过程 */break;
case array:getsymdo;if (sym == lparen){                   //是数组int ltmp = lev - table[i].level;int adrtmp = table[i].adr;getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);gendo(lda, ltmp, adrtmp);//getsymdo;                            }if (sym == rparen){//getsymdo;}break;
}

9、定义指令操作(定义指令sta,lda,arrcheck):源文件末尾能看到这些指令

case jmp:   /* 直接跳转 */...break;
case jpc:   /* 条件跳转 */...break;
case sta:                        // 栈顶的值存到数组下标为a的内存t--;s[base(i.l, s, b) + i.a + s[t - 1]] = s[t];t--;break;
case lda:s[t - 1] = s[base(i.l, s, b) + i.a + s[t - 1]];       //对数组的读取 第二个S[T]是放入偏移地址的              break;
case arrcheck:s[t] = i.a;if ((s[t - 1] < i.l) || (s[t - 1] > s[t] + i.l - 1)){printf("Error:Out of the array's size!\n");fprintf(fa2, "Error:Out of the array's size!\n");s[t] = 0;}else {s[t] = 1;}t++;break;

测试文件:

const c=3;
var b(2:c);
beginread(b(3));write(b(3));
end.

扩展完成代码:

头文件:

/** PL/0 complier program for win32 platform (implemented in C)** The program has been test on Visual C++ 6.0,  Visual C++.NET and* Visual C++.NET 2003,  on Win98,  WinNT,  Win2000,  WinXP and Win2003**/typedef enum {false,true
} bool;#define norw 16     /* 关键字个数 */
#define txmax 100   /* 名字表容量 */
#define nmax 14     /* number的最大位数 */
#define al 10       /* 符号的最大长度 */
#define amax 2047   /* 地址上界*/
#define levmax 3    /* 最大允许过程嵌套声明层数 [0,  levmax]*/
#define cxmax 500   /* 最多的虚拟机代码数 *//* 符号 */
enum symbol {nul,         ident,     number,     plus,      minus,times,       slash,     oddsym,     eql,       neq,lss,         leq,       gtr,        geq,       lparen,rparen,      comma,     semicolon,  period,    becomes,beginsym,    endsym,    ifsym,      thensym,   whilesym,writesym,    readsym,   dosym,      callsym,   constsym,varsym,      procsym, elsesym,    forsym,    tosym, colon
};
#define symnum 36/* 名字表中的类型 */
enum object {constant,variable,procedur,array       //add
};/* 虚拟机代码 */
enum fct {lit, opr, lod,sto, cal, inte,jmp, jpc, sta,lda, arrcheck
};
#define fctnum 10/* 虚拟机代码结构 */
struct instruction
{enum fct f; /* 虚拟机代码指令 */int l;      /* 引用层与声明层的层次差 */int a;      /* 根据f的不同而不同 */
};FILE* fas;  /* 输出名字表 */
FILE* fa;   /* 输出虚拟机代码 */
FILE* fa1;  /* 输出源文件及其各行对应的首地址 */
FILE* fa2;  /* 输出结果 */
bool listswitch;    /* 显示虚拟机代码与否 */
bool tableswitch;   /* 显示名字表与否 */
char ch;            /* 获取字符的缓冲区,getch 使用 */
enum symbol sym;    /* 当前的符号 */
char id[al+1];      /* 当前ident, 多出的一个字节用于存放0 */
int num;            /* 当前number */
int cc, ll;          /* getch使用的计数器,cc表示当前字符(ch)的位置 */
int cx;             /* 虚拟机代码指针, 取值范围[0, cxmax-1]*/
char line[81];      /* 读取行缓冲区 */
char a[al+1];       /* 临时符号, 多出的一个字节用于存放0 */
struct instruction code[cxmax]; /* 存放虚拟机代码的数组 */
char word[norw][al];        /* 保留字 */
enum symbol wsym[norw];     /* 保留字对应的符号值 */
enum symbol ssym[256];      /* 单字符的符号值 */
char mnemonic[fctnum][5];   /* 虚拟机代码指令名称 */
bool declbegsys[symnum];    /* 表示声明开始的符号集合 */
bool statbegsys[symnum];    /* 表示语句开始的符号集合 */
bool facbegsys[symnum];     /* 表示因子开始的符号集合 *//* 名字表结构 */
struct tablestruct
{char name[al];      /* 名字 */enum object kind;   /* 类型:const, var, array or procedure */int val;            /* 数值,仅const使用 */int level;          /* 所处层,仅const不使用 */int adr;            /* 地址,仅const不使用 */int size;           /* 需要分配的数据区空间, 仅procedure使用 */int startid;        /*数组的上界*/
};struct tablestruct table[txmax]; /* 名字表 */FILE* fin;
FILE* fout;
char fname[al];
int err; /* 错误计数器 *//* 当函数中会发生fatal error时,返回-1告知调用它的函数,最终退出程序 */
#define getsymdo                      if(-1 == getsym()) return -1
#define getchdo                       if(-1 == getch()) return -1
#define testdo(a, b, c)               if(-1 == test(a, b, c)) return -1
#define gendo(a, b, c)                if(-1 == gen(a, b, c)) return -1
#define expressiondo(a, b, c)         if(-1 == expression(a, b, c)) return -1
#define factordo(a, b, c)             if(-1 == factor(a, b, c)) return -1
#define termdo(a, b, c)               if(-1 == term(a, b, c)) return -1
#define conditiondo(a, b, c)          if(-1 == condition(a, b, c)) return -1
#define statementdo(a, b, c)          if(-1 == statement(a, b, c)) return -1
#define constdeclarationdo(a, b, c)   if(-1 == constdeclaration(a, b, c)) return -1
#define vardeclarationdo(a, b, c)     if(-1 == vardeclaration(a, b, c)) return -1void error(int n);
int getsym();
int getch();
void init();
int gen(enum fct x, int y, int z);
int test(bool* s1, bool* s2, int n);
int inset(int e, bool* s);
int addset(bool* sr, bool* s1, bool* s2, int n);
int subset(bool* sr, bool* s1, bool* s2, int n);
int mulset(bool* sr, bool* s1, bool* s2, int n);
int block(int lev, int tx, bool* fsys);
void interpret();
int factor(bool* fsys, int* ptx, int lev);
int term(bool* fsys, int* ptx, int lev);
int condition(bool* fsys, int* ptx, int lev);
int expression(bool* fsys, int* ptx, int lev);
int statement(bool* fsys, int* ptx, int lev);
void listcode(int cx0);
int vardeclaration(int* ptx, int lev, int* pdx);
int constdeclaration(int* ptx, int lev, int* pdx);
int position(char* idt, int tx);
void enter(enum object k, int* ptx, int lev, int* pdx);
int base(int l, int* s, int b);

源文件:

/** PL/0 complier program for win32 platform (implemented in C)** The program has been test on Visual C++ 6.0, Visual C++.NET and* Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003** 使用方法:* 运行后输入PL/0源程序文件?* 回答是否输出虚拟机代码* 回答是否输出名字表* fa.tmp输出虚拟机代码* fa1.tmp输出源文件及其各行对应的首地址* fa2.tmp输出结?* fas.tmp输出名字表*/#include <stdio.h>#include "pl0.h"
#include "string.h"/* 解释执行时使用的栈 */
#define stacksize 500int main()
{bool nxtlev[symnum];printf("Input pl/0 file?   ");scanf("%s", fname);     /* 输入文件名 */fin = fopen(fname, "r");if (fin){printf("List object code?(Y/N)");   /* 是否输出虚拟机代码 */scanf("%s", fname);listswitch = (fname[0]=='y' || fname[0]=='Y');printf("List symbol table?(Y/N)");  /* 是否输出名字表 */scanf("%s", fname);tableswitch = (fname[0]=='y' || fname[0]=='Y');fa1 = fopen("fa1.tmp", "w");fprintf(fa1,"Input pl/0 file?   ");fprintf(fa1,"%s\n",fname);init();     /* 初始化 */err = 0;cc = cx = ll = 0;ch = ' ';if(-1 != getsym()){fa = fopen("fa.tmp", "w");fas = fopen("fas.tmp", "w");addset(nxtlev, declbegsys, statbegsys, symnum);nxtlev[period] = true;if(-1 == block(0, 0, nxtlev))   /* 调用编译程序 */{fclose(fa);fclose(fa1);fclose(fas);fclose(fin);printf("\n");return 0;}fclose(fa);fclose(fa1);fclose(fas);if (sym != period){error(9);}if (err == 0){fa2 = fopen("fa2.tmp", "w");interpret();    /* 调用解释执行程序 */fclose(fa2);}else{printf("Errors in pl/0 program");}}fclose(fin);}else{printf("Can't open file!\n");}printf("\n");return 0;
}/*
* 初始化
*/
void init()
{int i;/* 设置单字符符号 */for (i=0; i<=255; i++){ssym[i] = nul;}ssym['+'] = plus;ssym['-'] = minus;ssym['*'] = times;ssym['/'] = slash;ssym['('] = lparen;ssym[')'] = rparen;ssym['='] = eql;ssym[','] = comma;ssym['.'] = period;ssym['#'] = neq;ssym[';'] = semicolon;/* 设置保留字名字,按照字母顺序,便于折半查找 */strcpy(&(word[0][0]), "begin");strcpy(&(word[1][0]), "call");strcpy(&(word[2][0]), "const");strcpy(&(word[3][0]), "do");strcpy(&(word[4][0]), "else");strcpy(&(word[5][0]), "end");strcpy(&(word[6][0]), "for");strcpy(&(word[7][0]), "if");strcpy(&(word[8][0]), "odd");strcpy(&(word[9][0]), "procedure");strcpy(&(word[10][0]), "read");strcpy(&(word[11][0]), "then");strcpy(&(word[12][0]), "to");strcpy(&(word[13][0]), "var");strcpy(&(word[14][0]), "while");strcpy(&(word[15][0]), "write");/* 设置保留字符号 */wsym[0] = beginsym;wsym[1] = callsym;wsym[2] = constsym;wsym[3] = dosym;wsym[4] = elsesym;wsym[5] = endsym;wsym[6] = forsym;wsym[7] = ifsym;wsym[8] = oddsym;wsym[9] = procsym;wsym[10] = readsym;wsym[11] = thensym;wsym[12] = tosym;wsym[13] = varsym;wsym[14] = whilesym;wsym[15] = writesym;/* 设置指令名称 */strcpy(&(mnemonic[lit][0]), "lit");strcpy(&(mnemonic[opr][0]), "opr");strcpy(&(mnemonic[lod][0]), "lod");strcpy(&(mnemonic[sto][0]), "sto");strcpy(&(mnemonic[cal][0]), "cal");strcpy(&(mnemonic[inte][0]), "int");strcpy(&(mnemonic[jmp][0]), "jmp");strcpy(&(mnemonic[jpc][0]), "jpc");strcpy(&(mnemonic[sta][0]), "sta");strcpy(&(mnemonic[lda][0]), "lda");strcpy(&(mnemonic[arrcheck][0]), "arrcheck");/* 设置符号集 */for (i=0; i<symnum; i++){declbegsys[i] = false;statbegsys[i] = false;facbegsys[i] = false;}/* 设置声明开始符号集 */declbegsys[constsym] = true;declbegsys[varsym] = true;declbegsys[procsym] = true;/* 设置语句开始符号集 */statbegsys[beginsym] = true;statbegsys[callsym] = true;statbegsys[ifsym] = true;statbegsys[whilesym] = true;/* 设置因子开始符号集 */facbegsys[ident] = true;facbegsys[number] = true;facbegsys[lparen] = true;
}/*
* 用数组实现集合的集合运算
*/
int inset(int e, bool* s)
{return s[e];
}int addset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]||s2[i];}return 0;
}int subset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]&&(!s2[i]);}return 0;
}int mulset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]&&s2[i];}return 0;
}/*
*   出错处理,打印出错位置和错误编码
*/
void error(int n)
{char space[81];memset(space,32,81);space[cc-1]=0; //出错时当前符号已经读完,所以cc-1printf("****%s!%d\n", space, n);fprintf(fa1,"****%s!%d\n", space, n);err++;
}/*
* 漏掉空格,读取一个字符。
*
* 每次读一行,存入line缓冲区,line被getsym取空后再读一行
*
* 被函数getsym调用。
*/
int getch()
{if (cc == ll){if (feof(fin)){printf("program incomplete");return -1;}ll=0;cc=0;printf("%d ", cx);fprintf(fa1,"%d ", cx);ch = ' ';while (ch != 10){//fscanf(fin,"%c", &ch)//richardif (EOF == fscanf(fin,"%c", &ch)){line[ll] = 0;break;}//end richardprintf("%c", ch);fprintf(fa1, "%c", ch);line[ll] = ch;ll++;}printf("\n");fprintf(fa1, "\n");}ch = line[cc];cc++;return 0;
}/*
* 词法分析,获取一个符号
*/
int getsym()
{int i,j,k;/* the original version lacks "\r", thanks to foolevery */while (ch==' ' || ch==10 || ch==13 || ch==9)  /* 忽略空格、换行、回车和TAB */{getchdo;}if (ch>='a' && ch<='z'){           /* 名字或保留字以a..z开头 */k = 0;do {if(k<al){a[k] = ch;k++;}getchdo;} while (ch>='a' && ch<='z' || ch>='0' && ch<='9');a[k] = 0;strcpy(id, a);i = 0;j = norw-1;do {    /* 搜索当前符号是否为保留字 */k = (i+j)/2;if (strcmp(id,word[k]) <= 0){j = k - 1;}if (strcmp(id,word[k]) >= 0){i = k + 1;}} while (i <= j);if (i-1 > j){sym = wsym[k];}else{sym = ident; /* 搜索失败则,是名字或数字 */}}else{if (ch>='0' && ch<='9'){           /* 检测是否为数字:以0..9开头 */k = 0;num = 0;sym = number;do {num = 10*num + ch - '0';k++;getchdo;} while (ch>='0' && ch<='9'); /* 获取数字的值 */k--;if (k > nmax){error(30);}}else{if (ch == ':')      /* 检测赋值符号 */{getchdo;if (ch == '='){sym = becomes;getchdo;}else{sym = colon;  /* 此处改为colon */}}else{if (ch == '<')      /* 检测小于或小于等于符号 */{getchdo;if (ch == '='){sym = leq;getchdo;}else{sym = lss;}}else{if (ch=='>')        /* 检测大于或大于等于符号 */{getchdo;if (ch == '='){sym = geq;getchdo;}else{sym = gtr;}}else{sym = ssym[ch];     /* 当符号不满足上述条件时,全部按照单字符符号处理 *///getchdo;//richardif (sym != period){getchdo;}//end richard}}}}}return 0;
}/*
* 生成虚拟机代码
*
* x: instruction.f;
* y: instruction.l;
* z: instruction.a;
*/
int gen(enum fct x, int y, int z )
{if (cx >= cxmax){printf("Program too long"); /* 程序过长 */return -1;}code[cx].f = x;code[cx].l = y;code[cx].a = z;cx++;return 0;
}/*
* 测试当前符号是否合法
*
* 在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集?
* (该部分的后跟符号),test负责这项检测,并且负责当检测不通过时的补救措施,
* 程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟
* 符号),以及检测不通过时的错误号。
*
* s1:   我们需要的符号
* s2:   如果不是我们需要的,则需要一个补救用的集?
* n:    错误号
*/
int test(bool* s1, bool* s2, int n)
{if (!inset(sym, s1)){error(n);/* 当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合 */while ((!inset(sym,s1)) && (!inset(sym,s2))){getsymdo;}}return 0;
}/*
* 编译程序主?
*
* lev:    当前分程序所在层
* tx:     名字表当前尾指针
* fsys:   当前模块后跟符号集?
*/
int block(int lev, int tx, bool* fsys)
{int i;int dx;                 /* 名字分配到的相对地址 */int tx0;                /* 保留初始tx */int cx0;                /* 保留初始cx */bool nxtlev[symnum];    /* 在下级函数的参数中,符号集合均为值参,但由于使用数组实现,传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空?传递给下级函数*/dx = 3;tx0 = tx;               /* 记录本层名字的初始位置 */table[tx].adr = cx;gendo(jmp, 0, 0);if (lev > levmax){error(32);}do {if (sym == constsym)    /* 收到常量声明符号,开始处理常量声明 */{getsymdo;/* the original do...while(sym == ident) is problematic, thanks to calculous *//* do { */constdeclarationdo(&tx, lev, &dx);  /* dx的值会被constdeclaration改变,使用指针 */while (sym == comma){getsymdo;constdeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);   /*漏掉了逗号或者分号*/}/* } while (sym == ident); */}if (sym == varsym)      /* 收到变量声明符号,开始处理变量声明 */{getsymdo;/* the original do...while(sym == ident) is problematic, thanks to calculous *//* do {  */vardeclarationdo(&tx, lev, &dx);while (sym == comma){getsymdo;vardeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);}/* } while (sym == ident);  */}while (sym == procsym) /* 收到过程声明符号,开始处理过程声明 */{getsymdo;if (sym == ident){enter(procedur, &tx, lev, &dx); /* 记录过程名字 */getsymdo;}else{error(4);   /* procedure后应为标识符 */}if (sym == semicolon){getsymdo;}else{error(5);   /* 漏掉了分号 */}memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[semicolon] = true;if (-1 == block(lev+1, tx, nxtlev)){return -1;  /* 递归调用 */}if(sym == semicolon){getsymdo;memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);nxtlev[ident] = true;nxtlev[procsym] = true;testdo(nxtlev, fsys, 6);}else{error(5);   /* 漏掉了分号 */}}memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);nxtlev[ident] = true;testdo(nxtlev, declbegsys, 7);} while (inset(sym, declbegsys));   /* 直到没有声明符号 */code[table[tx0].adr].a = cx;    /* 开始生成当前过程代码 */table[tx0].adr = cx;            /* 当前过程代码地址 */table[tx0].size = dx;           /* 声明部分中每增加一条声明都会给dx增加1,声明部分已经结束,dx就是当前过程数据的size */cx0 = cx;gendo(inte, 0, dx);             /* 生成分配内存代码 */if (tableswitch)        /* 输出名字表 */{printf("TABLE:\n");if (tx0+1 > tx){printf("    NULL\n");}for (i=tx0+1; i<=tx; i++){switch (table[i].kind){case constant:printf("    %d const %s ", i, table[i].name);printf("val=%d\n", table[i].val);fprintf(fas, "    %d const %s ", i, table[i].name);fprintf(fas, "val=%d\n", table[i].val);break;case variable:printf("    %d var   %s ", i, table[i].name);printf("lev=%d addr=%d\n", table[i].level, table[i].adr);fprintf(fas, "    %d var   %s ", i, table[i].name);fprintf(fas, "lev=%d addr=%d\n", table[i].level, table[i].adr);break;case procedur:printf("    %d proc  %s ", i, table[i].name);printf("lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);fprintf(fas,"    %d proc  %s ", i, table[i].name);fprintf(fas,"lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);break;case array:printf("    %d arr     %s   ", i, table[i].name);printf("lev=%d addr=%d size=%d startid=%d\n", table[i].level, table[i].adr, table[i].size, table[i].startid);fprintf(fas, "    %d arr    %s   ", i, table[i].name);fprintf(fas, "lev=%d addr=%d size=%d startid=%d\n", table[i].level, table[i].adr, table[i].size, table[i].startid);break;}}printf("\n");}/* 语句后跟符号为分号或end */memcpy(nxtlev, fsys, sizeof(bool)*symnum);  /* 每个后跟符号集和都包含上层后跟符号集和,以便补救 */nxtlev[semicolon] = true;nxtlev[endsym] = true;statementdo(nxtlev, &tx, lev);gendo(opr, 0, 0);                       /* 每个过程出口都要使用的释放数据段指令 */memset(nxtlev, 0, sizeof(bool)*symnum); /*分程序没有补救集合 */testdo(fsys, nxtlev, 8);                /* 检测后跟符号正确性 */listcode(cx0);                          /* 输出代码 */return 0;
}/*
* 在名字表中加入一项
*
* k:      名字种类const,var or procedure
* ptx:    名字表尾指针的指针,为了可以改变名字表尾指针的值
* lev:    名字所在的层次,,以后所有的lev都是这样
* pdx:    dx为当前应分配的变量的相对地址,分配后要增加1
*/
void enter(enum object k, int* ptx, int lev, int* pdx)
{(*ptx)++;strcpy(table[(*ptx)].name, id); /* 全局变量id中已存有当前名字的名字 */table[(*ptx)].kind = k;switch (k){case constant:  /* 常量名字 */if (num > amax){error(31);  /* 数越界 */num = 0;}table[(*ptx)].val = num;break;case variable:  /* 变量名字 */table[(*ptx)].level = lev;table[(*ptx)].adr = (*pdx);(*pdx)++;break;case array:table[(*ptx)].level = lev;table[(*ptx)].adr = (*pdx);break;case procedur:  /* 过程名字 */table[(*ptx)].level = lev;break;}
}/*
* 查找名字的位置.
* 找到则返回在名字表中的位置,否则返回0.
*
* idt:    要查找的名字
* tx:     当前名字表尾指针
*/
int position(char* idt, int tx)
{int i;strcpy(table[0].name, idt);i = tx;while (strcmp(table[i].name, idt) != 0){i--;}return i;
}/*
* 常量声明处理
*/
int constdeclaration(int* ptx, int lev, int* pdx)
{if (sym == ident){getsymdo;if (sym==eql || sym==becomes){if (sym == becomes){error(1);   /* 把=写成了:= */}getsymdo;if (sym == number){enter(constant, ptx, lev, pdx);getsymdo;}else{error(2);   /* 常量说明=后应是数字 */}}else{error(3);   /* 常量说明标识后应是= */}}else{error(4);   /* const后应是标识 */}return 0;
}/*
* 变量声明处理
*/
int vardeclaration(int* ptx, int lev, int* pdx)
{if (sym == ident){// 填写名字表并改变堆栈帧计数器int startid = 0, endid = 0;enter(variable, ptx, lev, pdx);//填写名字表(*pdx)++;getsymdo;if (sym == lparen) {getsymdo;switch (sym) {case ident:startid = table[position(id, *ptx)].val;break;case number:startid = num;break;case plus:getsymdo;startid = num;break;case minus:getsymdo;startid = -num;break;}table[(*ptx)].adr = table[(*ptx)].adr - startid;table[(*ptx)].startid = startid;getsymdo;if (sym != colon) {error(30);}else {getsymdo;switch (sym) {case ident:endid = table[position(id, *ptx)].val;table[(*ptx)].size = endid - startid + 1;break;case number:endid = num + 1;table[(*ptx)].size = endid - startid + 1;break;case plus:getsymdo;endid = num + 1;table[(*ptx)].size = endid - startid + 1;break;case minus:getsymdo;endid = -num + 1;table[(*ptx)].size = endid - startid + 1;break;}table[(*ptx)].kind = array;(*pdx) = (*pdx) + endid - startid + 1;   //计算数组中的各自dxgetsymdo;getsymdo;}}}else{error(4);}return 0;
}/*
* 输出目标代码清单
*/
void listcode(int cx0)
{int i;if (listswitch){for (i=cx0; i<cx; i++){printf("%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);fprintf(fa,"%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);}}
}/*
* 语句处理
*/
int statement(bool* fsys, int* ptx, int lev)
{int i, cx1, cx2, cx3;bool nxtlev[symnum];if (sym == ident)   /* 准备按照赋值语句处理 */{i = position(id, *ptx);if (i == 0){error(11);  /* 变量未找到 */}else{if(table[i].kind != variable){error(12);  /* 赋值语句格式错误 */i = 0;}else{getsymdo;if(sym == becomes){getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev); /* 处理赋值符号右侧表达式 */if (i != 0){/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */gendo(sto, lev - table[i].level, table[i].adr);}}else if (sym == lparen) {      // 数组啦...getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev);// parseExpression将产生一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值//在expression最后已经有这个了:nextSym();  //一个)getsymdo;  //一个:=//后面和var赋值相同,除了最后生成的语句getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);expressiondo(nxtlev, ptx, lev);// parseExpression将产生一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值gendo(sta, lev - table[i].level, table[i].adr);}else{error(13);  /* 没有检测到赋值符号 */}}}//if (i == 0)}else if(sym==forsym)      //检测到for语句{getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0) error(11);else{if(table[i].kind!=variable) //赋值语句中,赋值号左部标识符属性应是变量{error(12);i=0;}else{getsymdo;if(sym!=becomes) error(13);             //赋值语句左部标识符后应是赋值号:=else getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum); nxtlev[tosym]=true;                     //后跟符to和downtoexpressiondo(nxtlev,ptx,lev);           //处理赋值语句右部的表达式E1gendo(sto,lev-table[i].level,table[i].adr);     //保存初值if(sym==tosym){getsymdo;cx1=cx;       //保存循环开始点//将循环判断变量取出放到栈顶gendo(lod,lev-table[i].level,table[i].adr);                                        memcpy(nxtlev,fsys,sizeof(bool)*symnum);    //处理表达式E2nxtlev[dosym]=true;                         //后跟符doexpressiondo(nxtlev,ptx,lev);/*判断循环变量条件,比如for i:=E1 to E2 do S中,判断i是否小于E2,如小于等于,继续循环,大于的话,跳出循环*/     gendo(opr,0,13);             //生成比较指令,i是否小于等于E2的值cx2=cx;                      //保存循环结束点//生成条件跳转指令,跳出循环,跳出的地址未知gendo(jpc,0,0);                     if(sym==dosym)               //处理循环体S{getsymdo;statement(fsys,ptx,lev);  //循环体处理//增加循环变量步长为//将循环变量取出放在栈顶gendo(lod,lev-table[i].level,table[i].adr);                                       gendo(lit,0,1);                            //将步长取到栈顶gendo(opr,0,2);                            //循环变量加步长//将栈顶的值存入循环变量gendo(sto,lev-table[i].level,table[i].adr); gendo(jmp,0,cx1);                 //无条件跳转到循环开始点/*回填循环结束点的地址,cx为else后语句执行完的位置,它正是前面未定的跳转地址*/code[cx2].a=cx;                     }else{error(29);    //for语句中少了do}}}}}}else{if (sym == readsym) /* 准备按照read语句处理 */{getsymdo;if (sym != lparen){error(34);  /* 格式错误,应是左括号 */}else{do {getsymdo;if (sym == ident){i = position(id, *ptx); /* 查找要读的变量 */}else{i=0;}if (i == 0){error(35);  /* read()中应是声明过的变量名 */}else{if (table[i].kind == constant || table[i].kind == procedur) {error(32);       // read()中的标识符不是变量}else {getsymdo;if (sym != lparen) {         //非数组,即变量gendo(opr, 0, 16);gen(sto, lev - table[i].level, table[i].adr);}else {     //TODO 数组getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);int ltmp = lev - table[i].level;int adrtmp = table[i].adr;gendo(arrcheck, table[i].startid, table[i].size);gendo(jpc, 0, 0);gendo(opr, 0, 16);gendo(sta, ltmp, adrtmp);getsymdo;}}}} while (sym == comma); /* 一条read语句可读多个变量 */}if(sym != rparen){error(33);  /* 格式错误,应是右括号 */while (!inset(sym, fsys))   /* 出错补救,直到收到上层函数的后跟符号 */{getsymdo;}}else{getsymdo;}}else{if (sym == writesym)    /* 准备按照write语句处理,与read类似 */{getsymdo;if (sym == lparen){do {getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[rparen] = true;nxtlev[comma] = true;       /* write的后跟符号为) or , */expressiondo(nxtlev, ptx, lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */gendo(opr, 0, 14);  /* 生成输出指令,输出栈顶的值 */} while (sym == comma);if (sym != rparen){error(33);  /* write()中应为完整表达式 */}else{getsymdo;}}gendo(opr, 0, 15);  /* 输出换行 */}else{if (sym == callsym) /* 准备按照call语句处理 */{getsymdo;if (sym != ident){error(14);  /* call后应为标识符 */}else{i = position(id, *ptx);if (i == 0){error(11);  /* 过程未找到 */}else{if (table[i].kind == procedur){gendo(cal, lev-table[i].level, table[i].adr);   /* 生成call指令 */}else{error(15);  /* call后标识符应为过程 */}}getsymdo;}}else{if (sym == ifsym)   /* 准备按照if语句处理 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[thensym] = true;nxtlev[dosym] = true;   /* 后跟符号为then或do */conditiondo(nxtlev, ptx, lev); /* 调用条件处理(逻辑运算)函数 */if (sym == thensym){getsymdo;}else{error(16);  /* 缺少then */}cx1 = cx;   /* 保存当前指令地址 */gendo(jpc, 0, 0);   /* 生成条件跳转指令,跳转地址未知,暂时写0 */statementdo(fsys, ptx, lev);    /* 处理then后的语句 */if (sym == elsesym) {getsymdo;cx2 = cx;gendo(jmp, 0, 0);code[cx1].a = cx;statementdo(fsys, ptx, lev);code[cx2].a = cx;}else {//以上是处理else语句code[cx1].a = cx;      /*经statement处理后,cx为then后语句执行完的位置,它正是前面未定的跳转地址*/}}else{if (sym == beginsym)    /* 准备按照复合语句处理 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[semicolon] = true;nxtlev[endsym] = true;  /* 后跟符号为分号或end *//* 循环调用语句处理函数,直到下一个符号不是语句开始符号或收到end */statementdo(nxtlev, ptx, lev);while (inset(sym, statbegsys) || sym==semicolon){if (sym == semicolon){getsymdo;}else{error(10);  /* 缺少分号 */}statementdo(nxtlev, ptx, lev);}if(sym == endsym){getsymdo;}else{error(17);  /* 缺少end或分号 */}}else{if (sym == whilesym)    /* 准备按照while语句处理 */{cx1 = cx;   /* 保存判断条件操作的位置 */getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[dosym] = true;   /* 后跟符号为do */conditiondo(nxtlev, ptx, lev);  /* 调用条件处理 */cx2 = cx;   /* 保存循环体的结束的下一个位置 */gendo(jpc, 0, 0);   /* 生成条件跳转,但跳出循环的地址未知 */if (sym == dosym){getsymdo;}else{error(18);  /* 缺少do */}statementdo(fsys, ptx, lev);    /* 循环体 */gendo(jmp, 0, cx1); /* 回头重新判断条件 */code[cx2].a = cx;   /* 反填跳出循环的地址,与if类似 */}else{memset(nxtlev, 0, sizeof(bool)*symnum); /* 语句结束无补救集合 */testdo(fsys, nxtlev, 19);   /* 检测语句结束的正确性 */}}}}}}}return 0;
}/*
* 表达式处理
*/
int expression(bool* fsys, int* ptx, int lev)
{enum symbol addop;  /* 用于保存正负号 */bool nxtlev[symnum];if(sym==plus || sym==minus) /* 开头的正负号,此时当前表达式被看作一个正的或负的项 */{addop = sym;    /* 保存开头的正负号 */getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */if (addop == minus){gendo(opr,0,1); /* 如果开头为负号生成取负指令 */}}else    /* 此时表达式被看作项的加减 */{memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */}while (sym==plus || sym==minus){addop = sym;getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */if (addop == plus){gendo(opr, 0, 2);   /* 生成加法指令 */}else{gendo(opr, 0, 3);   /* 生成减法指令 */}}return 0;
}/*
* 项处理
*/
int term(bool* fsys, int* ptx, int lev)
{enum symbol mulop;  /* 用于保存乘除法符号 */bool nxtlev[symnum];memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[times] = true;nxtlev[slash] = true;factordo(nxtlev, ptx, lev); /* 处理因子 */while(sym==times || sym==slash){mulop = sym;getsymdo;factordo(nxtlev, ptx, lev);if(mulop == times){gendo(opr, 0, 4);   /* 生成乘法指令 */}else{gendo(opr, 0, 5);   /* 生成除法指令 */}}return 0;
}/*
* 因子处理
*/
int factor(bool* fsys, int* ptx, int lev)
{int i;bool nxtlev[symnum];testdo(facbegsys, fsys, 24);    /* 检测因子的开始符号 *//* while(inset(sym, facbegsys)) */  /* 循环直到不是因子开始符号 */if(inset(sym,facbegsys))    /* BUG: 原来的方法var1(var2+var3)会被错误识别为因子 */{if(sym == ident)    /* 因子为常量或变量 */{i = position(id, *ptx); /* 查找名字 */if (i == 0){error(11);  /* 标识符未声明 */}else{switch (table[i].kind){case constant:  /* 名字为常量 */gendo(lit, 0, table[i].val);    /* 直接把常量的值入栈 */break;case variable:  /* 名字为变量 */gendo(lod, lev-table[i].level, table[i].adr);   /* 找到变量地址并将其值入栈 */break;case procedur:  /* 名字为过程 */error(21);  /* 不能为过程 */break;case array:getsymdo;if (sym == lparen){                   //是数组int ltmp = lev - table[i].level;int adrtmp = table[i].adr;getsymdo;memcpy(nxtlev, fsys, sizeof(bool) * symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);gendo(lda, ltmp, adrtmp);//getsymdo;                            }if (sym == rparen){//getsymdo;}break;}}getsymdo;}else{if(sym == number)   /* 因子为数 */{if (num > amax){error(31);num = 0;}gendo(lit, 0, num);getsymdo;}else{if (sym == lparen)  /* 因子为表达式 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);if (sym == rparen){getsymdo;}else{error(22);  /* 缺少右括号 */}}testdo(fsys, facbegsys, 23);    /* 因子后有非法符号 */}}}return 0;
}/*
* 条件处理
*/
int condition(bool* fsys, int* ptx, int lev)
{enum symbol relop;bool nxtlev[symnum];if(sym == oddsym)   /* 准备按照odd运算处理 */{getsymdo;expressiondo(fsys, ptx, lev);gendo(opr, 0, 6);   /* 生成odd指令 */}else{/* 逻辑表达式处理 */memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[eql] = true;nxtlev[neq] = true;nxtlev[lss] = true;nxtlev[leq] = true;nxtlev[gtr] = true;nxtlev[geq] = true;expressiondo(nxtlev, ptx, lev);if (sym!=eql && sym!=neq && sym!=lss && sym!=leq && sym!=gtr && sym!=geq){error(20);}else{relop = sym;getsymdo;expressiondo(fsys, ptx, lev);switch (relop){case eql:gendo(opr, 0, 8);break;case neq:gendo(opr, 0, 9);break;case lss:gendo(opr, 0, 10);break;case geq:gendo(opr, 0, 11);break;case gtr:gendo(opr, 0, 12);break;case leq:gendo(opr, 0, 13);break;}}}return 0;
}/*
* 解释程序
*/
void interpret()
{int p, b, t;    /* 指令指针,指令基址,栈顶指针 */struct instruction i;   /* 存放当前指令 */int s[stacksize];   /* 栈 */printf("start pl0\n");t = 0;b = 0;p = 0;s[0] = s[1] = s[2] = 0;do {i = code[p];    /* 读当前指令 */p++;switch (i.f){case lit:   /* 将a的值取到栈顶 */s[t] = i.a;t++;break;case opr:   /* 数学、逻辑运算 */switch (i.a){case 0:t = b;p = s[t+2];b = s[t+1];break;case 1:s[t-1] = -s[t-1];break;case 2:t--;s[t-1] = s[t-1]+s[t];break;case 3:t--;s[t-1] = s[t-1]-s[t];break;case 4:t--;s[t-1] = s[t-1]*s[t];break;case 5:t--;s[t-1] = s[t-1]/s[t];break;case 6:s[t-1] = s[t-1]%2;break;case 8:t--;s[t-1] = (s[t-1] == s[t]);break;case 9:t--;s[t-1] = (s[t-1] != s[t]);break;case 10:t--;s[t-1] = (s[t-1] < s[t]);break;case 11:t--;s[t-1] = (s[t-1] >= s[t]);break;case 12:t--;s[t-1] = (s[t-1] > s[t]);break;case 13:t--;s[t-1] = (s[t-1] <= s[t]);break;case 14:printf("%d", s[t-1]);fprintf(fa2, "%d", s[t-1]);t--;break;case 15:printf("\n");fprintf(fa2,"\n");break;case 16:printf("?");fprintf(fa2, "?");scanf("%d", &(s[t]));fprintf(fa2, "%d\n", s[t]);t++;break;}break;case lod:   /* 取相对当前过程的数据基地址为a的内存的值到栈顶 */s[t] = s[base(i.l,s,b)+i.a];t++;break;case sto:   /* 栈顶的值存到相对当前过程的数据基地址为a的内存 */t--;s[base(i.l, s, b) + i.a] = s[t];break;   case cal:   /* 调用子过程 */s[t] = base(i.l, s, b); /* 将父过程基地址入栈 */s[t+1] = b; /* 将本过程基地址入栈,此两项用于base函数 */s[t+2] = p; /* 将当前指令指针入栈 */b = t;  /* 改变基地址指针值为新过程的基地址 */p = i.a;    /* 跳转 */break;case inte:  /* 分配内存 */t += i.a;break;case jmp:   /* 直接跳转 */p = i.a;break;case jpc:   /* 条件跳转 */t--;if (s[t] == 0){p = i.a;}break;case sta:                        // 栈顶的值存到数组下标为a的内存t--;s[base(i.l, s, b) + i.a + s[t - 1]] = s[t];t--;break;case lda:s[t - 1] = s[base(i.l, s, b) + i.a + s[t - 1]];       //对数组的读取 第二个S[T]是放入偏移地址的              break;case arrcheck:s[t] = i.a;if ((s[t - 1] < i.l) || (s[t - 1] > s[t] + i.l - 1)){printf("Error:Out of the array's size!\n");fprintf(fa2, "Error:Out of the array's size!\n");s[t] = 0;}else {s[t] = 1;}t++;break;}} while (p != 0);
}/* 通过过程基址求上l层过程的基址 */
int base(int l, int* s, int b)
{int b1;b1 = b;while (l > 0){b1 = s[b1];l--;}return b1;
}

PL0扩展一维数组array相关推荐

  1. C语言之指向一维数组的指针

    int array[5] = {1, 2, 3, 4, 5}; // 定义数组 int *intptr = array; // 定义指向数组元素的指针 int (*arrayptr)[5] = &am ...

  2. js 二维数组降一维数组

    join('').split(',') let arr = [[1,2],[3,4]] let newArr = arr.join('').spilt(','); console.loh(newArr ...

  3. 【java进阶06:数组】使用一维数组模拟栈数据结构 使用二维数组模拟酒店,酒店管理系统 Arrays工具类 冒泡排序算法、选择排序算法、二分法

    目录 数组 二维数组 总结 作业 Arrays工具类 数组 数组总结 及 静态初始化一维数组 /* Array:1.java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是Object ...

  4. numpy使用np.argmax函数获取一维数组中最大值所在的索引(index of largest value in numpy array with np.argmax)

    numpy使用np.argmax函数获取一维数组中最大值所在的索引(index of largest value in numpy array with np.argmax) 目录 numpy使用np ...

  5. PHP删除数组(array一维)中指定的某个值 (转)

    2019独角兽企业重金招聘Python工程师标准>>> PHP删除数组(array一维)中指定的某个值  PHP删除数组(array一维)中指定的某个值 根据指定的值删除: $stu ...

  6. 机器学习——使用array函数构建一维数组

    机器学习--使用array函数构建一维数组 import numpy as np#构建一维数组 a=[1,2,3,4] b=[5,6,7,8] print(a,type(a),b,type(b)) x ...

  7. 杨桃的Python进阶讲座16——数组array(六)一维数组和二维数组的索引和取值(配详细图解)

    本人CSDN博客专栏:https://blog.csdn.net/yty_7 Github地址:https://github.com/yot777/ 在进阶讲座8中讲过数组(矩阵)的维度,我们再看看多 ...

  8. 技术图文:Numpy 一维数组 VS. Pandas Series

    背景 Numpy 提供的最重要的数据结构是 ndarray,它是 Python 中 list 的扩展. Pandas 提供了两种非常重要的数据结构 Series和DataFrame. Numpy 中的 ...

  9. php n维数组扁平化,js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解...

    数组的扁平化:将多维数组变成一维数组 对于一个像这样的嵌套数组:a=[1,[2,[3,4]],5,6]我们想要把它变成一个一维数组,有下面几种方法: 方法一:递归一 function parseArr ...

最新文章

  1. MinGW安装与使用简介
  2. 在Source Insight中看Python代码
  3. 跟我学大数据分析之四:“钱”去哪儿了?
  4. PHP增加$_ENV变量
  5. 代码质量评判标准、设计模式、面向对象设计原则速查表
  6. 读书笔记:软件人才-管理的艺术
  7. python 文本处理2
  8. 华为鸿蒙系统明年8月份,华为鸿蒙系统明年上线,力争第五大操作系统,能否成功?...
  9. rpm打包的流程包括以下哪些_Linux 下RPM打包制作流程
  10. 明略数据(现在)是一家怎样的公司?
  11. linux编译fdk aac,编译x264和fdk-aac
  12. python基础和软件测试
  13. 手把手教你二维数组打印
  14. cas4.x 单点登录开发入门
  15. 基本知识 100176
  16. 复习330+天,我总结了一份对大多数人都适用的复习经验
  17. 如何用计算机求一个正数的算术平方根,《用计算器求一个正数的算术平方根》教学设计...
  18. express項目部署阿里云服务器
  19. 紫光同创国产FPGA学习之IP Compiler
  20. SPA(单页面)和MPA(多页面)的区别与优缺点

热门文章

  1. POI 设置单元格背景色,背景色编码与实际颜色对照表(SXSSFWorkbook4.1.2)
  2. python 之Entry
  3. camera中文版软件 ip_Synergy下载|Synergy 1.8.4 中文版
  4. 博客园电子期刊2012年9月刊发布啦
  5. 线性代数学习笔记——第六十九讲——正交向量组与标准正交基
  6. 谁让AI走进了隐秘的角落?
  7. SAP 发送外部邮件(增加到邮件队列)
  8. ActiveReports报表开发案例:ERP等管理系统的开发与维护模式
  9. 如何在2周内交付85%以上需求?阿里工程师这么做
  10. 2021-04-12 年鉴搜索网址