原文地址: GitHub - rswier/c4: C in four functions

说实话,我也没太看懂,但我试着给大家分析一下吧。

第一步 读取文件

  --argc; ++argv;if (argc > 0 && **argv == '-' && (*argv)[1] == 's') { src = 1; --argc; ++argv; }if (argc > 0 && **argv == '-' && (*argv)[1] == 'd') { debug = 1; --argc; ++argv; }//if (argc < 1) { printf("usage: c4 [-s] [-d] file ...\n"); return -1; }if ((fd = open(*argv, 0)) < 0) { printf("could not open(%s)\n", *argv); return -1; }
...if (!(lp = p = malloc(poolsz))) { printf("could not malloc(%d) source area\n", poolsz); return -1; }if ((i = read(fd, p, poolsz-1)) <= 0) { printf("read() returned %d\n", i); return -1; }

这个没啥好说的。在读文章的过程中同时看你有没有输入-s,-d。-s是输出每行c语言及其对应的汇编语言。-d是输出汇编语言。这里说一下,如果读取文件失败,可用以下方法查看原因。

   //举个例子int nc=0x2000;int fd = open(*argv,0);//打开文件if (fd == -1) {//打开失败,fd会为-1printf("Unable to open file\n");exit(-1);}pointer= (char *)malloc(nc);//nc是准备读取的字符数量,这里给pointer分配空间number=read(fd,pointer,nc);//读取文件if (number<=0){//读取失败的情况perror("read:");printf("read file error\n");exit(-1);}

第二步  初始话栈堆,代码段,数据段并分配空间

  poolsz = 256*1024; // arbitrary sizeif (!(sym = malloc(poolsz))) { printf("could not malloc(%d) symbol area\n", poolsz); return -1; }if (!(le = e = malloc(poolsz))) { printf("could not malloc(%d) text area\n", poolsz); return -1; }if (!(data = malloc(poolsz))) { printf("could not malloc(%d) data area\n", poolsz); return -1; }if (!(sp = malloc(poolsz))) { printf("could not malloc(%d) stack area\n", poolsz); return -1; }memset(sym,  0, poolsz);memset(e,    0, poolsz);memset(data, 0, poolsz);

就这么简单,略。

第三步 添加关键字

enum {Num = 128, Fun, Sys, Glo, Loc, Id,Char, Else, Enum, If, Int, Return, Sizeof, While,Assign, Cond, Lor, Lan, Or, Xor, And, Eq, Ne, Lt, Gt, Le, Ge, Shl, Shr, Add, Sub, Mul, Div, Mod, Inc, Dec, Brak
};
enum { LEA ,IMM ,JMP ,JSR ,BZ  ,BNZ ,ENT ,ADJ ,LEV ,LI  ,LC  ,SI  ,SC  ,PSH ,OR  ,XOR ,AND ,EQ  ,NE  ,LT  ,GT  ,LE  ,GE  ,SHL ,SHR ,ADD ,SUB ,MUL ,DIV ,MOD ,OPEN,READ,CLOS,PRTF,MALC,FREE,MSET,MCMP,EXIT };...i = Char; while (i <= While) { next(); id[Tk] = i++; } // add keywords to symbol tablei = OPEN; while (i <= EXIT) { next(); id[Class] = Sys; id[Type] = INT; id[Val] = i++; } // add library to symbol tablenext(); id[Tk] = Char; // handle void typenext(); idmain = id; // keep track of main

这个有点巧妙,它的enum不是乱排列的。事实上,它的每一个排列都有深意。在上面那个enum中第一行是类型,第二行是关键字,第三行是识别符号出来的token(记录如加减乘除之类的)。第二个enum是指令集。先排带参数的指令,再排不带参数的指令,最后再排为了自举(自己编译自己)而添加上去的指令。

第四步 next函数

