├── README.md ├── ch13 ├── bison_1 │ ├── calc.l │ ├── calc.y │ └── makefile └── bison_2 │ ├── makefile │ ├── parser.y │ ├── scanner.l │ ├── test.asm │ └── test.c ├── ch14 ├── p0.1 │ ├── makefile │ ├── parser.y │ ├── pysim.py │ ├── scanner.l │ ├── test.asm │ └── test.c ├── p0.5 │ ├── makefile │ ├── parser.y │ ├── pysim.py │ ├── scanner.l │ ├── test.asm │ └── test.c └── p1.0 │ ├── makefile │ ├── parser.y │ ├── pysim.py │ ├── samples.zip │ ├── samples │ ├── for_gcc_build.hh │ ├── sample_0_helloworld.c │ ├── sample_1_io.c │ ├── sample_2_arithmetic.c │ ├── sample_3_compare.c │ ├── sample_4_logic.c │ ├── sample_5_ifwhile.c │ └── sample_6_function.c │ ├── scanner.l │ ├── test.asm │ ├── test.c │ └── test_samples.sh ├── ch15 ├── c01-hello-nasm │ ├── hello.nasm │ └── makefile ├── c02-hello-macro-nasm │ ├── hello.nasm │ ├── hello.nasm.expand │ ├── macro.inc │ └── makefile ├── c03-print-macro │ ├── macro.inc │ ├── makefile │ ├── print.nasm │ └── tio.c ├── c04-readint-macro │ ├── macro.inc │ ├── makefile │ ├── test.nasm │ └── tio.c ├── c05-simple-macro │ ├── macro.inc │ ├── makefile │ ├── test.nasm │ └── tio.c └── c06-func-macro │ ├── macro.inc │ ├── makefile │ ├── test.funcmacro │ ├── test.origin.pcode │ ├── test.pcode │ ├── test.pcode.expand │ └── tio.c ├── ch16 ├── README.md └── c01-new-frontend │ ├── makefile │ ├── parser.y │ ├── scanner.l │ ├── test.c │ ├── test.funcmacro │ └── test.pcode ├── ch2 ├── for_gcc_build.hh └── tinyc.c ├── ch4 ├── pcode_1.asm ├── pcode_2.asm └── pysim.py ├── ch5 └── factor.asm ├── ch7 └── scan.py ├── ch8 ├── hide-digits.l ├── makefile ├── tinyc_scanner │ ├── makefile │ ├── samples.zip │ ├── samples │ │ ├── ilegal_input.c │ │ ├── sample_0_helloworld.c │ │ ├── sample_1_io.c │ │ ├── sample_2_arithmetic.c │ │ ├── sample_3_compare.c │ │ ├── sample_4_logic.c │ │ ├── sample_5_ifwhile.c │ │ └── sample_6_function.c │ ├── scanner.l │ ├── test.sh │ └── token.h └── word-spliter.l └── tinyc ├── build.sh ├── samples ├── for_gcc_build.hh ├── sample_0_helloworld.c ├── sample_1_io.c ├── sample_2_arithmetic.c ├── sample_3_compare.c ├── sample_4_logic.c ├── sample_5_ifwhile.c └── sample_6_function.c ├── sources ├── macro.inc ├── parser.y ├── pysim.py ├── pysimulate ├── scanner.l ├── tcc └── tio.c ├── test.c └── testall.sh /README.md: -------------------------------------------------------------------------------- 1 | https://pandolia.net/tinyc/ 2 | -------------------------------------------------------------------------------- /ch13/bison_1/calc.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "y.tab.h" 3 | %} 4 | 5 | %% 6 | [0-9]+ { yylval = atoi(yytext); return T_NUM; } 7 | [-/+*()\n] { return yytext[0]; } 8 | . { return 0; /* end when meet everything else */ } 9 | %% 10 | 11 | int yywrap(void) { 12 | return 1; 13 | } 14 | -------------------------------------------------------------------------------- /ch13/bison_1/calc.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | void yyerror(const char* msg) {} 4 | %} 5 | 6 | %token T_NUM 7 | 8 | %left '+' '-' 9 | %left '*' '/' 10 | 11 | %% 12 | 13 | S : S E '\n' { printf("ans = %d\n", $2); } 14 | | /* empty */ { /* empty */ } 15 | ; 16 | 17 | E : E '+' E { $$ = $1 + $3; } 18 | | E '-' E { $$ = $1 - $3; } 19 | | E '*' E { $$ = $1 * $3; } 20 | | E '/' E { $$ = $1 / $3; } 21 | | T_NUM { $$ = $1; } 22 | | '(' E ')' { $$ = $2; } 23 | ; 24 | 25 | %% 26 | 27 | int main() { 28 | return yyparse(); 29 | } 30 | -------------------------------------------------------------------------------- /ch13/bison_1/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | OBJ = lex.yy.o y.tab.o 3 | 4 | calc: $(OBJ) 5 | $(CC) -o calc $(OBJ) 6 | 7 | lex.yy.c: calc.l y.tab.c 8 | flex $< 9 | 10 | y.tab.c: calc.y 11 | bison -vdty $< 12 | 13 | clean: 14 | rm -f *.o lex.yy.c y.tab.c y.tab.h y.output calc -------------------------------------------------------------------------------- /ch13/bison_2/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | OUT = tcc 3 | OBJ = lex.yy.o y.tab.o 4 | SCANNER = scanner.l 5 | PARSER = parser.y 6 | 7 | build: $(OUT) 8 | 9 | run: $(OUT) 10 | ./$(OUT) < test.c > test.asm 11 | 12 | clean: 13 | rm -f *.o lex.yy.c y.tab.c y.tab.h y.output $(OUT) 14 | 15 | $(OUT): $(OBJ) 16 | $(CC) -o $(OUT) $(OBJ) 17 | 18 | lex.yy.c: $(SCANNER) y.tab.c 19 | flex $< 20 | 21 | y.tab.c: $(PARSER) 22 | bison -vdty $< 23 | -------------------------------------------------------------------------------- /ch13/bison_2/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | void yyerror(const char*); 5 | #define YYSTYPE char * 6 | %} 7 | 8 | %token T_IntConstant T_Identifier 9 | 10 | %left '+' '-' 11 | %left '*' '/' 12 | %right U_neg 13 | 14 | %% 15 | 16 | S : Stmt 17 | | S Stmt 18 | ; 19 | 20 | Stmt: T_Identifier '=' E ';' { printf("pop %s\n\n", $1); } 21 | ; 22 | 23 | E : E '+' E { printf("add\n"); } 24 | | E '-' E { printf("sub\n"); } 25 | | E '*' E { printf("mul\n"); } 26 | | E '/' E { printf("div\n"); } 27 | | '-' E %prec U_neg { printf("neg\n"); } 28 | | T_IntConstant { printf("push %s\n", $1); } 29 | | T_Identifier { printf("push %s\n", $1); } 30 | | '(' E ')' { /* empty */ } 31 | ; 32 | 33 | %% 34 | 35 | int main() { 36 | return yyparse(); 37 | } 38 | -------------------------------------------------------------------------------- /ch13/bison_2/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | %} 8 | 9 | OPERATOR [-/+*()=;] 10 | INTEGER [0-9]+ 11 | IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]* 12 | WHITESPACE [ \t]* 13 | 14 | %% 15 | {OPERATOR} { return yytext[0]; } 16 | {INTEGER} { yylval = strdup(yytext); return T_IntConstant; } 17 | {IDENTIFIER} { yylval = strdup(yytext); return T_Identifier; } 18 | {WHITESPACE} { /* ignore every whitespcace */ } 19 | \n { cur_line++; } 20 | . { unrecognized_char(yytext[0]); } 21 | %% 22 | 23 | int yywrap(void) { 24 | return 1; 25 | } 26 | 27 | void unrecognized_char(char c) { 28 | char buf[32] = "Unrecognized character: ?"; 29 | buf[24] = c; 30 | yyerror(buf); 31 | } 32 | 33 | void yyerror(const char *msg) { 34 | printf("Error at line %d:\n\t%s\n", cur_line, msg); 35 | exit(1); 36 | } 37 | -------------------------------------------------------------------------------- /ch13/bison_2/test.asm: -------------------------------------------------------------------------------- 1 | push 1 2 | push 2 3 | push 2 4 | push 2 5 | add 6 | mul 7 | add 8 | pop a 9 | 10 | push c 11 | push d 12 | add 13 | pop b 14 | 15 | push f 16 | push 7 17 | push 8 18 | mul 19 | push 9 20 | div 21 | add 22 | pop e 23 | 24 | -------------------------------------------------------------------------------- /ch13/bison_2/test.c: -------------------------------------------------------------------------------- 1 | a = 1 + 2 * ( 2 + 2 ); 2 | b = c + d; 3 | e = f + 7 * 8 / 9; -------------------------------------------------------------------------------- /ch14/p0.1/makefile: -------------------------------------------------------------------------------- 1 | OUT = tcc 2 | TESTFILE = test.c 3 | SCANNER = scanner.l 4 | PARSER = parser.y 5 | 6 | CC = gcc 7 | OBJ = lex.yy.o y.tab.o 8 | TESTOUT = $(basename $(TESTFILE)).asm 9 | OUTFILES = lex.yy.c y.tab.c y.tab.h y.output $(OUT) 10 | 11 | .PHONY: build test simulate clean 12 | 13 | build: $(OUT) 14 | 15 | test: $(TESTOUT) 16 | 17 | simulate: $(TESTOUT) 18 | python pysim.py $< 19 | 20 | clean: 21 | rm -f *.o $(OUTFILES) 22 | 23 | $(TESTOUT): $(TESTFILE) $(OUT) 24 | ./$(OUT) < $< > $@ 25 | 26 | $(OUT): $(OBJ) 27 | $(CC) -o $(OUT) $(OBJ) 28 | 29 | lex.yy.c: $(SCANNER) y.tab.c 30 | flex $< 31 | 32 | y.tab.c: $(PARSER) 33 | bison -vdty $< 34 | -------------------------------------------------------------------------------- /ch14/p0.1/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | void yyerror(const char*); 5 | #define YYSTYPE char * 6 | %} 7 | 8 | %token T_StringConstant T_IntConstant T_Identifier T_Int T_Print 9 | 10 | %left '+' '-' 11 | %left '*' '/' 12 | %right U_neg 13 | 14 | %% 15 | 16 | S: 17 | Stmt { /* empty */ } 18 | | S Stmt { /* empty */ } 19 | ; 20 | 21 | Stmt: 22 | VarDecl ';' { printf("\n\n"); } 23 | | Assign { /* empty */ } 24 | | Print { /* empty */ } 25 | ; 26 | 27 | VarDecl: 28 | T_Int T_Identifier { printf("var %s", $2); } 29 | | VarDecl ',' T_Identifier { printf(", %s", $3); } 30 | ; 31 | 32 | Assign: 33 | T_Identifier '=' E ';' { printf("pop %s\n\n", $1); } 34 | ; 35 | 36 | Print: 37 | T_Print '(' T_StringConstant Actuals ')' ';' 38 | { printf("print %s\n\n", $3); } 39 | ; 40 | 41 | Actuals: 42 | /* empty */ { /* empty */ } 43 | | Actuals ',' E { /* empty */ } 44 | ; 45 | 46 | E: 47 | E '+' E { printf("add\n"); } 48 | | E '-' E { printf("sub\n"); } 49 | | E '*' E { printf("mul\n"); } 50 | | E '/' E { printf("div\n"); } 51 | | '-' E %prec U_neg { printf("neg\n"); } 52 | | T_IntConstant { printf("push %s\n", $1); } 53 | | T_Identifier { printf("push %s\n", $1); } 54 | | '(' E ')' { /* empty */ } 55 | ; 56 | 57 | %% 58 | 59 | int main() { 60 | return yyparse(); 61 | } 62 | -------------------------------------------------------------------------------- /ch14/p0.1/pysim.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | PY3 = sys.version_info[0] == 3 4 | 5 | comment_mark = ";%" 6 | code = [] 7 | stack = [] 8 | var_table = {} 9 | label_table = {} 10 | func_table = {} 11 | eip = 0 12 | printout = [] 13 | debug = False 14 | 15 | def is_valid_identifier(ident): 16 | if ident == "": 17 | return False 18 | if not (ident[0].isalpha() or ident[0] == '_'): 19 | return False 20 | for ch in ident[1:]: 21 | if not (ch.isalnum() or ch == '_'): 22 | return False 23 | return True 24 | 25 | def assemb_error(line, msg): 26 | display(pause=False) 27 | print(line) 28 | print("^^^Error at last line: %s" % msg) 29 | exit(-1) 30 | 31 | def run_error(msg="Wrong instruction format"): 32 | code[eip][0] = "**%s**" % msg 33 | printout.append(msg) 34 | display(pause=False) 35 | exit(-1) 36 | 37 | def read_file(filename): 38 | if PY3: 39 | return open(filename, 'r').readlines() 40 | 41 | return file(filename).readlines() 42 | 43 | def get_input(msg): 44 | if PY3: 45 | return input(msg) 46 | 47 | return raw_input(msg) 48 | 49 | def table_has_key(table, key): 50 | if PY3: 51 | return key in table 52 | 53 | return table.has_key(key) 54 | 55 | def assemb(asmfilename): 56 | if len(sys.argv) > 2 and (sys.argv[2] == '-a' or sys.argv[2] == '-da'): 57 | code.append(('', '$main', '')) 58 | code.append(('', 'exit', '~')) 59 | 60 | label = "" 61 | for line in read_file(asmfilename): 62 | line = line.strip() 63 | if line == "" or line[0] in comment_mark: 64 | continue 65 | 66 | _label, sep, ist = line.partition(':') 67 | if sep and _label.find('"') == -1 and _label.find("'") == -1: 68 | _label, ist = _label.strip(), ist.strip() 69 | if not check_label(_label): 70 | assemb_error(line, "Wrong label") 71 | label = '%s,%s' % (label, _label) if label else _label 72 | if ist == "" or ist[0] in comment_mark: 73 | continue 74 | elif len(line) >= 7 and line[:7] == 'ENDFUNC': 75 | label = '%s,%s' % (label, 'ENDFUNC') \ 76 | if label else 'ENDFUNC' 77 | ist = 'ret' 78 | else: 79 | ist = line 80 | 81 | dire, sep, arg = ist.partition(' ') 82 | 83 | if len(dire) > 4 and \ 84 | (dire[-4:] == '.arg' or dire[-4:] == '.var'): 85 | dire = dire[-3:] 86 | 87 | code.append( [label, dire, arg.strip()] ) 88 | label = "" 89 | 90 | code.append(('', 'exit', '0')) 91 | 92 | def check_label(label): 93 | if label == "": 94 | return False 95 | 96 | func, sep, funcName = label.partition(' @') 97 | 98 | if sep: 99 | if func.strip() != 'FUNC' \ 100 | or not is_valid_identifier(funcName) \ 101 | or table_has_key(func_table, funcName): 102 | return False 103 | else: 104 | func_table[funcName] = len(code) 105 | return True 106 | else: 107 | if not is_valid_identifier(label) \ 108 | or table_has_key(func_table, label) \ 109 | or table_has_key(label_table, label): 110 | return False 111 | else: 112 | label_table[label] = len(code) 113 | return True 114 | 115 | def trim(s, size): 116 | return s[:size-3]+"..." if len(s) > size else s 117 | 118 | def display(pause=True): 119 | num_code_lines, num_terminal_lines = 24, 8 120 | if os.system("clear"): os.system('cls') 121 | print("%32s%-40s| %-13s|Bind var" % ("", "Code", "Stack")) 122 | j = 0 123 | for i in range( \ 124 | max(eip+1-num_code_lines, 0), max(eip+1, num_code_lines) ): 125 | if i < len(code): 126 | label, dire, arg = code[i] 127 | line = trim(dire + " " + arg, 40) 128 | label = trim(label, 28) 129 | else: 130 | label, line = "", "" 131 | 132 | if label: label += ':' 133 | point = " ->" if i == eip else "" 134 | st = stack[j] if j < len(stack) else "" 135 | st = "(RetInfo)" if type(st) is tuple else str(st) 136 | stvar = var_table.get(j, "") 137 | if j == len(stack) - 1: stvar += "<-" 138 | 139 | print("%29s%3s%-40s| %-13s|%s" % \ 140 | (label, point, line, st, stvar)) 141 | 142 | j += 1 143 | 144 | print("***Terminal***") 145 | n = len(printout) 146 | for i in range( \ 147 | max(n-num_terminal_lines, 0), max(n, num_terminal_lines) ): 148 | print(printout[i] if i < n else "") 149 | if i == n and not pause: 150 | break 151 | 152 | if pause: 153 | global debug 154 | if get_input("\npress enter to step, -r to run.") == "-r": 155 | debug = False 156 | 157 | def run(): 158 | global eip 159 | eip = 0 160 | del stack[:] 161 | 162 | while True: 163 | if debug: display() 164 | label, dire, arg = code[eip] 165 | if dire[0] == '$': 166 | action, arg = call, dire[1:] 167 | if not is_valid_identifier(arg): 168 | run_error("Wrong identifier") 169 | else: 170 | if not is_valid_identifier(dire): 171 | run_error("Wrong identifier") 172 | try: 173 | action = eval("do_" + dire) 174 | except NameError: 175 | run_error("Unknown instruction") 176 | action(arg) 177 | eip += 1 178 | 179 | def do_var(arg): 180 | if arg == "": return 181 | for var in arg.split(','): 182 | var = var.strip() 183 | if not is_valid_identifier(var) or table_has_key(var_table, var): 184 | run_error("Wrong var name") 185 | var_table[var] = len(stack) 186 | var_table[len(stack)] = var 187 | stack.append("/") 188 | 189 | def do_push(arg): 190 | try: 191 | arg = int(arg) 192 | except ValueError: 193 | try: 194 | arg = stack[var_table[arg]] 195 | except KeyError: 196 | run_error("Undefined variable") 197 | if type(arg) is not int: 198 | run_error("Cannot push uninitialed value") 199 | stack.append(arg) 200 | 201 | def do_pop(arg): 202 | value = stack.pop() 203 | if arg == "": 204 | return 205 | if type(value) is not int: 206 | run_error("Cannot pop non-number value to variable") 207 | try: 208 | stack[var_table[arg]] = value 209 | except KeyError: 210 | run_error("Undefined variable") 211 | 212 | def do_exit(arg): 213 | global going, exit_code 214 | going = False 215 | 216 | if arg == "~": 217 | exit_code = stack[-1] 218 | elif arg: 219 | try: 220 | exit_code = int(arg) 221 | except ValueError: 222 | try: 223 | exit_code = stack[var_table[arg]] 224 | except KeyError: 225 | run_error("Undefined variable") 226 | 227 | if type(exit_code) is not int: 228 | run_error("Wrong exit code") 229 | 230 | if debug: 231 | display(pause=False) 232 | 233 | exit(exit_code) 234 | 235 | 236 | 237 | def do_add(arg): stack[-2] += stack[-1]; stack.pop() 238 | def do_sub(arg): stack[-2] -= stack[-1]; stack.pop() 239 | def do_mul(arg): stack[-2] *= stack[-1]; stack.pop() 240 | def do_div(arg): stack[-2] /= stack[-1]; stack.pop() 241 | def do_mod(arg): stack[-2] %= stack[-1]; stack.pop() 242 | def do_and(arg): stack[-2] = int(stack[-2]!=0 and stack[-1]!=0); stack.pop() 243 | def do_or(arg): stack[-2] = int(stack[-2]!=0 or stack[-1]!=0); stack.pop() 244 | def do_cmpeq(arg): stack[-2] = int(stack[-2]==stack[-1]);stack.pop() 245 | def do_cmpne(arg): stack[-2] = int(stack[-2]!=stack[-1]);stack.pop() 246 | def do_cmpgt(arg): stack[-2] = int(stack[-2]>stack[-1]); stack.pop() 247 | def do_cmplt(arg): stack[-2] = int(stack[-2]=stack[-1]);stack.pop() 249 | def do_cmple(arg): stack[-2] = int(stack[-2]<=stack[-1]);stack.pop() 250 | def do_neg(arg): stack[-1] = -stack[-1] 251 | def do_not(arg): stack[-1] = int(not stack[-1]) 252 | 253 | def do_print(fmt): 254 | if len(fmt) < 2 or fmt[0] != fmt[-1] or fmt[0] not in '"\'': 255 | run_error("Format string error") 256 | argc = fmt.count("%d") 257 | out = fmt[1:-1] % tuple(stack[len(stack)-argc:]) 258 | print(out) 259 | printout.append(out) 260 | del stack[len(stack)-argc:] 261 | 262 | def do_readint(msg): 263 | if len(msg) < 2 or msg[0] != msg[-1] or msg[-1] not in '"\'': 264 | run_error("Message string error") 265 | msg = msg.strip('"').strip("'") 266 | if debug: display(pause=False) 267 | string = get_input(msg) 268 | try: 269 | value = int(string) 270 | except ValueError: 271 | value = 0 272 | stack.append(value) 273 | printout.append("\n " + msg + str(value)) 274 | 275 | def do_jmp(label): 276 | global eip 277 | try: 278 | # note: here we set eip just befor the label, 279 | # and when back to run(), we do eip += 1 280 | eip = label_table[label] - 1 281 | except KeyError: 282 | run_error("Wrong label") 283 | 284 | def do_jz(label): 285 | global eip 286 | try: 287 | # set eip just befor the label, 288 | # when back to run(), do eip += 1 289 | new_eip = label_table[label] - 1 290 | except KeyError: 291 | run_error("Wrong label") 292 | if stack.pop() == 0: 293 | eip = new_eip 294 | 295 | def call(funcName): 296 | global var_table, eip 297 | 298 | try: 299 | entry = func_table[funcName] 300 | except KeyError: 301 | run_error("Undefined function") 302 | 303 | if code[entry][1] == "arg": 304 | arg_list = code[entry][2].split(',') 305 | else: 306 | arg_list = [] 307 | 308 | new_var_table = {} 309 | for addr, arg in enumerate(arg_list, len(stack)-len(arg_list)): 310 | arg = arg.strip() 311 | if not is_valid_identifier(arg) or table_has_key(new_var_table, arg): 312 | run_error("Wrong arg name") 313 | 314 | new_var_table[arg] = addr 315 | new_var_table[addr] = arg 316 | 317 | stack.append( (len(arg_list), eip, var_table) ) 318 | var_table = new_var_table 319 | eip = entry if len(arg_list) else entry -1 320 | 321 | def do_ret(arg): 322 | global var_table, eip 323 | 324 | if arg == "~": 325 | retval = stack[-1] 326 | elif arg: 327 | try: 328 | retval = int(arg) 329 | except ValueError: 330 | try: 331 | retval = stack[var_table[arg]] 332 | except KeyError: 333 | run_error("Undefined variable") 334 | else: 335 | retval = '/' 336 | 337 | i = len(stack) - 1 338 | while type(stack[i]) is not tuple: 339 | i -= 1 340 | argc, eip, var_table = stack[i] 341 | del stack[i-argc:] 342 | stack.append(retval) 343 | 344 | if __name__ == "__main__": 345 | asmfileName = sys.argv[1] 346 | if len(sys.argv) > 2: 347 | debug = sys.argv[2] == '-d' or sys.argv[2] == '-da' 348 | assemb(asmfileName) 349 | run() -------------------------------------------------------------------------------- /ch14/p0.1/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | #define _DUPTEXT {yylval = strdup(yytext);} 8 | %} 9 | 10 | /* note \042 is '"' */ 11 | 12 | OPERATOR ([-/+*()=,;]) 13 | INTEGER ([0-9]+) 14 | STRING (\042[^\042\n]*\042) 15 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 16 | WHITESPACE ([ \t]*) 17 | 18 | %% 19 | {OPERATOR} { return yytext[0]; } 20 | "int" { return T_Int; } 21 | "print" { return T_Print; } 22 | 23 | {INTEGER} { _DUPTEXT; return T_IntConstant; } 24 | {STRING} { _DUPTEXT; return T_StringConstant; } 25 | {IDENTIFIER} { _DUPTEXT; return T_Identifier; } 26 | 27 | {WHITESPACE} { /* ignore every whitespace */ } 28 | \n { cur_line++; } 29 | . { unrecognized_char(yytext[0]); } 30 | %% 31 | 32 | int yywrap(void) { 33 | return 1; 34 | } 35 | 36 | void unrecognized_char(char c) { 37 | char buf[32] = "Unrecognized character: ?"; 38 | buf[24] = c; 39 | yyerror(buf); 40 | } 41 | 42 | void yyerror(const char *msg) { 43 | printf("Error at line %d:\n\t%s\n", cur_line, msg); 44 | exit(-1); 45 | } 46 | -------------------------------------------------------------------------------- /ch14/p0.1/test.asm: -------------------------------------------------------------------------------- 1 | var a, b, c, d 2 | 3 | push 1 4 | push 2 5 | push 2 6 | push 2 7 | add 8 | mul 9 | add 10 | pop a 11 | 12 | push 5 13 | pop c 14 | 15 | push 10 16 | pop d 17 | 18 | push c 19 | push d 20 | add 21 | pop b 22 | 23 | push a 24 | push b 25 | push c 26 | push d 27 | print "a = %d, b = %d, c = %d, d = %d" 28 | 29 | -------------------------------------------------------------------------------- /ch14/p0.1/test.c: -------------------------------------------------------------------------------- 1 | int a, b, c, d; 2 | a = 1 + 2 * ( 2 + 2 ); 3 | c = 5; 4 | d = 10; 5 | b = c + d; 6 | 7 | print("a = %d, b = %d, c = %d, d = %d", a, b, c, d); -------------------------------------------------------------------------------- /ch14/p0.5/makefile: -------------------------------------------------------------------------------- 1 | OUT = tcc 2 | TESTFILE = test.c 3 | SCANNER = scanner.l 4 | PARSER = parser.y 5 | 6 | CC = gcc 7 | OBJ = lex.yy.o y.tab.o 8 | TESTOUT = $(basename $(TESTFILE)).asm 9 | OUTFILES = lex.yy.c y.tab.c y.tab.h y.output $(OUT) 10 | 11 | .PHONY: build test simulate clean 12 | 13 | build: $(OUT) 14 | 15 | test: $(TESTOUT) 16 | 17 | simulate: $(TESTOUT) 18 | python pysim.py $< -a 19 | 20 | clean: 21 | rm -f *.o $(OUTFILES) 22 | 23 | $(TESTOUT): $(TESTFILE) $(OUT) 24 | ./$(OUT) < $< > $@ 25 | 26 | $(OUT): $(OBJ) 27 | $(CC) -o $(OUT) $(OBJ) 28 | 29 | lex.yy.c: $(SCANNER) y.tab.c 30 | flex $< 31 | 32 | y.tab.c: $(PARSER) 33 | bison -vdty $< 34 | -------------------------------------------------------------------------------- /ch14/p0.5/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | void yyerror(const char*); 5 | #define YYSTYPE char * 6 | %} 7 | 8 | %token T_Int T_Void T_Return T_Print T_IntConstant 9 | %token T_StringConstant T_Identifier 10 | 11 | %left '+' '-' 12 | %left '*' '/' 13 | %right U_neg 14 | 15 | %% 16 | 17 | Program: 18 | /* empty */ { /* empty */ } 19 | | Program FuncDecl { /* empty */ } 20 | ; 21 | 22 | FuncDecl: 23 | RetType FuncName '(' Args ')' '{' VarDecls Stmts '}' 24 | { printf("ENDFUNC\n\n"); } 25 | ; 26 | 27 | RetType: 28 | T_Int { /* empty */ } 29 | | T_Void { /* empty */ } 30 | ; 31 | 32 | FuncName: 33 | T_Identifier { printf("FUNC @%s:\n", $1); } 34 | ; 35 | 36 | Args: 37 | /* empty */ { /* empty */ } 38 | | _Args { printf("\n\n"); } 39 | ; 40 | 41 | _Args: 42 | T_Int T_Identifier { printf("arg %s", $2); } 43 | | _Args ',' T_Int T_Identifier 44 | { printf(", %s", $4); } 45 | ; 46 | 47 | VarDecls: 48 | /* empty */ { /* empty */ } 49 | | VarDecls VarDecl ';' { printf("\n\n"); } 50 | ; 51 | 52 | VarDecl: 53 | T_Int T_Identifier { printf("var %s", $2); } 54 | | VarDecl ',' T_Identifier 55 | { printf(", %s", $3); } 56 | ; 57 | 58 | Stmts: 59 | /* empty */ { /* empty */ } 60 | | Stmts Stmt { /* empty */ } 61 | ; 62 | 63 | Stmt: 64 | AssignStmt { /* empty */ } 65 | | PrintStmt { /* empty */ } 66 | | CallStmt { /* empty */ } 67 | | ReturnStmt { /* empty */ } 68 | ; 69 | 70 | AssignStmt: 71 | T_Identifier '=' Expr ';' 72 | { printf("pop %s\n\n", $1); } 73 | ; 74 | 75 | PrintStmt: 76 | T_Print '(' T_StringConstant PActuals ')' ';' 77 | { printf("print %s\n\n", $3); } 78 | ; 79 | 80 | PActuals: 81 | /* empty */ { /* empty */ } 82 | | PActuals ',' Expr { /* empty */ } 83 | ; 84 | 85 | CallStmt: 86 | CallExpr ';' { printf("pop\n\n"); } 87 | ; 88 | 89 | CallExpr: 90 | T_Identifier '(' Actuals ')' 91 | { printf("$%s\n", $1); } 92 | ; 93 | 94 | Actuals: 95 | /* empty */ { /* empty */ } 96 | | Expr PActuals { /* empty */ } 97 | ; 98 | 99 | ReturnStmt: 100 | T_Return Expr ';' { printf("ret ~\n\n"); } 101 | | T_Return ';' { printf("ret\n\n"); } 102 | ; 103 | 104 | Expr: 105 | Expr '+' Expr { printf("add\n"); } 106 | | Expr '-' Expr { printf("sub\n"); } 107 | | Expr '*' Expr { printf("mul\n"); } 108 | | Expr '/' Expr { printf("div\n"); } 109 | | '-' Expr %prec U_neg { printf("neg\n"); } 110 | | T_IntConstant { printf("push %s\n", $1); } 111 | | T_Identifier { printf("push %s\n", $1); } 112 | | CallExpr { /* empty */ } 113 | | '(' Expr ')' { /* empty */ } 114 | ; 115 | 116 | %% 117 | 118 | int main() { 119 | return yyparse(); 120 | } 121 | -------------------------------------------------------------------------------- /ch14/p0.5/pysim.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | PY3 = sys.version_info[0] == 3 4 | 5 | comment_mark = ";%" 6 | code = [] 7 | stack = [] 8 | var_table = {} 9 | label_table = {} 10 | func_table = {} 11 | eip = 0 12 | printout = [] 13 | debug = False 14 | 15 | def is_valid_identifier(ident): 16 | if ident == "": 17 | return False 18 | if not (ident[0].isalpha() or ident[0] == '_'): 19 | return False 20 | for ch in ident[1:]: 21 | if not (ch.isalnum() or ch == '_'): 22 | return False 23 | return True 24 | 25 | def assemb_error(line, msg): 26 | display(pause=False) 27 | print(line) 28 | print("^^^Error at last line: %s" % msg) 29 | exit(-1) 30 | 31 | def run_error(msg="Wrong instruction format"): 32 | code[eip][0] = "**%s**" % msg 33 | printout.append(msg) 34 | display(pause=False) 35 | exit(-1) 36 | 37 | def read_file(filename): 38 | if PY3: 39 | return open(filename, 'r').readlines() 40 | 41 | return file(filename).readlines() 42 | 43 | def get_input(msg): 44 | if PY3: 45 | return input(msg) 46 | 47 | return raw_input(msg) 48 | 49 | def table_has_key(table, key): 50 | if PY3: 51 | return key in table 52 | 53 | return table.has_key(key) 54 | 55 | def assemb(asmfilename): 56 | if len(sys.argv) > 2 and (sys.argv[2] == '-a' or sys.argv[2] == '-da'): 57 | code.append(('', '$main', '')) 58 | code.append(('', 'exit', '~')) 59 | 60 | label = "" 61 | for line in read_file(asmfilename): 62 | line = line.strip() 63 | if line == "" or line[0] in comment_mark: 64 | continue 65 | 66 | _label, sep, ist = line.partition(':') 67 | if sep and _label.find('"') == -1 and _label.find("'") == -1: 68 | _label, ist = _label.strip(), ist.strip() 69 | if not check_label(_label): 70 | assemb_error(line, "Wrong label") 71 | label = '%s,%s' % (label, _label) if label else _label 72 | if ist == "" or ist[0] in comment_mark: 73 | continue 74 | elif len(line) >= 7 and line[:7] == 'ENDFUNC': 75 | label = '%s,%s' % (label, 'ENDFUNC') \ 76 | if label else 'ENDFUNC' 77 | ist = 'ret' 78 | else: 79 | ist = line 80 | 81 | dire, sep, arg = ist.partition(' ') 82 | 83 | if len(dire) > 4 and \ 84 | (dire[-4:] == '.arg' or dire[-4:] == '.var'): 85 | dire = dire[-3:] 86 | 87 | code.append( [label, dire, arg.strip()] ) 88 | label = "" 89 | 90 | code.append(('', 'exit', '0')) 91 | 92 | def check_label(label): 93 | if label == "": 94 | return False 95 | 96 | func, sep, funcName = label.partition(' @') 97 | 98 | if sep: 99 | if func.strip() != 'FUNC' \ 100 | or not is_valid_identifier(funcName) \ 101 | or table_has_key(func_table, funcName): 102 | return False 103 | else: 104 | func_table[funcName] = len(code) 105 | return True 106 | else: 107 | if not is_valid_identifier(label) \ 108 | or table_has_key(func_table, label) \ 109 | or table_has_key(label_table, label): 110 | return False 111 | else: 112 | label_table[label] = len(code) 113 | return True 114 | 115 | def trim(s, size): 116 | return s[:size-3]+"..." if len(s) > size else s 117 | 118 | def display(pause=True): 119 | num_code_lines, num_terminal_lines = 24, 8 120 | if os.system("clear"): os.system('cls') 121 | print("%32s%-40s| %-13s|Bind var" % ("", "Code", "Stack")) 122 | j = 0 123 | for i in range( \ 124 | max(eip+1-num_code_lines, 0), max(eip+1, num_code_lines) ): 125 | if i < len(code): 126 | label, dire, arg = code[i] 127 | line = trim(dire + " " + arg, 40) 128 | label = trim(label, 28) 129 | else: 130 | label, line = "", "" 131 | 132 | if label: label += ':' 133 | point = " ->" if i == eip else "" 134 | st = stack[j] if j < len(stack) else "" 135 | st = "(RetInfo)" if type(st) is tuple else str(st) 136 | stvar = var_table.get(j, "") 137 | if j == len(stack) - 1: stvar += "<-" 138 | 139 | print("%29s%3s%-40s| %-13s|%s" % \ 140 | (label, point, line, st, stvar)) 141 | 142 | j += 1 143 | 144 | print("***Terminal***") 145 | n = len(printout) 146 | for i in range( \ 147 | max(n-num_terminal_lines, 0), max(n, num_terminal_lines) ): 148 | print(printout[i] if i < n else "") 149 | if i == n and not pause: 150 | break 151 | 152 | if pause: 153 | global debug 154 | if get_input("\npress enter to step, -r to run.") == "-r": 155 | debug = False 156 | 157 | def run(): 158 | global eip 159 | eip = 0 160 | del stack[:] 161 | 162 | while True: 163 | if debug: display() 164 | label, dire, arg = code[eip] 165 | if dire[0] == '$': 166 | action, arg = call, dire[1:] 167 | if not is_valid_identifier(arg): 168 | run_error("Wrong identifier") 169 | else: 170 | if not is_valid_identifier(dire): 171 | run_error("Wrong identifier") 172 | try: 173 | action = eval("do_" + dire) 174 | except NameError: 175 | run_error("Unknown instruction") 176 | action(arg) 177 | eip += 1 178 | 179 | def do_var(arg): 180 | if arg == "": return 181 | for var in arg.split(','): 182 | var = var.strip() 183 | if not is_valid_identifier(var) or table_has_key(var_table, var): 184 | run_error("Wrong var name") 185 | var_table[var] = len(stack) 186 | var_table[len(stack)] = var 187 | stack.append("/") 188 | 189 | def do_push(arg): 190 | try: 191 | arg = int(arg) 192 | except ValueError: 193 | try: 194 | arg = stack[var_table[arg]] 195 | except KeyError: 196 | run_error("Undefined variable") 197 | if type(arg) is not int: 198 | run_error("Cannot push uninitialed value") 199 | stack.append(arg) 200 | 201 | def do_pop(arg): 202 | value = stack.pop() 203 | if arg == "": 204 | return 205 | if type(value) is not int: 206 | run_error("Cannot pop non-number value to variable") 207 | try: 208 | stack[var_table[arg]] = value 209 | except KeyError: 210 | run_error("Undefined variable") 211 | 212 | def do_exit(arg): 213 | global going, exit_code 214 | going = False 215 | 216 | if arg == "~": 217 | exit_code = stack[-1] 218 | elif arg: 219 | try: 220 | exit_code = int(arg) 221 | except ValueError: 222 | try: 223 | exit_code = stack[var_table[arg]] 224 | except KeyError: 225 | run_error("Undefined variable") 226 | 227 | if type(exit_code) is not int: 228 | run_error("Wrong exit code") 229 | 230 | if debug: 231 | display(pause=False) 232 | 233 | exit(exit_code) 234 | 235 | 236 | 237 | def do_add(arg): stack[-2] += stack[-1]; stack.pop() 238 | def do_sub(arg): stack[-2] -= stack[-1]; stack.pop() 239 | def do_mul(arg): stack[-2] *= stack[-1]; stack.pop() 240 | def do_div(arg): stack[-2] /= stack[-1]; stack.pop() 241 | def do_mod(arg): stack[-2] %= stack[-1]; stack.pop() 242 | def do_and(arg): stack[-2] = int(stack[-2]!=0 and stack[-1]!=0); stack.pop() 243 | def do_or(arg): stack[-2] = int(stack[-2]!=0 or stack[-1]!=0); stack.pop() 244 | def do_cmpeq(arg): stack[-2] = int(stack[-2]==stack[-1]);stack.pop() 245 | def do_cmpne(arg): stack[-2] = int(stack[-2]!=stack[-1]);stack.pop() 246 | def do_cmpgt(arg): stack[-2] = int(stack[-2]>stack[-1]); stack.pop() 247 | def do_cmplt(arg): stack[-2] = int(stack[-2]=stack[-1]);stack.pop() 249 | def do_cmple(arg): stack[-2] = int(stack[-2]<=stack[-1]);stack.pop() 250 | def do_neg(arg): stack[-1] = -stack[-1] 251 | def do_not(arg): stack[-1] = int(not stack[-1]) 252 | 253 | def do_print(fmt): 254 | if len(fmt) < 2 or fmt[0] != fmt[-1] or fmt[0] not in '"\'': 255 | run_error("Format string error") 256 | argc = fmt.count("%d") 257 | out = fmt[1:-1] % tuple(stack[len(stack)-argc:]) 258 | print(out) 259 | printout.append(out) 260 | del stack[len(stack)-argc:] 261 | 262 | def do_readint(msg): 263 | if len(msg) < 2 or msg[0] != msg[-1] or msg[-1] not in '"\'': 264 | run_error("Message string error") 265 | msg = msg.strip('"').strip("'") 266 | if debug: display(pause=False) 267 | string = get_input(msg) 268 | try: 269 | value = int(string) 270 | except ValueError: 271 | value = 0 272 | stack.append(value) 273 | printout.append("\n " + msg + str(value)) 274 | 275 | def do_jmp(label): 276 | global eip 277 | try: 278 | # note: here we set eip just befor the label, 279 | # and when back to run(), we do eip += 1 280 | eip = label_table[label] - 1 281 | except KeyError: 282 | run_error("Wrong label") 283 | 284 | def do_jz(label): 285 | global eip 286 | try: 287 | # set eip just befor the label, 288 | # when back to run(), do eip += 1 289 | new_eip = label_table[label] - 1 290 | except KeyError: 291 | run_error("Wrong label") 292 | if stack.pop() == 0: 293 | eip = new_eip 294 | 295 | def call(funcName): 296 | global var_table, eip 297 | 298 | try: 299 | entry = func_table[funcName] 300 | except KeyError: 301 | run_error("Undefined function") 302 | 303 | if code[entry][1] == "arg": 304 | arg_list = code[entry][2].split(',') 305 | else: 306 | arg_list = [] 307 | 308 | new_var_table = {} 309 | for addr, arg in enumerate(arg_list, len(stack)-len(arg_list)): 310 | arg = arg.strip() 311 | if not is_valid_identifier(arg) or table_has_key(new_var_table, arg): 312 | run_error("Wrong arg name") 313 | 314 | new_var_table[arg] = addr 315 | new_var_table[addr] = arg 316 | 317 | stack.append( (len(arg_list), eip, var_table) ) 318 | var_table = new_var_table 319 | eip = entry if len(arg_list) else entry -1 320 | 321 | def do_ret(arg): 322 | global var_table, eip 323 | 324 | if arg == "~": 325 | retval = stack[-1] 326 | elif arg: 327 | try: 328 | retval = int(arg) 329 | except ValueError: 330 | try: 331 | retval = stack[var_table[arg]] 332 | except KeyError: 333 | run_error("Undefined variable") 334 | else: 335 | retval = '/' 336 | 337 | i = len(stack) - 1 338 | while type(stack[i]) is not tuple: 339 | i -= 1 340 | argc, eip, var_table = stack[i] 341 | del stack[i-argc:] 342 | stack.append(retval) 343 | 344 | if __name__ == "__main__": 345 | asmfileName = sys.argv[1] 346 | if len(sys.argv) > 2: 347 | debug = sys.argv[2] == '-d' or sys.argv[2] == '-da' 348 | assemb(asmfileName) 349 | run() -------------------------------------------------------------------------------- /ch14/p0.5/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | void unterminate_string(); 8 | #define _DUPTEXT {yylval = strdup(yytext);} 9 | %} 10 | 11 | /* note \042 is '"' */ 12 | WHITESPACE ([ \t\r\a]+) 13 | SINGLE_COMMENT1 ("//"[^\n]*) 14 | SINGLE_COMMENT2 ("#"[^\n]*) 15 | OPERATOR ([+*-/%=,;!<>(){}]) 16 | INTEGER ([0-9]+) 17 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 18 | UNTERM_STRING (\042[^\042\n]*) 19 | STRING (\042[^\042\n]*\042) 20 | 21 | %% 22 | 23 | \n { cur_line++; } 24 | {WHITESPACE} { /* ignore every whitespace */ } 25 | {SINGLE_COMMENT1} { /* skip for single line comment */ } 26 | {SINGLE_COMMENT2} { /* skip for single line comment */ } 27 | 28 | {OPERATOR} { return yytext[0]; } 29 | "int" { return T_Int; } 30 | "void" { return T_Void; } 31 | "return" { return T_Return; } 32 | "print" { return T_Print; } 33 | 34 | {INTEGER} { _DUPTEXT return T_IntConstant; } 35 | {STRING} { _DUPTEXT return T_StringConstant; } 36 | {IDENTIFIER} { _DUPTEXT return T_Identifier; } 37 | 38 | {UNTERM_STRING} { unterminate_string(); } 39 | . { unrecognized_char(yytext[0]); } 40 | 41 | %% 42 | 43 | int yywrap(void) { 44 | return 1; 45 | } 46 | 47 | void unrecognized_char(char c) { 48 | char buf[32] = "Unrecognized character: ?"; 49 | buf[24] = c; 50 | yyerror(buf); 51 | } 52 | 53 | void unterminate_string() { 54 | yyerror("Unterminate string constant"); 55 | } 56 | 57 | void yyerror(const char *msg) { 58 | fprintf(stderr, "Error at line %d:\n\t%s\n", cur_line, msg); 59 | exit(-1); 60 | } 61 | -------------------------------------------------------------------------------- /ch14/p0.5/test.asm: -------------------------------------------------------------------------------- 1 | FUNC @main: 2 | var a, b, c, d 3 | 4 | push 2 5 | pop c 6 | 7 | push c 8 | push 2 9 | mul 10 | pop d 11 | 12 | push c 13 | push d 14 | $sum 15 | pop a 16 | 17 | push a 18 | push d 19 | $sum 20 | pop b 21 | 22 | push c 23 | push d 24 | print "c = %d, d = %d" 25 | 26 | push a 27 | push b 28 | print "a = sum(c, d) = %d, b = sum(a, d) = %d" 29 | 30 | push 0 31 | ret ~ 32 | 33 | ENDFUNC 34 | 35 | FUNC @sum: 36 | arg a, b 37 | 38 | var c, d 39 | 40 | push a 41 | push b 42 | add 43 | ret ~ 44 | 45 | ENDFUNC 46 | 47 | -------------------------------------------------------------------------------- /ch14/p0.5/test.c: -------------------------------------------------------------------------------- 1 | // tiny c test file 2 | 3 | int main() { 4 | int a, b, c, d; 5 | 6 | c = 2; 7 | d = c * 2; 8 | 9 | a = sum(c, d); 10 | b = sum(a, d); 11 | print("c = %d, d = %d", c, d); 12 | print("a = sum(c, d) = %d, b = sum(a, d) = %d", a, b); 13 | 14 | return 0; 15 | } 16 | 17 | int sum(int a, int b) { 18 | int c, d; 19 | return a + b; 20 | } -------------------------------------------------------------------------------- /ch14/p1.0/makefile: -------------------------------------------------------------------------------- 1 | OUT = tcc 2 | TESTFILE = test.c 3 | SCANNER = scanner.l 4 | PARSER = parser.y 5 | 6 | CC = gcc 7 | OBJ = lex.yy.o y.tab.o 8 | TESTOUT = $(basename $(TESTFILE)).asm 9 | OUTFILES = lex.yy.c y.tab.c y.tab.h y.output $(OUT) 10 | 11 | .PHONY: build test simulate clean 12 | 13 | build: $(OUT) 14 | 15 | test: $(TESTOUT) 16 | 17 | simulate: $(TESTOUT) 18 | python pysim.py $< -a 19 | 20 | clean: 21 | rm -f *.o $(OUTFILES) 22 | 23 | $(TESTOUT): $(TESTFILE) $(OUT) 24 | ./$(OUT) < $< > $@ 25 | 26 | $(OUT): $(OBJ) 27 | $(CC) -o $(OUT) $(OBJ) 28 | 29 | lex.yy.c: $(SCANNER) y.tab.c 30 | flex $< 31 | 32 | y.tab.c: $(PARSER) 33 | bison -vdty $< 34 | -------------------------------------------------------------------------------- /ch14/p1.0/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | void yyerror(const char*); 5 | #define YYSTYPE char * 6 | 7 | int ii = 0, itop = -1, istack[100]; 8 | int ww = 0, wtop = -1, wstack[100]; 9 | 10 | #define _BEG_IF {istack[++itop] = ++ii;} 11 | #define _END_IF {itop--;} 12 | #define _i (istack[itop]) 13 | 14 | #define _BEG_WHILE {wstack[++wtop] = ++ww;} 15 | #define _END_WHILE {wtop--;} 16 | #define _w (wstack[wtop]) 17 | 18 | %} 19 | 20 | %token T_Int T_Void T_Return T_Print T_ReadInt T_While 21 | %token T_If T_Else T_Break T_Continue T_Le T_Ge T_Eq T_Ne 22 | %token T_And T_Or T_IntConstant T_StringConstant T_Identifier 23 | 24 | %left '=' 25 | %left T_Or 26 | %left T_And 27 | %left T_Eq T_Ne 28 | %left '<' '>' T_Le T_Ge 29 | %left '+' '-' 30 | %left '*' '/' '%' 31 | %left '!' 32 | 33 | %% 34 | 35 | Program: 36 | /* empty */ { /* empty */ } 37 | | Program FuncDecl { /* empty */ } 38 | ; 39 | 40 | FuncDecl: 41 | RetType FuncName '(' Args ')' '{' VarDecls Stmts '}' 42 | { printf("ENDFUNC\n\n"); } 43 | ; 44 | 45 | RetType: 46 | T_Int { /* empty */ } 47 | | T_Void { /* empty */ } 48 | ; 49 | 50 | FuncName: 51 | T_Identifier { printf("FUNC @%s:\n", $1); } 52 | ; 53 | 54 | Args: 55 | /* empty */ { /* empty */ } 56 | | _Args { printf("\n\n"); } 57 | ; 58 | 59 | _Args: 60 | T_Int T_Identifier { printf("\targ %s", $2); } 61 | | _Args ',' T_Int T_Identifier 62 | { printf(", %s", $4); } 63 | ; 64 | 65 | VarDecls: 66 | /* empty */ { /* empty */ } 67 | | VarDecls VarDecl ';' { printf("\n\n"); } 68 | ; 69 | 70 | VarDecl: 71 | T_Int T_Identifier { printf("\tvar %s", $2); } 72 | | VarDecl ',' T_Identifier 73 | { printf(", %s", $3); } 74 | ; 75 | 76 | Stmts: 77 | /* empty */ { /* empty */ } 78 | | Stmts Stmt { /* empty */ } 79 | ; 80 | 81 | Stmt: 82 | AssignStmt { /* empty */ } 83 | | PrintStmt { /* empty */ } 84 | | CallStmt { /* empty */ } 85 | | ReturnStmt { /* empty */ } 86 | | IfStmt { /* empty */ } 87 | | WhileStmt { /* empty */ } 88 | | BreakStmt { /* empty */ } 89 | | ContinueStmt { /* empty */ } 90 | ; 91 | 92 | AssignStmt: 93 | T_Identifier '=' Expr ';' 94 | { printf("\tpop %s\n\n", $1); } 95 | ; 96 | 97 | PrintStmt: 98 | T_Print '(' T_StringConstant PActuals ')' ';' 99 | { printf("\tprint %s\n\n", $3); } 100 | ; 101 | 102 | PActuals: 103 | /* empty */ { /* empty */ } 104 | | PActuals ',' Expr { /* empty */ } 105 | ; 106 | 107 | CallStmt: 108 | CallExpr ';' { printf("\tpop\n\n"); } 109 | ; 110 | 111 | CallExpr: 112 | T_Identifier '(' Actuals ')' 113 | { printf("\t$%s\n", $1); } 114 | ; 115 | 116 | Actuals: 117 | /* empty */ { /* empty */ } 118 | | Expr PActuals { /* empty */ } 119 | ; 120 | 121 | ReturnStmt: 122 | T_Return Expr ';' { printf("\tret ~\n\n"); } 123 | | T_Return ';' { printf("\tret\n\n"); } 124 | ; 125 | 126 | IfStmt: 127 | If TestExpr Then StmtsBlock EndThen EndIf 128 | { /* empty */ } 129 | | If TestExpr Then StmtsBlock EndThen Else StmtsBlock EndIf 130 | { /* empty */ } 131 | ; 132 | 133 | TestExpr: 134 | '(' Expr ')' { /* empty */ } 135 | ; 136 | 137 | StmtsBlock: 138 | '{' Stmts '}' { /* empty */ } 139 | ; 140 | 141 | If: 142 | T_If { _BEG_IF; printf("_begIf_%d:\n", _i); } 143 | ; 144 | 145 | Then: 146 | /* empty */ { printf("\tjz _elIf_%d\n", _i); } 147 | ; 148 | 149 | EndThen: 150 | /* empty */ { printf("\tjmp _endIf_%d\n_elIf_%d:\n", _i, _i); } 151 | ; 152 | 153 | Else: 154 | T_Else { /* empty */ } 155 | ; 156 | 157 | EndIf: 158 | /* empty */ { printf("_endIf_%d:\n\n", _i); _END_IF; } 159 | ; 160 | 161 | WhileStmt: 162 | While TestExpr Do StmtsBlock EndWhile 163 | { /* empty */ } 164 | ; 165 | 166 | While: 167 | T_While { _BEG_WHILE; printf("_begWhile_%d:\n", _w); } 168 | ; 169 | 170 | Do: 171 | /* empty */ { printf("\tjz _endWhile_%d\n", _w); } 172 | ; 173 | 174 | EndWhile: 175 | /* empty */ { printf("\tjmp _begWhile_%d\n_endWhile_%d:\n\n", 176 | _w, _w); _END_WHILE; } 177 | ; 178 | 179 | BreakStmt: 180 | T_Break ';' { printf("\tjmp _endWhile_%d\n", _w); } 181 | ; 182 | 183 | ContinueStmt: 184 | T_Continue ';' { printf("\tjmp _begWhile_%d\n", _w); } 185 | ; 186 | 187 | Expr: 188 | Expr '+' Expr { printf("\tadd\n"); } 189 | | Expr '-' Expr { printf("\tsub\n"); } 190 | | Expr '*' Expr { printf("\tmul\n"); } 191 | | Expr '/' Expr { printf("\tdiv\n"); } 192 | | Expr '%' Expr { printf("\tmod\n"); } 193 | | Expr '>' Expr { printf("\tcmpgt\n"); } 194 | | Expr '<' Expr { printf("\tcmplt\n"); } 195 | | Expr T_Ge Expr { printf("\tcmpge\n"); } 196 | | Expr T_Le Expr { printf("\tcmple\n"); } 197 | | Expr T_Eq Expr { printf("\tcmpeq\n"); } 198 | | Expr T_Ne Expr { printf("\tcmpne\n"); } 199 | | Expr T_Or Expr { printf("\tor\n"); } 200 | | Expr T_And Expr { printf("\tand\n"); } 201 | | '-' Expr %prec '!' { printf("\tneg\n"); } 202 | | '!' Expr { printf("\tnot\n"); } 203 | | T_IntConstant { printf("\tpush %s\n", $1); } 204 | | T_Identifier { printf("\tpush %s\n", $1); } 205 | | ReadInt { /* empty */ } 206 | | CallExpr { /* empty */ } 207 | | '(' Expr ')' { /* empty */ } 208 | ; 209 | 210 | ReadInt: 211 | T_ReadInt '(' T_StringConstant ')' 212 | { printf("\treadint %s\n", $3); } 213 | ; 214 | 215 | %% 216 | 217 | int main() { 218 | return yyparse(); 219 | } 220 | -------------------------------------------------------------------------------- /ch14/p1.0/pysim.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | PY3 = sys.version_info[0] == 3 4 | 5 | comment_mark = ";%" 6 | code = [] 7 | stack = [] 8 | var_table = {} 9 | label_table = {} 10 | func_table = {} 11 | eip = 0 12 | printout = [] 13 | debug = False 14 | 15 | def is_valid_identifier(ident): 16 | if ident == "": 17 | return False 18 | if not (ident[0].isalpha() or ident[0] == '_'): 19 | return False 20 | for ch in ident[1:]: 21 | if not (ch.isalnum() or ch == '_'): 22 | return False 23 | return True 24 | 25 | def assemb_error(line, msg): 26 | display(pause=False) 27 | print(line) 28 | print("^^^Error at last line: %s" % msg) 29 | exit(-1) 30 | 31 | def run_error(msg="Wrong instruction format"): 32 | code[eip][0] = "**%s**" % msg 33 | printout.append(msg) 34 | display(pause=False) 35 | exit(-1) 36 | 37 | def read_file(filename): 38 | if PY3: 39 | return open(filename, 'r').readlines() 40 | 41 | return file(filename).readlines() 42 | 43 | def get_input(msg): 44 | if PY3: 45 | return input(msg) 46 | 47 | return raw_input(msg) 48 | 49 | def table_has_key(table, key): 50 | if PY3: 51 | return key in table 52 | 53 | return table.has_key(key) 54 | 55 | def assemb(asmfilename): 56 | if len(sys.argv) > 2 and (sys.argv[2] == '-a' or sys.argv[2] == '-da'): 57 | code.append(('', '$main', '')) 58 | code.append(('', 'exit', '~')) 59 | 60 | label = "" 61 | for line in read_file(asmfilename): 62 | line = line.strip() 63 | if line == "" or line[0] in comment_mark: 64 | continue 65 | 66 | _label, sep, ist = line.partition(':') 67 | if sep and _label.find('"') == -1 and _label.find("'") == -1: 68 | _label, ist = _label.strip(), ist.strip() 69 | if not check_label(_label): 70 | assemb_error(line, "Wrong label") 71 | label = '%s,%s' % (label, _label) if label else _label 72 | if ist == "" or ist[0] in comment_mark: 73 | continue 74 | elif len(line) >= 7 and line[:7] == 'ENDFUNC': 75 | label = '%s,%s' % (label, 'ENDFUNC') \ 76 | if label else 'ENDFUNC' 77 | ist = 'ret' 78 | else: 79 | ist = line 80 | 81 | dire, sep, arg = ist.partition(' ') 82 | 83 | if len(dire) > 4 and \ 84 | (dire[-4:] == '.arg' or dire[-4:] == '.var'): 85 | dire = dire[-3:] 86 | 87 | code.append( [label, dire, arg.strip()] ) 88 | label = "" 89 | 90 | code.append(('', 'exit', '0')) 91 | 92 | def check_label(label): 93 | if label == "": 94 | return False 95 | 96 | func, sep, funcName = label.partition(' @') 97 | 98 | if sep: 99 | if func.strip() != 'FUNC' \ 100 | or not is_valid_identifier(funcName) \ 101 | or table_has_key(func_table, funcName): 102 | return False 103 | else: 104 | func_table[funcName] = len(code) 105 | return True 106 | else: 107 | if not is_valid_identifier(label) \ 108 | or table_has_key(func_table, label) \ 109 | or table_has_key(label_table, label): 110 | return False 111 | else: 112 | label_table[label] = len(code) 113 | return True 114 | 115 | def trim(s, size): 116 | return s[:size-3]+"..." if len(s) > size else s 117 | 118 | def display(pause=True): 119 | num_code_lines, num_terminal_lines = 24, 8 120 | if os.system("clear"): os.system('cls') 121 | print("%32s%-40s| %-13s|Bind var" % ("", "Code", "Stack")) 122 | j = 0 123 | for i in range( \ 124 | max(eip+1-num_code_lines, 0), max(eip+1, num_code_lines) ): 125 | if i < len(code): 126 | label, dire, arg = code[i] 127 | line = trim(dire + " " + arg, 40) 128 | label = trim(label, 28) 129 | else: 130 | label, line = "", "" 131 | 132 | if label: label += ':' 133 | point = " ->" if i == eip else "" 134 | st = stack[j] if j < len(stack) else "" 135 | st = "(RetInfo)" if type(st) is tuple else str(st) 136 | stvar = var_table.get(j, "") 137 | if j == len(stack) - 1: stvar += "<-" 138 | 139 | print("%29s%3s%-40s| %-13s|%s" % \ 140 | (label, point, line, st, stvar)) 141 | 142 | j += 1 143 | 144 | print("***Terminal***") 145 | n = len(printout) 146 | for i in range( \ 147 | max(n-num_terminal_lines, 0), max(n, num_terminal_lines) ): 148 | print(printout[i] if i < n else "") 149 | if i == n and not pause: 150 | break 151 | 152 | if pause: 153 | global debug 154 | if get_input("\npress enter to step, -r to run.") == "-r": 155 | debug = False 156 | 157 | def run(): 158 | global eip 159 | eip = 0 160 | del stack[:] 161 | 162 | while True: 163 | if debug: display() 164 | label, dire, arg = code[eip] 165 | if dire[0] == '$': 166 | action, arg = call, dire[1:] 167 | if not is_valid_identifier(arg): 168 | run_error("Wrong identifier") 169 | else: 170 | if not is_valid_identifier(dire): 171 | run_error("Wrong identifier") 172 | try: 173 | action = eval("do_" + dire) 174 | except NameError: 175 | run_error("Unknown instruction") 176 | action(arg) 177 | eip += 1 178 | 179 | def do_var(arg): 180 | if arg == "": return 181 | for var in arg.split(','): 182 | var = var.strip() 183 | if not is_valid_identifier(var) or table_has_key(var_table, var): 184 | run_error("Wrong var name") 185 | var_table[var] = len(stack) 186 | var_table[len(stack)] = var 187 | stack.append("/") 188 | 189 | def do_push(arg): 190 | try: 191 | arg = int(arg) 192 | except ValueError: 193 | try: 194 | arg = stack[var_table[arg]] 195 | except KeyError: 196 | run_error("Undefined variable") 197 | if type(arg) is not int: 198 | run_error("Cannot push uninitialed value") 199 | stack.append(arg) 200 | 201 | def do_pop(arg): 202 | value = stack.pop() 203 | if arg == "": 204 | return 205 | if type(value) is not int: 206 | run_error("Cannot pop non-number value to variable") 207 | try: 208 | stack[var_table[arg]] = value 209 | except KeyError: 210 | run_error("Undefined variable") 211 | 212 | def do_exit(arg): 213 | global going, exit_code 214 | going = False 215 | 216 | if arg == "~": 217 | exit_code = stack[-1] 218 | elif arg: 219 | try: 220 | exit_code = int(arg) 221 | except ValueError: 222 | try: 223 | exit_code = stack[var_table[arg]] 224 | except KeyError: 225 | run_error("Undefined variable") 226 | 227 | if type(exit_code) is not int: 228 | run_error("Wrong exit code") 229 | 230 | if debug: 231 | display(pause=False) 232 | 233 | exit(exit_code) 234 | 235 | 236 | 237 | def do_add(arg): stack[-2] += stack[-1]; stack.pop() 238 | def do_sub(arg): stack[-2] -= stack[-1]; stack.pop() 239 | def do_mul(arg): stack[-2] *= stack[-1]; stack.pop() 240 | def do_div(arg): stack[-2] /= stack[-1]; stack.pop() 241 | def do_mod(arg): stack[-2] %= stack[-1]; stack.pop() 242 | def do_and(arg): stack[-2] = int(stack[-2]!=0 and stack[-1]!=0); stack.pop() 243 | def do_or(arg): stack[-2] = int(stack[-2]!=0 or stack[-1]!=0); stack.pop() 244 | def do_cmpeq(arg): stack[-2] = int(stack[-2]==stack[-1]);stack.pop() 245 | def do_cmpne(arg): stack[-2] = int(stack[-2]!=stack[-1]);stack.pop() 246 | def do_cmpgt(arg): stack[-2] = int(stack[-2]>stack[-1]); stack.pop() 247 | def do_cmplt(arg): stack[-2] = int(stack[-2]=stack[-1]);stack.pop() 249 | def do_cmple(arg): stack[-2] = int(stack[-2]<=stack[-1]);stack.pop() 250 | def do_neg(arg): stack[-1] = -stack[-1] 251 | def do_not(arg): stack[-1] = int(not stack[-1]) 252 | 253 | def do_print(fmt): 254 | if len(fmt) < 2 or fmt[0] != fmt[-1] or fmt[0] not in '"\'': 255 | run_error("Format string error") 256 | argc = fmt.count("%d") 257 | out = fmt[1:-1] % tuple(stack[len(stack)-argc:]) 258 | print(out) 259 | printout.append(out) 260 | del stack[len(stack)-argc:] 261 | 262 | def do_readint(msg): 263 | if len(msg) < 2 or msg[0] != msg[-1] or msg[-1] not in '"\'': 264 | run_error("Message string error") 265 | msg = msg.strip('"').strip("'") 266 | if debug: display(pause=False) 267 | string = get_input(msg) 268 | try: 269 | value = int(string) 270 | except ValueError: 271 | value = 0 272 | stack.append(value) 273 | printout.append("\n " + msg + str(value)) 274 | 275 | def do_jmp(label): 276 | global eip 277 | try: 278 | # note: here we set eip just befor the label, 279 | # and when back to run(), we do eip += 1 280 | eip = label_table[label] - 1 281 | except KeyError: 282 | run_error("Wrong label") 283 | 284 | def do_jz(label): 285 | global eip 286 | try: 287 | # set eip just befor the label, 288 | # when back to run(), do eip += 1 289 | new_eip = label_table[label] - 1 290 | except KeyError: 291 | run_error("Wrong label") 292 | if stack.pop() == 0: 293 | eip = new_eip 294 | 295 | def call(funcName): 296 | global var_table, eip 297 | 298 | try: 299 | entry = func_table[funcName] 300 | except KeyError: 301 | run_error("Undefined function") 302 | 303 | if code[entry][1] == "arg": 304 | arg_list = code[entry][2].split(',') 305 | else: 306 | arg_list = [] 307 | 308 | new_var_table = {} 309 | for addr, arg in enumerate(arg_list, len(stack)-len(arg_list)): 310 | arg = arg.strip() 311 | if not is_valid_identifier(arg) or table_has_key(new_var_table, arg): 312 | run_error("Wrong arg name") 313 | 314 | new_var_table[arg] = addr 315 | new_var_table[addr] = arg 316 | 317 | stack.append( (len(arg_list), eip, var_table) ) 318 | var_table = new_var_table 319 | eip = entry if len(arg_list) else entry -1 320 | 321 | def do_ret(arg): 322 | global var_table, eip 323 | 324 | if arg == "~": 325 | retval = stack[-1] 326 | elif arg: 327 | try: 328 | retval = int(arg) 329 | except ValueError: 330 | try: 331 | retval = stack[var_table[arg]] 332 | except KeyError: 333 | run_error("Undefined variable") 334 | else: 335 | retval = '/' 336 | 337 | i = len(stack) - 1 338 | while type(stack[i]) is not tuple: 339 | i -= 1 340 | argc, eip, var_table = stack[i] 341 | del stack[i-argc:] 342 | stack.append(retval) 343 | 344 | if __name__ == "__main__": 345 | asmfileName = sys.argv[1] 346 | if len(sys.argv) > 2: 347 | debug = sys.argv[2] == '-d' or sys.argv[2] == '-da' 348 | assemb(asmfileName) 349 | run() -------------------------------------------------------------------------------- /ch14/p1.0/samples.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandolia/tinyc/a1a8f424e291f981b910fe8682a98507b5713743/ch14/p1.0/samples.zip -------------------------------------------------------------------------------- /ch14/p1.0/samples/for_gcc_build.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print(char *format, ...) { 6 | va_list args; 7 | va_start(args, format); 8 | vprintf(format, args); 9 | va_end(args); 10 | puts(""); 11 | } 12 | 13 | int readint(char *prompt) { 14 | int i; 15 | printf(prompt); 16 | scanf("%d", &i); 17 | return i; 18 | } 19 | 20 | #define auto 21 | #define short 22 | #define long 23 | #define float 24 | #define double 25 | #define char 26 | #define struct 27 | #define union 28 | #define enum 29 | #define typedef 30 | #define const 31 | #define unsigned 32 | #define signed 33 | #define extern 34 | #define register 35 | #define static 36 | #define volatile 37 | #define switch 38 | #define case 39 | #define for 40 | #define do 41 | #define goto 42 | #define default 43 | #define sizeof 44 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_0_helloworld.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("Hello world!"); 5 | 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_1_io.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = readint("Please input an integer: "); 6 | print("Your input number is: %d", n); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_2_arithmetic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int x, y, z; 5 | 6 | x = 1; 7 | y = 2; 8 | z = x + y; 9 | 10 | print("x = %d, y = %d, z = %d", x, y, z); 11 | print("x + y = %d", x + y); 12 | print("x - y = %d", x - y); 13 | print("x * y = %d", x * y); 14 | print("x / y = %d", x / y); 15 | print("x %% y = %d", x % y); 16 | print("-x = %d, -y = %d", -x, -y); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_3_compare.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("1 == 2 is %d", 1 == 2); 5 | print("2 == 2 is %d", 2 == 2); 6 | print("2 == 3 is %d", 2 == 3); 7 | 8 | print("1 != 2 is %d", 1 != 2); 9 | print("2 != 2 is %d", 2 != 2); 10 | print("2 != 3 is %d", 2 != 3); 11 | 12 | print("1 > 2 is %d", 1 > 2); 13 | print("2 > 2 is %d", 2 > 2); 14 | print("2 > 3 is %d", 2 > 3); 15 | 16 | print("1 < 2 is %d", 1 < 2); 17 | print("2 < 2 is %d", 2 < 2); 18 | print("2 < 3 is %d", 2 < 3); 19 | 20 | print("1 >= 2 is %d", 1 >= 2); 21 | print("2 >= 2 is %d", 2 >= 2); 22 | print("2 >= 3 is %d", 2 >= 3); 23 | 24 | print("1 <= 2 is %d", 1 <= 2); 25 | print("2 <= 2 is %d", 2 <= 2); 26 | print("2 <= 3 is %d", 2 <= 3); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_4_logic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("0 && 0 is %d", 0 && 0); 5 | print("0 && 1 is %d", 0 && 1); 6 | print("1 && 0 is %d", 1 && 0); 7 | print("1 && 1 is %d", 1 && 1); 8 | 9 | 10 | print("0 || 0 is %d", 0 || 0); 11 | print("0 || 1 is %d", 0 || 1); 12 | print("1 || 0 is %d", 1 || 0); 13 | print("1 || 1 is %d", 1 || 1); 14 | 15 | print("!1 is %d", !1); 16 | print("!0 is %d", !0); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_5_ifwhile.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 10; 6 | 7 | while (n != 0) { 8 | print("n = %d", n); 9 | if (n == 5) { 10 | break; 11 | } 12 | n = n - 1; 13 | } 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /ch14/p1.0/samples/sample_6_function.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 1; 6 | 7 | print("The first 10 number of the fibonacci sequence:"); 8 | while (n <= 10) { 9 | print("fib(%d)=%d", n, fib(n)); 10 | n = n + 1; 11 | } 12 | 13 | return 0; 14 | } 15 | 16 | int fib(int n) { 17 | if (n <= 2) { 18 | return 1; 19 | } 20 | return fib(n - 1) + fib(n - 2); 21 | } -------------------------------------------------------------------------------- /ch14/p1.0/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | void unterminate_string(); 8 | #define _DUPTEXT {yylval = strdup(yytext);} 9 | %} 10 | 11 | /* note \042 is '"' */ 12 | WHITESPACE ([ \t\r\a]+) 13 | SINGLE_COMMENT1 ("//"[^\n]*) 14 | SINGLE_COMMENT2 ("#"[^\n]*) 15 | OPERATOR ([+*-/%=,;!<>(){}]) 16 | INTEGER ([0-9]+) 17 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 18 | UNTERM_STRING (\042[^\042\n]*) 19 | STRING (\042[^\042\n]*\042) 20 | 21 | %% 22 | 23 | \n { cur_line++; } 24 | {WHITESPACE} { /* ignore every whitespace */ } 25 | {SINGLE_COMMENT1} { /* skip for single line comment */ } 26 | {SINGLE_COMMENT2} { /* skip for single line comment */ } 27 | 28 | {OPERATOR} { return yytext[0]; } 29 | "int" { return T_Int; } 30 | "void" { return T_Void; } 31 | "return" { return T_Return; } 32 | "print" { return T_Print; } 33 | "readint" { return T_ReadInt; } 34 | "while" { return T_While; } 35 | "if" { return T_If; } 36 | "else" { return T_Else; } 37 | "break" { return T_Break; } 38 | "continue" { return T_Continue; } 39 | "<=" { return T_Le; } 40 | ">=" { return T_Ge; } 41 | "==" { return T_Eq; } 42 | "!=" { return T_Ne; } 43 | "&&" { return T_And; } 44 | "||" { return T_Or; } 45 | 46 | {INTEGER} { _DUPTEXT return T_IntConstant; } 47 | {STRING} { _DUPTEXT return T_StringConstant; } 48 | {IDENTIFIER} { _DUPTEXT return T_Identifier; } 49 | 50 | {UNTERM_STRING} { unterminate_string(); } 51 | . { unrecognized_char(yytext[0]); } 52 | 53 | %% 54 | 55 | int yywrap(void) { 56 | return 1; 57 | } 58 | 59 | void unrecognized_char(char c) { 60 | char buf[32] = "Unrecognized character: ?"; 61 | buf[24] = c; 62 | yyerror(buf); 63 | } 64 | 65 | void unterminate_string() { 66 | yyerror("Unterminate string constant"); 67 | } 68 | 69 | void yyerror(const char *msg) { 70 | fprintf(stderr, "Error at line %d:\n\t%s\n", cur_line, msg); 71 | exit(-1); 72 | } -------------------------------------------------------------------------------- /ch14/p1.0/test.asm: -------------------------------------------------------------------------------- 1 | FUNC @main: 2 | var i 3 | 4 | push 0 5 | pop i 6 | 7 | _begWhile_1: 8 | push i 9 | push 10 10 | cmplt 11 | jz _endWhile_1 12 | push i 13 | push 1 14 | add 15 | pop i 16 | 17 | _begIf_1: 18 | push i 19 | push 3 20 | cmpeq 21 | push i 22 | push 5 23 | cmpeq 24 | or 25 | jz _elIf_1 26 | jmp _begWhile_1 27 | jmp _endIf_1 28 | _elIf_1: 29 | _endIf_1: 30 | 31 | _begIf_2: 32 | push i 33 | push 8 34 | cmpeq 35 | jz _elIf_2 36 | jmp _endWhile_1 37 | jmp _endIf_2 38 | _elIf_2: 39 | _endIf_2: 40 | 41 | push i 42 | push i 43 | $factor 44 | print "%d! = %d" 45 | 46 | jmp _begWhile_1 47 | _endWhile_1: 48 | 49 | push 0 50 | ret ~ 51 | 52 | ENDFUNC 53 | 54 | FUNC @factor: 55 | arg n 56 | 57 | _begIf_3: 58 | push n 59 | push 2 60 | cmplt 61 | jz _elIf_3 62 | push 1 63 | ret ~ 64 | 65 | jmp _endIf_3 66 | _elIf_3: 67 | _endIf_3: 68 | 69 | push n 70 | push n 71 | push 1 72 | sub 73 | $factor 74 | mul 75 | ret ~ 76 | 77 | ENDFUNC 78 | 79 | -------------------------------------------------------------------------------- /ch14/p1.0/test.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int i; 5 | i = 0; 6 | while (i < 10) { 7 | i = i + 1; 8 | if (i == 3 || i == 5) { 9 | continue; 10 | } 11 | if (i == 8) { 12 | break; 13 | } 14 | print("%d! = %d", i, factor(i)); 15 | } 16 | return 0; 17 | } 18 | 19 | int factor(int n) { 20 | if (n < 2) { 21 | return 1; 22 | } 23 | return n * factor(n - 1); 24 | } -------------------------------------------------------------------------------- /ch14/p1.0/test_samples.sh: -------------------------------------------------------------------------------- 1 | for src in $(ls samples/*.c) 2 | do 3 | clear 4 | file=${src%%.c} 5 | echo build with tcc 6 | ./tcc < $file.c > $file.asm 7 | python pysim.py $file.asm -a 8 | echo 9 | echo build with gcc 10 | gcc -o $file $file.c 11 | ./$file 12 | echo 13 | echo press any key to continue... 14 | read -n 1 15 | done -------------------------------------------------------------------------------- /ch15/c01-hello-nasm/hello.nasm: -------------------------------------------------------------------------------- 1 | GLOBAL _start 2 | 3 | [SECTION .TEXT] 4 | _start: 5 | MOV EAX, 4 ; write 6 | MOV EBX, 1 ; stdout 7 | MOV ECX, msg 8 | MOV EDX, len 9 | INT 0x80 ; write(stdout, msg, len) 10 | 11 | MOV EAX, 1 ; exit 12 | MOV EBX, 0 13 | INT 0x80 ; exit(0) 14 | 15 | [SECTION .DATA] 16 | msg: DB "Hello, world!", 10 17 | len: EQU $-msg -------------------------------------------------------------------------------- /ch15/c01-hello-nasm/makefile: -------------------------------------------------------------------------------- 1 | run: hello 2 | ./hello 3 | 4 | hello: hello.nasm 5 | nasm -f elf32 -o hello.o hello.nasm 6 | ld -m elf_i386 -o hello hello.o 7 | 8 | clean: 9 | rm hello.o hello -------------------------------------------------------------------------------- /ch15/c02-hello-macro-nasm/hello.nasm: -------------------------------------------------------------------------------- 1 | %include "macro.inc" 2 | 3 | print "Hello world!" 4 | print "Hello again!" 5 | exit 0 6 | -------------------------------------------------------------------------------- /ch15/c02-hello-macro-nasm/hello.nasm.expand: -------------------------------------------------------------------------------- 1 | [global _start] 2 | 3 | [SECTION .TEXT] 4 | _start: 5 | 6 | ; print "Hello world!" 7 | [SECTION .DATA] 8 | ..@1.STRING: DB "Hello world!", 10 9 | ..@1.LEN: EQU $-..@1.STRING 10 | [SECTION .TEXT] 11 | MOV EAX, 4 12 | MOV EBX, 1 13 | MOV ECX, ..@1.STRING 14 | MOV EDX, ..@1.LEN 15 | INT 0x80 16 | 17 | ; print "Hello again" 18 | [SECTION .DATA] 19 | ..@2.STRING: DB "Hello again!", 10 20 | ..@2.LEN: EQU $-..@2.STRING 21 | [SECTION .TEXT] 22 | MOV EAX, 4 23 | MOV EBX, 1 24 | MOV ECX, ..@2.STRING 25 | MOV EDX, ..@2.LEN 26 | INT 0x80 27 | 28 | ; exit 0 29 | MOV EAX, 1 30 | MOV EBX, 0 31 | INT 0x80 32 | -------------------------------------------------------------------------------- /ch15/c02-hello-macro-nasm/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10 4 | %%LEN: EQU $-%%STRING 5 | [SECTION .TEXT] 6 | MOV EAX, 4 ; write 7 | MOV EBX, 1 ; stdout 8 | MOV ECX, %%STRING 9 | MOV EDX, %%LEN 10 | INT 0x80 ; write(stdout, %%STRING, %%LEN) 11 | %ENDMACRO 12 | 13 | %MACRO exit 1 14 | MOV EAX, 1 15 | MOV EBX, %1 16 | INT 0x80 17 | %ENDMACRO 18 | 19 | GLOBAL _start 20 | 21 | [SECTION .TEXT] 22 | _start: 23 | -------------------------------------------------------------------------------- /ch15/c02-hello-macro-nasm/makefile: -------------------------------------------------------------------------------- 1 | run: hello 2 | ./hello 3 | 4 | hello: hello.nasm macro.inc 5 | nasm -f elf32 -o hello.o hello.nasm 6 | ld -m elf_i386 -o hello hello.o 7 | 8 | clean: 9 | rm hello.o hello -------------------------------------------------------------------------------- /ch15/c03-print-macro/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10, 0 4 | [SECTION .TEXT] 5 | PUSH DWORD %%STRING 6 | CALL PRINT 7 | SHL EAX, 2 8 | ADD ESP, EAX 9 | %ENDMACRO 10 | 11 | %MACRO exit 1 12 | MOV EAX, 1 13 | MOV EBX, %1 14 | INT 0x80 15 | %ENDMACRO 16 | 17 | EXTERN PRINT 18 | GLOBAL _start 19 | 20 | [SECTION .TEXT] 21 | _start: 22 | -------------------------------------------------------------------------------- /ch15/c03-print-macro/makefile: -------------------------------------------------------------------------------- 1 | run: print 2 | ./print 3 | 4 | print: print.o libtio.a 5 | ld -m elf_i386 -o print print.o -L. -ltio 6 | 7 | print.o: print.nasm print-macro.inc 8 | nasm -f elf32 -P"print-macro.inc" -o print.o print.nasm 9 | 10 | libtio.a: tio.c 11 | gcc -m32 -c -o tio.o tio.c 12 | ar -crv libtio.a tio.o 13 | 14 | clean: 15 | rm print.o print tio.o libtio.a 16 | -------------------------------------------------------------------------------- /ch15/c03-print-macro/print.nasm: -------------------------------------------------------------------------------- 1 | PUSH DWORD 1 2 | PUSH DWORD 2 3 | PUSH DWORD 3 4 | print "a = %d, b = %d, c = %d" 5 | 6 | exit 0 -------------------------------------------------------------------------------- /ch15/c03-print-macro/tio.c: -------------------------------------------------------------------------------- 1 | void SYS_PRINT(char *string, int len); 2 | 3 | #define BUFLEN 1024 4 | 5 | int PRINT(char *fmt, ...) 6 | { 7 | int *args = (int*)&fmt; 8 | char buf[BUFLEN]; 9 | char *p1 = fmt, *p2 = buf + BUFLEN; 10 | int len = -1, argc = 1; 11 | 12 | while (*p1++) ; 13 | 14 | do { 15 | p1--; 16 | if (*p1 == '%' && *(p1+1) == 'd') { 17 | p2++; len--; argc++; 18 | int num = *(++args), negative = 0; 19 | 20 | if (num < 0) { 21 | negative = 1; 22 | num = -num; 23 | } 24 | 25 | do { 26 | *(--p2) = num % 10 + '0'; len++; 27 | num /= 10; 28 | } while (num); 29 | 30 | if (negative) { 31 | *(--p2) = '-'; len++; 32 | } 33 | } else { 34 | *(--p2) = *p1; len++; 35 | } 36 | } while (p1 != fmt); 37 | 38 | SYS_PRINT(p2, len); 39 | 40 | return argc; 41 | } 42 | 43 | void SYS_PRINT(char *string, int len) 44 | { 45 | __asm__( 46 | ".intel_syntax noprefix\n\ 47 | PUSH EAX\n\ 48 | PUSH EBX\n\ 49 | PUSH ECX\n\ 50 | PUSH EDX\n\ 51 | \n\ 52 | MOV EAX, 4\n\ 53 | MOV EBX, 1\n\ 54 | MOV ECX, [EBP+4*2]\n\ 55 | MOV EDX, [EBP+4*3]\n\ 56 | INT 0X80\n\ 57 | \n\ 58 | POP EDX\n\ 59 | POP ECX\n\ 60 | POP EBX\n\ 61 | POP EAX\n\ 62 | .att_syntax" 63 | ); 64 | } -------------------------------------------------------------------------------- /ch15/c04-readint-macro/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10, 0 4 | [SECTION .TEXT] 5 | PUSH DWORD %%STRING 6 | CALL PRINT 7 | SHL EAX, 2 8 | ADD ESP, EAX 9 | %ENDMACRO 10 | 11 | %MACRO readint 1 12 | [SECTION .DATA] 13 | %%STRING: DB %1, 0 14 | [SECTION .TEXT] 15 | PUSH DWORD %%STRING 16 | CALL READINT 17 | MOV [ESP], EAX 18 | %ENDMACRO 19 | 20 | %MACRO exit 1 21 | MOV EAX, 1 22 | MOV EBX, %1 23 | INT 0x80 24 | %ENDMACRO 25 | 26 | EXTERN PRINT, READINT 27 | GLOBAL _start 28 | 29 | [SECTION .TEXT] 30 | _start: 31 | -------------------------------------------------------------------------------- /ch15/c04-readint-macro/makefile: -------------------------------------------------------------------------------- 1 | test: test.o libtio.a 2 | ld -m elf_i386 -o test test.o -L. -ltio 3 | 4 | run: test 5 | ./test 6 | 7 | test.o: test.nasm macro.inc 8 | nasm -f elf32 -P"macro.inc" -o test.o test.nasm 9 | 10 | libtio.a: tio.c 11 | gcc -m32 -c -o tio.o tio.c 12 | ar -crv libtio.a tio.o 13 | 14 | clean: 15 | rm test.o test tio.o libtio.a 16 | -------------------------------------------------------------------------------- /ch15/c04-readint-macro/test.nasm: -------------------------------------------------------------------------------- 1 | readint "Please input an number: " 2 | print "Your input is: %d" 3 | exit 0 -------------------------------------------------------------------------------- /ch15/c04-readint-macro/tio.c: -------------------------------------------------------------------------------- 1 | void SYS_PRINT(char *string, int len); 2 | 3 | #define BUFLEN 1024 4 | 5 | int PRINT(char *fmt, ...) 6 | { 7 | int *args = (int*)&fmt; 8 | char buf[BUFLEN]; 9 | char *p1 = fmt, *p2 = buf + BUFLEN; 10 | int len = -1, argc = 1; 11 | 12 | while (*p1++) ; 13 | 14 | do { 15 | p1--; 16 | if (*p1 == '%' && *(p1+1) == 'd') { 17 | p2++; len--; argc++; 18 | int num = *(++args), negative = 0; 19 | 20 | if (num < 0) { 21 | negative = 1; 22 | num = -num; 23 | } 24 | 25 | do { 26 | *(--p2) = num % 10 + '0'; len++; 27 | num /= 10; 28 | } while (num); 29 | 30 | if (negative) { 31 | *(--p2) = '-'; len++; 32 | } 33 | } else { 34 | *(--p2) = *p1; len++; 35 | } 36 | } while (p1 != fmt); 37 | 38 | SYS_PRINT(p2, len); 39 | 40 | return argc; 41 | } 42 | 43 | void SYS_PRINT(char *string, int len) 44 | { 45 | __asm__( 46 | ".intel_syntax noprefix\n\ 47 | PUSH EAX\n\ 48 | PUSH EBX\n\ 49 | PUSH ECX\n\ 50 | PUSH EDX\n\ 51 | \n\ 52 | MOV EAX, 4\n\ 53 | MOV EBX, 1\n\ 54 | MOV ECX, [EBP+4*2]\n\ 55 | MOV EDX, [EBP+4*3]\n\ 56 | INT 0X80\n\ 57 | \n\ 58 | POP EDX\n\ 59 | POP ECX\n\ 60 | POP EBX\n\ 61 | POP EAX\n\ 62 | .att_syntax" 63 | ); 64 | } 65 | 66 | int STRLEN(char *s); 67 | int SYS_READ(char *buf, int len); 68 | 69 | int READINT(char *prompt) { 70 | char buf[BUFLEN], *p = buf, *p_end; 71 | SYS_PRINT(prompt, STRLEN(prompt)); 72 | int len = SYS_READ(buf, BUFLEN-1), value = 0, negative = 0; 73 | 74 | p_end = buf + len + 1; 75 | 76 | while (p != p_end) { 77 | if (*p == ' ' || *p == '\t') { 78 | p++; 79 | } else { 80 | break; 81 | } 82 | } 83 | 84 | if (p != p_end && *p == '-') { 85 | negative = 1; 86 | p++; 87 | } 88 | 89 | while (p != p_end) { 90 | if (*p <= '9' && *p >= '0') { 91 | value = value * 10 + *p - '0'; 92 | *p++; 93 | } else { 94 | break; 95 | } 96 | } 97 | 98 | if (negative) { 99 | value = -value; 100 | } 101 | 102 | return value; 103 | } 104 | 105 | int STRLEN(char *s) { 106 | int i = 0; 107 | while(*s++) i++; 108 | return i; 109 | } 110 | 111 | int SYS_READ(char *buf, int len) { 112 | __asm__( 113 | ".intel_syntax noprefix\n\ 114 | PUSH EBX\n\ 115 | PUSH ECX\n\ 116 | PUSH EDX\n\ 117 | \n\ 118 | MOV EAX, 3\n\ 119 | MOV EBX, 2\n\ 120 | MOV ECX, [EBP+4*2]\n\ 121 | MOV EDX, [EBP+4*3]\n\ 122 | INT 0X80\n\ 123 | \n\ 124 | POP EDX\n\ 125 | POP ECX\n\ 126 | POP EBX\n\ 127 | .att_syntax" 128 | ); 129 | } -------------------------------------------------------------------------------- /ch15/c05-simple-macro/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10, 0 4 | [SECTION .TEXT] 5 | PUSH DWORD %%STRING 6 | CALL PRINT 7 | SHL EAX, 2 8 | ADD ESP, EAX 9 | %ENDMACRO 10 | 11 | %MACRO readint 1 12 | [SECTION .DATA] 13 | %%STRING: DB %1, 0 14 | [SECTION .TEXT] 15 | PUSH DWORD %%STRING 16 | CALL READINT 17 | MOV [ESP], EAX 18 | %ENDMACRO 19 | 20 | %MACRO exit 1 21 | MOV EAX, 1 22 | MOV EBX, %1 23 | INT 0x80 24 | %ENDMACRO 25 | 26 | %MACRO add 0 27 | POP EAX 28 | ADD DWORD [ESP], EAX 29 | %ENDMACRO 30 | 31 | %MACRO sub 0 32 | POP EAX 33 | SUB DWORD [ESP], EAX 34 | %ENDMACRO 35 | 36 | %MACRO mul 0 37 | POP EAX 38 | MUL DWORD [ESP] 39 | MOV [ESP], EAX 40 | %ENDMACRO 41 | 42 | %MACRO div 0 43 | XOR EDX, EDX 44 | POP EBX 45 | POP EAX 46 | DIV EBX 47 | PUSH EAX 48 | %ENDMACRO 49 | 50 | %MACRO mod 0 51 | XOR EDX, EDX 52 | POP EBX 53 | POP EAX 54 | DIV EBX 55 | PUSH EDX 56 | %ENDMACRO 57 | 58 | %MACRO neg 0 59 | NEG DWORD [ESP] 60 | %ENDMACRO 61 | 62 | %MACRO cmpeq 0 63 | MOV EAX, [ESP+4] 64 | CMP EAX, [ESP] 65 | PUSHF 66 | POP EAX 67 | SHR EAX, 6 68 | AND EAX, 0X1 69 | ADD ESP, 4 70 | MOV [ESP], EAX 71 | %ENDMACRO 72 | 73 | %MACRO cmpne 0 74 | MOV EAX, [ESP+4] 75 | CMP EAX, [ESP] 76 | PUSHF 77 | POP EAX 78 | SHR EAX, 6 79 | AND EAX, 0X1 80 | XOR EAX, 0X1 81 | ADD ESP, 4 82 | MOV [ESP], EAX 83 | %ENDMACRO 84 | 85 | %MACRO cmpge 0 86 | MOV EAX, [ESP+4] 87 | CMP EAX, [ESP] 88 | PUSHF 89 | POP EAX 90 | SHR EAX, 7 91 | AND EAX, 0X1 92 | XOR EAX, 0X1 93 | ADD ESP, 4 94 | MOV [ESP], EAX 95 | %ENDMACRO 96 | 97 | %MACRO cmplt 0 98 | MOV EAX, [ESP+4] 99 | CMP EAX, [ESP] 100 | PUSHF 101 | POP EAX 102 | SHR EAX, 7 103 | AND EAX, 0X1 104 | ADD ESP, 4 105 | MOV [ESP], EAX 106 | %ENDMACRO 107 | 108 | %MACRO cmpgt 0 109 | POP EAX 110 | CMP EAX, [ESP] 111 | PUSHF 112 | POP EAX 113 | SHR EAX, 7 114 | AND EAX, 0X1 115 | MOV [ESP], EAX 116 | %ENDMACRO 117 | 118 | %MACRO cmple 0 119 | POP EAX 120 | CMP EAX, [ESP] 121 | PUSHF 122 | POP EAX 123 | SHR EAX, 7 124 | AND EAX, 0X1 125 | XOR EAX, 0X1 126 | MOV [ESP], EAX 127 | %ENDMACRO 128 | 129 | %MACRO and 0 130 | POP EAX 131 | AND EAX, [ESP] 132 | PUSHF 133 | POP EAX 134 | SHR EAX, 6 135 | AND EAX, 0X1 136 | XOR EAX, 0X1 137 | MOV [ESP], EAX 138 | %ENDMACRO 139 | 140 | %MACRO or 0 141 | POP EAX 142 | OR EAX, [ESP] 143 | PUSHF 144 | POP EAX 145 | SHR EAX, 6 146 | AND EAX, 0X1 147 | XOR EAX, 0X1 148 | MOV [ESP], EAX 149 | %ENDMACRO 150 | 151 | %MACRO not 0 152 | MOV EAX, [ESP] 153 | OR EAX, EAX 154 | PUSHF 155 | POP EAX 156 | SHR EAX, 6 157 | AND EAX, 0X1 158 | MOV [ESP], EAX 159 | %ENDMACRO 160 | 161 | %MACRO jz 1 162 | POP EAX 163 | OR EAX, EAX 164 | JZ %1 165 | %ENDMACRO 166 | 167 | %MACRO jmp 1 168 | JMP %1 169 | %ENDMACRO 170 | 171 | %MACRO push 1 172 | PUSH DWORD %1 173 | %ENDMACRO 174 | 175 | %MACRO pop 0-1 176 | %IFIDN %0, 0 177 | ADD ESP, 4 178 | %ELSE 179 | POP DWORD %1 180 | %ENDIF 181 | %ENDMACRO 182 | 183 | EXTERN PRINT, READINT 184 | GLOBAL _start 185 | 186 | [SECTION .TEXT] 187 | _start: 188 | -------------------------------------------------------------------------------- /ch15/c05-simple-macro/makefile: -------------------------------------------------------------------------------- 1 | test: test.o libtio.a 2 | ld -m elf_i386 -o test test.o -L. -ltio 3 | 4 | run: test 5 | ./test 6 | 7 | test.o: test.nasm macro.inc 8 | nasm -f elf32 -P"macro.inc" -o test.o test.nasm 9 | 10 | libtio.a: tio.c 11 | gcc -m32 -c -o tio.o tio.c 12 | ar -crv libtio.a tio.o 13 | 14 | clean: 15 | rm test.o test tio.o libtio.a 16 | -------------------------------------------------------------------------------- /ch15/c05-simple-macro/test.nasm: -------------------------------------------------------------------------------- 1 | MOV EBP, ESP 2 | SUB ESP, 8 3 | %define a [EBP-4] 4 | %define b [EBP-8] 5 | 6 | ; a = readint("Please input an number `a`: ") 7 | readint "Please input an number `a`: " 8 | pop a ; ==> POP DWORD [EBP-4] 9 | 10 | ; b = readint("Please input another number `b`: ") 11 | readint "Please input another number `b`: " 12 | pop b ; ==> POP DWORD [EBP-8] 13 | 14 | ; print("a = %d", a) 15 | push a ; ==> PUSH DWORD [EBP-4] 16 | print "a = %d" 17 | 18 | ; print("b = %d", b) 19 | push b ; ==> PUSH DWORD [EBP-8] 20 | print "b = %d" 21 | 22 | ; print("a - b = %d", a - b) 23 | push a 24 | push b 25 | sub 26 | print "a - b = %d" 27 | 28 | ; if (a > b) { print("a > b"); } else { print("a <= b") } 29 | push a 30 | push b 31 | cmpgt 32 | jz _LESSEQUAL 33 | print "a > b" 34 | jmp _EXIT 35 | _LESSEQUAL: 36 | print "a <= b" 37 | _EXIT: 38 | exit 0 -------------------------------------------------------------------------------- /ch15/c05-simple-macro/tio.c: -------------------------------------------------------------------------------- 1 | void SYS_PRINT(char *string, int len); 2 | 3 | #define BUFLEN 1024 4 | 5 | int PRINT(char *fmt, ...) 6 | { 7 | int *args = (int*)&fmt; 8 | char buf[BUFLEN]; 9 | char *p1 = fmt, *p2 = buf + BUFLEN; 10 | int len = -1, argc = 1; 11 | 12 | while (*p1++) ; 13 | 14 | do { 15 | p1--; 16 | if (*p1 == '%' && *(p1+1) == 'd') { 17 | p2++; len--; argc++; 18 | int num = *(++args), negative = 0; 19 | 20 | if (num < 0) { 21 | negative = 1; 22 | num = -num; 23 | } 24 | 25 | do { 26 | *(--p2) = num % 10 + '0'; len++; 27 | num /= 10; 28 | } while (num); 29 | 30 | if (negative) { 31 | *(--p2) = '-'; len++; 32 | } 33 | } else { 34 | *(--p2) = *p1; len++; 35 | } 36 | } while (p1 != fmt); 37 | 38 | SYS_PRINT(p2, len); 39 | 40 | return argc; 41 | } 42 | 43 | void SYS_PRINT(char *string, int len) 44 | { 45 | __asm__( 46 | ".intel_syntax noprefix\n\ 47 | PUSH EAX\n\ 48 | PUSH EBX\n\ 49 | PUSH ECX\n\ 50 | PUSH EDX\n\ 51 | \n\ 52 | MOV EAX, 4\n\ 53 | MOV EBX, 1\n\ 54 | MOV ECX, [EBP+4*2]\n\ 55 | MOV EDX, [EBP+4*3]\n\ 56 | INT 0X80\n\ 57 | \n\ 58 | POP EDX\n\ 59 | POP ECX\n\ 60 | POP EBX\n\ 61 | POP EAX\n\ 62 | .att_syntax" 63 | ); 64 | } 65 | 66 | int STRLEN(char *s); 67 | int SYS_READ(char *buf, int len); 68 | 69 | int READINT(char *prompt) { 70 | char buf[BUFLEN], *p = buf, *p_end; 71 | SYS_PRINT(prompt, STRLEN(prompt)); 72 | int len = SYS_READ(buf, BUFLEN-1), value = 0, negative = 0; 73 | 74 | p_end = buf + len + 1; 75 | 76 | while (p != p_end) { 77 | if (*p == ' ' || *p == '\t') { 78 | p++; 79 | } else { 80 | break; 81 | } 82 | } 83 | 84 | if (p != p_end && *p == '-') { 85 | negative = 1; 86 | p++; 87 | } 88 | 89 | while (p != p_end) { 90 | if (*p <= '9' && *p >= '0') { 91 | value = value * 10 + *p - '0'; 92 | *p++; 93 | } else { 94 | break; 95 | } 96 | } 97 | 98 | if (negative) { 99 | value = -value; 100 | } 101 | 102 | return value; 103 | } 104 | 105 | int STRLEN(char *s) { 106 | int i = 0; 107 | while(*s++) i++; 108 | return i; 109 | } 110 | 111 | int SYS_READ(char *buf, int len) { 112 | __asm__( 113 | ".intel_syntax noprefix\n\ 114 | PUSH EBX\n\ 115 | PUSH ECX\n\ 116 | PUSH EDX\n\ 117 | \n\ 118 | MOV EAX, 3\n\ 119 | MOV EBX, 2\n\ 120 | MOV ECX, [EBP+4*2]\n\ 121 | MOV EDX, [EBP+4*3]\n\ 122 | INT 0X80\n\ 123 | \n\ 124 | POP EDX\n\ 125 | POP ECX\n\ 126 | POP EBX\n\ 127 | .att_syntax" 128 | ); 129 | } -------------------------------------------------------------------------------- /ch15/c06-func-macro/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10, 0 4 | [SECTION .TEXT] 5 | PUSH DWORD %%STRING 6 | CALL PRINT 7 | SHL EAX, 2 8 | ADD ESP, EAX 9 | %ENDMACRO 10 | 11 | %MACRO readint 1 12 | [SECTION .DATA] 13 | %%STRING: DB %1, 0 14 | [SECTION .TEXT] 15 | PUSH DWORD %%STRING 16 | CALL READINT 17 | MOV [ESP], EAX 18 | %ENDMACRO 19 | 20 | %MACRO exit 1 21 | MOV EAX, 1 22 | MOV EBX, %1 23 | INT 0x80 24 | %ENDMACRO 25 | 26 | %MACRO add 0 27 | POP EAX 28 | ADD DWORD [ESP], EAX 29 | %ENDMACRO 30 | 31 | %MACRO sub 0 32 | POP EAX 33 | SUB DWORD [ESP], EAX 34 | %ENDMACRO 35 | 36 | %MACRO mul 0 37 | POP EAX 38 | MUL DWORD [ESP] 39 | MOV [ESP], EAX 40 | %ENDMACRO 41 | 42 | %MACRO div 0 43 | XOR EDX, EDX 44 | POP EBX 45 | POP EAX 46 | DIV EBX 47 | PUSH EAX 48 | %ENDMACRO 49 | 50 | %MACRO mod 0 51 | XOR EDX, EDX 52 | POP EBX 53 | POP EAX 54 | DIV EBX 55 | PUSH EDX 56 | %ENDMACRO 57 | 58 | %MACRO neg 0 59 | NEG DWORD [ESP] 60 | %ENDMACRO 61 | 62 | %MACRO cmpeq 0 63 | MOV EAX, [ESP+4] 64 | CMP EAX, [ESP] 65 | PUSHF 66 | POP EAX 67 | SHR EAX, 6 68 | AND EAX, 0X1 69 | ADD ESP, 4 70 | MOV [ESP], EAX 71 | %ENDMACRO 72 | 73 | %MACRO cmpne 0 74 | MOV EAX, [ESP+4] 75 | CMP EAX, [ESP] 76 | PUSHF 77 | POP EAX 78 | SHR EAX, 6 79 | AND EAX, 0X1 80 | XOR EAX, 0X1 81 | ADD ESP, 4 82 | MOV [ESP], EAX 83 | %ENDMACRO 84 | 85 | %MACRO cmpge 0 86 | MOV EAX, [ESP+4] 87 | CMP EAX, [ESP] 88 | PUSHF 89 | POP EAX 90 | SHR EAX, 7 91 | AND EAX, 0X1 92 | XOR EAX, 0X1 93 | ADD ESP, 4 94 | MOV [ESP], EAX 95 | %ENDMACRO 96 | 97 | %MACRO cmplt 0 98 | MOV EAX, [ESP+4] 99 | CMP EAX, [ESP] 100 | PUSHF 101 | POP EAX 102 | SHR EAX, 7 103 | AND EAX, 0X1 104 | ADD ESP, 4 105 | MOV [ESP], EAX 106 | %ENDMACRO 107 | 108 | %MACRO cmpgt 0 109 | POP EAX 110 | CMP EAX, [ESP] 111 | PUSHF 112 | POP EAX 113 | SHR EAX, 7 114 | AND EAX, 0X1 115 | MOV [ESP], EAX 116 | %ENDMACRO 117 | 118 | %MACRO cmple 0 119 | POP EAX 120 | CMP EAX, [ESP] 121 | PUSHF 122 | POP EAX 123 | SHR EAX, 7 124 | AND EAX, 0X1 125 | XOR EAX, 0X1 126 | MOV [ESP], EAX 127 | %ENDMACRO 128 | 129 | %MACRO and 0 130 | POP EAX 131 | AND EAX, [ESP] 132 | PUSHF 133 | POP EAX 134 | SHR EAX, 6 135 | AND EAX, 0X1 136 | XOR EAX, 0X1 137 | MOV [ESP], EAX 138 | %ENDMACRO 139 | 140 | %MACRO or 0 141 | POP EAX 142 | OR EAX, [ESP] 143 | PUSHF 144 | POP EAX 145 | SHR EAX, 6 146 | AND EAX, 0X1 147 | XOR EAX, 0X1 148 | MOV [ESP], EAX 149 | %ENDMACRO 150 | 151 | %MACRO not 0 152 | MOV EAX, [ESP] 153 | OR EAX, EAX 154 | PUSHF 155 | POP EAX 156 | SHR EAX, 6 157 | AND EAX, 0X1 158 | MOV [ESP], EAX 159 | %ENDMACRO 160 | 161 | %MACRO jz 1 162 | POP EAX 163 | OR EAX, EAX 164 | JZ %1 165 | %ENDMACRO 166 | 167 | %MACRO jmp 1 168 | JMP %1 169 | %ENDMACRO 170 | 171 | %MACRO push 1 172 | PUSH DWORD %1 173 | %ENDMACRO 174 | 175 | %MACRO pop 0-1 176 | %IFIDN %0, 0 177 | ADD ESP, 4 178 | %ELSE 179 | POP DWORD %1 180 | %ENDIF 181 | %ENDMACRO 182 | 183 | %MACRO FUNC 1 184 | %1 185 | PUSH EBP 186 | MOV EBP, ESP 187 | %ENDMACRO 188 | 189 | %MACRO ret 0-1 190 | %IFIDN %0, 1 191 | %IFIDN %1, ~ 192 | MOV EAX, [ESP] 193 | %ELSE 194 | MOV EAX, %1 195 | %ENDIF 196 | %ENDIF 197 | LEAVE 198 | RET 199 | %ENDMACRO 200 | 201 | EXTERN PRINT, READINT 202 | GLOBAL _start 203 | 204 | [SECTION .TEXT] 205 | _start: 206 | CALL @main 207 | PUSH EAX 208 | exit [ESP] -------------------------------------------------------------------------------- /ch15/c06-func-macro/makefile: -------------------------------------------------------------------------------- 1 | test: test.o libtio.a 2 | ld -m elf_i386 -o test test.o -L. -ltio 3 | 4 | run: test 5 | ./test 6 | 7 | test.o: test.pcode test.funcmacro macro.inc 8 | nasm -f elf32 -P"macro.inc" -P"test.funcmacro" -o test.o test.pcode 9 | 10 | libtio.a: tio.c 11 | gcc -m32 -c -o tio.o tio.c 12 | ar -crv libtio.a tio.o 13 | 14 | clean: 15 | rm test.o test tio.o libtio.a 16 | -------------------------------------------------------------------------------- /ch15/c06-func-macro/test.funcmacro: -------------------------------------------------------------------------------- 1 | ; ==== begin function `main` ==== 2 | %define main.varc 1 3 | 4 | %MACRO main.var main.varc 5 | %define a [EBP - 4*1] 6 | SUB ESP, 4*main.varc 7 | %ENDMACRO 8 | 9 | %MACRO ENDFUNC@main 0 10 | LEAVE 11 | RET 12 | %undef a 13 | %ENDMACRO 14 | ; ==== end function `main` ==== 15 | 16 | ; ==== begin function `sum` ==== 17 | %define sum.argc 2 18 | %define sum.varc 1 19 | 20 | %MACRO $sum 0 21 | CALL @sum 22 | ADD ESP, 4*sum.argc 23 | PUSH EAX 24 | %ENDMACRO 25 | 26 | %MACRO sum.arg sum.argc 27 | %define a [EBP + 8 + 4*sum.argc - 4*1] 28 | %define b [EBP + 8 + 4*sum.argc - 4*2] 29 | %ENDMACRO 30 | 31 | %MACRO sum.var sum.varc 32 | %define c [EBP - 4*1] 33 | SUB ESP, 4*sum.varc 34 | %ENDMACRO 35 | 36 | %MACRO ENDFUNC@sum 0 37 | LEAVE 38 | RET 39 | %undef a 40 | %undef b 41 | %undef c 42 | %ENDMACRO 43 | ; ==== end function `sum` ==== -------------------------------------------------------------------------------- /ch15/c06-func-macro/test.origin.pcode: -------------------------------------------------------------------------------- 1 | ; int main() { 2 | FUNC @main: 3 | ; int a; 4 | var a 5 | 6 | ; a = 3; 7 | push 3 8 | pop a 9 | 10 | ; print("sum = %d", sum(4, a)); 11 | push 4 12 | push a 13 | $sum 14 | print "sum = %d" 15 | 16 | ; return 0; 17 | ret 0 18 | ; } 19 | ENDFUNC 20 | 21 | ; int sum(int a, int b) { 22 | FUNC @sum: 23 | arg a, b 24 | 25 | ; int c; 26 | var c 27 | 28 | ; c = a + b; 29 | push a 30 | push b 31 | add 32 | pop c 33 | 34 | ; return c; 35 | ret c 36 | ; } 37 | ENDFUNC -------------------------------------------------------------------------------- /ch15/c06-func-macro/test.pcode: -------------------------------------------------------------------------------- 1 | ; int main() { 2 | FUNC @main: 3 | ; int a; 4 | main.var a 5 | 6 | ; a = 3; 7 | push 3 8 | pop a 9 | 10 | ; print("sum = %d", sum(4, a)); 11 | push 4 12 | push a 13 | $sum 14 | print "sum = %d" 15 | 16 | ; return 0; 17 | ret 0 18 | ; } 19 | ENDFUNC@main 20 | 21 | ; int sum(int a, int b) { 22 | FUNC @sum: 23 | sum.arg a, b 24 | 25 | ; int c; 26 | sum.var c 27 | 28 | ; c = a + b; 29 | push a 30 | push b 31 | add 32 | pop c 33 | 34 | ; return c; 35 | ret c 36 | ; } 37 | ENDFUNC@sum -------------------------------------------------------------------------------- /ch15/c06-func-macro/test.pcode.expand: -------------------------------------------------------------------------------- 1 | @sum: 2 | PUSH EBP 3 | MOV EBP, ESP 4 | SUB ESP, 4*1 5 | 6 | PUSH DWORD [EBP + 12] ; push a 7 | PUSH DWORD [EBP + 8] ; push b 8 | add 9 | POP DWORD [EBP - 4*1] ; pop c 10 | 11 | MOV EAX, [EBP - 4*1] ; MOV EAX, c 12 | LEAVE 13 | RET 14 | -------------------------------------------------------------------------------- /ch15/c06-func-macro/tio.c: -------------------------------------------------------------------------------- 1 | void SYS_PRINT(char *string, int len); 2 | 3 | #define BUFLEN 1024 4 | 5 | int PRINT(char *fmt, ...) 6 | { 7 | int *args = (int*)&fmt; 8 | char buf[BUFLEN]; 9 | char *p1 = fmt, *p2 = buf + BUFLEN; 10 | int len = -1, argc = 1; 11 | 12 | while (*p1++) ; 13 | 14 | do { 15 | p1--; 16 | if (*p1 == '%' && *(p1+1) == 'd') { 17 | p2++; len--; argc++; 18 | int num = *(++args), negative = 0; 19 | 20 | if (num < 0) { 21 | negative = 1; 22 | num = -num; 23 | } 24 | 25 | do { 26 | *(--p2) = num % 10 + '0'; len++; 27 | num /= 10; 28 | } while (num); 29 | 30 | if (negative) { 31 | *(--p2) = '-'; len++; 32 | } 33 | } else { 34 | *(--p2) = *p1; len++; 35 | } 36 | } while (p1 != fmt); 37 | 38 | SYS_PRINT(p2, len); 39 | 40 | return argc; 41 | } 42 | 43 | void SYS_PRINT(char *string, int len) 44 | { 45 | __asm__( 46 | ".intel_syntax noprefix\n\ 47 | PUSH EAX\n\ 48 | PUSH EBX\n\ 49 | PUSH ECX\n\ 50 | PUSH EDX\n\ 51 | \n\ 52 | MOV EAX, 4\n\ 53 | MOV EBX, 1\n\ 54 | MOV ECX, [EBP+4*2]\n\ 55 | MOV EDX, [EBP+4*3]\n\ 56 | INT 0X80\n\ 57 | \n\ 58 | POP EDX\n\ 59 | POP ECX\n\ 60 | POP EBX\n\ 61 | POP EAX\n\ 62 | .att_syntax" 63 | ); 64 | } 65 | 66 | int STRLEN(char *s); 67 | int SYS_READ(char *buf, int len); 68 | 69 | int READINT(char *prompt) { 70 | char buf[BUFLEN], *p = buf, *p_end; 71 | SYS_PRINT(prompt, STRLEN(prompt)); 72 | int len = SYS_READ(buf, BUFLEN-1), value = 0, negative = 0; 73 | 74 | p_end = buf + len + 1; 75 | 76 | while (p != p_end) { 77 | if (*p == ' ' || *p == '\t') { 78 | p++; 79 | } else { 80 | break; 81 | } 82 | } 83 | 84 | if (p != p_end && *p == '-') { 85 | negative = 1; 86 | p++; 87 | } 88 | 89 | while (p != p_end) { 90 | if (*p <= '9' && *p >= '0') { 91 | value = value * 10 + *p - '0'; 92 | *p++; 93 | } else { 94 | break; 95 | } 96 | } 97 | 98 | if (negative) { 99 | value = -value; 100 | } 101 | 102 | return value; 103 | } 104 | 105 | int STRLEN(char *s) { 106 | int i = 0; 107 | while(*s++) i++; 108 | return i; 109 | } 110 | 111 | int SYS_READ(char *buf, int len) { 112 | __asm__( 113 | ".intel_syntax noprefix\n\ 114 | PUSH EBX\n\ 115 | PUSH ECX\n\ 116 | PUSH EDX\n\ 117 | \n\ 118 | MOV EAX, 3\n\ 119 | MOV EBX, 2\n\ 120 | MOV ECX, [EBP+4*2]\n\ 121 | MOV EDX, [EBP+4*3]\n\ 122 | INT 0X80\n\ 123 | \n\ 124 | POP EDX\n\ 125 | POP ECX\n\ 126 | POP EBX\n\ 127 | .att_syntax" 128 | ); 129 | } -------------------------------------------------------------------------------- /ch16/README.md: -------------------------------------------------------------------------------- 1 | empty 2 | -------------------------------------------------------------------------------- /ch16/c01-new-frontend/makefile: -------------------------------------------------------------------------------- 1 | OUT = tcc-frontend 2 | TESTFILE = test.c 3 | SCANNER = scanner.l 4 | PARSER = parser.y 5 | 6 | CC = gcc 7 | OBJ = lex.yy.o y.tab.o 8 | TESTOUT = $(basename $(TESTFILE)).asm 9 | OUTFILES = lex.yy.c y.tab.c y.tab.h y.output $(OUT) \ 10 | $(basename $(TESTFILE)).asm $(basename $(TESTFILE)).inc 11 | 12 | .PHONY: build test simulate clean 13 | 14 | build: $(OUT) 15 | 16 | test: $(TESTOUT) 17 | 18 | clean: 19 | rm -f *.o $(OUTFILES) 20 | 21 | $(TESTOUT): $(TESTFILE) $(OUT) 22 | ./$(OUT) $< 23 | 24 | $(OUT): $(OBJ) 25 | $(CC) -o $(OUT) $(OBJ) 26 | 27 | lex.yy.c: $(SCANNER) y.tab.c 28 | flex $< 29 | 30 | y.tab.c: $(PARSER) 31 | bison -vdty $< 32 | -------------------------------------------------------------------------------- /ch16/c01-new-frontend/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void init_parser(int argc, char *argv[]); 9 | void quit_parser(); 10 | 11 | extern FILE* yyin; 12 | FILE *asmfile, *incfile; 13 | #define BUFSIZE 256 14 | 15 | #define out_asm(fmt, ...) \ 16 | {fprintf(asmfile, fmt, ##__VA_ARGS__); fprintf(asmfile, "\n");} 17 | 18 | #define out_inc(fmt, ...) \ 19 | {fprintf(incfile, fmt, ##__VA_ARGS__); fprintf(incfile, "\n");} 20 | 21 | void file_error(char *msg); 22 | 23 | int ii = 0, itop = -1, istack[100]; 24 | int ww = 0, wtop = -1, wstack[100]; 25 | 26 | #define _BEG_IF (istack[++itop] = ++ii) 27 | #define _END_IF (itop--) 28 | #define _i (istack[itop]) 29 | 30 | #define _BEG_WHILE (wstack[++wtop] = ++ww) 31 | #define _END_WHILE (wtop--) 32 | #define _w (wstack[wtop]) 33 | 34 | int argc = 0, varc = 0; 35 | char *cur_func_name, *args[128], *vars[128]; 36 | void write_func_head(); 37 | void write_func_tail(); 38 | 39 | #define _BEG_FUNCDEF(name) (cur_func_name = (name)) 40 | #define _APPEND_ARG(arg) (args[argc++] = (arg)) 41 | #define _APPEND_VAR(var) (vars[varc++] = (var)) 42 | #define _WRITE_FUNCHEAD write_func_head 43 | #define _END_FUNCDEF write_func_tail 44 | 45 | #define YYSTYPE char * 46 | 47 | %} 48 | 49 | %token T_Void T_Int T_While T_If T_Else T_Return T_Break T_Continue 50 | %token T_Print T_ReadInt T_Le T_Ge T_Eq T_Ne T_And T_Or 51 | %token T_IntConstant T_StringConstant T_Identifier 52 | 53 | %left '=' 54 | %left T_Or 55 | %left T_And 56 | %left T_Eq T_Ne 57 | %left '<' '>' T_Le T_Ge 58 | %left '+' '-' 59 | %left '*' '/' '%' 60 | %left '!' 61 | 62 | %% 63 | 64 | Start: 65 | Program { /* empty */ } 66 | ; 67 | 68 | Program: 69 | /* empty */ { /* empty */ } 70 | | Program FuncDef { /* empty */ } 71 | ; 72 | 73 | FuncDef: 74 | T_Int FuncName Args Vars Stmts EndFuncDef 75 | | T_Void FuncName Args Vars Stmts EndFuncDef 76 | ; 77 | 78 | FuncName: 79 | T_Identifier { _BEG_FUNCDEF($1); } 80 | ; 81 | 82 | Args: 83 | '(' ')' { /* empty */ } 84 | | '(' _Args ')' { /* empty */ } 85 | ; 86 | 87 | _Args: 88 | T_Int T_Identifier { _APPEND_ARG($2); } 89 | | _Args ',' T_Int T_Identifier { _APPEND_ARG($4); } 90 | ; 91 | 92 | Vars: 93 | _Vars { _WRITE_FUNCHEAD(); } 94 | ; 95 | 96 | _Vars: 97 | '{' { /* empty */ } 98 | | _Vars Var ';' { /* empty */ } 99 | ; 100 | 101 | Var: 102 | T_Int T_Identifier { _APPEND_VAR($2); } 103 | | Var ',' T_Identifier { _APPEND_VAR($3); } 104 | ; 105 | 106 | Stmts: 107 | /* empty */ { /* empty */ } 108 | | Stmts Stmt { /* empty */ } 109 | ; 110 | 111 | EndFuncDef: 112 | '}' { _END_FUNCDEF(); } 113 | ; 114 | 115 | Stmt: 116 | AssignStmt { /* empty */ } 117 | | CallStmt { /* empty */ } 118 | | IfStmt { /* empty */ } 119 | | WhileStmt { /* empty */ } 120 | | BreakStmt { /* empty */ } 121 | | ContinueStmt { /* empty */ } 122 | | ReturnStmt { /* empty */ } 123 | | PrintStmt { /* empty */ } 124 | ; 125 | 126 | AssignStmt: 127 | T_Identifier '=' Expr ';' { out_asm("\tpop %s", $1); } 128 | ; 129 | 130 | CallStmt: 131 | CallExpr ';' { out_asm("\tpop"); } 132 | ; 133 | 134 | IfStmt: 135 | If '(' Expr ')' Then '{' Stmts '}' EndThen EndIf 136 | { /* empty */ } 137 | | If '(' Expr ')' Then '{' Stmts '}' EndThen T_Else '{' Stmts '}' EndIf 138 | { /* empty */ } 139 | ; 140 | 141 | If: 142 | T_If { _BEG_IF; out_asm("_begIf_%d:", _i); } 143 | ; 144 | 145 | Then: 146 | /* empty */ { out_asm("\tjz _elIf_%d", _i); } 147 | ; 148 | 149 | EndThen: 150 | /* empty */ { out_asm("\tjmp _endIf_%d\n_elIf_%d:", _i, _i); } 151 | ; 152 | 153 | EndIf: 154 | /* empty */ { out_asm("_endIf_%d:", _i); _END_IF; } 155 | ; 156 | 157 | WhileStmt: 158 | While '(' Expr ')' Do '{' Stmts '}' EndWhile 159 | { /* empty */ } 160 | ; 161 | 162 | While: 163 | T_While { _BEG_WHILE; out_asm("_begWhile_%d:", _w); } 164 | ; 165 | 166 | Do: 167 | /* empty */ { out_asm("\tjz _endWhile_%d", _w); } 168 | ; 169 | 170 | EndWhile: 171 | /* empty */ { out_asm("\tjmp _begWhile_%d\n_endWhile_%d:", 172 | _w, _w); _END_WHILE; } 173 | ; 174 | 175 | BreakStmt: 176 | T_Break ';' { out_asm("\tjmp _endWhile_%d", _w); } 177 | ; 178 | 179 | ContinueStmt: 180 | T_Continue ';' { out_asm("\tjmp _begWhile_%d", _w); } 181 | ; 182 | 183 | ReturnStmt: 184 | T_Return ';' { out_asm("\tret"); } 185 | | T_Return Expr ';' { out_asm("\tret ~"); } 186 | ; 187 | 188 | PrintStmt: 189 | T_Print '(' T_StringConstant PrintIntArgs ')' ';' 190 | { out_asm("\tprint %s", $3); } 191 | ; 192 | 193 | PrintIntArgs: 194 | /* empty */ { /* empty */ } 195 | | PrintIntArgs ',' Expr { /* empty */ } 196 | ; 197 | 198 | Expr: 199 | T_IntConstant { out_asm("\tpush %s", $1); } 200 | | T_Identifier { out_asm("\tpush %s", $1); } 201 | | Expr '+' Expr { out_asm("\tadd"); } 202 | | Expr '-' Expr { out_asm("\tsub"); } 203 | | Expr '*' Expr { out_asm("\tmul"); } 204 | | Expr '/' Expr { out_asm("\tdiv"); } 205 | | Expr '%' Expr { out_asm("\tmod"); } 206 | | Expr '>' Expr { out_asm("\tcmpgt"); } 207 | | Expr '<' Expr { out_asm("\tcmplt"); } 208 | | Expr T_Ge Expr { out_asm("\tcmpge"); } 209 | | Expr T_Le Expr { out_asm("\tcmple"); } 210 | | Expr T_Eq Expr { out_asm("\tcmpeq"); } 211 | | Expr T_Ne Expr { out_asm("\tcmpne"); } 212 | | Expr T_Or Expr { out_asm("\tor"); } 213 | | Expr T_And Expr { out_asm("\tand"); } 214 | | '-' Expr %prec '!' { out_asm("\tneg"); } 215 | | '!' Expr { out_asm("\tnot"); } 216 | | ReadInt { /* empty */ } 217 | | CallExpr { /* empty */ } 218 | | '(' Expr ')' { /* empty */ } 219 | ; 220 | 221 | ReadInt: 222 | T_ReadInt '(' T_StringConstant ')' 223 | { out_asm("\treadint %s", $3); } 224 | ; 225 | 226 | CallExpr: 227 | T_Identifier Actuals 228 | { out_asm("\t$%s", $1); } 229 | ; 230 | 231 | Actuals: 232 | '(' ')' 233 | | '(' _Actuals ')' 234 | ; 235 | 236 | _Actuals: 237 | Expr 238 | | _Actuals ',' Expr 239 | ; 240 | 241 | %% 242 | 243 | int main(int argc, char *argv[]) { 244 | init_parser(argc, argv); 245 | yyparse(); 246 | quit_parser(); 247 | } 248 | 249 | void init_parser(int argc, char *argv[]) { 250 | if (argc < 2) { 251 | file_error("Must provide an input source file!"); 252 | } 253 | 254 | if (argc > 2) { 255 | file_error("Too much command line arguments!"); 256 | } 257 | 258 | char *in_file_name = argv[1]; 259 | int len = strlen(in_file_name); 260 | 261 | if (len <= 2 || in_file_name[len-1] != 'c' \ 262 | || in_file_name[len-2] != '.') { 263 | file_error("Must provide an '.c' source file!"); 264 | } 265 | 266 | if (!(yyin = fopen(in_file_name, "r"))) { 267 | file_error("Input file open error"); 268 | } 269 | 270 | char out_file_name[BUFSIZE]; 271 | strcpy(out_file_name, in_file_name); 272 | 273 | out_file_name[len-1] = 'a'; 274 | out_file_name[len] = 's'; 275 | out_file_name[len+1] = 'm'; 276 | out_file_name[len+2] = '\0'; 277 | if (!(asmfile = fopen(out_file_name, "w"))) { 278 | file_error("Output 'asm' file open error"); 279 | } 280 | 281 | out_file_name[len-1] = 'i'; 282 | out_file_name[len] = 'n'; 283 | out_file_name[len+1] = 'c'; 284 | if (!(incfile = fopen(out_file_name, "w"))) { 285 | file_error("Output 'inc' file open error"); 286 | } 287 | } 288 | 289 | void file_error(char *msg) { 290 | printf("\n*** Error ***\n\t%s\n", msg); 291 | puts(""); 292 | exit(-1); 293 | } 294 | 295 | char *cat_strs(char *buf, char *strs[], int strc) { 296 | int i; 297 | strcpy(buf, strs[0]); 298 | for (i = 1; i < strc; i++) { 299 | strcat(strcat(buf, ", "), strs[i]); 300 | } 301 | return buf; 302 | } 303 | 304 | #define _fn (cur_func_name) 305 | 306 | void write_func_head() { 307 | char buf[BUFSIZE]; 308 | int i; 309 | 310 | out_asm("FUNC @%s:", _fn); 311 | if (argc > 0) { 312 | out_asm("\t%s.arg %s", _fn, cat_strs(buf, args, argc)); 313 | } 314 | if (varc > 0) { 315 | out_asm("\t%s.var %s", _fn, cat_strs(buf, vars, varc)); 316 | } 317 | 318 | out_inc("; ==== begin function `%s` ====", _fn); 319 | out_inc("%%define %s.argc %d", _fn, argc); 320 | out_inc("\n%%MACRO $%s 0\n" 321 | " CALL @%s\n" 322 | " ADD ESP, 4*%s.argc\n" 323 | " PUSH EAX\n" 324 | "%%ENDMACRO", 325 | _fn, _fn, _fn); 326 | if (argc) { 327 | out_inc("\n%%MACRO %s.arg %s.argc", _fn, _fn); 328 | for (i = 0; i < argc; i++) { 329 | out_inc("\t%%define %s [EBP + 8 + 4*%s.argc - 4*%d]", 330 | args[i], _fn, i+1); 331 | } 332 | out_inc("%%ENDMACRO"); 333 | } 334 | if (varc) { 335 | out_inc("\n%%define %s.varc %d", _fn, varc); 336 | out_inc("\n%%MACRO %s.var %s.varc", _fn, _fn); 337 | for (i = 0; i < varc; i++) { 338 | out_inc("\t%%define %s [EBP - 4*%d]", 339 | vars[i], i+1); 340 | } 341 | out_inc("\tSUB ESP, 4*%s.varc", _fn); 342 | out_inc("%%ENDMACRO"); 343 | } 344 | } 345 | 346 | void write_func_tail() { 347 | int i; 348 | 349 | out_asm("ENDFUNC@%s\n", _fn); 350 | 351 | out_inc("\n%%MACRO ENDFUNC@%s 0\n\tLEAVE\n\tRET", _fn); 352 | for (i = 0; i < argc; i++) { 353 | out_inc("\t%%undef %s", args[i]); 354 | } 355 | for (i = 0; i < varc; i++) { 356 | out_inc("\t%%undef %s", vars[i]); 357 | } 358 | out_inc("%%ENDMACRO"); 359 | out_inc("; ==== end function `%s` ====\n", _fn); 360 | 361 | argc = 0; 362 | varc = 0; 363 | } 364 | 365 | void quit_parser() { 366 | fclose(yyin); fclose(asmfile); fclose(incfile); 367 | } 368 | -------------------------------------------------------------------------------- /ch16/c01-new-frontend/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | void unterminate_string(); 8 | #define _DUPTEXT {yylval = strdup(yytext);} 9 | %} 10 | 11 | /* note \042 is '"' */ 12 | WHITESPACE ([ \t\r\a]+) 13 | SINGLE_COMMENT1 ("//"[^\n]*) 14 | SINGLE_COMMENT2 ("#"[^\n]*) 15 | OPERATOR ([+*-/%=,;!<>(){}]) 16 | INTEGER ([0-9]+) 17 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 18 | UNTERM_STRING (\042[^\042\n]*) 19 | STRING (\042[^\042\n]*\042) 20 | 21 | %% 22 | 23 | \n { cur_line++; } 24 | {WHITESPACE} { /* ignore every whitespace */ } 25 | {SINGLE_COMMENT1} { /* skip for single line comment */ } 26 | {SINGLE_COMMENT2} { /* skip for single line comment */ } 27 | 28 | {OPERATOR} { return yytext[0]; } 29 | "int" { return T_Int; } 30 | "void" { return T_Void; } 31 | "return" { return T_Return; } 32 | "print" { return T_Print; } 33 | "readint" { return T_ReadInt; } 34 | "while" { return T_While; } 35 | "if" { return T_If; } 36 | "else" { return T_Else; } 37 | "break" { return T_Break; } 38 | "continue" { return T_Continue; } 39 | "<=" { return T_Le; } 40 | ">=" { return T_Ge; } 41 | "==" { return T_Eq; } 42 | "!=" { return T_Ne; } 43 | "&&" { return T_And; } 44 | "||" { return T_Or; } 45 | 46 | {INTEGER} { _DUPTEXT return T_IntConstant; } 47 | {STRING} { _DUPTEXT return T_StringConstant; } 48 | {IDENTIFIER} { _DUPTEXT return T_Identifier; } 49 | 50 | {UNTERM_STRING} { unterminate_string(); } 51 | . { unrecognized_char(yytext[0]); } 52 | 53 | %% 54 | 55 | int yywrap(void) { 56 | return 1; 57 | } 58 | 59 | void unrecognized_char(char c) { 60 | char buf[32] = "Unrecognized character: ?"; 61 | buf[24] = c; 62 | yyerror(buf); 63 | } 64 | 65 | void unterminate_string() { 66 | yyerror("Unterminate string constant"); 67 | } 68 | 69 | void yyerror(const char *msg) { 70 | fprintf(stderr, "Error at line %d:\n\t%s\n", cur_line, msg); 71 | exit(-1); 72 | } -------------------------------------------------------------------------------- /ch16/c01-new-frontend/test.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a; 3 | a = 3; 4 | print("sum = %d", sum(4, a)); 5 | return 0; 6 | } 7 | 8 | int sum(int a, int b) { 9 | int c; 10 | c = a + b; 11 | return c; 12 | } -------------------------------------------------------------------------------- /ch16/c01-new-frontend/test.funcmacro: -------------------------------------------------------------------------------- 1 | ; ==== begin function `main` ==== 2 | %define main.varc 1 3 | 4 | %MACRO main.var main.varc 5 | %define a [EBP - 4*1] 6 | SUB ESP, 4*main.varc 7 | %ENDMACRO 8 | 9 | %MACRO ENDFUNC@main 0 10 | LEAVE 11 | RET 12 | %undef a 13 | %ENDMACRO 14 | ; ==== end function `main` ==== 15 | 16 | ; ==== begin function `sum` ==== 17 | %define sum.argc 2 18 | %define sum.varc 1 19 | 20 | %MACRO $sum 0 21 | CALL @sum 22 | ADD ESP, 4*sum.argc 23 | PUSH EAX 24 | %ENDMACRO 25 | 26 | %MACRO sum.arg sum.argc 27 | %define a [EBP + 8 + 4*sum.argc - 4*1] 28 | %define b [EBP + 8 + 4*sum.argc - 4*2] 29 | %ENDMACRO 30 | 31 | %MACRO sum.var sum.varc 32 | %define c [EBP - 4*1] 33 | SUB ESP, 4*sum.varc 34 | %ENDMACRO 35 | 36 | %MACRO ENDFUNC@sum 0 37 | LEAVE 38 | RET 39 | %undef a 40 | %undef b 41 | %undef c 42 | %ENDMACRO 43 | ; ==== end function `sum` ==== -------------------------------------------------------------------------------- /ch16/c01-new-frontend/test.pcode: -------------------------------------------------------------------------------- 1 | FUNC @main: 2 | main.var a 3 | 4 | push 3 5 | pop a 6 | 7 | push 4 8 | push a 9 | $sum 10 | print "sum = %d" 11 | 12 | ret 0 13 | ENDFUNC@main 14 | 15 | FUNC @sum: 16 | sum.arg a, b 17 | sum.var c 18 | 19 | push a 20 | push b 21 | add 22 | pop c 23 | 24 | ret c 25 | ENDFUNC@sum -------------------------------------------------------------------------------- /ch2/for_gcc_build.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print(char *format, ...) { 6 | va_list args; 7 | va_start(args, format); 8 | vprintf(format, args); 9 | va_end(args); 10 | puts(""); 11 | } 12 | 13 | int readint(char *prompt) { 14 | int i; 15 | printf(prompt); 16 | scanf("%d", &i); 17 | return i; 18 | } 19 | 20 | #define auto 21 | #define short 22 | #define long 23 | #define float 24 | #define double 25 | #define char 26 | #define struct 27 | #define union 28 | #define enum 29 | #define typedef 30 | #define const 31 | #define unsigned 32 | #define signed 33 | #define extern 34 | #define register 35 | #define static 36 | #define volatile 37 | #define switch 38 | #define case 39 | #define for 40 | #define do 41 | #define goto 42 | #define default 43 | #define sizeof 44 | -------------------------------------------------------------------------------- /ch2/tinyc.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int i; 5 | i = 0; 6 | while (i < 10) { 7 | i = i + 1; 8 | if (i == 3 || i == 5) { 9 | continue; 10 | } 11 | if (i == 8) { 12 | break; 13 | } 14 | print("%d! = %d", i, factor(i)); 15 | } 16 | return 0; 17 | } 18 | 19 | int factor(int n) { 20 | if (n < 2) { 21 | return 1; 22 | } 23 | return n * factor(n - 1); 24 | } -------------------------------------------------------------------------------- /ch4/pcode_1.asm: -------------------------------------------------------------------------------- 1 | ; int a, b; 2 | var a, b 3 | 4 | ; a = 1 + 2; 5 | push 1 6 | push 2 7 | add 8 | pop a 9 | 10 | ; b = a * 2 11 | push a 12 | push 2 13 | mul 14 | pop b 15 | 16 | ; print("a = %d, b = %d", a, b); 17 | push a 18 | push b 19 | print "a = %d, b = %d" 20 | -------------------------------------------------------------------------------- /ch4/pcode_2.asm: -------------------------------------------------------------------------------- 1 | var a, b, c 2 | push 1 3 | push 2 4 | $sum 5 | exit 0 6 | 7 | FUNC @sum: 8 | arg a, b 9 | 10 | push a 11 | push b 12 | add 13 | ret ~ 14 | ENDFUNC 15 | -------------------------------------------------------------------------------- /ch4/pysim.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | PY3 = sys.version_info[0] == 3 4 | 5 | comment_mark = ";%" 6 | code = [] 7 | stack = [] 8 | var_table = {} 9 | label_table = {} 10 | func_table = {} 11 | eip = 0 12 | printout = [] 13 | debug = False 14 | 15 | def is_valid_identifier(ident): 16 | if ident == "": 17 | return False 18 | if not (ident[0].isalpha() or ident[0] == '_'): 19 | return False 20 | for ch in ident[1:]: 21 | if not (ch.isalnum() or ch == '_'): 22 | return False 23 | return True 24 | 25 | def assemb_error(line, msg): 26 | display(pause=False) 27 | print(line) 28 | print("^^^Error at last line: %s" % msg) 29 | exit(-1) 30 | 31 | def run_error(msg="Wrong instruction format"): 32 | code[eip][0] = "**%s**" % msg 33 | printout.append(msg) 34 | display(pause=False) 35 | exit(-1) 36 | 37 | def read_file(filename): 38 | if PY3: 39 | return open(filename, 'r').readlines() 40 | 41 | return file(filename).readlines() 42 | 43 | def get_input(msg): 44 | if PY3: 45 | return input(msg) 46 | 47 | return raw_input(msg) 48 | 49 | def table_has_key(table, key): 50 | if PY3: 51 | return key in table 52 | 53 | return table.has_key(key) 54 | 55 | def assemb(asmfilename): 56 | if len(sys.argv) > 2 and (sys.argv[2] == '-a' or sys.argv[2] == '-da'): 57 | code.append(('', '$main', '')) 58 | code.append(('', 'exit', '~')) 59 | 60 | label = "" 61 | for line in read_file(asmfilename): 62 | line = line.strip() 63 | if line == "" or line[0] in comment_mark: 64 | continue 65 | 66 | _label, sep, ist = line.partition(':') 67 | if sep and _label.find('"') == -1 and _label.find("'") == -1: 68 | _label, ist = _label.strip(), ist.strip() 69 | if not check_label(_label): 70 | assemb_error(line, "Wrong label") 71 | label = '%s,%s' % (label, _label) if label else _label 72 | if ist == "" or ist[0] in comment_mark: 73 | continue 74 | elif len(line) >= 7 and line[:7] == 'ENDFUNC': 75 | label = '%s,%s' % (label, 'ENDFUNC') \ 76 | if label else 'ENDFUNC' 77 | ist = 'ret' 78 | else: 79 | ist = line 80 | 81 | dire, sep, arg = ist.partition(' ') 82 | 83 | if len(dire) > 4 and \ 84 | (dire[-4:] == '.arg' or dire[-4:] == '.var'): 85 | dire = dire[-3:] 86 | 87 | code.append( [label, dire, arg.strip()] ) 88 | label = "" 89 | 90 | code.append(('', 'exit', '0')) 91 | 92 | def check_label(label): 93 | if label == "": 94 | return False 95 | 96 | func, sep, funcName = label.partition(' @') 97 | 98 | if sep: 99 | if func.strip() != 'FUNC' \ 100 | or not is_valid_identifier(funcName) \ 101 | or table_has_key(func_table, funcName): 102 | return False 103 | else: 104 | func_table[funcName] = len(code) 105 | return True 106 | else: 107 | if not is_valid_identifier(label) \ 108 | or table_has_key(func_table, label) \ 109 | or table_has_key(label_table, label): 110 | return False 111 | else: 112 | label_table[label] = len(code) 113 | return True 114 | 115 | def trim(s, size): 116 | return s[:size-3]+"..." if len(s) > size else s 117 | 118 | def display(pause=True): 119 | num_code_lines, num_terminal_lines = 24, 8 120 | if os.system("clear"): os.system('cls') 121 | print("%32s%-40s| %-13s|Bind var" % ("", "Code", "Stack")) 122 | j = 0 123 | for i in range( \ 124 | max(eip+1-num_code_lines, 0), max(eip+1, num_code_lines) ): 125 | if i < len(code): 126 | label, dire, arg = code[i] 127 | line = trim(dire + " " + arg, 40) 128 | label = trim(label, 28) 129 | else: 130 | label, line = "", "" 131 | 132 | if label: label += ':' 133 | point = " ->" if i == eip else "" 134 | st = stack[j] if j < len(stack) else "" 135 | st = "(RetInfo)" if type(st) is tuple else str(st) 136 | stvar = var_table.get(j, "") 137 | if j == len(stack) - 1: stvar += "<-" 138 | 139 | print("%29s%3s%-40s| %-13s|%s" % \ 140 | (label, point, line, st, stvar)) 141 | 142 | j += 1 143 | 144 | print("***Terminal***") 145 | n = len(printout) 146 | for i in range( \ 147 | max(n-num_terminal_lines, 0), max(n, num_terminal_lines) ): 148 | print(printout[i] if i < n else "") 149 | if i == n and not pause: 150 | break 151 | 152 | if pause: 153 | global debug 154 | if get_input("\npress enter to step, -r to run.") == "-r": 155 | debug = False 156 | 157 | def run(): 158 | global eip 159 | eip = 0 160 | del stack[:] 161 | 162 | while True: 163 | if debug: display() 164 | label, dire, arg = code[eip] 165 | if dire[0] == '$': 166 | action, arg = call, dire[1:] 167 | if not is_valid_identifier(arg): 168 | run_error("Wrong identifier") 169 | else: 170 | if not is_valid_identifier(dire): 171 | run_error("Wrong identifier") 172 | try: 173 | action = eval("do_" + dire) 174 | except NameError: 175 | run_error("Unknown instruction") 176 | action(arg) 177 | eip += 1 178 | 179 | def do_var(arg): 180 | if arg == "": return 181 | for var in arg.split(','): 182 | var = var.strip() 183 | if not is_valid_identifier(var) or table_has_key(var_table, var): 184 | run_error("Wrong var name") 185 | var_table[var] = len(stack) 186 | var_table[len(stack)] = var 187 | stack.append("/") 188 | 189 | def do_push(arg): 190 | try: 191 | arg = int(arg) 192 | except ValueError: 193 | try: 194 | arg = stack[var_table[arg]] 195 | except KeyError: 196 | run_error("Undefined variable") 197 | if type(arg) is not int: 198 | run_error("Cannot push uninitialed value") 199 | stack.append(arg) 200 | 201 | def do_pop(arg): 202 | value = stack.pop() 203 | if arg == "": 204 | return 205 | if type(value) is not int: 206 | run_error("Cannot pop non-number value to variable") 207 | try: 208 | stack[var_table[arg]] = value 209 | except KeyError: 210 | run_error("Undefined variable") 211 | 212 | def do_exit(arg): 213 | global going, exit_code 214 | going = False 215 | 216 | if arg == "~": 217 | exit_code = stack[-1] 218 | elif arg: 219 | try: 220 | exit_code = int(arg) 221 | except ValueError: 222 | try: 223 | exit_code = stack[var_table[arg]] 224 | except KeyError: 225 | run_error("Undefined variable") 226 | 227 | if type(exit_code) is not int: 228 | run_error("Wrong exit code") 229 | 230 | if debug: 231 | display(pause=False) 232 | 233 | exit(exit_code) 234 | 235 | 236 | 237 | def do_add(arg): stack[-2] += stack[-1]; stack.pop() 238 | def do_sub(arg): stack[-2] -= stack[-1]; stack.pop() 239 | def do_mul(arg): stack[-2] *= stack[-1]; stack.pop() 240 | def do_div(arg): stack[-2] /= stack[-1]; stack.pop() 241 | def do_mod(arg): stack[-2] %= stack[-1]; stack.pop() 242 | def do_and(arg): stack[-2] = int(stack[-2]!=0 and stack[-1]!=0); stack.pop() 243 | def do_or(arg): stack[-2] = int(stack[-2]!=0 or stack[-1]!=0); stack.pop() 244 | def do_cmpeq(arg): stack[-2] = int(stack[-2]==stack[-1]);stack.pop() 245 | def do_cmpne(arg): stack[-2] = int(stack[-2]!=stack[-1]);stack.pop() 246 | def do_cmpgt(arg): stack[-2] = int(stack[-2]>stack[-1]); stack.pop() 247 | def do_cmplt(arg): stack[-2] = int(stack[-2]=stack[-1]);stack.pop() 249 | def do_cmple(arg): stack[-2] = int(stack[-2]<=stack[-1]);stack.pop() 250 | def do_neg(arg): stack[-1] = -stack[-1] 251 | def do_not(arg): stack[-1] = int(not stack[-1]) 252 | 253 | def do_print(fmt): 254 | if len(fmt) < 2 or fmt[0] != fmt[-1] or fmt[0] not in '"\'': 255 | run_error("Format string error") 256 | argc = fmt.count("%d") 257 | out = fmt[1:-1] % tuple(stack[len(stack)-argc:]) 258 | print(out) 259 | printout.append(out) 260 | del stack[len(stack)-argc:] 261 | 262 | def do_readint(msg): 263 | if len(msg) < 2 or msg[0] != msg[-1] or msg[-1] not in '"\'': 264 | run_error("Message string error") 265 | msg = msg.strip('"').strip("'") 266 | if debug: display(pause=False) 267 | string = get_input(msg) 268 | try: 269 | value = int(string) 270 | except ValueError: 271 | value = 0 272 | stack.append(value) 273 | printout.append("\n " + msg + str(value)) 274 | 275 | def do_jmp(label): 276 | global eip 277 | try: 278 | # note: here we set eip just befor the label, 279 | # and when back to run(), we do eip += 1 280 | eip = label_table[label] - 1 281 | except KeyError: 282 | run_error("Wrong label") 283 | 284 | def do_jz(label): 285 | global eip 286 | try: 287 | # set eip just befor the label, 288 | # when back to run(), do eip += 1 289 | new_eip = label_table[label] - 1 290 | except KeyError: 291 | run_error("Wrong label") 292 | if stack.pop() == 0: 293 | eip = new_eip 294 | 295 | def call(funcName): 296 | global var_table, eip 297 | 298 | try: 299 | entry = func_table[funcName] 300 | except KeyError: 301 | run_error("Undefined function") 302 | 303 | if code[entry][1] == "arg": 304 | arg_list = code[entry][2].split(',') 305 | else: 306 | arg_list = [] 307 | 308 | new_var_table = {} 309 | for addr, arg in enumerate(arg_list, len(stack)-len(arg_list)): 310 | arg = arg.strip() 311 | if not is_valid_identifier(arg) or table_has_key(new_var_table, arg): 312 | run_error("Wrong arg name") 313 | 314 | new_var_table[arg] = addr 315 | new_var_table[addr] = arg 316 | 317 | stack.append( (len(arg_list), eip, var_table) ) 318 | var_table = new_var_table 319 | eip = entry if len(arg_list) else entry -1 320 | 321 | def do_ret(arg): 322 | global var_table, eip 323 | 324 | if arg == "~": 325 | retval = stack[-1] 326 | elif arg: 327 | try: 328 | retval = int(arg) 329 | except ValueError: 330 | try: 331 | retval = stack[var_table[arg]] 332 | except KeyError: 333 | run_error("Undefined variable") 334 | else: 335 | retval = '/' 336 | 337 | i = len(stack) - 1 338 | while type(stack[i]) is not tuple: 339 | i -= 1 340 | argc, eip, var_table = stack[i] 341 | del stack[i-argc:] 342 | stack.append(retval) 343 | 344 | if __name__ == "__main__": 345 | asmfileName = sys.argv[1] 346 | if len(sys.argv) > 2: 347 | debug = sys.argv[2] == '-d' or sys.argv[2] == '-da' 348 | assemb(asmfileName) 349 | run() -------------------------------------------------------------------------------- /ch5/factor.asm: -------------------------------------------------------------------------------- 1 | ; int main() { 2 | FUNC @main: 3 | 4 | ; int i; 5 | var i 6 | 7 | ; i = 0; 8 | push 0 9 | pop i 10 | 11 | 12 | ; while (i < 10) { 13 | _beg_while: 14 | 15 | push i 16 | push 10 17 | cmplt 18 | 19 | jz _end_while 20 | 21 | ; i = i + 1; 22 | push i 23 | push 1 24 | add 25 | pop i 26 | 27 | ; if (i == 3 || i == 5) { 28 | _beg_if1: 29 | 30 | push i 31 | push 3 32 | cmpeq 33 | push i 34 | push 5 35 | cmpeq 36 | or 37 | 38 | jz _end_if1 39 | 40 | ; continue; 41 | jmp _beg_while 42 | 43 | ; } 44 | _end_if1: 45 | 46 | ; if (i == 8) { 47 | _beg_if2: 48 | 49 | push i 50 | push 8 51 | cmpeq 52 | 53 | jz _end_if2 54 | 55 | ; break; 56 | jmp _end_while 57 | 58 | ; } 59 | _end_if2: 60 | 61 | ; print("%d! = %d", i, factor(i)); 62 | push i 63 | push i 64 | $factor 65 | print "%d! = %d" 66 | 67 | ; } 68 | jmp _beg_while 69 | _end_while: 70 | 71 | ; return 0; 72 | ret 0 73 | 74 | ; } 75 | ENDFUNC 76 | 77 | ; int factor(int n) { 78 | FUNC @factor: 79 | arg n 80 | 81 | ; if (n < 2) { 82 | _beg_if3: 83 | 84 | push n 85 | push 2 86 | cmplt 87 | 88 | jz _end_if3 89 | 90 | ; return 1; 91 | ret 1 92 | 93 | ; } 94 | _end_if3: 95 | 96 | ; return n * factor(n - 1); 97 | push n 98 | push n 99 | push 1 100 | sub 101 | $factor 102 | mul 103 | ret ~ 104 | 105 | ; } 106 | ENDFUNC -------------------------------------------------------------------------------- /ch7/scan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | single_char_operators_typeA = { 4 | ";", ",", "(", ")", "{", "}", "[", 5 | "]", "/", "+", "-", "*", "%", ".", 6 | ":" 7 | } 8 | 9 | single_char_operators_typeB = { 10 | "<", ">", "=", "!" 11 | } 12 | 13 | double_char_operators = { 14 | ">=", "<=", "==", "~=" 15 | } 16 | 17 | reservedWords = { 18 | "class", "for", "while", "if", "else", 19 | "return", "break", "True", "False", "raise", "pass" 20 | "in", "continue", "elif", "yield", "not", "def" 21 | } 22 | 23 | class Token: 24 | def __init__(self, _type, _val = None): 25 | if _val is None: 26 | self.type = "T_" + _type; 27 | self.val = _type; 28 | else: 29 | self.type, self.val = _type, _val 30 | 31 | def __str__(self): 32 | return "%-20s%s" % (self.type, self.val) 33 | 34 | class NoneTerminateQuoteError(Exception): 35 | pass 36 | 37 | def isWhiteSpace(ch): 38 | return ch in " \t\r\a\n" 39 | 40 | def isDigit(ch): 41 | return ch in "0123456789" 42 | 43 | def isLetter(ch): 44 | return ch in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 45 | 46 | def scan(s): 47 | n, i = len(s), 0 48 | while i < n: 49 | ch, i = s[i], i + 1 50 | 51 | if isWhiteSpace(ch): 52 | continue 53 | 54 | if ch == "#": 55 | return 56 | 57 | if ch in single_char_operators_typeA: 58 | yield Token(ch) 59 | elif ch in single_char_operators_typeB: 60 | if i < n and s[i] == "=": 61 | yield Token(ch + "=") 62 | else: 63 | yield Token(ch) 64 | elif isLetter(ch) or ch == "_": 65 | begin = i - 1 66 | while i < n and (isLetter(s[i]) or isDigit(s[i]) or s[i] == "_"): 67 | i += 1 68 | word = s[begin:i] 69 | if word in reservedWords: 70 | yield Token(word) 71 | else: 72 | yield Token("T_identifier", word) 73 | elif isDigit(ch): 74 | begin = i - 1 75 | aDot = False 76 | while i < n: 77 | if s[i] == ".": 78 | if aDot: 79 | raise Exception("Too many dot in a number!\n\tline:"+line) 80 | aDot = True 81 | elif not isDigit(s[i]): 82 | break 83 | i += 1 84 | yield Token("T_double" if aDot else "T_integer", s[begin:i]) 85 | elif ord(ch) == 34: # 34 means '"' 86 | begin = i 87 | while i < n and ord(s[i]) != 34: 88 | i += 1 89 | if i == n: 90 | raise Exception("Non-terminated string quote!\n\tline:"+line) 91 | yield Token("T_string", chr(34) + s[begin:i] + chr(34)) 92 | i += 1 93 | else: 94 | raise Exception("Unknown symbol!\n\tline:"+line+"\n\tchar:"+ch) 95 | 96 | 97 | 98 | if __name__ == "__main__": 99 | print "%-20s%s" % ("TOKEN TYPE", "TOKEN VALUE") 100 | print "-" * 50 101 | for line in file("scan.py"): 102 | for token in scan(line): 103 | print token -------------------------------------------------------------------------------- /ch8/hide-digits.l: -------------------------------------------------------------------------------- 1 | %% 2 | [0-9]+ printf("?"); 3 | # return 0; 4 | . ECHO; 5 | %% 6 | 7 | int main(int argc, char* argv[]) { 8 | yylex(); 9 | return 0; 10 | } 11 | 12 | int yywrap() { 13 | return 1; 14 | } -------------------------------------------------------------------------------- /ch8/makefile: -------------------------------------------------------------------------------- 1 | run: word-spliter 2 | ./word-spliter < word-spliter.l 3 | 4 | word-spliter: lex.yy.c 5 | gcc -o $@ $< 6 | 7 | lex.yy.c: word-spliter.l 8 | flex $< 9 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/makefile: -------------------------------------------------------------------------------- 1 | out: scanner 2 | 3 | scanner: lex.yy.c token.h 4 | gcc -o $@ $< 5 | 6 | lex.yy.c: scanner.l 7 | flex $< -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pandolia/tinyc/a1a8f424e291f981b910fe8682a98507b5713743/ch8/tinyc_scanner/samples.zip -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/ilegal_input.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | "asdfsdf 6 | & 7 | $ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_0_helloworld.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("Hello world!"); 5 | 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_1_io.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = readint("Please input an integer: "); 6 | print("Your input number is: %d", n); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_2_arithmetic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int x, y, z; 5 | 6 | x = 1; 7 | y = 2; 8 | z = x + y; 9 | 10 | print("x = %d, y = %d, z = %d", x, y, z); 11 | print("x + y = %d", x + y); 12 | print("x - y = %d", x - y); 13 | print("x * y = %d", x * y); 14 | print("x / y = %d", x / y); 15 | print("x %% y = %d", x % y); 16 | print("-x = %d, -y = %d", -x, -y); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_3_compare.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("1 == 2 is %d", 1 == 2); 5 | print("2 == 2 is %d", 2 == 2); 6 | print("2 == 3 is %d", 2 == 3); 7 | 8 | print("1 != 2 is %d", 1 != 2); 9 | print("2 != 2 is %d", 2 != 2); 10 | print("2 != 3 is %d", 2 != 3); 11 | 12 | print("1 > 2 is %d", 1 > 2); 13 | print("2 > 2 is %d", 2 > 2); 14 | print("2 > 3 is %d", 2 > 3); 15 | 16 | print("1 < 2 is %d", 1 < 2); 17 | print("2 < 2 is %d", 2 < 2); 18 | print("2 < 3 is %d", 2 < 3); 19 | 20 | print("1 >= 2 is %d", 1 >= 2); 21 | print("2 >= 2 is %d", 2 >= 2); 22 | print("2 >= 3 is %d", 2 >= 3); 23 | 24 | print("1 <= 2 is %d", 1 <= 2); 25 | print("2 <= 2 is %d", 2 <= 2); 26 | print("2 <= 3 is %d", 2 <= 3); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_4_logic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("0 && 0 is %d", 0 && 0); 5 | print("0 && 1 is %d", 0 && 1); 6 | print("1 && 0 is %d", 1 && 0); 7 | print("1 && 1 is %d", 1 && 1); 8 | 9 | 10 | print("0 || 0 is %d", 0 || 0); 11 | print("0 || 1 is %d", 0 || 1); 12 | print("1 || 0 is %d", 1 || 0); 13 | print("1 || 1 is %d", 1 || 1); 14 | 15 | print("!1 is %d", !1); 16 | print("!0 is %d", !0); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_5_ifwhile.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 10; 6 | 7 | while (n != 0) { 8 | print("n = %d", n); 9 | if (n == 5) { 10 | break; 11 | } 12 | n = n - 1; 13 | } 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /ch8/tinyc_scanner/samples/sample_6_function.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 1; 6 | 7 | print("The first 10 number of the fibonacci sequence:"); 8 | while (n <= 10) { 9 | print("fib(%d)=%d", n, fib(n)); 10 | n = n + 1; 11 | } 12 | 13 | return 0; 14 | } 15 | 16 | int fib(int n) { 17 | if (n <= 2) { 18 | return 1; 19 | } 20 | return fib(n - 1) + fib(n - 2); 21 | } -------------------------------------------------------------------------------- /ch8/tinyc_scanner/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "token.h" 3 | int cur_line_num = 1; 4 | void init_scanner(); 5 | void lex_error(char* msg, int line); 6 | %} 7 | 8 | /* Definitions, note: \042 is '"' */ 9 | INTEGER ([0-9]+) 10 | UNTERM_STRING (\042[^\042\n]*) 11 | STRING (\042[^\042\n]*\042) 12 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 13 | OPERATOR ([+*-/%=,;!<>(){}]) 14 | SINGLE_COMMENT1 ("//"[^\n]*) 15 | SINGLE_COMMENT2 ("#"[^\n]*) 16 | 17 | %% 18 | 19 | [\n] { cur_line_num++; } 20 | [ \t\r\a]+ { /* ignore all spaces */ } 21 | {SINGLE_COMMENT1} { /* skip for single line comment */ } 22 | {SINGLE_COMMENT2} { /* skip for single line commnet */ } 23 | 24 | {OPERATOR} { return yytext[0]; } 25 | 26 | "<=" { return T_Le; } 27 | ">=" { return T_Ge; } 28 | "==" { return T_Eq; } 29 | "!=" { return T_Ne; } 30 | "&&" { return T_And; } 31 | "||" { return T_Or; } 32 | "void" { return T_Void; } 33 | "int" { return T_Int; } 34 | "while" { return T_While; } 35 | "if" { return T_If; } 36 | "else" { return T_Else; } 37 | "return" { return T_Return; } 38 | "break" { return T_Break; } 39 | "continue" { return T_Continue; } 40 | "print" { return T_Print; } 41 | "readint" { return T_ReadInt; } 42 | 43 | {INTEGER} { return T_IntConstant; } 44 | {STRING} { return T_StringConstant; } 45 | {IDENTIFIER} { return T_Identifier; } 46 | 47 | <> { return 0; } 48 | 49 | {UNTERM_STRING} { lex_error("Unterminated string constant", cur_line_num); } 50 | . { lex_error("Unrecognized character", cur_line_num); } 51 | 52 | %% 53 | 54 | int main(int argc, char* argv[]) { 55 | int token; 56 | init_scanner(); 57 | while (token = yylex()) { 58 | print_token(token); 59 | puts(yytext); 60 | } 61 | return 0; 62 | } 63 | 64 | void init_scanner() { 65 | printf("%-20s%s\n", "TOKEN-TYPE", "TOKEN-VALUE"); 66 | printf("-------------------------------------------------\n"); 67 | } 68 | 69 | void lex_error(char* msg, int line) { 70 | printf("\nError at line %-3d: %s\n\n", line, msg); 71 | } 72 | 73 | int yywrap(void) { 74 | return 1; 75 | } -------------------------------------------------------------------------------- /ch8/tinyc_scanner/test.sh: -------------------------------------------------------------------------------- 1 | for src in $(ls samples/*.c); do ./scanner < $src > $src.lex; done 2 | -------------------------------------------------------------------------------- /ch8/tinyc_scanner/token.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_H 2 | #define TOKEN_H 3 | 4 | typedef enum { 5 | T_Le = 256, T_Ge, T_Eq, T_Ne, T_And, T_Or, T_IntConstant, 6 | T_StringConstant, T_Identifier, T_Void, T_Int, T_While, 7 | T_If, T_Else, T_Return, T_Break, T_Continue, T_Print, 8 | T_ReadInt 9 | } TokenType; 10 | 11 | static void print_token(int token) { 12 | static char* token_strs[] = { 13 | "T_Le", "T_Ge", "T_Eq", "T_Ne", "T_And", "T_Or", "T_IntConstant", 14 | "T_StringConstant", "T_Identifier", "T_Void", "T_Int", "T_While", 15 | "T_If", "T_Else", "T_Return", "T_Break", "T_Continue", "T_Print", 16 | "T_ReadInt" 17 | }; 18 | 19 | if (token < 256) { 20 | printf("%-20c", token); 21 | } else { 22 | printf("%-20s", token_strs[token-256]); 23 | } 24 | } 25 | 26 | #endif -------------------------------------------------------------------------------- /ch8/word-spliter.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define T_WORD 1 3 | int numChars = 0, numWords = 0, numLines = 0; 4 | %} 5 | 6 | WORD ([^ \t\n\r\a]+) 7 | 8 | %% 9 | \n { numLines++; numChars++; } 10 | {WORD} { numWords++; numChars += yyleng; return T_WORD; } 11 | <> { return 0; } 12 | . { numChars++; } 13 | %% 14 | 15 | int main() { 16 | int token_type; 17 | while (token_type = yylex()) { 18 | printf("WORD:\t%s\n", yytext); 19 | } 20 | printf("\nChars\tWords\tLines\n"); 21 | printf("%d\t%d\t%d\n", numChars, numWords, numLines); 22 | return 0; 23 | } 24 | 25 | int yywrap() { 26 | return 1; 27 | } -------------------------------------------------------------------------------- /tinyc/build.sh: -------------------------------------------------------------------------------- 1 | mkdir -p release 2 | flex sources/scanner.l 3 | bison -vdty sources/parser.y 4 | gcc -o release/tcc-frontend lex.yy.c y.tab.c 5 | rm -f y.* lex.* 6 | gcc -m32 -c -o tio.o sources/tio.c 7 | ar -crv release/libtio.a tio.o > /dev/null 8 | rm -f tio.o 9 | cp sources/macro.inc sources/pysim.py sources/tcc sources/pysimulate release/ 10 | chmod u+x release/tcc release/pysimulate 11 | export PATH=$PATH:$PWD/release 12 | echo "export PATH=\$PATH:$PWD/release" >> ~/.bashrc 13 | 14 | -------------------------------------------------------------------------------- /tinyc/samples/for_gcc_build.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print(char *format, ...) { 6 | va_list args; 7 | va_start(args, format); 8 | vprintf(format, args); 9 | va_end(args); 10 | puts(""); 11 | } 12 | 13 | int readint(char *prompt) { 14 | int i; 15 | printf(prompt); 16 | scanf("%d", &i); 17 | return i; 18 | } 19 | 20 | #define auto 21 | #define short 22 | #define long 23 | #define float 24 | #define double 25 | #define char 26 | #define struct 27 | #define union 28 | #define enum 29 | #define typedef 30 | #define const 31 | #define unsigned 32 | #define signed 33 | #define extern 34 | #define register 35 | #define static 36 | #define volatile 37 | #define switch 38 | #define case 39 | #define for 40 | #define do 41 | #define goto 42 | #define default 43 | #define sizeof 44 | -------------------------------------------------------------------------------- /tinyc/samples/sample_0_helloworld.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("Hello world!"); 5 | 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tinyc/samples/sample_1_io.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = readint("Please input an integer: "); 6 | print("Your input number is: %d", n); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tinyc/samples/sample_2_arithmetic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int x, y, z; 5 | 6 | x = 1; 7 | y = 2; 8 | z = x + y; 9 | 10 | print("x = %d, y = %d, z = %d", x, y, z); 11 | print("x + y = %d", x + y); 12 | print("x - y = %d", x - y); 13 | print("x * y = %d", x * y); 14 | print("x / y = %d", x / y); 15 | print("x %% y = %d", x % y); 16 | print("-x = %d, -y = %d", -x, -y); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tinyc/samples/sample_3_compare.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("1 == 2 is %d", 1 == 2); 5 | print("2 == 2 is %d", 2 == 2); 6 | print("2 == 3 is %d", 2 == 3); 7 | 8 | print("1 != 2 is %d", 1 != 2); 9 | print("2 != 2 is %d", 2 != 2); 10 | print("2 != 3 is %d", 2 != 3); 11 | 12 | print("1 > 2 is %d", 1 > 2); 13 | print("2 > 2 is %d", 2 > 2); 14 | print("2 > 3 is %d", 2 > 3); 15 | 16 | print("1 < 2 is %d", 1 < 2); 17 | print("2 < 2 is %d", 2 < 2); 18 | print("2 < 3 is %d", 2 < 3); 19 | 20 | print("1 >= 2 is %d", 1 >= 2); 21 | print("2 >= 2 is %d", 2 >= 2); 22 | print("2 >= 3 is %d", 2 >= 3); 23 | 24 | print("1 <= 2 is %d", 1 <= 2); 25 | print("2 <= 2 is %d", 2 <= 2); 26 | print("2 <= 3 is %d", 2 <= 3); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tinyc/samples/sample_4_logic.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | print("0 && 0 is %d", 0 && 0); 5 | print("0 && 1 is %d", 0 && 1); 6 | print("1 && 0 is %d", 1 && 0); 7 | print("1 && 1 is %d", 1 && 1); 8 | 9 | 10 | print("0 || 0 is %d", 0 || 0); 11 | print("0 || 1 is %d", 0 || 1); 12 | print("1 || 0 is %d", 1 || 0); 13 | print("1 || 1 is %d", 1 || 1); 14 | 15 | print("!1 is %d", !1); 16 | print("!0 is %d", !0); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tinyc/samples/sample_5_ifwhile.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 10; 6 | 7 | while (n != 0) { 8 | print("n = %d", n); 9 | if (n == 5) { 10 | break; 11 | } 12 | n = n - 1; 13 | } 14 | 15 | return 0; 16 | } -------------------------------------------------------------------------------- /tinyc/samples/sample_6_function.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int n; 5 | n = 1; 6 | 7 | print("The first 10 number of the fibonacci sequence:"); 8 | while (n <= 10) { 9 | print("fib(%d)=%d", n, fib(n)); 10 | n = n + 1; 11 | } 12 | 13 | return 0; 14 | } 15 | 16 | int fib(int n) { 17 | if (n <= 2) { 18 | return 1; 19 | } 20 | return fib(n - 1) + fib(n - 2); 21 | } -------------------------------------------------------------------------------- /tinyc/sources/macro.inc: -------------------------------------------------------------------------------- 1 | %MACRO print 1 2 | [SECTION .DATA] 3 | %%STRING: DB %1, 10, 0 4 | [SECTION .TEXT] 5 | PUSH DWORD %%STRING 6 | CALL PRINT 7 | SHL EAX, 2 8 | ADD ESP, EAX 9 | %ENDMACRO 10 | 11 | %MACRO readint 1 12 | [SECTION .DATA] 13 | %%STRING: DB %1, 0 14 | [SECTION .TEXT] 15 | PUSH DWORD %%STRING 16 | CALL READINT 17 | MOV [ESP], EAX 18 | %ENDMACRO 19 | 20 | %MACRO exit 1 21 | MOV EAX, 1 22 | MOV EBX, %1 23 | INT 0x80 24 | %ENDMACRO 25 | 26 | %MACRO add 0 27 | POP EAX 28 | ADD DWORD [ESP], EAX 29 | %ENDMACRO 30 | 31 | %MACRO sub 0 32 | POP EAX 33 | SUB DWORD [ESP], EAX 34 | %ENDMACRO 35 | 36 | %MACRO mul 0 37 | POP EAX 38 | MUL DWORD [ESP] 39 | MOV [ESP], EAX 40 | %ENDMACRO 41 | 42 | %MACRO div 0 43 | XOR EDX, EDX 44 | POP EBX 45 | POP EAX 46 | DIV EBX 47 | PUSH EAX 48 | %ENDMACRO 49 | 50 | %MACRO mod 0 51 | XOR EDX, EDX 52 | POP EBX 53 | POP EAX 54 | DIV EBX 55 | PUSH EDX 56 | %ENDMACRO 57 | 58 | %MACRO neg 0 59 | NEG DWORD [ESP] 60 | %ENDMACRO 61 | 62 | %MACRO cmpeq 0 63 | MOV EAX, [ESP+4] 64 | CMP EAX, [ESP] 65 | PUSHF 66 | POP EAX 67 | SHR EAX, 6 68 | AND EAX, 0X1 69 | ADD ESP, 4 70 | MOV [ESP], EAX 71 | %ENDMACRO 72 | 73 | %MACRO cmpne 0 74 | MOV EAX, [ESP+4] 75 | CMP EAX, [ESP] 76 | PUSHF 77 | POP EAX 78 | SHR EAX, 6 79 | AND EAX, 0X1 80 | XOR EAX, 0X1 81 | ADD ESP, 4 82 | MOV [ESP], EAX 83 | %ENDMACRO 84 | 85 | %MACRO cmpge 0 86 | MOV EAX, [ESP+4] 87 | CMP EAX, [ESP] 88 | PUSHF 89 | POP EAX 90 | SHR EAX, 7 91 | AND EAX, 0X1 92 | XOR EAX, 0X1 93 | ADD ESP, 4 94 | MOV [ESP], EAX 95 | %ENDMACRO 96 | 97 | %MACRO cmplt 0 98 | MOV EAX, [ESP+4] 99 | CMP EAX, [ESP] 100 | PUSHF 101 | POP EAX 102 | SHR EAX, 7 103 | AND EAX, 0X1 104 | ADD ESP, 4 105 | MOV [ESP], EAX 106 | %ENDMACRO 107 | 108 | %MACRO cmpgt 0 109 | POP EAX 110 | CMP EAX, [ESP] 111 | PUSHF 112 | POP EAX 113 | SHR EAX, 7 114 | AND EAX, 0X1 115 | MOV [ESP], EAX 116 | %ENDMACRO 117 | 118 | %MACRO cmple 0 119 | POP EAX 120 | CMP EAX, [ESP] 121 | PUSHF 122 | POP EAX 123 | SHR EAX, 7 124 | AND EAX, 0X1 125 | XOR EAX, 0X1 126 | MOV [ESP], EAX 127 | %ENDMACRO 128 | 129 | %MACRO and 0 130 | POP EAX 131 | AND EAX, [ESP] 132 | PUSHF 133 | POP EAX 134 | SHR EAX, 6 135 | AND EAX, 0X1 136 | XOR EAX, 0X1 137 | MOV [ESP], EAX 138 | %ENDMACRO 139 | 140 | %MACRO or 0 141 | POP EAX 142 | OR EAX, [ESP] 143 | PUSHF 144 | POP EAX 145 | SHR EAX, 6 146 | AND EAX, 0X1 147 | XOR EAX, 0X1 148 | MOV [ESP], EAX 149 | %ENDMACRO 150 | 151 | %MACRO not 0 152 | MOV EAX, [ESP] 153 | OR EAX, EAX 154 | PUSHF 155 | POP EAX 156 | SHR EAX, 6 157 | AND EAX, 0X1 158 | MOV [ESP], EAX 159 | %ENDMACRO 160 | 161 | %MACRO jz 1 162 | POP EAX 163 | OR EAX, EAX 164 | JZ %1 165 | %ENDMACRO 166 | 167 | %MACRO jmp 1 168 | JMP %1 169 | %ENDMACRO 170 | 171 | %MACRO push 1 172 | PUSH DWORD %1 173 | %ENDMACRO 174 | 175 | %MACRO pop 0-1 176 | %IFIDN %0, 0 177 | ADD ESP, 4 178 | %ELSE 179 | POP DWORD %1 180 | %ENDIF 181 | %ENDMACRO 182 | 183 | %MACRO FUNC 1 184 | %1 185 | PUSH EBP 186 | MOV EBP, ESP 187 | %ENDMACRO 188 | 189 | %MACRO ret 0-1 190 | %IFIDN %0, 1 191 | %IFIDN %1, ~ 192 | MOV EAX, [ESP] 193 | %ELSE 194 | MOV EAX, %1 195 | %ENDIF 196 | %ENDIF 197 | LEAVE 198 | RET 199 | %ENDMACRO 200 | 201 | EXTERN PRINT, READINT 202 | GLOBAL _start 203 | 204 | [SECTION .TEXT] 205 | _start: 206 | CALL @main 207 | PUSH EAX 208 | exit [ESP] 209 | -------------------------------------------------------------------------------- /tinyc/sources/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void init_parser(int argc, char *argv[]); 9 | void quit_parser(); 10 | 11 | extern FILE* yyin; 12 | FILE *asmfile, *incfile; 13 | #define BUFSIZE 256 14 | 15 | #define out_asm(fmt, ...) \ 16 | {fprintf(asmfile, fmt, ##__VA_ARGS__); fprintf(asmfile, "\n");} 17 | 18 | #define out_inc(fmt, ...) \ 19 | {fprintf(incfile, fmt, ##__VA_ARGS__); fprintf(incfile, "\n");} 20 | 21 | void file_error(char *msg); 22 | 23 | int ii = 0, itop = -1, istack[100]; 24 | int ww = 0, wtop = -1, wstack[100]; 25 | 26 | #define _BEG_IF (istack[++itop] = ++ii) 27 | #define _END_IF (itop--) 28 | #define _i (istack[itop]) 29 | 30 | #define _BEG_WHILE (wstack[++wtop] = ++ww) 31 | #define _END_WHILE (wtop--) 32 | #define _w (wstack[wtop]) 33 | 34 | int argc = 0, varc = 0; 35 | char *cur_func_name, *args[128], *vars[128]; 36 | void write_func_head(); 37 | void write_func_tail(); 38 | 39 | #define _BEG_FUNCDEF(name) (cur_func_name = (name)) 40 | #define _APPEND_ARG(arg) (args[argc++] = (arg)) 41 | #define _APPEND_VAR(var) (vars[varc++] = (var)) 42 | #define _WRITE_FUNCHEAD write_func_head 43 | #define _END_FUNCDEF write_func_tail 44 | 45 | #define YYSTYPE char * 46 | 47 | %} 48 | 49 | %token T_Void T_Int T_While T_If T_Else T_Return T_Break T_Continue 50 | %token T_Print T_ReadInt T_Le T_Ge T_Eq T_Ne T_And T_Or 51 | %token T_IntConstant T_StringConstant T_Identifier 52 | 53 | %left '=' 54 | %left T_Or 55 | %left T_And 56 | %left T_Eq T_Ne 57 | %left '<' '>' T_Le T_Ge 58 | %left '+' '-' 59 | %left '*' '/' '%' 60 | %left '!' 61 | 62 | %% 63 | 64 | Start: 65 | Program { /* empty */ } 66 | ; 67 | 68 | Program: 69 | /* empty */ { /* empty */ } 70 | | Program FuncDef { /* empty */ } 71 | ; 72 | 73 | FuncDef: 74 | T_Int FuncName Args Vars Stmts EndFuncDef 75 | | T_Void FuncName Args Vars Stmts EndFuncDef 76 | ; 77 | 78 | FuncName: 79 | T_Identifier { _BEG_FUNCDEF($1); } 80 | ; 81 | 82 | Args: 83 | '(' ')' { /* empty */ } 84 | | '(' _Args ')' { /* empty */ } 85 | ; 86 | 87 | _Args: 88 | T_Int T_Identifier { _APPEND_ARG($2); } 89 | | _Args ',' T_Int T_Identifier { _APPEND_ARG($4); } 90 | ; 91 | 92 | Vars: 93 | _Vars { _WRITE_FUNCHEAD(); } 94 | ; 95 | 96 | _Vars: 97 | '{' { /* empty */ } 98 | | _Vars Var ';' { /* empty */ } 99 | ; 100 | 101 | Var: 102 | T_Int T_Identifier { _APPEND_VAR($2); } 103 | | Var ',' T_Identifier { _APPEND_VAR($3); } 104 | ; 105 | 106 | Stmts: 107 | /* empty */ { /* empty */ } 108 | | Stmts Stmt { /* empty */ } 109 | ; 110 | 111 | EndFuncDef: 112 | '}' { _END_FUNCDEF(); } 113 | ; 114 | 115 | Stmt: 116 | AssignStmt { /* empty */ } 117 | | CallStmt { /* empty */ } 118 | | IfStmt { /* empty */ } 119 | | WhileStmt { /* empty */ } 120 | | BreakStmt { /* empty */ } 121 | | ContinueStmt { /* empty */ } 122 | | ReturnStmt { /* empty */ } 123 | | PrintStmt { /* empty */ } 124 | ; 125 | 126 | AssignStmt: 127 | T_Identifier '=' Expr ';' { out_asm("\tpop %s", $1); } 128 | ; 129 | 130 | CallStmt: 131 | CallExpr ';' { out_asm("\tpop"); } 132 | ; 133 | 134 | IfStmt: 135 | If '(' Expr ')' Then '{' Stmts '}' EndThen EndIf 136 | { /* empty */ } 137 | | If '(' Expr ')' Then '{' Stmts '}' EndThen T_Else '{' Stmts '}' EndIf 138 | { /* empty */ } 139 | ; 140 | 141 | If: 142 | T_If { _BEG_IF; out_asm("_begIf_%d:", _i); } 143 | ; 144 | 145 | Then: 146 | /* empty */ { out_asm("\tjz _elIf_%d", _i); } 147 | ; 148 | 149 | EndThen: 150 | /* empty */ { out_asm("\tjmp _endIf_%d\n_elIf_%d:", _i, _i); } 151 | ; 152 | 153 | EndIf: 154 | /* empty */ { out_asm("_endIf_%d:", _i); _END_IF; } 155 | ; 156 | 157 | WhileStmt: 158 | While '(' Expr ')' Do '{' Stmts '}' EndWhile 159 | { /* empty */ } 160 | ; 161 | 162 | While: 163 | T_While { _BEG_WHILE; out_asm("_begWhile_%d:", _w); } 164 | ; 165 | 166 | Do: 167 | /* empty */ { out_asm("\tjz _endWhile_%d", _w); } 168 | ; 169 | 170 | EndWhile: 171 | /* empty */ { out_asm("\tjmp _begWhile_%d\n_endWhile_%d:", 172 | _w, _w); _END_WHILE; } 173 | ; 174 | 175 | BreakStmt: 176 | T_Break ';' { out_asm("\tjmp _endWhile_%d", _w); } 177 | ; 178 | 179 | ContinueStmt: 180 | T_Continue ';' { out_asm("\tjmp _begWhile_%d", _w); } 181 | ; 182 | 183 | ReturnStmt: 184 | T_Return ';' { out_asm("\tret"); } 185 | | T_Return Expr ';' { out_asm("\tret ~"); } 186 | ; 187 | 188 | PrintStmt: 189 | T_Print '(' T_StringConstant PrintIntArgs ')' ';' 190 | { out_asm("\tprint %s", $3); } 191 | ; 192 | 193 | PrintIntArgs: 194 | /* empty */ { /* empty */ } 195 | | PrintIntArgs ',' Expr { /* empty */ } 196 | ; 197 | 198 | Expr: 199 | T_IntConstant { out_asm("\tpush %s", $1); } 200 | | T_Identifier { out_asm("\tpush %s", $1); } 201 | | Expr '+' Expr { out_asm("\tadd"); } 202 | | Expr '-' Expr { out_asm("\tsub"); } 203 | | Expr '*' Expr { out_asm("\tmul"); } 204 | | Expr '/' Expr { out_asm("\tdiv"); } 205 | | Expr '%' Expr { out_asm("\tmod"); } 206 | | Expr '>' Expr { out_asm("\tcmpgt"); } 207 | | Expr '<' Expr { out_asm("\tcmplt"); } 208 | | Expr T_Ge Expr { out_asm("\tcmpge"); } 209 | | Expr T_Le Expr { out_asm("\tcmple"); } 210 | | Expr T_Eq Expr { out_asm("\tcmpeq"); } 211 | | Expr T_Ne Expr { out_asm("\tcmpne"); } 212 | | Expr T_Or Expr { out_asm("\tor"); } 213 | | Expr T_And Expr { out_asm("\tand"); } 214 | | '-' Expr %prec '!' { out_asm("\tneg"); } 215 | | '!' Expr { out_asm("\tnot"); } 216 | | ReadInt { /* empty */ } 217 | | CallExpr { /* empty */ } 218 | | '(' Expr ')' { /* empty */ } 219 | ; 220 | 221 | ReadInt: 222 | T_ReadInt '(' T_StringConstant ')' 223 | { out_asm("\treadint %s", $3); } 224 | ; 225 | 226 | CallExpr: 227 | T_Identifier Actuals 228 | { out_asm("\t$%s", $1); } 229 | ; 230 | 231 | Actuals: 232 | '(' ')' 233 | | '(' _Actuals ')' 234 | ; 235 | 236 | _Actuals: 237 | Expr 238 | | _Actuals ',' Expr 239 | ; 240 | 241 | %% 242 | 243 | int main(int argc, char *argv[]) { 244 | init_parser(argc, argv); 245 | yyparse(); 246 | quit_parser(); 247 | } 248 | 249 | void init_parser(int argc, char *argv[]) { 250 | if (argc < 2) { 251 | file_error("Must provide an input source file!"); 252 | } 253 | 254 | if (argc > 2) { 255 | file_error("Too much command line arguments!"); 256 | } 257 | 258 | char *in_file_name = argv[1]; 259 | int len = strlen(in_file_name); 260 | 261 | if (len <= 2 || in_file_name[len-1] != 'c' \ 262 | || in_file_name[len-2] != '.') { 263 | file_error("Must provide an '.c' source file!"); 264 | } 265 | 266 | if (!(yyin = fopen(in_file_name, "r"))) { 267 | file_error("Input file open error"); 268 | } 269 | 270 | char out_file_name[BUFSIZE]; 271 | strcpy(out_file_name, in_file_name); 272 | 273 | out_file_name[len-1] = 'a'; 274 | out_file_name[len] = 's'; 275 | out_file_name[len+1] = 'm'; 276 | out_file_name[len+2] = '\0'; 277 | if (!(asmfile = fopen(out_file_name, "w"))) { 278 | file_error("Output 'asm' file open error"); 279 | } 280 | 281 | out_file_name[len-1] = 'i'; 282 | out_file_name[len] = 'n'; 283 | out_file_name[len+1] = 'c'; 284 | if (!(incfile = fopen(out_file_name, "w"))) { 285 | file_error("Output 'inc' file open error"); 286 | } 287 | } 288 | 289 | void file_error(char *msg) { 290 | printf("\n*** Error ***\n\t%s\n", msg); 291 | puts(""); 292 | exit(-1); 293 | } 294 | 295 | char *cat_strs(char *buf, char *strs[], int strc) { 296 | int i; 297 | strcpy(buf, strs[0]); 298 | for (i = 1; i < strc; i++) { 299 | strcat(strcat(buf, ", "), strs[i]); 300 | } 301 | return buf; 302 | } 303 | 304 | #define _fn (cur_func_name) 305 | 306 | void write_func_head() { 307 | char buf[BUFSIZE]; 308 | int i; 309 | 310 | out_asm("FUNC @%s:", _fn); 311 | if (argc > 0) { 312 | out_asm("\t%s.arg %s", _fn, cat_strs(buf, args, argc)); 313 | } 314 | if (varc > 0) { 315 | out_asm("\t%s.var %s", _fn, cat_strs(buf, vars, varc)); 316 | } 317 | 318 | out_inc("; ==== begin function `%s` ====", _fn); 319 | out_inc("%%define %s.argc %d", _fn, argc); 320 | out_inc("\n%%MACRO $%s 0\n" 321 | " CALL @%s\n" 322 | " ADD ESP, 4*%s.argc\n" 323 | " PUSH EAX\n" 324 | "%%ENDMACRO", 325 | _fn, _fn, _fn); 326 | if (argc) { 327 | out_inc("\n%%MACRO %s.arg %s.argc", _fn, _fn); 328 | for (i = 0; i < argc; i++) { 329 | out_inc("\t%%define %s [EBP + 8 + 4*%s.argc - 4*%d]", 330 | args[i], _fn, i+1); 331 | } 332 | out_inc("%%ENDMACRO"); 333 | } 334 | if (varc) { 335 | out_inc("\n%%define %s.varc %d", _fn, varc); 336 | out_inc("\n%%MACRO %s.var %s.varc", _fn, _fn); 337 | for (i = 0; i < varc; i++) { 338 | out_inc("\t%%define %s [EBP - 4*%d]", 339 | vars[i], i+1); 340 | } 341 | out_inc("\tSUB ESP, 4*%s.varc", _fn); 342 | out_inc("%%ENDMACRO"); 343 | } 344 | } 345 | 346 | void write_func_tail() { 347 | int i; 348 | 349 | out_asm("ENDFUNC@%s\n", _fn); 350 | 351 | out_inc("\n%%MACRO ENDFUNC@%s 0\n\tLEAVE\n\tRET", _fn); 352 | for (i = 0; i < argc; i++) { 353 | out_inc("\t%%undef %s", args[i]); 354 | } 355 | for (i = 0; i < varc; i++) { 356 | out_inc("\t%%undef %s", vars[i]); 357 | } 358 | out_inc("%%ENDMACRO"); 359 | out_inc("; ==== end function `%s` ====\n", _fn); 360 | 361 | argc = 0; 362 | varc = 0; 363 | } 364 | 365 | void quit_parser() { 366 | fclose(yyin); fclose(asmfile); fclose(incfile); 367 | } 368 | -------------------------------------------------------------------------------- /tinyc/sources/pysim.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | PY3 = sys.version_info[0] == 3 4 | 5 | comment_mark = ";%" 6 | code = [] 7 | stack = [] 8 | var_table = {} 9 | label_table = {} 10 | func_table = {} 11 | eip = 0 12 | printout = [] 13 | debug = False 14 | 15 | def is_valid_identifier(ident): 16 | if ident == "": 17 | return False 18 | if not (ident[0].isalpha() or ident[0] == '_'): 19 | return False 20 | for ch in ident[1:]: 21 | if not (ch.isalnum() or ch == '_'): 22 | return False 23 | return True 24 | 25 | def assemb_error(line, msg): 26 | display(pause=False) 27 | print(line) 28 | print("^^^Error at last line: %s" % msg) 29 | exit(-1) 30 | 31 | def run_error(msg="Wrong instruction format"): 32 | code[eip][0] = "**%s**" % msg 33 | printout.append(msg) 34 | display(pause=False) 35 | exit(-1) 36 | 37 | def read_file(filename): 38 | if PY3: 39 | return open(filename, 'r').readlines() 40 | 41 | return file(filename).readlines() 42 | 43 | def get_input(msg): 44 | if PY3: 45 | return input(msg) 46 | 47 | return raw_input(msg) 48 | 49 | def table_has_key(table, key): 50 | if PY3: 51 | return key in table 52 | 53 | return table.has_key(key) 54 | 55 | def assemb(asmfilename): 56 | if len(sys.argv) > 2 and (sys.argv[2] == '-a' or sys.argv[2] == '-da'): 57 | code.append(('', '$main', '')) 58 | code.append(('', 'exit', '~')) 59 | 60 | label = "" 61 | for line in read_file(asmfilename): 62 | line = line.strip() 63 | if line == "" or line[0] in comment_mark: 64 | continue 65 | 66 | _label, sep, ist = line.partition(':') 67 | if sep and _label.find('"') == -1 and _label.find("'") == -1: 68 | _label, ist = _label.strip(), ist.strip() 69 | if not check_label(_label): 70 | assemb_error(line, "Wrong label") 71 | label = '%s,%s' % (label, _label) if label else _label 72 | if ist == "" or ist[0] in comment_mark: 73 | continue 74 | elif len(line) >= 7 and line[:7] == 'ENDFUNC': 75 | label = '%s,%s' % (label, 'ENDFUNC') \ 76 | if label else 'ENDFUNC' 77 | ist = 'ret' 78 | else: 79 | ist = line 80 | 81 | dire, sep, arg = ist.partition(' ') 82 | 83 | if len(dire) > 4 and \ 84 | (dire[-4:] == '.arg' or dire[-4:] == '.var'): 85 | dire = dire[-3:] 86 | 87 | code.append( [label, dire, arg.strip()] ) 88 | label = "" 89 | 90 | code.append(('', 'exit', '0')) 91 | 92 | def check_label(label): 93 | if label == "": 94 | return False 95 | 96 | func, sep, funcName = label.partition(' @') 97 | 98 | if sep: 99 | if func.strip() != 'FUNC' \ 100 | or not is_valid_identifier(funcName) \ 101 | or table_has_key(func_table, funcName): 102 | return False 103 | else: 104 | func_table[funcName] = len(code) 105 | return True 106 | else: 107 | if not is_valid_identifier(label) \ 108 | or table_has_key(func_table, label) \ 109 | or table_has_key(label_table, label): 110 | return False 111 | else: 112 | label_table[label] = len(code) 113 | return True 114 | 115 | def trim(s, size): 116 | return s[:size-3]+"..." if len(s) > size else s 117 | 118 | def display(pause=True): 119 | num_code_lines, num_terminal_lines = 24, 8 120 | if os.system("clear"): os.system('cls') 121 | print("%32s%-40s| %-13s|Bind var" % ("", "Code", "Stack")) 122 | j = 0 123 | for i in range( \ 124 | max(eip+1-num_code_lines, 0), max(eip+1, num_code_lines) ): 125 | if i < len(code): 126 | label, dire, arg = code[i] 127 | line = trim(dire + " " + arg, 40) 128 | label = trim(label, 28) 129 | else: 130 | label, line = "", "" 131 | 132 | if label: label += ':' 133 | point = " ->" if i == eip else "" 134 | st = stack[j] if j < len(stack) else "" 135 | st = "(RetInfo)" if type(st) is tuple else str(st) 136 | stvar = var_table.get(j, "") 137 | if j == len(stack) - 1: stvar += "<-" 138 | 139 | print("%29s%3s%-40s| %-13s|%s" % \ 140 | (label, point, line, st, stvar)) 141 | 142 | j += 1 143 | 144 | print("***Terminal***") 145 | n = len(printout) 146 | for i in range( \ 147 | max(n-num_terminal_lines, 0), max(n, num_terminal_lines) ): 148 | print(printout[i] if i < n else "") 149 | if i == n and not pause: 150 | break 151 | 152 | if pause: 153 | global debug 154 | if get_input("\npress enter to step, -r to run.") == "-r": 155 | debug = False 156 | 157 | def run(): 158 | global eip 159 | eip = 0 160 | del stack[:] 161 | 162 | while True: 163 | if debug: display() 164 | label, dire, arg = code[eip] 165 | if dire[0] == '$': 166 | action, arg = call, dire[1:] 167 | if not is_valid_identifier(arg): 168 | run_error("Wrong identifier") 169 | else: 170 | if not is_valid_identifier(dire): 171 | run_error("Wrong identifier") 172 | try: 173 | action = eval("do_" + dire) 174 | except NameError: 175 | run_error("Unknown instruction") 176 | action(arg) 177 | eip += 1 178 | 179 | def do_var(arg): 180 | if arg == "": return 181 | for var in arg.split(','): 182 | var = var.strip() 183 | if not is_valid_identifier(var) or table_has_key(var_table, var): 184 | run_error("Wrong var name") 185 | var_table[var] = len(stack) 186 | var_table[len(stack)] = var 187 | stack.append("/") 188 | 189 | def do_push(arg): 190 | try: 191 | arg = int(arg) 192 | except ValueError: 193 | try: 194 | arg = stack[var_table[arg]] 195 | except KeyError: 196 | run_error("Undefined variable") 197 | if type(arg) is not int: 198 | run_error("Cannot push uninitialed value") 199 | stack.append(arg) 200 | 201 | def do_pop(arg): 202 | value = stack.pop() 203 | if arg == "": 204 | return 205 | if type(value) is not int: 206 | run_error("Cannot pop non-number value to variable") 207 | try: 208 | stack[var_table[arg]] = value 209 | except KeyError: 210 | run_error("Undefined variable") 211 | 212 | def do_exit(arg): 213 | global going, exit_code 214 | going = False 215 | 216 | if arg == "~": 217 | exit_code = stack[-1] 218 | elif arg: 219 | try: 220 | exit_code = int(arg) 221 | except ValueError: 222 | try: 223 | exit_code = stack[var_table[arg]] 224 | except KeyError: 225 | run_error("Undefined variable") 226 | 227 | if type(exit_code) is not int: 228 | run_error("Wrong exit code") 229 | 230 | if debug: 231 | display(pause=False) 232 | 233 | exit(exit_code) 234 | 235 | 236 | 237 | def do_add(arg): stack[-2] += stack[-1]; stack.pop() 238 | def do_sub(arg): stack[-2] -= stack[-1]; stack.pop() 239 | def do_mul(arg): stack[-2] *= stack[-1]; stack.pop() 240 | def do_div(arg): stack[-2] /= stack[-1]; stack.pop() 241 | def do_mod(arg): stack[-2] %= stack[-1]; stack.pop() 242 | def do_and(arg): stack[-2] = int(stack[-2]!=0 and stack[-1]!=0); stack.pop() 243 | def do_or(arg): stack[-2] = int(stack[-2]!=0 or stack[-1]!=0); stack.pop() 244 | def do_cmpeq(arg): stack[-2] = int(stack[-2]==stack[-1]);stack.pop() 245 | def do_cmpne(arg): stack[-2] = int(stack[-2]!=stack[-1]);stack.pop() 246 | def do_cmpgt(arg): stack[-2] = int(stack[-2]>stack[-1]); stack.pop() 247 | def do_cmplt(arg): stack[-2] = int(stack[-2]=stack[-1]);stack.pop() 249 | def do_cmple(arg): stack[-2] = int(stack[-2]<=stack[-1]);stack.pop() 250 | def do_neg(arg): stack[-1] = -stack[-1] 251 | def do_not(arg): stack[-1] = int(not stack[-1]) 252 | 253 | def do_print(fmt): 254 | if len(fmt) < 2 or fmt[0] != fmt[-1] or fmt[0] not in '"\'': 255 | run_error("Format string error") 256 | argc = fmt.count("%d") 257 | out = fmt[1:-1] % tuple(stack[len(stack)-argc:]) 258 | print(out) 259 | printout.append(out) 260 | del stack[len(stack)-argc:] 261 | 262 | def do_readint(msg): 263 | if len(msg) < 2 or msg[0] != msg[-1] or msg[-1] not in '"\'': 264 | run_error("Message string error") 265 | msg = msg.strip('"').strip("'") 266 | if debug: display(pause=False) 267 | string = get_input(msg) 268 | try: 269 | value = int(string) 270 | except ValueError: 271 | value = 0 272 | stack.append(value) 273 | printout.append("\n " + msg + str(value)) 274 | 275 | def do_jmp(label): 276 | global eip 277 | try: 278 | # note: here we set eip just befor the label, 279 | # and when back to run(), we do eip += 1 280 | eip = label_table[label] - 1 281 | except KeyError: 282 | run_error("Wrong label") 283 | 284 | def do_jz(label): 285 | global eip 286 | try: 287 | # set eip just befor the label, 288 | # when back to run(), do eip += 1 289 | new_eip = label_table[label] - 1 290 | except KeyError: 291 | run_error("Wrong label") 292 | if stack.pop() == 0: 293 | eip = new_eip 294 | 295 | def call(funcName): 296 | global var_table, eip 297 | 298 | try: 299 | entry = func_table[funcName] 300 | except KeyError: 301 | run_error("Undefined function") 302 | 303 | if code[entry][1] == "arg": 304 | arg_list = code[entry][2].split(',') 305 | else: 306 | arg_list = [] 307 | 308 | new_var_table = {} 309 | for addr, arg in enumerate(arg_list, len(stack)-len(arg_list)): 310 | arg = arg.strip() 311 | if not is_valid_identifier(arg) or table_has_key(new_var_table, arg): 312 | run_error("Wrong arg name") 313 | 314 | new_var_table[arg] = addr 315 | new_var_table[addr] = arg 316 | 317 | stack.append( (len(arg_list), eip, var_table) ) 318 | var_table = new_var_table 319 | eip = entry if len(arg_list) else entry -1 320 | 321 | def do_ret(arg): 322 | global var_table, eip 323 | 324 | if arg == "~": 325 | retval = stack[-1] 326 | elif arg: 327 | try: 328 | retval = int(arg) 329 | except ValueError: 330 | try: 331 | retval = stack[var_table[arg]] 332 | except KeyError: 333 | run_error("Undefined variable") 334 | else: 335 | retval = '/' 336 | 337 | i = len(stack) - 1 338 | while type(stack[i]) is not tuple: 339 | i -= 1 340 | argc, eip, var_table = stack[i] 341 | del stack[i-argc:] 342 | stack.append(retval) 343 | 344 | if __name__ == "__main__": 345 | asmfileName = sys.argv[1] 346 | if len(sys.argv) > 2: 347 | debug = sys.argv[2] == '-d' or sys.argv[2] == '-da' 348 | assemb(asmfileName) 349 | run() -------------------------------------------------------------------------------- /tinyc/sources/pysimulate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ ($# != 1) && ($# != 2) ]]; 4 | then 5 | echo "Usage: $0 [-da]" 6 | exit 1 7 | fi 8 | 9 | if ! [ -f $1 ]; 10 | then 11 | echo "Error: File $1 does NOT exists." 12 | exit 1 13 | fi 14 | 15 | python "$(dirname $0)/pysim.py" $1 $2 16 | -------------------------------------------------------------------------------- /tinyc/sources/scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE char * 3 | #include "y.tab.h" 4 | int cur_line = 1; 5 | void yyerror(const char *msg); 6 | void unrecognized_char(char c); 7 | void unterminate_string(); 8 | #define _DUPTEXT {yylval = strdup(yytext);} 9 | %} 10 | 11 | /* note \042 is '"' */ 12 | WHITESPACE ([ \t\r\a]+) 13 | SINGLE_COMMENT1 ("//"[^\n]*) 14 | SINGLE_COMMENT2 ("#"[^\n]*) 15 | OPERATOR ([+*-/%=,;!<>(){}]) 16 | INTEGER ([0-9]+) 17 | IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) 18 | UNTERM_STRING (\042[^\042\n]*) 19 | STRING (\042[^\042\n]*\042) 20 | 21 | %% 22 | 23 | \n { cur_line++; } 24 | {WHITESPACE} { /* ignore every whitespace */ } 25 | {SINGLE_COMMENT1} { /* skip for single line comment */ } 26 | {SINGLE_COMMENT2} { /* skip for single line comment */ } 27 | 28 | {OPERATOR} { return yytext[0]; } 29 | "int" { return T_Int; } 30 | "void" { return T_Void; } 31 | "return" { return T_Return; } 32 | "print" { return T_Print; } 33 | "readint" { return T_ReadInt; } 34 | "while" { return T_While; } 35 | "if" { return T_If; } 36 | "else" { return T_Else; } 37 | "break" { return T_Break; } 38 | "continue" { return T_Continue; } 39 | "<=" { return T_Le; } 40 | ">=" { return T_Ge; } 41 | "==" { return T_Eq; } 42 | "!=" { return T_Ne; } 43 | "&&" { return T_And; } 44 | "||" { return T_Or; } 45 | 46 | {INTEGER} { _DUPTEXT return T_IntConstant; } 47 | {STRING} { _DUPTEXT return T_StringConstant; } 48 | {IDENTIFIER} { _DUPTEXT return T_Identifier; } 49 | 50 | {UNTERM_STRING} { unterminate_string(); } 51 | . { unrecognized_char(yytext[0]); } 52 | 53 | %% 54 | 55 | int yywrap(void) { 56 | return 1; 57 | } 58 | 59 | void unrecognized_char(char c) { 60 | char buf[32] = "Unrecognized character: ?"; 61 | buf[24] = c; 62 | yyerror(buf); 63 | } 64 | 65 | void unterminate_string() { 66 | yyerror("Unterminate string constant"); 67 | } 68 | 69 | void yyerror(const char *msg) { 70 | fprintf(stderr, "Error at line %d:\n\t%s\n", cur_line, msg); 71 | exit(-1); 72 | } -------------------------------------------------------------------------------- /tinyc/sources/tcc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# != 1 ]; 4 | then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | if ! [ -f $1 ]; 10 | then 11 | echo "Error: File $1 does NOT exists." 12 | exit 1 13 | fi 14 | 15 | tccdir=$(dirname $0) 16 | filename=${1%.*} 17 | fileext=${1##*.} 18 | objdir=$filename-$fileext-build 19 | 20 | "$(dirname $0)/tcc-frontend" $1 21 | nasm -f elf32 -P"$tccdir/macro.inc" -P"$filename.inc" -o "$filename.o" "$filename.asm" 22 | ld -m elf_i386 -o "$filename" "$filename.o" -L"$tccdir" -ltio 23 | mkdir -p "$objdir" 24 | mv "$filename.asm" "$filename.inc" "$filename.o" "$filename" "$objdir/" 25 | -------------------------------------------------------------------------------- /tinyc/sources/tio.c: -------------------------------------------------------------------------------- 1 | void SYS_PRINT(char *string, int len); 2 | 3 | #define BUFLEN 1024 4 | 5 | int PRINT(char *fmt, ...) 6 | { 7 | int *args = (int*)&fmt; 8 | char buf[BUFLEN]; 9 | char *p1 = fmt, *p2 = buf + BUFLEN; 10 | int len = -1, argc = 1; 11 | 12 | while (*p1++) ; 13 | 14 | do { 15 | p1--; 16 | if (*p1 == '%' && *(p1+1) == 'd') { 17 | p2++; len--; argc++; 18 | int num = *(++args), negative = 0; 19 | 20 | if (num < 0) { 21 | negative = 1; 22 | num = -num; 23 | } 24 | 25 | do { 26 | *(--p2) = num % 10 + '0'; len++; 27 | num /= 10; 28 | } while (num); 29 | 30 | if (negative) { 31 | *(--p2) = '-'; len++; 32 | } 33 | } else { 34 | *(--p2) = *p1; len++; 35 | } 36 | } while (p1 != fmt); 37 | 38 | SYS_PRINT(p2, len); 39 | 40 | return argc; 41 | } 42 | 43 | void SYS_PRINT(char *string, int len) 44 | { 45 | __asm__( 46 | ".intel_syntax noprefix\n\ 47 | PUSH EAX\n\ 48 | PUSH EBX\n\ 49 | PUSH ECX\n\ 50 | PUSH EDX\n\ 51 | \n\ 52 | MOV EAX, 4\n\ 53 | MOV EBX, 1\n\ 54 | MOV ECX, [EBP+4*2]\n\ 55 | MOV EDX, [EBP+4*3]\n\ 56 | INT 0X80\n\ 57 | \n\ 58 | POP EDX\n\ 59 | POP ECX\n\ 60 | POP EBX\n\ 61 | POP EAX\n\ 62 | .att_syntax" 63 | ); 64 | } 65 | 66 | int STRLEN(char *s); 67 | int SYS_READ(char *buf, int len); 68 | 69 | int READINT(char *prompt) { 70 | char buf[BUFLEN], *p = buf, *p_end; 71 | SYS_PRINT(prompt, STRLEN(prompt)); 72 | int len = SYS_READ(buf, BUFLEN-1), value = 0, negative = 0; 73 | 74 | p_end = buf + len + 1; 75 | 76 | while (p != p_end) { 77 | if (*p == ' ' || *p == '\t') { 78 | p++; 79 | } else { 80 | break; 81 | } 82 | } 83 | 84 | if (p != p_end && *p == '-') { 85 | negative = 1; 86 | p++; 87 | } 88 | 89 | while (p != p_end) { 90 | if (*p <= '9' && *p >= '0') { 91 | value = value * 10 + *p - '0'; 92 | *p++; 93 | } else { 94 | break; 95 | } 96 | } 97 | 98 | if (negative) { 99 | value = -value; 100 | } 101 | 102 | return value; 103 | } 104 | 105 | int STRLEN(char *s) { 106 | int i = 0; 107 | while(*s++) i++; 108 | return i; 109 | } 110 | 111 | int SYS_READ(char *buf, int len) { 112 | __asm__( 113 | ".intel_syntax noprefix\n\ 114 | PUSH EBX\n\ 115 | PUSH ECX\n\ 116 | PUSH EDX\n\ 117 | \n\ 118 | MOV EAX, 3\n\ 119 | MOV EBX, 2\n\ 120 | MOV ECX, [EBP+4*2]\n\ 121 | MOV EDX, [EBP+4*3]\n\ 122 | INT 0X80\n\ 123 | \n\ 124 | POP EDX\n\ 125 | POP ECX\n\ 126 | POP EBX\n\ 127 | .att_syntax" 128 | ); 129 | } -------------------------------------------------------------------------------- /tinyc/test.c: -------------------------------------------------------------------------------- 1 | #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it. 2 | 3 | int main() { 4 | int i; 5 | i = 0; 6 | while (i < 10) { 7 | i = i + 1; 8 | if (i == 3 || i == 5) { 9 | continue; 10 | } 11 | if (i == 8) { 12 | break; 13 | } 14 | print("%d! = %d", i, factor(i)); 15 | } 16 | return 0; 17 | } 18 | 19 | int factor(int n) { 20 | if (n < 2) { 21 | return 1; 22 | } 23 | return n * factor(n - 1); 24 | } -------------------------------------------------------------------------------- /tinyc/testall.sh: -------------------------------------------------------------------------------- 1 | for src in $(ls samples/*.c) 2 | do 3 | filename=${src%.*} 4 | fileext=${src##*.} 5 | filenakedname=${filename##*/} 6 | objdir=$filename-$fileext-build 7 | 8 | clear 9 | echo build \"$src\" and run 10 | echo 11 | tcc "$src" 12 | "$objdir/$filenakedname" 13 | echo 14 | echo press any key to continue... 15 | read -n 1 16 | done 17 | --------------------------------------------------------------------------------