void next()
{char *pp;while (tk = *p) {++p;if (tk == '\n') {if (src) {printf("%d: %.*s", line, p - lp, lp);lp = p;while (le < e) {printf("%8.4s", &"LEA ,IMM ,JMP ,JSR ,BZ  ,BNZ ,ENT ,ADJ ,LEV ,LI  ,LC  ,SI  ,SC  ,PSH ,""OR  ,XOR ,AND ,EQ  ,NE  ,LT  ,GT  ,LE  ,GE  ,SHL ,SHR ,ADD ,SUB ,MUL ,DIV ,MOD ,""OPEN,READ,CLOS,PRTF,MALC,FREE,MSET,MCMP,EXIT,"[*++le * 5]);if (*le <= ADJ) printf(" %d\n", *++le); else printf("\n");}}++line;}else if (tk == '#') {while (*p != 0 && *p != '\n') ++p;}else if ((tk >= 'a' && tk <= 'z') || (tk >= 'A' && tk <= 'Z') || tk == '_') {pp = p - 1;while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')tk = tk * 147 + *p++;tk = (tk << 6) + (p - pp);id = sym;while (id[Tk]) {if (tk == id[Hash] && !memcmp((char *)id[Name], pp, p - pp)) { tk = id[Tk]; return; }id = id + Idsz;}id[Name] = (int)pp;id[Hash] = tk;tk = id[Tk] = Id;return;}else if (tk >= '0' && tk <= '9') {if (ival = tk - '0') { while (*p >= '0' && *p <= '9') ival = ival * 10 + *p++ - '0'; }else if (*p == 'x' || *p == 'X') {while ((tk = *++p) && ((tk >= '0' && tk <= '9') || (tk >= 'a' && tk <= 'f') || (tk >= 'A' && tk <= 'F')))ival = ival * 16 + (tk & 15) + (tk >= 'A' ? 9 : 0);}else { while (*p >= '0' && *p <= '7') ival = ival * 8 + *p++ - '0'; }tk = Num;return;}else if (tk == '/') {if (*p == '/') {++p;while (*p != 0 && *p != '\n') ++p;}else {tk = Div;return;}}else if (tk == '\'' || tk == '"') {pp = data;while (*p != 0 && *p != tk) {if ((ival = *p++) == '\\') {if ((ival = *p++) == 'n') ival = '\n';}if (tk == '"') *data++ = ival;}++p;if (tk == '"') ival = (int)pp; else tk = Num;return;}else if (tk == '=') { if (*p == '=') { ++p; tk = Eq; } else tk = Assign; return; }else if (tk == '+') { if (*p == '+') { ++p; tk = Inc; } else tk = Add; return; }else if (tk == '-') { if (*p == '-') { ++p; tk = Dec; } else tk = Sub; return; }else if (tk == '!') { if (*p == '=') { ++p; tk = Ne; } return; }else if (tk == '<') { if (*p == '=') { ++p; tk = Le; } else if (*p == '<') { ++p; tk = Shl; } else tk = Lt; return; }else if (tk == '>') { if (*p == '=') { ++p; tk = Ge; } else if (*p == '>') { ++p; tk = Shr; } else tk = Gt; return; }else if (tk == '|') { if (*p == '|') { ++p; tk = Lor; } else tk = Or; return; }else if (tk == '&') { if (*p == '&') { ++p; tk = Lan; } else tk = And; return; }else if (tk == '^') { tk = Xor; return; }else if (tk == '%') { tk = Mod; return; }else if (tk == '*') { tk = Mul; return; }else if (tk == '[') { tk = Brak; return; }else if (tk == '?') { tk = Cond; return; }else if (tk == '~' || tk == ';' || tk == '{' || tk == '}' || tk == '(' || tk == ')' || tk == ']' || tk == ',' || tk == ':') return;}
}

这个就很清晰易懂了。对每个字符分析出tk。这里有几点注意:

1、c4不支持数组。

2、c4不能读库如#include <stdio.h>,故对于#后面的内容全当注释看。

3、先开始我不知道为什么tk要乘以147,后来才明白它想用数字来对应字符。故理论上乘以128以上的数都可以。字符读取完后与id库里的比较,看看是不是之前出现过的。对于刚出现的字符,我们都将其识别为Id。

4、在这里先不分*是乘号还是指针,之后再说。

5、它用p指向tk前面一个的字符,有效的识别前后文。如三种数字的表达(十进制,八进制,十六进制,并将其放到ival中),如一些二元运算符及一元运算符。

6、很细节啊,读到\n就先判断有没有输入-s,然后line++,它甚至还统计了代码的行数滴。

第五步 处理tk

while (tk) {bt = INT; // basetypeif (tk == Int) next();else if (tk == Char) { next(); bt = CHAR; }else if (tk == Enum) {next();if (tk != '{') next();if (tk == '{') {next();i = 0;while (tk != '}') {if (tk != Id) { printf("%d: bad enum identifier %d\n", line, tk); return -1; }next();if (tk == Assign) {next();if (tk != Num) { printf("%d: bad enum initializer\n", line); return -1; }i = ival;next();}id[Class] = Num; id[Type] = INT; id[Val] = i++;if (tk == ',') next();}next();}}while (tk != ';' && tk != '}') {ty = bt;while (tk == Mul) { next(); ty = ty + PTR; }if (tk != Id) { printf("%d: bad global declaration\n", line); return -1; }if (id[Class]) { printf("%d: duplicate global definition\n", line); return -1; }next();id[Type] = ty;if (tk == '(') { // functionid[Class] = Fun;id[Val] = (int)(e + 1);next(); i = 0;while (tk != ')') {ty = INT;if (tk == Int) next();else if (tk == Char) { next(); ty = CHAR; }while (tk == Mul) { next(); ty = ty + PTR; }if (tk != Id) { printf("%d: bad parameter declaration\n", line); return -1; }if (id[Class] == Loc) { printf("%d: duplicate parameter definition\n", line); return -1; }id[HClass] = id[Class]; id[Class] = Loc;id[HType]  = id[Type];  id[Type] = ty;id[HVal]   = id[Val];   id[Val] = i++;next();if (tk == ',') next();}next();if (tk != '{') { printf("%d: bad function definition\n", line); return -1; }loc = ++i;next();while (tk == Int || tk == Char) {bt = (tk == Int) ? INT : CHAR;next();while (tk != ';') {ty = bt;while (tk == Mul) { next(); ty = ty + PTR; }if (tk != Id) { printf("%d: bad local declaration\n", line); return -1; }if (id[Class] == Loc) { printf("%d: duplicate local definition\n", line); return -1; }id[HClass] = id[Class]; id[Class] = Loc;id[HType]  = id[Type];  id[Type] = ty;id[HVal]   = id[Val];   id[Val] = ++i;next();if (tk == ',') next();}next();}*++e = ENT; *++e = i - loc;while (tk != '}') stmt();*++e = LEV;id = sym; // unwind symbol table localswhile (id[Tk]) {if (id[Class] == Loc) {id[Class] = id[HClass];id[Type] = id[HType];id[Val] = id[HVal];}id = id + Idsz;}}else {id[Class] = Glo;id[Val] = (int)data;data = data + sizeof(int);}if (tk == ',') next();}next();}

介个有点复杂,我们一点一点说。

首先先判断类型,理论上默认都是int ,当然如果你是char,那再改成char。

如果你是enum ,由于需要自举,所以要能编译介个。

enum {Num = 128, Fun, Sys, Glo, Loc, Id,Char, Else, Enum, If, Int, Return, Sizeof, While,Assign, Cond, Lor, Lan, Or, Xor, And, Eq, Ne, Lt, Gt, Le, Ge, Shl, Shr, Add, Sub, Mul, Div, Mod, Inc, Dec, Brak
};

我们先识别空格,不是'{',故跳过,来到{,跳过,接下来就是内容了,这里先初始化i为0,之后用i给这些Id赋值,对于接下来的Id而言,如果我们没有主动赋值,那他们的值就是当前的位置(就和数组一样0,1,2,3,4...),如果赋值就从赋的值开始增加,这里就是128,129,130,131....

然后开始识别函数还是全局变量了,这个时候出现的*必定是指针(如果是乘号,前面总得有数字或者字符而这里才开始识别,前面是int,char这种东西)。对于 int *,ty就是int和*,char=0,int=1,*=2,所以int *=3。如果你看见ty=3,就能一下子反应过来是int *。

确定类型后,看有没有'(',有就是函数,没有就是全局变量。

对于函数而言里面是局部变量,作用域仅在本函数内。对于传进去的参数。如果遇到与外面同名的变量怎么办?这时,他用了Hclass,Htype,Hval来掩盖之前存的class,type,val。参数传递完后再还原class,type,val。对于函数内部的参数,如果与外面的重名后也是一样的办法哦。

然后我们注意到i和loc,我们可以很明显的发现,i与loc的差是函数内部参数的数量,这有什么用呢?

大家还记得函数开头的汇编语言吧 push ebp mov ebp,esp  sub esp,xx。 这个xx就是该函数可分配的栈的大小。在c4中参数的数量就是分配的栈的大小。上面的三条指令由ENT xx一条指令直接搞定。同理这里的LEV代替原来的add esp,xx pop ebp ret。

第六步 stmt函数

void stmt()
{int *a, *b;if (tk == If) {next();if (tk == '(') next(); else { printf("%d: open paren expected\n", line); exit(-1); }expr(Assign);if (tk == ')') next(); else { printf("%d: close paren expected\n", line); exit(-1); }*++e = BZ; b = ++e;stmt();if (tk == Else) {*b = (int)(e + 3); *++e = JMP; b = ++e;next();stmt();}*b = (int)(e + 1);}else if (tk == While) {next();a = e + 1;if (tk == '(') next(); else { printf("%d: open paren expected\n", line); exit(-1); }expr(Assign);if (tk == ')') next(); else { printf("%d: close paren expected\n", line); exit(-1); }*++e = BZ; b = ++e;stmt();*++e = JMP; *++e = (int)a;*b = (int)(e + 1);}else if (tk == Return) {next();if (tk != ';') expr(Assign);*++e = LEV;if (tk == ';') next(); else { printf("%d: semicolon expected\n", line); exit(-1); }}else if (tk == '{') {next();while (tk != '}') stmt();next();}else if (tk == ';') {next();}else {expr(Assign);if (tk == ';') next(); else { printf("%d: semicolon expected\n", line); exit(-1); }}
}

识别特殊关键字if while return。这50行代码我看了好久。

大家看见e了吗?我们最开始初始化e,将他看作text段,而现在e指的是当前的代码段的位置。这个你在用c4.exe 的-s参数看是看不出来的,要-d看才行。

先解释BZ,BNZ。BZ是指,如果为真就继续,如果为假就跳转。BNZ相反。

在if中,如果条件成立,那就接着执行就行了。但如果不成立,那就要往下跳。跳到哪里其实现在是不知道的,它必须要执行完if才知道调到哪里。对于if内部的内容,就都在expr函数中一行一行的消化了。对于else,对于if条件为真的话,那就往下跳到else结束,同理else内有多少也是不知道的,只有把else分析完才知道。

在while中,如果不满足条件,跳到while结束,不然,在while结束前来个JMP xx(while开头)。while内部有多少这只有while分析后才知道。

这是这么做到的?这就要看expr函数了。(注:以上内容不是我从上述代码中分析出来的,是我用c4编译c语言文件看出来的。)

第七步 expr函数

void expr(int lev)
{int t, *d;if (!tk) { printf("%d: unexpected eof in expression\n", line); exit(-1); }else if (tk == Num) { *++e = IMM; *++e = ival; next(); ty = INT; }else if (tk == '"') {*++e = IMM; *++e = ival; next();while (tk == '"') next();data = (char *)((int)data + sizeof(int) & -sizeof(int)); ty = PTR;}else if (tk == Sizeof) {next(); if (tk == '(') next(); else { printf("%d: open paren expected in sizeof\n", line); exit(-1); }ty = INT; if (tk == Int) next(); else if (tk == Char) { next(); ty = CHAR; }while (tk == Mul) { next(); ty = ty + PTR; }if (tk == ')') next(); else { printf("%d: close paren expected in sizeof\n", line); exit(-1); }*++e = IMM; *++e = (ty == CHAR) ? sizeof(char) : sizeof(int);ty = INT;}else if (tk == Id) {d = id; next();if (tk == '(') {next();t = 0;while (tk != ')') { expr(Assign); *++e = PSH; ++t; if (tk == ',') next(); }next();if (d[Class] == Sys) *++e = d[Val];else if (d[Class] == Fun) { *++e = JSR; *++e = d[Val]; }else { printf("%d: bad function call\n", line); exit(-1); }if (t) { *++e = ADJ; *++e = t; }ty = d[Type];}else if (d[Class] == Num) { *++e = IMM; *++e = d[Val]; ty = INT; }else {if (d[Class] == Loc) { *++e = LEA; *++e = loc - d[Val]; }else if (d[Class] == Glo) { *++e = IMM; *++e = d[Val]; }else { printf("%d: undefined variable\n", line); exit(-1); }*++e = ((ty = d[Type]) == CHAR) ? LC : LI;}}else if (tk == '(') {next();if (tk == Int || tk == Char) {t = (tk == Int) ? INT : CHAR; next();while (tk == Mul) { next(); t = t + PTR; }if (tk == ')') next(); else { printf("%d: bad cast\n", line); exit(-1); }expr(Inc);ty = t;}else {expr(Assign);if (tk == ')') next(); else { printf("%d: close paren expected\n", line); exit(-1); }}}else if (tk == Mul) {next(); expr(Inc);if (ty > INT) ty = ty - PTR; else { printf("%d: bad dereference\n", line); exit(-1); }*++e = (ty == CHAR) ? LC : LI;}else if (tk == And) {next(); expr(Inc);if (*e == LC || *e == LI) --e; else { printf("%d: bad address-of\n", line); exit(-1); }ty = ty + PTR;}else if (tk == '!') { next(); expr(Inc); *++e = PSH; *++e = IMM; *++e = 0; *++e = EQ; ty = INT; }else if (tk == '~') { next(); expr(Inc); *++e = PSH; *++e = IMM; *++e = -1; *++e = XOR; ty = INT; }else if (tk == Add) { next(); expr(Inc); ty = INT; }else if (tk == Sub) {next(); *++e = IMM;if (tk == Num) { *++e = -ival; next(); } else { *++e = -1; *++e = PSH; expr(Inc); *++e = MUL; }ty = INT;}else if (tk == Inc || tk == Dec) {t = tk; next(); expr(Inc);if (*e == LC) { *e = PSH; *++e = LC; }else if (*e == LI) { *e = PSH; *++e = LI; }else { printf("%d: bad lvalue in pre-increment\n", line); exit(-1); }*++e = PSH;*++e = IMM; *++e = (ty > PTR) ? sizeof(int) : sizeof(char);*++e = (t == Inc) ? ADD : SUB;*++e = (ty == CHAR) ? SC : SI;}else { printf("%d: bad expression\n", line); exit(-1); }while (tk >= lev) { // "precedence climbing" or "Top Down Operator Precedence" methodt = ty;if (tk == Assign) {next();if (*e == LC || *e == LI) *e = PSH; else { printf("%d: bad lvalue in assignment\n", line); exit(-1); }expr(Assign); *++e = ((ty = t) == CHAR) ? SC : SI;}else if (tk == Cond) {next();*++e = BZ; d = ++e;expr(Assign);if (tk == ':') next(); else { printf("%d: conditional missing colon\n", line); exit(-1); }*d = (int)(e + 3); *++e = JMP; d = ++e;expr(Cond);*d = (int)(e + 1);}else if (tk == Lor) { next(); *++e = BNZ; d = ++e; expr(Lan); *d = (int)(e + 1); ty = INT; }else if (tk == Lan) { next(); *++e = BZ;  d = ++e; expr(Or);  *d = (int)(e + 1); ty = INT; }else if (tk == Or)  { next(); *++e = PSH; expr(Xor); *++e = OR;  ty = INT; }else if (tk == Xor) { next(); *++e = PSH; expr(And); *++e = XOR; ty = INT; }else if (tk == And) { next(); *++e = PSH; expr(Eq);  *++e = AND; ty = INT; }else if (tk == Eq)  { next(); *++e = PSH; expr(Lt);  *++e = EQ;  ty = INT; }else if (tk == Ne)  { next(); *++e = PSH; expr(Lt);  *++e = NE;  ty = INT; }else if (tk == Lt)  { next(); *++e = PSH; expr(Shl); *++e = LT;  ty = INT; }else if (tk == Gt)  { next(); *++e = PSH; expr(Shl); *++e = GT;  ty = INT; }else if (tk == Le)  { next(); *++e = PSH; expr(Shl); *++e = LE;  ty = INT; }else if (tk == Ge)  { next(); *++e = PSH; expr(Shl); *++e = GE;  ty = INT; }else if (tk == Shl) { next(); *++e = PSH; expr(Add); *++e = SHL; ty = INT; }else if (tk == Shr) { next(); *++e = PSH; expr(Add); *++e = SHR; ty = INT; }else if (tk == Add) {next(); *++e = PSH; expr(Mul);if ((ty = t) > PTR) { *++e = PSH; *++e = IMM; *++e = sizeof(int); *++e = MUL;  }*++e = ADD;}else if (tk == Sub) {next(); *++e = PSH; expr(Mul);if (t > PTR && t == ty) { *++e = SUB; *++e = PSH; *++e = IMM; *++e = sizeof(int); *++e = DIV; ty = INT; }else if ((ty = t) > PTR) { *++e = PSH; *++e = IMM; *++e = sizeof(int); *++e = MUL; *++e = SUB; }else *++e = SUB;}else if (tk == Mul) { next(); *++e = PSH; expr(Inc); *++e = MUL; ty = INT; }else if (tk == Div) { next(); *++e = PSH; expr(Inc); *++e = DIV; ty = INT; }else if (tk == Mod) { next(); *++e = PSH; expr(Inc); *++e = MOD; ty = INT; }else if (tk == Inc || tk == Dec) {if (*e == LC) { *e = PSH; *++e = LC; }else if (*e == LI) { *e = PSH; *++e = LI; }else { printf("%d: bad lvalue in post-increment\n", line); exit(-1); }*++e = PSH; *++e = IMM; *++e = (ty > PTR) ? sizeof(int) : sizeof(char);*++e = (tk == Inc) ? ADD : SUB;*++e = (ty == CHAR) ? SC : SI;*++e = PSH; *++e = IMM; *++e = (ty > PTR) ? sizeof(int) : sizeof(char);*++e = (tk == Inc) ? SUB : ADD;next();}else if (tk == Brak) {next(); *++e = PSH; expr(Assign);if (tk == ']') next(); else { printf("%d: close bracket expected\n", line); exit(-1); }if (t > PTR) { *++e = PSH; *++e = IMM; *++e = sizeof(int); *++e = MUL;  }else if (t < PTR) { printf("%d: pointer type expected\n", line); exit(-1); }*++e = ADD;*++e = ((ty = t - PTR) == CHAR) ? LC : LI;}else { printf("%d: compiler error tk=%d\n", line, tk); exit(-1); }}
}

这个我没看懂,建议看手把手教你构建 C 语言编译器(8)- 表达式 | 三点水 (lotabout.me)

b站有up讲的挺不错的「700行手写编译器」Part 3.3:表达式与优先级爬山_哔哩哔哩_bilibili

第八步 实现指令

  while (1) {i = *pc++; ++cycle;if (debug) {printf("%d> %.4s", cycle,&"LEA ,IMM ,JMP ,JSR ,BZ  ,BNZ ,ENT ,ADJ ,LEV ,LI  ,LC  ,SI  ,SC  ,PSH ,""OR  ,XOR ,AND ,EQ  ,NE  ,LT  ,GT  ,LE  ,GE  ,SHL ,SHR ,ADD ,SUB ,MUL ,DIV ,MOD ,""OPEN,READ,CLOS,PRTF,MALC,FREE,MSET,MCMP,EXIT,"[i * 5]);if (i <= ADJ) printf(" %d\n", *pc); else printf("\n");}if      (i == LEA) a = (int)(bp + *pc++);                             // load local addresselse if (i == IMM) a = *pc++;                                         // load global address or immediateelse if (i == JMP) pc = (int *)*pc;                                   // jumpelse if (i == JSR) { *--sp = (int)(pc + 1); pc = (int *)*pc; }        // jump to subroutineelse if (i == BZ)  pc = a ? pc + 1 : (int *)*pc;                      // branch if zeroelse if (i == BNZ) pc = a ? (int *)*pc : pc + 1;                      // branch if not zeroelse if (i == ENT) { *--sp = (int)bp; bp = sp; sp = sp - *pc++; }     // enter subroutineelse if (i == ADJ) sp = sp + *pc++;                                   // stack adjustelse if (i == LEV) { sp = bp; bp = (int *)*sp++; pc = (int *)*sp++; } // leave subroutineelse if (i == LI)  a = *(int *)a;                                     // load intelse if (i == LC)  a = *(char *)a;                                    // load charelse if (i == SI)  *(int *)*sp++ = a;                                 // store intelse if (i == SC)  a = *(char *)*sp++ = a;                            // store charelse if (i == PSH) *--sp = a;                                         // pushelse if (i == OR)  a = *sp++ |  a;else if (i == XOR) a = *sp++ ^  a;else if (i == AND) a = *sp++ &  a;else if (i == EQ)  a = *sp++ == a;else if (i == NE)  a = *sp++ != a;else if (i == LT)  a = *sp++ <  a;else if (i == GT)  a = *sp++ >  a;else if (i == LE)  a = *sp++ <= a;else if (i == GE)  a = *sp++ >= a;else if (i == SHL) a = *sp++ << a;else if (i == SHR) a = *sp++ >> a;else if (i == ADD) a = *sp++ +  a;else if (i == SUB) a = *sp++ -  a;else if (i == MUL) a = *sp++ *  a;else if (i == DIV) a = *sp++ /  a;else if (i == MOD) a = *sp++ %  a;else if (i == OPEN) a = open((char *)sp[1], *sp);else if (i == READ) a = read(sp[2], (char *)sp[1], *sp);else if (i == CLOS) a = close(*sp);else if (i == PRTF) { t = sp + pc[1]; a = printf((char *)t[-1], t[-2], t[-3], t[-4], t[-5], t[-6]); }else if (i == MALC) a = (int)malloc(*sp);else if (i == FREE) free((void *)*sp);else if (i == MSET) a = (int)memset((char *)sp[2], sp[1], *sp);else if (i == MCMP) a = memcmp((char *)sp[2], (char *)sp[1], *sp);else if (i == EXIT) { printf("exit(%d) cycle = %d\n", *sp, cycle); return *sp; }else { printf("unknown instruction = %d! cycle = %d\n", i, cycle); return -1; }}

这个if(debug)很有意思,在ADJ及之前的都有参数,另外把指令空格设置好,与enum相对应好,这样它能很容易的输出指令。

而open到exit这个是我们为了自举外加的指令,我们注意参数从右往左传到函数,如sp[2],sp[1],*sp,这是因为函数从右往左传参。

总结:这是一个优秀的编译器,只有500多行代码,非常的精简。如果要想了解编译器,这是一个不错的选择,如果要仔细研究,那得看tinycc这种正式的东西了(毕竟这玩意只是一个玩具)。这代码我还有许多没细说的,比如函数的传参,比如load/save int/char之类。大家慢慢看,如果有兴趣,自己也写一个呗!

浅析c4编译器--一个优秀的编译器是如何生成的相关推荐

  1. 实现一个简单的编译器

    简单的说 编译器 就是语言翻译器,它一般将高级语言翻译成更低级的语言,如 GCC 可将 C/C++ 语言翻译成可执行机器语言,Java 编译器可以将 Java 源代码翻译成 Java 虚拟机可以执行的 ...

  2. 使用JavaScript实现一个简单的编译器

    本文同步在个人博客shymean.com上,欢迎关注 在前端开发中也会或多或少接触到一些与编译相关的内容,常见的有 将ES6.7代码编译成ES5的代码 将SCSS.LESS代码转换成浏览器支持的CSS ...

  3. java简单编译器源代码_25行代码实现一个简单的编译器

    起因 <25行JavaScript语句实现一个简单的编译器>实现的是一个简单到不能再简单的玩具的玩具,他的魔法是函数式编程简化了js代码.java 8提供了函数式编程的支持,昨晚脑子抽风突 ...

  4. 学习较底层编程:动手写一个C语言编译器

    动手编写一个编译器,学习一下较为底层的编程方式,是一种学习计算机到底是如何工作的非常有效方法. 编译器通常被看作是十分复杂的工程.事实上,编写一个产品级的编译器也确实是一个庞大的任务.但是写一个小巧可 ...

  5. Qt实现一个简单的编译器(软件生成器)

    Qt实现一个简单的编译器(软件生成器) 本文章只记录如何用Qt实现一个简单编译器,即点击本软件中的按钮便可在另一目录中生成一个新的软件(与本软件不冲突). 文章目录 Qt实现一个简单的编译器(软件生成 ...

  6. java实现编译器_实现一个简单的编译器

    简单的说 编译器 就是语言翻译器,它一般将高级语言翻译成更低级的语言,如 GCC 可将 C/C++ 语言翻译成可执行机器语言,Java 编译器可以将 Java 源代码翻译成 Java 虚拟机可以执行的 ...

  7. 第一个C语言编译器是怎样编写的

    点击蓝字 关注我们 因公众号更改推送规则,请点"在看"并加"星标"第一时间获取精彩技术分享 来源于网络,侵删 不知道你有没有想过,某种编程语言的第一个编译器是怎 ...

  8. 第一个C语言编译器是怎样编写的?

    点击上方蓝字关注我,了解更多咨询 以我们嵌入式开发中经常使用的C语言为例,我们来介绍一下第一个C语言编译器的来源. 还是让我们回顾一下C语言历史:1970年Tomphson和Ritchie在BCPL( ...

  9. 【知乎】怎么成为一个优秀的程序员,而不是一个优秀的码农?

    怎么成为一个优秀的程序员,而不是一个优秀的码农? 9 条评论 分享 默认排序按时间排序 98 个回答 3844赞同反对,不会显示你的姓名 萧井陌 微信公众号:炼瓜研究所 技术社区 - 3844 人赞同 ...

最新文章

  1. 软件体系结构的第3次实验(软件体系结构风格之应用 )
  2. 漫话云计算,这次加了点儿剧情
  3. Tips--动态申请内存的优势
  4. html中抓取span中的值,JavaScript如何获取span元素的文本?
  5. python文件读写到list_Python文件读写
  6. [UITableView]简介
  7. Hibernate异常归总
  8. 51单片机简易智能温度检测系统
  9. 西电2019计算机等级考试,西安电子科技大学2019《计算方法》期末考试试题
  10. C语言I博客作业03
  11. 全网最全JAVA面试八股文,终于整理完了
  12. html2pdf无法导出图片解决方案(2020版)
  13. 【第10章】接口与Lambda表达式
  14. 西华大学副教授评聘条件
  15. Macbook Pro常用快捷键及问题汇总
  16. iOS面试题经典语录(持续收集中...)
  17. 【论文写作】如何画出好看的图表
  18. NSSCTF刷题wp——单表替换密码
  19. bat: 删除文件、文件夹
  20. java txtreader_Java读取和写入文本文件内容

热门文章

  1. AndroidStudio0.5.5发布
  2. 计算机视觉摔倒检测,有关摔倒检测数据集(fall detection databases)
  3. 零基础 学 零知识证明
  4. 使用Arduino开发板控制伺服电机
  5. 因为计算机丢失vrender,3DMAX打开文件时显示缺少dll解决方案
  6. 用户登录程序C语言实现
  7. Spark学习(1)-Spark基础
  8. Superset系列6-制作折线图
  9. 字符串转为 double 类型
  10. java的jre和jdk