├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cmd └── x64cc │ ├── emit.go │ ├── main.go │ └── mdesc.go ├── cpp ├── cpp.go ├── cppexpr.go ├── cppexpr_test.go ├── error.go ├── hideset.go ├── include.go ├── lex.go ├── macro.go ├── token.go └── tokenlist.go ├── parse ├── ast.go ├── ctypes.go ├── fold.go ├── parse.go └── scope.go ├── report └── report.go ├── test.sh └── test ├── runner.go └── testcases ├── bugs ├── .gitignore └── 0007.c └── execute ├── .gitignore ├── 0001-sanity.c ├── 0002-operators.c ├── 0003-globals.c ├── 0004-if.c ├── 0005-for.c ├── 0006-while.c ├── 0007-dowhile.c ├── 0008-breakcont.c ├── 0009-goto.c ├── 0010-locals.c ├── 0011-switch.c ├── 0012-char.c ├── 0013-typedef.c ├── 0014-struct1.c ├── 0014-struct2.c ├── 0014-struct3.c ├── 0015-calls.c ├── 0016-calls.c ├── 0017-calls.c ├── 0018-cast.c ├── 0019-cast.c └── 0020-inits.c /.gitignore: -------------------------------------------------------------------------------- 1 | cc 2 | cc.exe 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | script: sh ./test.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andrew Chambers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # { "Minimalist", "C", "Compiler"}; /* WIP */ 2 | 3 | ![](https://raw.githubusercontent.com/andrewchambers/cc-images/master/Gopher.png) 4 | 5 | Artwork by [Egon Elbre](https://twitter.com/egonelbre) based on the [Go gopher](https://blog.golang.org/gopher) by [Renee French](http://reneefrench.blogspot.com/) 6 | 7 | ## Goals 8 | 9 | - Aggressive removal of cruft. 10 | - Fast. 11 | - Simple. 12 | 13 | ## Status 14 | 15 | *NOT UNDER DEVELOPMENT* 16 | 17 | This compiler has been ported to C here https://github.com/andrewchambers/c where development continues. This was done to allow self hosting far earlier, increase 18 | speed, increase portability, and decrease binary size. Interestingly, the C version is less lines of code too. 19 | 20 | ## Building 21 | 22 | ```go get github.com/andrewchambers/cc/cmd/x64cc``` 23 | 24 | ## Contact 25 | 26 | [![Join the chat at https://gitter.im/andrewchambers/cc](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/andrewchambers/cc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 27 | 28 | ## Hacking 29 | 30 | The code is heavily inspired by https://github.com/rui314/8cc as well as http://bellard.org/tcc/. 31 | I recommend studying the source code of 8cc before contributing here, as 8cc is currently far more mature. 32 | 33 | - The compiler is implemented in Go. 34 | - The compiler *currenty* does no optimization, this is intentional. 35 | - Contributions to this project are welcome, I will respond on gitter or via email. 36 | 37 | ## Bugs 38 | 39 | Yes. 40 | 41 | When the compiler is more mature, we can do automatic bug hunting using the following resources: 42 | 43 | - https://embed.cs.utah.edu/csmith/ (https://github.com/csmith-project/csmith) 44 | - https://github.com/gcc-mirror/gcc/tree/master/gcc/testsuite/gcc.c-torture 45 | - https://github.com/rui314/8cc/tree/master/test 46 | 47 | The bugs can then be automatically reduced to minimal form using http://embed.cs.utah.edu/creduce/ (https://github.com/csmith-project/creduce). 48 | 49 | ## Ideas 50 | - Concurrency using goroutines - C can be compiled a function at a time, so there is a lot of room for this. 51 | - Compile toolchain to javascript using Gopherjs, make a demo site. 52 | - Implement a companion Go -> C compiler, then compile ourselves with it. 53 | - Allow preprocessor include paths from archives to allow sdk's to be packaged as a single binary + archive. 54 | - Implement a backend that is similar to llvm, expose this as a library for other language frontends. 55 | - A companion assembler/linker to remove the dependence on binutils. 56 | - An SSA optimizing backend. 57 | - Compilation of Go1.4 so we can bootstrap ourselves. 58 | -------------------------------------------------------------------------------- /cmd/x64cc/emit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/andrewchambers/cc/cpp" 6 | "github.com/andrewchambers/cc/parse" 7 | "io" 8 | ) 9 | 10 | type emitter struct { 11 | o io.Writer 12 | labelcounter int 13 | loffsets map[*parse.LSymbol]int 14 | f *parse.CFunc 15 | } 16 | 17 | func (e *emitter) NextLabel() string { 18 | e.labelcounter += 1 19 | return fmt.Sprintf(".LL%d", e.labelcounter) 20 | } 21 | 22 | func Emit(tu *parse.TranslationUnit, o io.Writer) error { 23 | e := &emitter{ 24 | o: o, 25 | } 26 | 27 | for _, init := range tu.AnonymousInits { 28 | switch init := init.(type) { 29 | case *parse.String: 30 | e.raw(".data\n") 31 | e.raw("%s:\n", init.Label) 32 | e.raw(".string %s\n", init.Val) 33 | default: 34 | panic(init) 35 | } 36 | } 37 | 38 | for _, tl := range tu.TopLevels { 39 | switch tl := tl.(type) { 40 | case *parse.CFunc: 41 | e.CFunc(tl) 42 | case *parse.DeclList: 43 | if tl.Storage == parse.SC_TYPEDEF { 44 | continue 45 | } 46 | for idx, decl := range tl.Symbols { 47 | global, ok := decl.(*parse.GSymbol) 48 | if !ok { 49 | panic("internal error") 50 | } 51 | e.Global(global, tl.Inits[idx]) 52 | } 53 | default: 54 | panic(tl) 55 | } 56 | } 57 | return nil 58 | } 59 | 60 | func (e *emitter) raw(s string, args ...interface{}) { 61 | _, err := fmt.Fprintf(e.o, s, args...) 62 | if err != nil { 63 | // Fail emitting assembly. 64 | // We need to die. 65 | panic(err) 66 | } 67 | } 68 | 69 | func (e *emitter) asm(s string, args ...interface{}) { 70 | e.raw(" "+s, args...) 71 | } 72 | 73 | func (e *emitter) Global(g *parse.GSymbol, init parse.Expr) { 74 | _, ok := g.Type.(*parse.CFuncT) 75 | if ok { 76 | return 77 | } 78 | e.raw(".data\n") 79 | e.raw(".global %s\n", g.Label) 80 | if init == nil { 81 | e.raw(".lcomm %s, %d\n", g.Label, getSize(g.Type)) 82 | } else { 83 | e.raw("%s:\n", g.Label) 84 | switch { 85 | case parse.IsIntType(g.Type): 86 | v := init.(*parse.Constant) 87 | switch getSize(g.Type) { 88 | case 8: 89 | e.raw(".quad %d\n", v.Val) 90 | case 4: 91 | e.raw(".long %d\n", v.Val) 92 | case 2: 93 | e.raw(".short %d\n", v.Val) 94 | case 1: 95 | e.raw(".byte %d\n", v.Val) 96 | } 97 | case parse.IsPtrType(g.Type): 98 | switch init := init.(type) { 99 | case *parse.ConstantGPtr: 100 | switch { 101 | case init.Offset > 0: 102 | e.raw(".quad %s + %d\n", init.Offset) 103 | case init.Offset < 0: 104 | e.raw(".quad %s - %d\n", init.Offset) 105 | default: 106 | e.raw(".quad %s\n", init.PtrLabel) 107 | } 108 | case *parse.String: 109 | e.raw(".quad %s\n", init.Label) 110 | } 111 | default: 112 | panic("unimplemented") 113 | } 114 | } 115 | } 116 | 117 | func (e *emitter) LoadScalarFromPtr(reg string, sz int, signed bool) { 118 | if signed { 119 | switch sz { 120 | case 8: 121 | e.asm("movq (%%%s), %%rax\n", reg) 122 | case 4: 123 | e.asm("movslq (%%%s), %%rax\n", reg) 124 | case 2: 125 | e.asm("movswq (%%%s), %%rax\n", reg) 126 | case 1: 127 | e.asm("movsbq (%%%s), %%rax\n", reg) 128 | default: 129 | panic("internal error") 130 | } 131 | } else { 132 | switch sz { 133 | case 8: 134 | e.asm("movq (%%%s), %%rax\n", reg) 135 | case 4: 136 | e.asm("movzlq (%%%s), %%rax\n", reg) 137 | case 2: 138 | e.asm("movzwq (%%%s), %%rax\n", reg) 139 | case 1: 140 | e.asm("movzbq (%%%s), %%rax\n", reg) 141 | default: 142 | panic("internal error") 143 | } 144 | } 145 | } 146 | 147 | func (e *emitter) StoreScalarToPtr(reg string, sz int) { 148 | switch sz { 149 | case 8: 150 | e.asm("movq %%rax, (%%%s)\n", reg) 151 | case 4: 152 | e.asm("movl %%eax, (%%%s)\n", reg) 153 | case 2: 154 | e.asm("movw %%ax, (%%%s)\n", reg) 155 | case 1: 156 | e.asm("movb %%al, (%%%s)\n", reg) 157 | default: 158 | panic("internal error") 159 | } 160 | } 161 | 162 | func (e *emitter) LoadFromPtr(reg string, ty parse.CType) { 163 | switch { 164 | case parse.IsIntType(ty): 165 | if parse.IsSignedIntType(ty) { 166 | e.LoadScalarFromPtr(reg, getSize(ty), true) 167 | } else { 168 | e.LoadScalarFromPtr(reg, getSize(ty), false) 169 | } 170 | case parse.IsPtrType(ty): 171 | e.LoadScalarFromPtr(reg, getSize(ty), false) 172 | case parse.IsCFuncType(ty): 173 | case parse.IsArrType(ty): 174 | case parse.IsStructType(ty): 175 | default: 176 | panic(ty) 177 | } 178 | } 179 | 180 | func (e *emitter) StoreToPtr(reg string, ty parse.CType) { 181 | switch { 182 | case parse.IsIntType(ty): 183 | if parse.IsSignedIntType(ty) { 184 | e.StoreScalarToPtr(reg, getSize(ty)) 185 | } else { 186 | e.StoreScalarToPtr(reg, getSize(ty)) 187 | } 188 | case parse.IsPtrType(ty): 189 | e.StoreScalarToPtr(reg, getSize(ty)) 190 | default: 191 | panic(ty) 192 | } 193 | } 194 | 195 | func (e *emitter) GetAddr(n parse.Expr) { 196 | switch n := n.(type) { 197 | case *parse.Ident: 198 | switch s := n.Sym.(type) { 199 | case *parse.LSymbol: 200 | offset := e.loffsets[s] 201 | e.asm("leaq %d(%%rbp), %%rax\n", offset) 202 | case *parse.GSymbol: 203 | e.asm("leaq %s(%%rip), %%rax\n", s.Label) 204 | default: 205 | panic(n) 206 | } 207 | case *parse.Unop: 208 | // Op must be '*' in this case. 209 | // If not, it is a bug in the frontend. 210 | e.Expr(n.Operand) 211 | case *parse.Index: 212 | e.Expr(n.Idx) 213 | sz := getSize(n.GetType()) 214 | if sz != 1 { 215 | e.asm("imul $%d, %%rax\n", sz) 216 | } 217 | e.asm("pushq %%rax\n") 218 | e.Expr(n.Arr) 219 | e.asm("popq %%rcx\n") 220 | e.asm("addq %%rcx, %%rax\n") 221 | case *parse.Selector: 222 | var ty *parse.CStruct 223 | if n.Op == cpp.ARROW { 224 | e.Expr(n.Operand) 225 | ty = n.Operand.GetType().(*parse.Ptr).PointsTo.(*parse.CStruct) 226 | } else { 227 | e.GetAddr(n.Operand) 228 | ty = n.Operand.GetType().(*parse.CStruct) 229 | } 230 | e.asm("addq $%d, %%rax\n", getStructOffset(ty, n.Sel)) 231 | } 232 | } 233 | 234 | var intParamLUT = [...]string{ 235 | "%rdi", "%rsi", "%rdx", "%rcx", "r8", "r9", 236 | } 237 | 238 | func (e *emitter) CFunc(f *parse.CFunc) { 239 | e.f = f 240 | e.raw(".text\n") 241 | e.raw(".global %s\n", f.Name) 242 | e.raw("%s:\n", f.Name) 243 | e.asm("pushq %%rbp\n") 244 | e.asm("movq %%rsp, %%rbp\n") 245 | curlocaloffset, loffsets := e.calcLocalOffsets(f) 246 | e.loffsets = loffsets 247 | if curlocaloffset != 0 { 248 | e.asm("sub $%d, %%rsp\n", -curlocaloffset) 249 | } 250 | for idx, psym := range f.ParamSymbols { 251 | e.asm("movq %s, %d(%%rbp)\n", intParamLUT[idx], e.loffsets[psym]) 252 | } 253 | for _, stmt := range f.Body { 254 | e.Stmt(stmt) 255 | } 256 | e.asm("leave\n") 257 | e.asm("ret\n") 258 | e.f = nil 259 | } 260 | 261 | func (e *emitter) calcLocalOffsets(f *parse.CFunc) (int, map[*parse.LSymbol]int) { 262 | loffset := 0 263 | loffsets := make(map[*parse.LSymbol]int) 264 | addLSymbol := func(lsym *parse.LSymbol) { 265 | sz := getSize(lsym.Type) 266 | if sz < 8 { 267 | sz = 8 268 | } 269 | sz = sz + (sz % 8) 270 | loffset -= sz 271 | loffsets[lsym] = loffset 272 | } 273 | for _, lsym := range f.ParamSymbols { 274 | addLSymbol(lsym) 275 | } 276 | for _, n := range f.Body { 277 | switch n := n.(type) { 278 | case *parse.DeclList: 279 | for _, sym := range n.Symbols { 280 | lsym, ok := sym.(*parse.LSymbol) 281 | if !ok { 282 | continue 283 | } 284 | addLSymbol(lsym) 285 | } 286 | 287 | } 288 | } 289 | return loffset, loffsets 290 | } 291 | 292 | func (e *emitter) Stmt(stmt parse.Node) { 293 | switch stmt := stmt.(type) { 294 | case *parse.If: 295 | e.If(stmt) 296 | case *parse.While: 297 | e.While(stmt) 298 | case *parse.DoWhile: 299 | e.DoWhile(stmt) 300 | case *parse.For: 301 | e.For(stmt) 302 | case *parse.Return: 303 | e.Return(stmt) 304 | case *parse.Block: 305 | e.Block(stmt) 306 | case *parse.ExprStmt: 307 | e.Expr(stmt.Expr) 308 | case *parse.Goto: 309 | e.asm("jmp %s\n", stmt.Label) 310 | case *parse.LabeledStmt: 311 | e.raw("%s:\n", stmt.AnonLabel) 312 | e.Stmt(stmt.Stmt) 313 | case *parse.Switch: 314 | e.Switch(stmt) 315 | case *parse.EmptyStmt: 316 | // pass 317 | case *parse.DeclList: 318 | // pass 319 | default: 320 | panic(stmt) 321 | } 322 | } 323 | 324 | func (e *emitter) Switch(sw *parse.Switch) { 325 | e.Expr(sw.Expr) 326 | for _, swc := range sw.Cases { 327 | e.asm("mov $%d, %%rcx\n", swc.V) 328 | e.asm("cmp %%rax, %%rcx\n") 329 | e.asm("je %s\n", swc.Label) 330 | } 331 | if sw.LDefault != "" { 332 | e.asm("jmp %s\n", sw.LDefault) 333 | } else { 334 | e.asm("jmp %s\n", sw.LAfter) 335 | } 336 | e.Stmt(sw.Stmt) 337 | e.raw("%s:\n", sw.LAfter) 338 | } 339 | 340 | func (e *emitter) While(w *parse.While) { 341 | e.raw("%s:\n", w.LStart) 342 | e.Expr(w.Cond) 343 | e.asm("test %%rax, %%rax\n") 344 | e.asm("jz %s\n", w.LEnd) 345 | e.Stmt(w.Body) 346 | e.asm("jmp %s\n", w.LStart) 347 | e.raw("%s:\n", w.LEnd) 348 | } 349 | 350 | func (e *emitter) DoWhile(d *parse.DoWhile) { 351 | e.raw("%s:\n", d.LStart) 352 | e.Stmt(d.Body) 353 | e.raw("%s:\n", d.LCond) 354 | e.Expr(d.Cond) 355 | e.asm("test %%rax, %%rax\n") 356 | e.asm("jz %s\n", d.LEnd) 357 | e.asm("jmp %s\n", d.LStart) 358 | e.raw("%s:\n", d.LEnd) 359 | } 360 | 361 | func (e *emitter) For(fr *parse.For) { 362 | if fr.Init != nil { 363 | e.Expr(fr.Init) 364 | } 365 | e.raw("%s:\n", fr.LStart) 366 | if fr.Cond != nil { 367 | e.Expr(fr.Cond) 368 | } 369 | e.asm("test %%rax, %%rax\n") 370 | e.asm("jz %s\n", fr.LEnd) 371 | e.Stmt(fr.Body) 372 | if fr.Step != nil { 373 | e.Expr(fr.Step) 374 | } 375 | e.asm("jmp %s\n", fr.LStart) 376 | e.raw("%s:\n", fr.LEnd) 377 | } 378 | 379 | func (e *emitter) Block(c *parse.Block) { 380 | for _, stmt := range c.Body { 381 | e.Stmt(stmt) 382 | } 383 | } 384 | 385 | func (e *emitter) If(i *parse.If) { 386 | e.Expr(i.Cond) 387 | e.asm("test %%rax, %%rax\n") 388 | e.asm("jz %s\n", i.LElse) 389 | e.Stmt(i.Stmt) 390 | e.raw("%s:\n", i.LElse) 391 | if i.Else != nil { 392 | e.Stmt(i.Else) 393 | } 394 | } 395 | 396 | func (e *emitter) Return(r *parse.Return) { 397 | e.Expr(r.Ret) 398 | e.asm("leave\n") 399 | e.asm("ret\n") 400 | } 401 | 402 | func (e *emitter) Expr(expr parse.Node) { 403 | switch expr := expr.(type) { 404 | case *parse.Ident: 405 | e.Ident(expr) 406 | case *parse.Call: 407 | e.Call(expr) 408 | case *parse.Constant: 409 | e.asm("movq $%v, %%rax\n", expr.Val) 410 | case *parse.Unop: 411 | e.Unop(expr) 412 | case *parse.Binop: 413 | e.emitBinop(expr) 414 | case *parse.Index: 415 | e.Index(expr) 416 | case *parse.Cast: 417 | e.Cast(expr) 418 | case *parse.Selector: 419 | e.Selector(expr) 420 | case *parse.String: 421 | e.asm("leaq %s(%%rip), %%rax\n", expr.Label) 422 | default: 423 | panic(expr) 424 | } 425 | } 426 | 427 | func getStructOffset(s *parse.CStruct, member string) int { 428 | offset := 0 429 | for idx, n := range s.Names { 430 | if n == member { 431 | return offset 432 | } 433 | offset += getSize(s.Types[idx]) 434 | } 435 | // Error should have been caught in parse. 436 | panic("internal error") 437 | } 438 | 439 | func (e *emitter) Selector(s *parse.Selector) { 440 | e.Expr(s.Operand) 441 | ty := s.Operand.GetType().(*parse.CStruct) 442 | offset := getStructOffset(ty, s.Sel) 443 | if offset != 0 { 444 | e.asm("add $%d, %%rax\n", offset) 445 | } 446 | e.LoadFromPtr("rax", s.GetType()) 447 | } 448 | 449 | func (e *emitter) Ident(i *parse.Ident) { 450 | e.GetAddr(i) 451 | e.LoadFromPtr("rax", i.GetType()) 452 | } 453 | 454 | func isIntRegArg(t parse.CType) bool { 455 | return parse.IsIntType(t) || parse.IsPtrType(t) 456 | } 457 | 458 | func classifyArgs(args []parse.Expr) ([]parse.Expr, []parse.Expr) { 459 | var intargs, memargs []parse.Expr 460 | nintargs := 0 461 | for _, arg := range args { 462 | if nintargs < 6 && isIntRegArg(arg.GetType()) { 463 | nintargs += 1 464 | intargs = append(intargs, arg) 465 | } else { 466 | memargs = append(memargs, arg) 467 | } 468 | } 469 | return intargs, memargs 470 | } 471 | 472 | func (e *emitter) Call(c *parse.Call) { 473 | intargs, memargs := classifyArgs(c.Args) 474 | sz := 0 475 | for i := len(memargs) - 1; i >= 0; i-- { 476 | arg := memargs[i] 477 | e.Expr(arg) 478 | e.asm("push %%rax\n") 479 | sz += 8 480 | } 481 | for i := len(intargs) - 1; i >= 0; i-- { 482 | arg := intargs[i] 483 | e.Expr(arg) 484 | e.asm("push %%rax\n") 485 | } 486 | for idx, _ := range intargs { 487 | e.asm("pop %s\n", intParamLUT[idx]) 488 | } 489 | e.Expr(c.FuncLike) 490 | e.asm("call *%%rax\n") 491 | if sz != 0 { 492 | e.asm("add $%d, %%rsp\n", sz) 493 | } 494 | } 495 | 496 | func (e *emitter) Cast(c *parse.Cast) { 497 | e.Expr(c.Operand) 498 | from := c.Operand.GetType() 499 | to := c.Type 500 | switch { 501 | case parse.IsPtrType(to): 502 | if parse.IsPtrType(from) || parse.IsIntType(from) { 503 | return 504 | } 505 | case parse.IsIntType(to): 506 | if parse.IsPtrType(from) || parse.IsIntType(from) { 507 | return 508 | } 509 | } 510 | panic("unimplemented cast") 511 | } 512 | 513 | func (e *emitter) emitBinop(b *parse.Binop) { 514 | if b.Op == '=' { 515 | e.Assign(b) 516 | return 517 | } 518 | e.Expr(b.L) 519 | e.asm("pushq %%rax\n") 520 | e.Expr(b.R) 521 | e.asm("movq %%rax, %%rcx\n") 522 | e.asm("popq %%rax\n") 523 | switch { 524 | case parse.IsIntType(b.Type): 525 | switch b.Op { 526 | case '+': 527 | e.asm("addq %%rcx, %%rax\n") 528 | case '-': 529 | e.asm("subq %%rcx, %%rax\n") 530 | case '*': 531 | e.asm("imul %%rcx, %%rax\n") 532 | case '|': 533 | e.asm("or %%rcx, %%rax\n") 534 | case '&': 535 | e.asm("and %%rcx, %%rax\n") 536 | case '^': 537 | e.asm("xor %%rcx, %%rax\n") 538 | case '/': 539 | e.asm("cqto\n") 540 | e.asm("idiv %%rcx\n") 541 | case '%': 542 | e.asm("idiv %%rcx\n") 543 | e.asm("mov %%rdx, %%rax\n") 544 | case cpp.SHL: 545 | e.asm("sal %%cl, %%rax\n") 546 | case cpp.SHR: 547 | e.asm("sar %%cl, %%rax\n") 548 | case cpp.EQL, cpp.NEQ, '>', '<': 549 | lset := e.NextLabel() 550 | lafter := e.NextLabel() 551 | opc := "" 552 | switch b.Op { 553 | case cpp.EQL: 554 | opc = "jz" 555 | case cpp.NEQ: 556 | opc = "jnz" 557 | case '<': 558 | opc = "jl" 559 | case '>': 560 | opc = "jg" 561 | default: 562 | panic("internal error") 563 | } 564 | e.asm("cmp %%rcx, %%rax\n") 565 | e.asm("%s %s\n", opc, lset) 566 | e.asm("movq $0, %%rax\n") 567 | e.asm("jmp %s\n", lafter) 568 | e.asm("%s:\n", lset) 569 | e.asm("movq $1, %%rax\n") 570 | e.asm("%s:\n", lafter) 571 | default: 572 | panic("unimplemented " + b.Op.String()) 573 | } 574 | default: 575 | panic(b) 576 | } 577 | } 578 | 579 | func (e *emitter) Unop(u *parse.Unop) { 580 | switch u.Op { 581 | case '&': 582 | switch operand := u.Operand.(type) { 583 | case *parse.Unop: 584 | if operand.Op != '*' { 585 | panic("internal error") 586 | } 587 | e.Expr(operand.Operand) 588 | case *parse.Ident: 589 | sym := operand.Sym 590 | switch sym := sym.(type) { 591 | case *parse.GSymbol: 592 | e.asm("leaq %s(%%rip), %%rax\n", sym.Label) 593 | case *parse.LSymbol: 594 | offset := e.loffsets[sym] 595 | e.asm("leaq %d(%%rbp), %%rax\n", offset) 596 | default: 597 | panic("internal error") 598 | } 599 | case *parse.Index: 600 | e.Expr(operand.Idx) 601 | sz := getSize(operand.GetType()) 602 | if sz != 1 { 603 | e.asm("imul $%d, %%rax\n", sz) 604 | } 605 | e.asm("pushq %%rax\n") 606 | e.Expr(operand.Arr) 607 | e.asm("popq %%rcx\n") 608 | e.asm("addq %%rcx, %%rax\n") 609 | default: 610 | panic("internal error") 611 | } 612 | case '!': 613 | e.Expr(u.Operand) 614 | e.asm("xor %%rcx, %%rcx\n") 615 | e.asm("test %%rax, %%rax\n") 616 | e.asm("setnz %%cl\n") 617 | e.asm("movq %%rcx, %%rax\n") 618 | case '-': 619 | e.Expr(u.Operand) 620 | e.asm("neg %%rax\n") 621 | case '*': 622 | e.Expr(u.Operand) 623 | e.asm("movq (%%rax), %%rax\n") 624 | } 625 | } 626 | 627 | func (e *emitter) Index(idx *parse.Index) { 628 | e.Expr(idx.Idx) 629 | sz := getSize(idx.GetType()) 630 | if sz != 1 { 631 | e.asm("imul $%d, %%rax\n", sz) 632 | } 633 | e.asm("push %%rax\n") 634 | e.Expr(idx.Arr) 635 | e.asm("pop %%rcx\n") 636 | e.asm("addq %%rcx, %%rax\n") 637 | e.LoadFromPtr("rax", idx.GetType()) 638 | } 639 | 640 | func (e *emitter) Assign(b *parse.Binop) { 641 | e.Expr(b.R) 642 | e.asm("pushq %%rax\n") 643 | e.GetAddr(b.L) 644 | e.asm("movq %%rax, %%rcx\n") 645 | e.asm("popq %%rax\n") 646 | e.StoreToPtr("rcx", b.L.GetType()) 647 | } 648 | -------------------------------------------------------------------------------- /cmd/x64cc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/andrewchambers/cc/cpp" 7 | "github.com/andrewchambers/cc/parse" 8 | "github.com/andrewchambers/cc/report" 9 | "io" 10 | "os" 11 | ) 12 | 13 | func printVersion() { 14 | fmt.Println("x64cc") 15 | } 16 | 17 | func printUsage() { 18 | printVersion() 19 | fmt.Println() 20 | fmt.Println("Usage:") 21 | fmt.Println(" x64cc [FLAGS] FILE.c") 22 | fmt.Println() 23 | fmt.Println("Environment variables:") 24 | fmt.Println(" CCDEBUG=true enables extended error messages for debugging the compiler.") 25 | fmt.Println() 26 | fmt.Println("Flags:") 27 | flag.PrintDefaults() 28 | fmt.Println() 29 | fmt.Println("Software by Andrew Chambers 2014-2015 - andrewchamberss@gmail.com") 30 | } 31 | 32 | func compileFile(path string, out io.Writer) error { 33 | f, err := os.Open(path) 34 | if err != nil { 35 | err = fmt.Errorf("Failed to open source file %s for parsing: %s\n", path, err) 36 | return err 37 | } 38 | lexer := cpp.Lex(path, f) 39 | pp := cpp.New(lexer, nil) 40 | tu, err := parse.Parse(x64SzDesc, pp) 41 | if err != nil { 42 | return err 43 | } 44 | return Emit(tu, out) 45 | } 46 | 47 | func main() { 48 | flag.Usage = printUsage 49 | version := flag.Bool("version", false, "Print version info and exit.") 50 | outputPath := flag.String("o", "-", "Write output to `file`, '-' for stdout.") 51 | flag.Parse() 52 | if *version { 53 | printVersion() 54 | return 55 | } 56 | if flag.NArg() == 0 { 57 | printUsage() 58 | os.Exit(1) 59 | } 60 | if flag.NArg() != 1 { 61 | fmt.Fprintf(os.Stderr, "Bad number of args, please specify a single source file.\n") 62 | os.Exit(1) 63 | } 64 | input := flag.Args()[0] 65 | var output io.WriteCloser 66 | var err error 67 | if *outputPath == "-" { 68 | output = os.Stdout 69 | } else { 70 | output, err = os.Create(*outputPath) 71 | if err != nil { 72 | fmt.Fprintf(os.Stderr, "Failed to open output file %s\n", err) 73 | os.Exit(1) 74 | } 75 | } 76 | err = compileFile(input, output) 77 | if err != nil { 78 | report.ReportError(err) 79 | os.Exit(1) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cmd/x64cc/mdesc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/andrewchambers/cc/parse" 5 | ) 6 | 7 | var primSizeTab = [...]int{ 8 | parse.CVoid: 0, 9 | parse.CChar: 1, 10 | parse.CUChar: 1, 11 | parse.CShort: 2, 12 | parse.CUShort: 2, 13 | parse.CInt: 4, 14 | parse.CUInt: 4, 15 | parse.CLong: 8, 16 | parse.CULong: 8, 17 | parse.CLLong: 8, 18 | parse.CULLong: 8, 19 | } 20 | 21 | var primAlignTab = [...]int{ 22 | parse.CVoid: 0, 23 | parse.CBool: 1, 24 | parse.CChar: 1, 25 | parse.CUChar: 1, 26 | parse.CShort: 2, 27 | parse.CUShort: 2, 28 | parse.CInt: 4, 29 | parse.CUInt: 4, 30 | parse.CLong: 8, 31 | parse.CULong: 8, 32 | parse.CLLong: 8, 33 | parse.CULLong: 8, 34 | } 35 | 36 | func getSize(t parse.CType) int { 37 | switch t := t.(type) { 38 | case *parse.CStruct: 39 | sz := 0 40 | for _, t := range t.Types { 41 | sz += getSize(t) 42 | } 43 | return sz 44 | case *parse.Array: 45 | return t.Dim * getSize(t.MemberType) 46 | case *parse.Ptr: 47 | return 8 48 | case parse.Primitive: 49 | return primSizeTab[t] 50 | } 51 | panic(t) 52 | } 53 | 54 | func getAlign(t parse.CType) int { 55 | switch t := t.(type) { 56 | case *parse.Array: 57 | return 8 58 | case *parse.Ptr: 59 | return 8 60 | case parse.Primitive: 61 | return primAlignTab[t] 62 | } 63 | panic(t) 64 | } 65 | 66 | var x64SzDesc = parse.TargetSizeDesc{ 67 | GetSize: getSize, 68 | GetAlign: getAlign, 69 | } 70 | -------------------------------------------------------------------------------- /cpp/cpp.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "container/list" 5 | "errors" 6 | "fmt" 7 | "io" 8 | ) 9 | 10 | type Preprocessor struct { 11 | lxidx int 12 | lexers [1024]*Lexer 13 | 14 | is IncludeSearcher 15 | //List of all pushed back tokens 16 | tl *tokenList 17 | //Map of defined macros 18 | objMacros map[string]*objMacro 19 | //Map of defined FUNC macros 20 | funcMacros map[string]*funcMacro 21 | 22 | //Stack of condContext about #ifdefs blocks 23 | conditionalStack *list.List 24 | } 25 | 26 | type condContext struct { 27 | hasSucceeded bool 28 | } 29 | 30 | func (pp *Preprocessor) pushCondContext() { 31 | pp.conditionalStack.PushBack(&condContext{false}) 32 | } 33 | 34 | func (pp *Preprocessor) popCondContext() { 35 | if pp.condDepth() == 0 { 36 | panic("internal bug") 37 | } 38 | pp.conditionalStack.Remove(pp.conditionalStack.Back()) 39 | } 40 | 41 | func (pp *Preprocessor) markCondContextSucceeded() { 42 | pp.conditionalStack.Back().Value.(*condContext).hasSucceeded = true 43 | } 44 | 45 | func (pp *Preprocessor) condDepth() int { 46 | return pp.conditionalStack.Len() 47 | } 48 | 49 | func New(l *Lexer, is IncludeSearcher) *Preprocessor { 50 | ret := new(Preprocessor) 51 | ret.lexers[0] = l 52 | ret.is = is 53 | ret.tl = newTokenList() 54 | ret.objMacros = make(map[string]*objMacro) 55 | ret.funcMacros = make(map[string]*funcMacro) 56 | ret.conditionalStack = list.New() 57 | return ret 58 | } 59 | 60 | type cppbreakout struct { 61 | t *Token 62 | err error 63 | } 64 | 65 | func (pp *Preprocessor) nextNoExpand() *Token { 66 | if pp.tl.isEmpty() { 67 | for { 68 | t, err := pp.lexers[pp.lxidx].Next() 69 | if err != nil { 70 | panic(&cppbreakout{t, err}) 71 | } 72 | if t.Kind == EOF { 73 | if pp.lxidx == 0 { 74 | return t 75 | } 76 | pp.lxidx -= 1 77 | continue 78 | } 79 | return t 80 | } 81 | } 82 | return pp.tl.popFront() 83 | } 84 | 85 | func (pp *Preprocessor) cppError(e string, pos FilePos) { 86 | err := ErrWithLoc(errors.New(e), pos) 87 | panic(&cppbreakout{ 88 | t: &Token{}, 89 | err: err, 90 | }) 91 | } 92 | 93 | func (pp *Preprocessor) Next() (t *Token, err error) { 94 | 95 | defer func() { 96 | if e := recover(); e != nil { 97 | var b *cppbreakout 98 | b = e.(*cppbreakout) 99 | t = b.t 100 | err = b.err 101 | } 102 | }() 103 | 104 | t = pp.nextNoExpand() 105 | 106 | for t.Kind == DIRECTIVE { 107 | pp.handleDirective(t) 108 | t = pp.nextNoExpand() 109 | } 110 | 111 | if t.hs.contains(t.Val) { 112 | return t, nil 113 | } 114 | macro, ok := pp.objMacros[t.Val] 115 | if ok { 116 | replacementTokens := macro.tokens.copy() 117 | replacementTokens.addToHideSets(t) 118 | replacementTokens.setPositions(t.Pos) 119 | pp.ungetTokens(replacementTokens) 120 | return pp.Next() 121 | } 122 | fmacro, ok := pp.funcMacros[t.Val] 123 | if ok { 124 | opening := pp.nextNoExpand() 125 | if opening.Kind == LPAREN { 126 | args, rparen, err := pp.readMacroInvokeArguments() 127 | if len(args) != fmacro.nargs { 128 | return &Token{}, fmt.Errorf("macro %s invoked with %d arguments but %d were expected at %s", t.Val, len(args), fmacro.nargs, t.Pos) 129 | } 130 | if err != nil { 131 | return &Token{}, err 132 | } 133 | hs := t.hs.intersection(rparen.hs) 134 | hs = hs.add(t.Val) 135 | pp.subst(fmacro, t.Pos, args, hs) 136 | return pp.Next() 137 | } 138 | } 139 | return t, nil 140 | } 141 | 142 | func (pp *Preprocessor) subst(macro *funcMacro, invokePos FilePos, args []*tokenList, hs *hideset) { 143 | expandedTokens := newTokenList() 144 | for e := macro.tokens.front(); e != nil; e = e.Next() { 145 | t := e.Value.(*Token) 146 | idx, tIsArg := macro.isArg(t) 147 | if tIsArg { 148 | expandedTokens.appendList(args[idx]) 149 | } else { 150 | tcpy := t.copy() 151 | tcpy.Pos = invokePos 152 | expandedTokens.append(tcpy) 153 | } 154 | } 155 | expandedTokens.setHideSets(hs) 156 | pp.ungetTokens(expandedTokens) 157 | } 158 | 159 | //Read the tokens that are part of a macro invocation, not including the first paren. 160 | //But including the last paren. Handles nested parens. 161 | //returns a slice of token lists and the closing paren. 162 | //Each token list in the returned value represents a read macro param. 163 | //e.g. FOO(BAR,(A,B),C) -> { , <(A,B)> , } , ) 164 | //Where FOO( has already been consumed. 165 | func (pp *Preprocessor) readMacroInvokeArguments() ([]*tokenList, *Token, error) { 166 | parenDepth := 1 167 | argIdx := 0 168 | ret := make([]*tokenList, 0, 16) 169 | ret = append(ret, newTokenList()) 170 | for { 171 | t := pp.nextNoExpand() 172 | if t.Kind == EOF { 173 | return nil, nil, fmt.Errorf("EOF while reading macro arguments") 174 | } 175 | switch t.Kind { 176 | case LPAREN: 177 | parenDepth += 1 178 | if parenDepth != 1 { 179 | ret[argIdx].append(t) 180 | } 181 | case RPAREN: 182 | parenDepth -= 1 183 | if parenDepth == 0 { 184 | return ret, t, nil 185 | } else { 186 | ret[argIdx].append(t) 187 | } 188 | case COMMA: 189 | if parenDepth == 1 { 190 | //nextArg 191 | argIdx += 1 192 | ret = append(ret, newTokenList()) 193 | } else { 194 | ret[argIdx].append(t) 195 | } 196 | default: 197 | ret[argIdx].append(t) 198 | } 199 | } 200 | } 201 | 202 | func (pp *Preprocessor) ungetTokens(tl *tokenList) { 203 | pp.tl.prependList(tl) 204 | } 205 | 206 | func (pp *Preprocessor) ungetToken(t *Token) { 207 | pp.tl.prepend(t) 208 | } 209 | 210 | func (pp *Preprocessor) handleIf(pos FilePos) { 211 | pp.pushCondContext() 212 | //Pretend it fails... 213 | pp.skipTillEndif(pos) 214 | } 215 | 216 | func (pp *Preprocessor) handleIfDef(pos FilePos) { 217 | pp.pushCondContext() 218 | //Pretend it fails... 219 | pp.skipTillEndif(pos) 220 | } 221 | 222 | func (pp *Preprocessor) handleEndif(pos FilePos) { 223 | if pp.condDepth() <= 0 { 224 | pp.cppError("stray #endif", pos) 225 | } 226 | pp.popCondContext() 227 | endTok := pp.nextNoExpand() 228 | if endTok.Kind != END_DIRECTIVE { 229 | pp.cppError("unexpected token after #endif", endTok.Pos) 230 | } 231 | } 232 | 233 | //XXX untested 234 | func (pp *Preprocessor) skipTillEndif(pos FilePos) { 235 | depth := 1 236 | for { 237 | //Dont care about expands since we are skipping. 238 | t := pp.nextNoExpand() 239 | if t == nil { 240 | pp.cppError("unclosed preprocessor conditional", pos) 241 | } 242 | 243 | if t.Kind == DIRECTIVE && (t.Val == "if" || t.Val == "ifdef" || t.Val == "ifndef") { 244 | depth += 1 245 | continue 246 | } 247 | 248 | if t.Kind == DIRECTIVE && t.Val == "endif" { 249 | depth -= 1 250 | } 251 | 252 | if depth == 0 { 253 | break 254 | } 255 | } 256 | } 257 | 258 | func (pp *Preprocessor) handleDirective(dirTok *Token) { 259 | if dirTok.Kind != DIRECTIVE { 260 | pp.cppError(fmt.Sprintf("internal error %s", dirTok), dirTok.Pos) 261 | } 262 | switch dirTok.Val { 263 | case "if": 264 | pp.handleIf(dirTok.Pos) 265 | case "ifdef": 266 | pp.handleIfDef(dirTok.Pos) 267 | case "endif": 268 | pp.handleEndif(dirTok.Pos) 269 | //case "ifndef": 270 | //case "elif": 271 | //case "else": 272 | case "undef": 273 | pp.handleUndefine() 274 | case "define": 275 | pp.handleDefine() 276 | case "include": 277 | pp.handleInclude() 278 | case "error": 279 | pp.handleError() 280 | case "warning": 281 | pp.handleWarning() 282 | default: 283 | pp.cppError(fmt.Sprintf("unknown directive error %s", dirTok), dirTok.Pos) 284 | } 285 | } 286 | 287 | func (pp *Preprocessor) handleError() { 288 | tok := pp.nextNoExpand() 289 | if tok.Kind != STRING { 290 | pp.cppError("error string %s", tok.Pos) 291 | } 292 | pp.cppError(tok.String(), tok.Pos) 293 | } 294 | 295 | func (pp *Preprocessor) handleWarning() { 296 | //XXX 297 | pp.handleError() 298 | } 299 | 300 | func (pp *Preprocessor) handleInclude() { 301 | tok := pp.nextNoExpand() 302 | if tok.Kind != HEADER { 303 | pp.cppError("expected a header at %s", tok.Pos) 304 | } 305 | headerStr := tok.Val 306 | path := headerStr[1 : len(headerStr)-1] 307 | var headerName string 308 | var rdr io.Reader 309 | var err error 310 | switch headerStr[0] { 311 | case '<': 312 | headerName, rdr, err = pp.is.IncludeAngled(tok.Pos.File, path) 313 | case '"': 314 | headerName, rdr, err = pp.is.IncludeQuote(tok.Pos.File, path) 315 | default: 316 | pp.cppError("internal error %s", tok.Pos) 317 | } 318 | tok = pp.nextNoExpand() 319 | if tok.Kind != END_DIRECTIVE { 320 | pp.cppError("Expected newline after include", tok.Pos) 321 | } 322 | if err != nil { 323 | pp.cppError(fmt.Sprintf("error during include %s", err), tok.Pos) 324 | } 325 | pp.lxidx += 1 326 | pp.lexers[pp.lxidx] = Lex(headerName, rdr) 327 | } 328 | 329 | func (pp *Preprocessor) handleUndefine() { 330 | ident := pp.nextNoExpand() 331 | if ident.Kind != IDENT { 332 | pp.cppError("#undefine expected an ident", ident.Pos) 333 | } 334 | if !pp.isDefined(ident.Val) { 335 | pp.cppError(fmt.Sprintf("cannot undefine %s, not defined", ident.Val), ident.Pos) 336 | } 337 | delete(pp.objMacros, ident.Val) 338 | delete(pp.funcMacros, ident.Val) 339 | end := pp.nextNoExpand() 340 | if end.Kind != END_DIRECTIVE { 341 | pp.cppError("expected end of directive", end.Pos) 342 | } 343 | } 344 | 345 | func (pp *Preprocessor) handleDefine() { 346 | ident := pp.nextNoExpand() 347 | //XXX should also support keywords and maybe other things 348 | if ident.Kind != IDENT { 349 | pp.cppError("#define expected an ident", ident.Pos) 350 | } 351 | t := pp.nextNoExpand() 352 | if t.Kind == FUNCLIKE_DEFINE { 353 | pp.handleFuncLikeDefine(ident) 354 | } else { 355 | pp.ungetToken(t) 356 | pp.handleObjDefine(ident) 357 | } 358 | 359 | } 360 | 361 | func (pp *Preprocessor) isDefined(s string) bool { 362 | _, ok1 := pp.funcMacros[s] 363 | _, ok2 := pp.objMacros[s] 364 | return ok1 || ok2 365 | } 366 | 367 | func (pp *Preprocessor) handleFuncLikeDefine(ident *Token) { 368 | //First read the arguments. 369 | paren := pp.nextNoExpand() 370 | if paren.Kind != LPAREN { 371 | panic("Bug, func like define without opening LPAREN") 372 | } 373 | 374 | if pp.isDefined(ident.Val) { 375 | pp.cppError("macro redefinition "+ident.Val, ident.Pos) 376 | } 377 | 378 | args := newTokenList() 379 | tokens := newTokenList() 380 | 381 | for { 382 | t := pp.nextNoExpand() 383 | if t.Kind == RPAREN { 384 | break 385 | } 386 | if t.Kind != IDENT { 387 | pp.cppError("Expected macro argument", t.Pos) 388 | } 389 | args.append(t) 390 | t2 := pp.nextNoExpand() 391 | if t2.Kind == COMMA { 392 | continue 393 | } else if t2.Kind == RPAREN { 394 | break 395 | } else { 396 | pp.cppError("Error in macro definition expected , or )", t2.Pos) 397 | } 398 | } 399 | 400 | for { 401 | t := pp.nextNoExpand() 402 | if t.Kind == END_DIRECTIVE { 403 | break 404 | } 405 | tokens.append(t) 406 | } 407 | 408 | macro, err := newFuncMacro(args, tokens) 409 | if err != nil { 410 | pp.cppError("Error in macro definition "+err.Error(), ident.Pos) 411 | } 412 | pp.funcMacros[ident.Val] = macro 413 | } 414 | 415 | func (pp *Preprocessor) handleObjDefine(ident *Token) { 416 | if pp.isDefined(ident.Val) { 417 | pp.cppError("macro redefinition "+ident.Val, ident.Pos) 418 | } 419 | tl := newTokenList() 420 | for { 421 | t := pp.nextNoExpand() 422 | if t == nil { 423 | panic("Bug, EOF before END_DIRECTIVE in define at" + t.String()) 424 | } 425 | if t.Kind == END_DIRECTIVE { 426 | break 427 | } 428 | tl.append(t) 429 | } 430 | m := newObjMacro(tl) 431 | pp.objMacros[ident.Val] = m 432 | } 433 | -------------------------------------------------------------------------------- /cpp/cppexpr.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | /* 10 | Implements the expression parsing and evaluation for #if statements 11 | 12 | Note that "defined name" and "define(name)" are handled before this part of code. 13 | 14 | #if expression 15 | controlled text 16 | #endif 17 | 18 | expression may be: 19 | 20 | Integer constants. 21 | 22 | Character constants, which are interpreted as they would be in normal code. 23 | 24 | Arithmetic operators for most of C 25 | 26 | Identifiers that are not macros, which are all considered to be the number zero. 27 | */ 28 | 29 | type cppExprCtx struct { 30 | e *list.Element 31 | isDefined func(string) bool 32 | } 33 | 34 | func (ctx *cppExprCtx) nextToken() *Token { 35 | if ctx.e == nil { 36 | return nil 37 | } 38 | tok := ctx.e.Value.(*Token) 39 | ctx.e = ctx.e.Next() 40 | return tok 41 | } 42 | 43 | func (ctx *cppExprCtx) peek() *Token { 44 | if ctx.e == nil { 45 | return nil 46 | } 47 | return ctx.e.Value.(*Token) 48 | } 49 | 50 | func parseCPPExprAtom(ctx *cppExprCtx) (int64, error) { 51 | toCheck := ctx.nextToken() 52 | if toCheck == nil { 53 | return 0, fmt.Errorf("expected integer, char, or defined but got nothing") 54 | } 55 | switch toCheck.Kind { 56 | case NOT: 57 | v, err := parseCPPExprAtom(ctx) 58 | if v == 0 { 59 | return 1, nil 60 | } 61 | return 0, err 62 | case BNOT: 63 | v, err := parseCPPExprAtom(ctx) 64 | if err != nil { 65 | return 0, err 66 | } 67 | return ^v, nil 68 | case SUB: 69 | v, err := parseCPPExprAtom(ctx) 70 | if err != nil { 71 | return 0, err 72 | } 73 | return -v, nil 74 | case ADD: 75 | v, err := parseCPPExprAtom(ctx) 76 | if err != nil { 77 | return 0, err 78 | } 79 | return v, nil 80 | case LPAREN: 81 | v, err := parseCPPExpr(ctx) 82 | if err != nil { 83 | return 0, err 84 | } 85 | rparen := ctx.nextToken() 86 | if rparen == nil || rparen.Kind != RPAREN { 87 | return 0, fmt.Errorf("unclosed parenthesis") 88 | } 89 | return v, nil 90 | case INT_CONSTANT: 91 | v, err := strconv.ParseInt(toCheck.Val, 0, 64) 92 | if err != nil { 93 | return 0, fmt.Errorf("internal error parsing int constant") 94 | } 95 | return v, nil 96 | case CHAR_CONSTANT: 97 | 98 | return 0, fmt.Errorf("unimplemented char literal in cpp expression") 99 | case IDENT: 100 | if toCheck.Val == "defined" { 101 | toCheck = ctx.nextToken() 102 | if toCheck == nil { 103 | return 0, fmt.Errorf("expected ( or an identifier but got nothing") 104 | } 105 | switch toCheck.Kind { 106 | case LPAREN: 107 | toCheck = ctx.nextToken() 108 | rparen := ctx.nextToken() 109 | if rparen == nil || rparen.Kind != RPAREN { 110 | return 0, fmt.Errorf("malformed defined check, missing )") 111 | } 112 | case IDENT: 113 | //calls isDefined as intended 114 | default: 115 | return 0, fmt.Errorf("malformed defined statement at %s", toCheck.Pos) 116 | } 117 | } 118 | default: 119 | return 0, fmt.Errorf("expected integer, char, or defined but got %s", toCheck.Val) 120 | } 121 | if toCheck == nil { 122 | return 0, fmt.Errorf("expected identifier but got nothing") 123 | } 124 | if ctx.isDefined(toCheck.Val) { 125 | return 1, nil 126 | } 127 | return 0, nil 128 | } 129 | 130 | func evalCPPBinop(ctx *cppExprCtx, k TokenKind, l int64, r int64) (int64, error) { 131 | switch k { 132 | case LOR: 133 | if l != 0 || r != 0 { 134 | return 1, nil 135 | } 136 | return 0, nil 137 | case LAND: 138 | if l != 0 && r != 0 { 139 | return 1, nil 140 | } 141 | return 0, nil 142 | case OR: 143 | return l | r, nil 144 | case XOR: 145 | return l ^ r, nil 146 | case AND: 147 | return l & r, nil 148 | case ADD: 149 | return l + r, nil 150 | case SUB: 151 | return l - r, nil 152 | case MUL: 153 | return l * r, nil 154 | case SHR: 155 | return l >> uint64(r), nil 156 | case SHL: 157 | return l << uint64(r), nil 158 | case QUO: 159 | if r == 0 { 160 | return 0, fmt.Errorf("divide by zero in expression") 161 | } 162 | return l / r, nil 163 | case REM: 164 | if r == 0 { 165 | return 0, fmt.Errorf("divide by zero in expression") 166 | } 167 | return l % r, nil 168 | case EQL: 169 | if l == r { 170 | return 1, nil 171 | } 172 | return 0, nil 173 | case LSS: 174 | if l < r { 175 | return 1, nil 176 | } 177 | return 0, nil 178 | case GTR: 179 | if l > r { 180 | return 1, nil 181 | } 182 | return 0, nil 183 | case LEQ: 184 | if l <= r { 185 | return 1, nil 186 | } 187 | return 0, nil 188 | case GEQ: 189 | if l >= r { 190 | return 1, nil 191 | } 192 | return 0, nil 193 | case NEQ: 194 | if l != r { 195 | return 1, nil 196 | } 197 | return 0, nil 198 | case COMMA: 199 | return r, nil 200 | default: 201 | return 0, fmt.Errorf("internal error %s", k) 202 | } 203 | } 204 | 205 | func parseCPPTernary(ctx *cppExprCtx) (int64, error) { 206 | cond, err := parseCPPBinop(ctx) 207 | if err != nil { 208 | return 0, err 209 | } 210 | t := ctx.peek() 211 | var a, b int64 212 | if t != nil && t.Kind == QUESTION { 213 | ctx.nextToken() 214 | a, err = parseCPPExpr(ctx) 215 | if err != nil { 216 | return 0, err 217 | } 218 | colon := ctx.nextToken() 219 | if colon == nil || colon.Kind != COLON { 220 | return 0, fmt.Errorf("ternary without :") 221 | } 222 | b, err = parseCPPExpr(ctx) 223 | if err != nil { 224 | return 0, err 225 | } 226 | if cond != 0 { 227 | return a, nil 228 | } 229 | return b, nil 230 | } 231 | return cond, nil 232 | } 233 | 234 | func parseCPPComma(ctx *cppExprCtx) (int64, error) { 235 | v, err := parseCPPTernary(ctx) 236 | if err != nil { 237 | return 0, err 238 | } 239 | for { 240 | t := ctx.peek() 241 | if t == nil || t.Kind != COMMA { 242 | break 243 | } 244 | ctx.nextToken() 245 | v, err = parseCPPTernary(ctx) 246 | if err != nil { 247 | return 0, err 248 | } 249 | } 250 | return v, nil 251 | } 252 | 253 | func getPrec(k TokenKind) int { 254 | switch k { 255 | case MUL, REM, QUO: 256 | return 10 257 | case ADD, SUB: 258 | return 9 259 | case SHR, SHL: 260 | return 8 261 | case LSS, GTR, GEQ, LEQ: 262 | return 7 263 | case EQL, NEQ: 264 | return 6 265 | case AND: 266 | return 5 267 | case XOR: 268 | return 4 269 | case OR: 270 | return 3 271 | case LAND: 272 | return 2 273 | case LOR: 274 | return 1 275 | } 276 | return -1 277 | } 278 | 279 | // This is the precedence climbing algorithm, simplified because 280 | // all the operators are left associative. The CPP doesn't 281 | // deal with assignment operators. 282 | func parseCPPBinop_1(ctx *cppExprCtx, prec int) (int64, error) { 283 | l, err := parseCPPExprAtom(ctx) 284 | if err != nil { 285 | return 0, err 286 | } 287 | for { 288 | t := ctx.peek() 289 | if t == nil { 290 | break 291 | } 292 | p := getPrec(t.Kind) 293 | if p == -1 { 294 | break 295 | } 296 | if p < prec { 297 | break 298 | } 299 | ctx.nextToken() 300 | r, err := parseCPPBinop_1(ctx, p+1) 301 | if err != nil { 302 | return 0, err 303 | } 304 | l, err = evalCPPBinop(ctx, t.Kind, l, r) 305 | if err != nil { 306 | return 0, err 307 | } 308 | } 309 | return l, nil 310 | } 311 | 312 | func parseCPPBinop(ctx *cppExprCtx) (int64, error) { 313 | return parseCPPBinop_1(ctx, 0) 314 | } 315 | 316 | func parseCPPExpr(ctx *cppExprCtx) (int64, error) { 317 | return parseCPPComma(ctx) 318 | } 319 | 320 | func evalIfExpr(isDefined func(string) bool, tl *tokenList) (int64, error) { 321 | ctx := &cppExprCtx{isDefined: isDefined, e: tl.l.Front()} 322 | ret, err := parseCPPExpr(ctx) 323 | if err != nil { 324 | return 0, err 325 | } 326 | t := ctx.nextToken() 327 | if t != nil { 328 | return 0, fmt.Errorf("stray token %s", t.Val) 329 | } 330 | return ret, nil 331 | } 332 | -------------------------------------------------------------------------------- /cpp/cppexpr_test.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | var exprTestCases = []struct { 9 | expr string 10 | expected int64 11 | expectErr bool 12 | }{ 13 | {"1", 1, false}, 14 | {"2", 2, false}, 15 | {"0x1", 0x1, false}, 16 | {"0x1", 0x1, false}, 17 | {"-1", -1, false}, 18 | {"-2", -2, false}, 19 | {"(2)", 2, false}, 20 | {"(-2)", -2, false}, 21 | {"0x1234", 0x1234, false}, 22 | {"foo", 1, false}, 23 | {"bang", 0, false}, 24 | {"defined foo", 1, false}, 25 | {"defined bang", 0, false}, 26 | {"defined(foo)", 1, false}, 27 | {"defined(bang)", 0, false}, 28 | {"defined", 0, true}, 29 | {"defined(bang", 0, true}, 30 | {"defined bang)", 0, true}, 31 | {"0 || 0", 0, false}, 32 | {"1 || 0", 1, false}, 33 | {"0 || 1", 1, false}, 34 | {"1 || 1", 1, false}, 35 | {"0 && 0", 0, false}, 36 | {"1 && 0", 0, false}, 37 | {"0 && 1", 0, false}, 38 | {"1 && 1", 1, false}, 39 | {"0xf0 | 1", 0xf1, false}, 40 | {"0xf0 & 1", 0, false}, 41 | {"0xf0 & 0x1f", 0x10, false}, 42 | {"1 ^ 1", 0, false}, 43 | {"1 == 1", 1, false}, 44 | {"1 == 0", 0, false}, 45 | {"1 != 1", 0, false}, 46 | {"0 != 1", 1, false}, 47 | {"0 > 1", 0, false}, 48 | {"0 < 1", 1, false}, 49 | {"0 > -1", 1, false}, 50 | {"0 < -1", 0, false}, 51 | {"0 >= 1", 0, false}, 52 | {"0 <= 1", 1, false}, 53 | {"0 >= -1", 1, false}, 54 | {"0 <= -1", 0, false}, 55 | {"0 < 0", 0, false}, 56 | {"0 <= 0", 1, false}, 57 | {"0 > 0", 0, false}, 58 | {"0 >= 0", 1, false}, 59 | {"1 << 1", 2, false}, 60 | {"2 >> 1", 1, false}, 61 | {"2 + 1", 3, false}, 62 | {"2 - 3", -1, false}, 63 | {"2 * 3", 6, false}, 64 | {"6 / 3", 2, false}, 65 | {"7 % 3", 1, false}, 66 | {"0,1", 1, false}, 67 | {"1,0", 0, false}, 68 | {"2+2*3+2", 10, false}, 69 | {"(2+2)*(3+2)", 20, false}, 70 | {"2 + 2 + 2 + 2 == 2 + 2 * 3", 1, false}, 71 | {"0 ? 1 : 2", 2, false}, 72 | {"1 ? 1 : 2", 1, false}, 73 | {"(1 ? 1 ? 1337 : 1234 : 2) == 1337", 1, false}, 74 | {"(1 ? 0 ? 1337 : 1234 : 2) == 1234", 1, false}, 75 | {"(0 ? 1 ? 1337 : 1234 : 2) == 2", 1, false}, 76 | {"(0 ? 1 ? 1337 : 1234 : 2 ? 3 : 4) == 3", 1, false}, 77 | {"0 , 1 ? 1 , 0 : 2 ", 0, false}, 78 | } 79 | 80 | var testExprPredefined = map[string]struct{}{ 81 | "foo": {}, 82 | "bar": {}, 83 | "baz": {}, 84 | } 85 | 86 | func TestExprEval(t *testing.T) { 87 | for idx := range exprTestCases { 88 | tc := &exprTestCases[idx] 89 | r := bytes.NewBufferString(tc.expr) 90 | lexer := Lex("testcase.c", r) 91 | isDefined := func(s string) bool { 92 | _, ok := testExprPredefined[s] 93 | return ok 94 | } 95 | tl := newTokenList() 96 | for { 97 | tok, err := lexer.Next() 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | if tok.Kind == EOF { 102 | break 103 | } 104 | tl.append(tok) 105 | } 106 | result, err := evalIfExpr(isDefined, tl) 107 | if err != nil { 108 | if !tc.expectErr { 109 | t.Errorf("test %s failed - got error <%s>", tc.expr, err) 110 | } 111 | } else if tc.expectErr { 112 | t.Errorf("test %s failed - expected an error", tc.expr) 113 | } else if result != tc.expected { 114 | t.Errorf("test %s failed - got %d expected %d", tc.expr, result, tc.expected) 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /cpp/error.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "fmt" 4 | 5 | type ErrorLoc struct { 6 | Err error 7 | Pos FilePos 8 | } 9 | 10 | func ErrWithLoc(e error, pos FilePos) error { 11 | return ErrorLoc{ 12 | Err: e, 13 | Pos: pos, 14 | } 15 | } 16 | 17 | func (e ErrorLoc) Error() string { 18 | return fmt.Sprintf("%s at %s", e.Err, e.Pos) 19 | } 20 | -------------------------------------------------------------------------------- /cpp/hideset.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | // The hideset of a token is the set of identifiers whose expansion resulted in the token. 4 | // 5 | // Hidesets prevent infinite macro expansion. 6 | // It is implemented as an immutable singly linked list for code clarity. 7 | // Performance should be ok for most real world code (hidesets are small in practice). 8 | 9 | type hideset struct { 10 | r *hideset 11 | val string 12 | } 13 | 14 | var emptyHS *hideset = nil 15 | 16 | func (hs *hideset) rest() *hideset { 17 | if hs == emptyHS { 18 | return emptyHS 19 | } 20 | return hs.r 21 | } 22 | 23 | func (hs *hideset) len() int { 24 | l := 0 25 | for hs != emptyHS { 26 | l += 1 27 | hs = hs.rest() 28 | } 29 | return l 30 | } 31 | 32 | func (hs *hideset) contains(s string) bool { 33 | for hs != emptyHS { 34 | if hs.val == s { 35 | return true 36 | } 37 | hs = hs.rest() 38 | } 39 | return false 40 | } 41 | 42 | func (hs *hideset) add(s string) *hideset { 43 | if hs.contains(s) { 44 | return hs 45 | } 46 | return &hideset{ 47 | r: hs, 48 | val: s, 49 | } 50 | } 51 | 52 | func (hs *hideset) union(b *hideset) *hideset { 53 | for hs != emptyHS { 54 | b = b.add(hs.val) 55 | hs = hs.rest() 56 | } 57 | return b 58 | } 59 | 60 | func (hs *hideset) intersection(b *hideset) *hideset { 61 | ret := emptyHS 62 | for hs != emptyHS { 63 | if b.contains(hs.val) { 64 | ret = ret.add(hs.val) 65 | } 66 | hs = hs.rest() 67 | } 68 | return ret 69 | } 70 | -------------------------------------------------------------------------------- /cpp/include.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "path" 8 | "strings" 9 | ) 10 | 11 | type IncludeSearcher interface { 12 | //IncludeQuote is invoked when the preprocessor 13 | //encounters an include of the form #include "foo.h". 14 | //returns the full path of the file, a reader of the contents or an error. 15 | IncludeQuote(requestingFile, headerPath string) (string, io.Reader, error) 16 | //IncludeAngled is invoked when the preprocessor 17 | //encounters an include of the form #include . 18 | //returns the full path of the file, a reader of the contents or an error. 19 | IncludeAngled(requestingFile, headerPath string) (string, io.Reader, error) 20 | } 21 | 22 | type StandardIncludeSearcher struct { 23 | //Priority order list of paths to search for headers 24 | systemHeadersPath []string 25 | } 26 | 27 | func fileExists(path string) (bool, error) { 28 | _, err := os.Stat(path) 29 | if os.IsNotExist(err) { 30 | return false, nil 31 | } 32 | if err != nil { 33 | return false, err 34 | } 35 | return true, nil 36 | } 37 | 38 | func (is *StandardIncludeSearcher) IncludeQuote(requestingFile, headerPath string) (string, io.Reader, error) { 39 | dir := path.Dir(requestingFile) 40 | path := path.Join(dir, headerPath) 41 | exists, err := fileExists(path) 42 | if err != nil { 43 | return "", nil, err 44 | } 45 | if !exists { 46 | return is.IncludeAngled(requestingFile, headerPath) 47 | } 48 | rdr, err := os.Open(path) 49 | return path, rdr, err 50 | } 51 | 52 | func (is *StandardIncludeSearcher) IncludeAngled(requestingFile, headerPath string) (string, io.Reader, error) { 53 | for idx := range is.systemHeadersPath { 54 | dir := path.Dir(is.systemHeadersPath[idx]) 55 | path := path.Join(dir, headerPath) 56 | exists, err := fileExists(path) 57 | if err != nil { 58 | return "", nil, err 59 | } 60 | if exists { 61 | rdr, err := os.Open(path) 62 | return path, rdr, err 63 | } 64 | } 65 | return "", nil, fmt.Errorf("header %s not found", headerPath) 66 | } 67 | 68 | //A ; seperated list of paths 69 | func NewStandardIncludeSearcher(includePaths string) IncludeSearcher { 70 | ret := &StandardIncludeSearcher{} 71 | ret.systemHeadersPath = strings.Split(includePaths, ";") 72 | return ret 73 | } 74 | -------------------------------------------------------------------------------- /cpp/lex.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | // TODO: 12 | // Prefer this to not use a goroutine to allow proper garbage collection of lexers. 13 | // 14 | // Prefer tokens had flags indicating whitespace and newlines, instead of special tokens. 15 | // The preprocessor needs this info to correctly identify directives etc. 16 | 17 | type Lexer struct { 18 | brdr *bufio.Reader 19 | pos FilePos 20 | lastPos FilePos 21 | markedPos FilePos 22 | lastChar rune 23 | // At the beginning on line not including whitespace. 24 | bol bool 25 | // Set to true if we have hit the end of file. 26 | eof bool 27 | // Set to true if we are currently reading a # directive line 28 | inDirective bool 29 | stream chan *Token 30 | 31 | err error 32 | } 33 | 34 | type breakout struct{} 35 | 36 | // Lex starts a goroutine which lexes the contents of the reader. 37 | // fname is used for error messages when showing the source location. 38 | // No preprocessing is done, this is just pure reading of the unprocessed 39 | // source file. 40 | // The goroutine will not stop until all tokens are read 41 | func Lex(fname string, r io.Reader) *Lexer { 42 | lx := new(Lexer) 43 | lx.pos.File = fname 44 | lx.pos.Line = 1 45 | lx.pos.Col = 1 46 | lx.markedPos = lx.pos 47 | lx.lastPos = lx.pos 48 | lx.stream = make(chan *Token, 4096) 49 | lx.brdr = bufio.NewReader(r) 50 | lx.bol = true 51 | go lx.lex() 52 | return lx 53 | } 54 | 55 | func (lx *Lexer) Next() (*Token, error) { 56 | tok := <-lx.stream 57 | if tok == nil { 58 | return &Token{Kind: EOF, Pos: lx.pos}, nil 59 | } 60 | if tok.Kind == ERROR { 61 | return tok, lx.err 62 | } 63 | return tok, nil 64 | } 65 | 66 | func (lx *Lexer) markPos() { 67 | lx.markedPos = lx.pos 68 | } 69 | 70 | func (lx *Lexer) sendTok(kind TokenKind, val string) { 71 | var tok Token 72 | tok.Kind = kind 73 | tok.Val = val 74 | tok.Pos = lx.markedPos 75 | tok.hs = emptyHS 76 | switch kind { 77 | case END_DIRECTIVE: 78 | //Do nothing as this is a pseudo directive. 79 | default: 80 | lx.bol = false 81 | } 82 | lx.stream <- &tok 83 | } 84 | 85 | func (lx *Lexer) unreadRune() { 86 | lx.pos = lx.lastPos 87 | if lx.lastChar == '\n' { 88 | lx.bol = false 89 | } 90 | if lx.eof { 91 | return 92 | } 93 | lx.brdr.UnreadRune() 94 | } 95 | 96 | func (lx *Lexer) readRune() (rune, bool) { 97 | r, _, err := lx.brdr.ReadRune() 98 | lx.lastPos = lx.pos 99 | if err != nil { 100 | if err == io.EOF { 101 | lx.eof = true 102 | lx.lastChar = 0 103 | return 0, true 104 | } 105 | lx.Error(err.Error()) 106 | } 107 | switch r { 108 | case '\n': 109 | lx.pos.Line += 1 110 | lx.pos.Col = 1 111 | lx.bol = true 112 | case '\t': 113 | lx.pos.Col += 4 114 | default: 115 | lx.pos.Col += 1 116 | } 117 | lx.lastChar = r 118 | return r, false 119 | } 120 | 121 | func (lx *Lexer) Error(e string) { 122 | eWithPos := ErrWithLoc(errors.New(e), lx.pos) 123 | lx.err = eWithPos 124 | lx.sendTok(ERROR, eWithPos.Error()) 125 | close(lx.stream) 126 | //recover exits the lexer cleanly 127 | panic(&breakout{}) 128 | } 129 | 130 | func (lx *Lexer) lex() { 131 | 132 | defer func() { 133 | if e := recover(); e != nil { 134 | _ = e.(*breakout) // Will re-panic if not a breakout. 135 | } 136 | 137 | }() 138 | for { 139 | lx.markPos() 140 | first, eof := lx.readRune() 141 | if eof { 142 | if lx.inDirective { 143 | lx.sendTok(END_DIRECTIVE, "") 144 | } 145 | lx.sendTok(EOF, "") 146 | break 147 | } 148 | switch { 149 | case isAlpha(first) || first == '_': 150 | lx.unreadRune() 151 | lx.readIdentOrKeyword() 152 | case isNumeric(first): 153 | lx.unreadRune() 154 | lx.readConstantIntOrFloat(false) 155 | case isWhiteSpace(first): 156 | lx.unreadRune() 157 | lx.skipWhiteSpace() 158 | default: 159 | switch first { 160 | case '#': 161 | if lx.isAtLineStart() { 162 | lx.readDirective() 163 | } else { 164 | lx.sendTok(HASH, "#") 165 | } 166 | case '!': 167 | second, _ := lx.readRune() 168 | switch second { 169 | case '=': 170 | lx.sendTok(NEQ, "!=") 171 | default: 172 | lx.unreadRune() 173 | lx.sendTok(NOT, "!") 174 | } 175 | case '?': 176 | lx.sendTok(QUESTION, "?") 177 | case ':': 178 | lx.sendTok(COLON, ":") 179 | case '\'': 180 | lx.unreadRune() 181 | lx.readCChar() 182 | case '"': 183 | lx.unreadRune() 184 | lx.readCString() 185 | case '(': 186 | lx.sendTok(LPAREN, "(") 187 | case ')': 188 | lx.sendTok(RPAREN, ")") 189 | case '{': 190 | lx.sendTok(LBRACE, "{") 191 | case '}': 192 | lx.sendTok(RBRACE, "}") 193 | case '[': 194 | lx.sendTok(LBRACK, "[") 195 | case ']': 196 | lx.sendTok(RBRACK, "]") 197 | case '<': 198 | second, _ := lx.readRune() 199 | switch second { 200 | case '<': 201 | lx.sendTok(SHL, "<<") 202 | case '=': 203 | lx.sendTok(LEQ, "<=") 204 | default: 205 | lx.unreadRune() 206 | lx.sendTok(LSS, "<") 207 | } 208 | case '>': 209 | second, _ := lx.readRune() 210 | switch second { 211 | case '>': 212 | lx.sendTok(SHR, ">>") 213 | case '=': 214 | lx.sendTok(GEQ, ">=") 215 | default: 216 | lx.unreadRune() 217 | lx.sendTok(GTR, ">") 218 | } 219 | case '+': 220 | second, _ := lx.readRune() 221 | switch second { 222 | case '+': 223 | lx.sendTok(INC, "++") 224 | case '=': 225 | lx.sendTok(ADD_ASSIGN, "+=") 226 | default: 227 | lx.unreadRune() 228 | lx.sendTok(ADD, "+") 229 | } 230 | case '.': 231 | second, _ := lx.readRune() 232 | lx.unreadRune() 233 | if isNumeric(second) { 234 | lx.readConstantIntOrFloat(true) 235 | } else { 236 | lx.sendTok(PERIOD, ".") 237 | } 238 | case '~': 239 | lx.sendTok(BNOT, "~") 240 | case '^': 241 | second, _ := lx.readRune() 242 | switch second { 243 | case '=': 244 | lx.sendTok(XOR_ASSIGN, "-=") 245 | default: 246 | lx.unreadRune() 247 | lx.sendTok(XOR, "^") 248 | } 249 | case '-': 250 | second, _ := lx.readRune() 251 | switch second { 252 | case '>': 253 | lx.sendTok(ARROW, "->") 254 | case '-': 255 | lx.sendTok(DEC, "--") 256 | case '=': 257 | lx.sendTok(SUB_ASSIGN, "-=") 258 | default: 259 | lx.unreadRune() 260 | lx.sendTok(SUB, "-") 261 | } 262 | case ',': 263 | lx.sendTok(COMMA, ",") 264 | case '*': 265 | second, _ := lx.readRune() 266 | switch second { 267 | case '=': 268 | lx.sendTok(MUL_ASSIGN, "*=") 269 | default: 270 | lx.unreadRune() 271 | lx.sendTok(MUL, "*") 272 | } 273 | case '\\': 274 | r, _ := lx.readRune() 275 | if r == '\n' { 276 | break 277 | } 278 | lx.Error("misplaced '\\'.") 279 | case '/': 280 | second, _ := lx.readRune() 281 | switch second { 282 | case '*': 283 | for { 284 | c, eof := lx.readRune() 285 | if eof { 286 | lx.Error("unclosed comment.") 287 | } 288 | if c == '*' { 289 | closeBar, eof := lx.readRune() 290 | if eof { 291 | lx.Error("unclosed comment.") 292 | } 293 | if closeBar == '/' { 294 | break 295 | } 296 | //Unread so that we dont lose newlines. 297 | lx.unreadRune() 298 | } 299 | } 300 | case '/': 301 | for { 302 | c, eof := lx.readRune() 303 | if c == '\n' || eof { 304 | break 305 | } 306 | } 307 | case '=': 308 | lx.sendTok(QUO_ASSIGN, "/") 309 | default: 310 | lx.unreadRune() 311 | lx.sendTok(QUO, "/") 312 | } 313 | case '%': 314 | second, _ := lx.readRune() 315 | switch second { 316 | case '=': 317 | lx.sendTok(REM_ASSIGN, "%=") 318 | default: 319 | lx.unreadRune() 320 | lx.sendTok(REM, "%") 321 | } 322 | case '|': 323 | second, _ := lx.readRune() 324 | switch second { 325 | case '|': 326 | lx.sendTok(LOR, "||") 327 | case '=': 328 | lx.sendTok(OR_ASSIGN, "|=") 329 | default: 330 | lx.unreadRune() 331 | lx.sendTok(OR, "|") 332 | } 333 | case '&': 334 | second, _ := lx.readRune() 335 | switch second { 336 | case '&': 337 | lx.sendTok(LAND, "&&") 338 | case '=': 339 | lx.sendTok(AND_ASSIGN, "&=") 340 | default: 341 | lx.unreadRune() 342 | lx.sendTok(AND, "&") 343 | } 344 | case '=': 345 | second, _ := lx.readRune() 346 | switch second { 347 | case '=': 348 | lx.sendTok(EQL, "==") 349 | default: 350 | lx.unreadRune() 351 | lx.sendTok(ASSIGN, "=") 352 | } 353 | case ';': 354 | lx.sendTok(SEMICOLON, ";") 355 | default: 356 | lx.Error(fmt.Sprintf("Internal Error - bad char code '%d'", first)) 357 | } 358 | } 359 | } 360 | close(lx.stream) 361 | } 362 | 363 | func (lx *Lexer) readDirective() { 364 | directiveLine := lx.pos.Line 365 | lx.skipWhiteSpace() 366 | if lx.pos.Line != directiveLine { 367 | return 368 | } 369 | var buff bytes.Buffer 370 | directiveChar, eof := lx.readRune() 371 | if eof { 372 | lx.Error("end of file in directive.") 373 | } 374 | if isAlpha(directiveChar) { 375 | lx.inDirective = true 376 | for isAlpha(directiveChar) { 377 | buff.WriteRune(directiveChar) 378 | directiveChar, eof = lx.readRune() 379 | } 380 | if !eof { 381 | lx.unreadRune() 382 | } 383 | directive := buff.String() 384 | lx.sendTok(DIRECTIVE, directive) 385 | switch directive { 386 | case "include": 387 | lx.readHeaderInclude() 388 | case "define": 389 | lx.readDefine() 390 | default: 391 | } 392 | } else { 393 | //wasnt a directive, error will be caught by 394 | //cpp or parser. 395 | lx.unreadRune() 396 | } 397 | 398 | } 399 | 400 | func (lx *Lexer) readDefine() { 401 | line := lx.pos.Line 402 | lx.skipWhiteSpace() 403 | if lx.pos.Line != line { 404 | lx.Error("No identifier after define") 405 | } 406 | lx.readIdentOrKeyword() 407 | r, eof := lx.readRune() 408 | if eof { 409 | lx.Error("End of File in #efine") 410 | } 411 | //Distinguish between a funclike macro 412 | //and a regular macro. 413 | if r == '(' { 414 | lx.sendTok(FUNCLIKE_DEFINE, "") 415 | lx.unreadRune() 416 | } 417 | 418 | } 419 | 420 | func (lx *Lexer) readHeaderInclude() { 421 | var buff bytes.Buffer 422 | line := lx.pos.Line 423 | lx.skipWhiteSpace() 424 | if lx.pos.Line != line { 425 | lx.Error("No header after include.") 426 | } 427 | lx.markPos() 428 | opening, _ := lx.readRune() 429 | var terminator rune 430 | if opening == '"' { 431 | terminator = '"' 432 | } else if opening == '<' { 433 | terminator = '>' 434 | } else { 435 | lx.Error("bad start to header include.") 436 | } 437 | buff.WriteRune(opening) 438 | for { 439 | c, eof := lx.readRune() 440 | if eof { 441 | lx.Error("EOF encountered in header include.") 442 | } 443 | if c == '\n' { 444 | lx.Error("new line in header include.") 445 | } 446 | buff.WriteRune(c) 447 | if c == terminator { 448 | break 449 | } 450 | } 451 | lx.sendTok(HEADER, buff.String()) 452 | } 453 | 454 | func (lx *Lexer) readIdentOrKeyword() { 455 | var buff bytes.Buffer 456 | lx.markPos() 457 | first, _ := lx.readRune() 458 | if !isValidIdentStart(first) { 459 | panic("internal error") 460 | } 461 | buff.WriteRune(first) 462 | for { 463 | b, _ := lx.readRune() 464 | if isValidIdentTail(b) { 465 | buff.WriteRune(b) 466 | } else { 467 | lx.unreadRune() 468 | str := buff.String() 469 | tokType, ok := keywordLUT[str] 470 | if !ok { 471 | tokType = IDENT 472 | } 473 | lx.sendTok(tokType, str) 474 | break 475 | } 476 | } 477 | } 478 | 479 | func (lx *Lexer) skipWhiteSpace() { 480 | for { 481 | r, _ := lx.readRune() 482 | if !isWhiteSpace(r) { 483 | lx.unreadRune() 484 | break 485 | } 486 | if r == '\n' { 487 | if lx.inDirective { 488 | lx.sendTok(END_DIRECTIVE, "") 489 | lx.inDirective = false 490 | } 491 | } 492 | } 493 | } 494 | 495 | // Due to the 1 character lookahead we need this bool 496 | func (lx *Lexer) readConstantIntOrFloat(startedWithPeriod bool) { 497 | var buff bytes.Buffer 498 | const ( 499 | START = iota 500 | SECOND 501 | HEX 502 | DEC 503 | FLOAT_START 504 | FLOAT_AFTER_E 505 | FLOAT_AFTER_E_SIGN 506 | INT_TAIL 507 | FLOAT_TAIL 508 | END 509 | ) 510 | var tokType TokenKind 511 | var state int 512 | if startedWithPeriod { 513 | state = FLOAT_START 514 | tokType = FLOAT_CONSTANT 515 | buff.WriteRune('.') 516 | } else { 517 | state = START 518 | tokType = INT_CONSTANT 519 | } 520 | for state != END { 521 | r, eof := lx.readRune() 522 | if eof { 523 | state = END 524 | break 525 | } 526 | switch state { 527 | case START: 528 | if r == '.' { 529 | state = FLOAT 530 | buff.WriteRune(r) 531 | break 532 | } 533 | if !isNumeric(r) { 534 | lx.Error("internal error") 535 | } 536 | buff.WriteRune(r) 537 | state = SECOND 538 | case SECOND: 539 | if r == 'x' || r == 'X' { 540 | state = HEX 541 | buff.WriteRune(r) 542 | } else if isNumeric(r) { 543 | state = DEC 544 | buff.WriteRune(r) 545 | } else if r == 'e' || r == 'E' { 546 | state = FLOAT_AFTER_E 547 | tokType = FLOAT_CONSTANT 548 | buff.WriteRune(r) 549 | } else if r == '.' { 550 | state = FLOAT_START 551 | tokType = FLOAT_CONSTANT 552 | buff.WriteRune(r) 553 | } else { 554 | state = END 555 | } 556 | case DEC: 557 | if !isNumeric(r) { 558 | switch r { 559 | case 'l', 'L', 'u', 'U': 560 | state = INT_TAIL 561 | buff.WriteRune(r) 562 | case 'e', 'E': 563 | state = FLOAT_AFTER_E 564 | tokType = FLOAT_CONSTANT 565 | buff.WriteRune(r) 566 | case '.': 567 | state = FLOAT_START 568 | tokType = FLOAT_CONSTANT 569 | buff.WriteRune(r) 570 | default: 571 | if isValidIdentStart(r) { 572 | lx.Error("invalid constant int") 573 | } 574 | state = END 575 | } 576 | } else { 577 | buff.WriteRune(r) 578 | } 579 | case HEX: 580 | if !isHexDigit(r) { 581 | switch r { 582 | case 'l', 'L', 'u', 'U': 583 | state = INT_TAIL 584 | buff.WriteRune(r) 585 | default: 586 | if isValidIdentStart(r) { 587 | lx.Error("invalid constant int") 588 | } 589 | state = END 590 | } 591 | } else { 592 | buff.WriteRune(r) 593 | } 594 | case INT_TAIL: 595 | switch r { 596 | case 'l', 'L', 'u', 'U': 597 | buff.WriteRune(r) 598 | default: 599 | state = END 600 | } 601 | case FLOAT_START: 602 | if !isNumeric(r) { 603 | switch r { 604 | case 'e', 'E': 605 | state = FLOAT_AFTER_E 606 | buff.WriteRune(r) 607 | default: 608 | if isValidIdentStart(r) { 609 | lx.Error("invalid floating point constant.") 610 | } 611 | state = END 612 | } 613 | } else { 614 | buff.WriteRune(r) 615 | } 616 | case FLOAT_AFTER_E: 617 | if r == '-' || r == '+' { 618 | state = FLOAT_AFTER_E_SIGN 619 | buff.WriteRune(r) 620 | } else if isNumeric(r) { 621 | state = FLOAT_AFTER_E_SIGN 622 | buff.WriteRune(r) 623 | } else { 624 | lx.Error("invalid float constant - expected number or signed after e") 625 | } 626 | case FLOAT_AFTER_E_SIGN: 627 | if isNumeric(r) { 628 | buff.WriteRune(r) 629 | } else { 630 | switch r { 631 | case 'l', 'L', 'f', 'F': 632 | buff.WriteRune(r) 633 | state = FLOAT_TAIL 634 | default: 635 | if isValidIdentStart(r) { 636 | lx.Error("invalid float constant") 637 | } else { 638 | state = END 639 | } 640 | } 641 | } 642 | case FLOAT_TAIL: 643 | switch r { 644 | case 'l', 'L', 'f', 'F': 645 | buff.WriteRune(r) 646 | default: 647 | if isValidIdentStart(r) { 648 | lx.Error("invalid float constant") 649 | } 650 | state = END 651 | } 652 | default: 653 | lx.Error("internal error.") 654 | } 655 | } 656 | lx.unreadRune() 657 | lx.sendTok(tokType, buff.String()) 658 | } 659 | 660 | func (lx *Lexer) readCString() { 661 | const ( 662 | START = iota 663 | MID 664 | ESCAPED 665 | END 666 | ) 667 | var buff bytes.Buffer 668 | var state int 669 | lx.markPos() 670 | for state != END { 671 | r, eof := lx.readRune() 672 | if eof { 673 | lx.Error("eof in string literal") 674 | } 675 | switch state { 676 | case START: 677 | if r != '"' { 678 | lx.Error("internal error") 679 | } 680 | buff.WriteRune(r) 681 | state = MID 682 | case MID: 683 | switch r { 684 | case '\\': 685 | state = ESCAPED 686 | case '"': 687 | buff.WriteRune(r) 688 | state = END 689 | default: 690 | buff.WriteRune(r) 691 | } 692 | case ESCAPED: 693 | switch r { 694 | case '\r': 695 | // empty 696 | case '\n': 697 | state = MID 698 | default: 699 | buff.WriteRune('\\') 700 | buff.WriteRune(r) 701 | state = MID 702 | } 703 | } 704 | } 705 | lx.sendTok(STRING, buff.String()) 706 | } 707 | 708 | func (lx *Lexer) readCChar() { 709 | const ( 710 | START = iota 711 | MID 712 | ESCAPED 713 | END 714 | ) 715 | var buff bytes.Buffer 716 | var state int 717 | lx.markPos() 718 | for state != END { 719 | r, eof := lx.readRune() 720 | if eof { 721 | lx.Error("eof in char literal") 722 | } 723 | switch state { 724 | case START: 725 | if r != '\'' { 726 | lx.Error("internal error") 727 | } 728 | buff.WriteRune(r) 729 | state = MID 730 | case MID: 731 | switch r { 732 | case '\\': 733 | state = ESCAPED 734 | case '\'': 735 | buff.WriteRune(r) 736 | state = END 737 | default: 738 | buff.WriteRune(r) 739 | } 740 | case ESCAPED: 741 | switch r { 742 | case '\r': 743 | // empty 744 | case '\n': 745 | state = MID 746 | default: 747 | buff.WriteRune('\\') 748 | buff.WriteRune(r) 749 | state = MID 750 | } 751 | } 752 | } 753 | lx.sendTok(CHAR_CONSTANT, buff.String()) 754 | 755 | } 756 | 757 | func (lx *Lexer) isAtLineStart() bool { 758 | return lx.bol 759 | } 760 | 761 | func isValidIdentTail(b rune) bool { 762 | return isValidIdentStart(b) || isNumeric(b) || b == '$' 763 | } 764 | 765 | func isValidIdentStart(b rune) bool { 766 | return b == '_' || isAlpha(b) 767 | } 768 | 769 | func isAlpha(b rune) bool { 770 | if b >= 'a' && b <= 'z' { 771 | return true 772 | } 773 | if b >= 'A' && b <= 'Z' { 774 | return true 775 | } 776 | return false 777 | } 778 | 779 | func isWhiteSpace(b rune) bool { 780 | return b == ' ' || b == '\r' || b == '\n' || b == '\t' || b == '\f' 781 | } 782 | 783 | func isNumeric(b rune) bool { 784 | if b >= '0' && b <= '9' { 785 | return true 786 | } 787 | return false 788 | } 789 | 790 | func isHexDigit(b rune) bool { 791 | return isNumeric(b) || (b >= 'a' && b <= 'f') || (b >= 'A' && b <= 'F') 792 | } 793 | -------------------------------------------------------------------------------- /cpp/macro.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "fmt" 4 | 5 | //Data structures representing macros inside the cpreprocessor. 6 | //These should be immutable. 7 | 8 | type objMacro struct { 9 | tokens *tokenList 10 | } 11 | 12 | func newObjMacro(tokens *tokenList) *objMacro { 13 | return &objMacro{tokens} 14 | } 15 | 16 | type funcMacro struct { 17 | //Map of macro string to arg position 18 | nargs int 19 | //0 based index 20 | args map[string]int 21 | //Tokens of the macro. 22 | tokens *tokenList 23 | } 24 | 25 | //Returns if the token is an argument to the macro 26 | //Also returns the zero based index of which argument it is. 27 | //This corresponds to the position in the invocation list. 28 | func (fm *funcMacro) isArg(t *Token) (int, bool) { 29 | v, ok := fm.args[t.Val] 30 | return v, ok 31 | } 32 | 33 | //args should be a list of ident tokens 34 | func newFuncMacro(args *tokenList, tokens *tokenList) (*funcMacro, error) { 35 | ret := new(funcMacro) 36 | ret.nargs = 0 37 | ret.args = make(map[string]int) 38 | idx := 0 39 | for e := args.front(); e != nil; e = e.Next() { 40 | tok := e.Value.(*Token) 41 | _, ok := ret.args[tok.Val] 42 | if ok { 43 | return nil, fmt.Errorf("error duplicate argument " + tok.Val) 44 | } 45 | ret.args[tok.Val] = idx 46 | ret.nargs += 1 47 | idx += 1 48 | } 49 | ret.tokens = tokens 50 | return ret, nil 51 | } 52 | -------------------------------------------------------------------------------- /cpp/token.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // The list of tokens. 8 | const ( 9 | 10 | // Single char tokens are themselves. 11 | ADD = '+' 12 | SUB = '-' 13 | MUL = '*' 14 | QUO = '/' 15 | REM = '%' 16 | AND = '&' 17 | OR = '|' 18 | XOR = '^' 19 | QUESTION = '?' 20 | HASH = '#' 21 | LSS = '<' 22 | GTR = '>' 23 | ASSIGN = '=' 24 | NOT = '!' 25 | BNOT = '~' 26 | LPAREN = '(' 27 | LBRACK = '[' 28 | LBRACE = '{' 29 | COMMA = ',' 30 | PERIOD = '.' 31 | RPAREN = ')' 32 | RBRACK = ']' 33 | RBRACE = '}' 34 | SEMICOLON = ';' 35 | COLON = ':' 36 | 37 | ERROR = 10000 + iota 38 | EOF 39 | //some cpp only tokens 40 | FUNCLIKE_DEFINE //Occurs after ident before paren #define ident( 41 | DIRECTIVE //#if #include etc 42 | END_DIRECTIVE //New line at the end of a directive 43 | HEADER 44 | // Identifiers and basic type literals 45 | // (these tokens stand for classes of literals) 46 | IDENT // main 47 | INT_CONSTANT // 12345 48 | FLOAT_CONSTANT // 123.45 49 | CHAR_CONSTANT // 'a' 50 | STRING // "abc" 51 | 52 | SHL // << 53 | SHR // >> 54 | ADD_ASSIGN // += 55 | SUB_ASSIGN // -= 56 | MUL_ASSIGN // *= 57 | QUO_ASSIGN // /= 58 | REM_ASSIGN // %= 59 | AND_ASSIGN // &= 60 | OR_ASSIGN // |= 61 | XOR_ASSIGN // ^= 62 | SHL_ASSIGN // <<= 63 | SHR_ASSIGN // >>= 64 | LAND // && 65 | LOR // || 66 | ARROW // -> 67 | INC // ++ 68 | DEC // -- 69 | EQL // == 70 | NEQ // != 71 | LEQ // <= 72 | GEQ // >= 73 | ELLIPSIS // ... 74 | 75 | // Keywords 76 | REGISTER 77 | EXTERN 78 | STATIC 79 | SHORT 80 | BREAK 81 | CASE 82 | DO 83 | CONST 84 | CONTINUE 85 | DEFAULT 86 | ELSE 87 | FOR 88 | WHILE 89 | GOTO 90 | IF 91 | RETURN 92 | STRUCT 93 | UNION 94 | VOLATILE 95 | SWITCH 96 | TYPEDEF 97 | SIZEOF 98 | VOID 99 | CHAR 100 | INT 101 | FLOAT 102 | DOUBLE 103 | SIGNED 104 | UNSIGNED 105 | LONG 106 | ) 107 | 108 | var tokenKindToStr = [...]string{ 109 | HASH: "#", 110 | EOF: "EOF", 111 | FUNCLIKE_DEFINE: "funclikedefine", 112 | DIRECTIVE: "cppdirective", 113 | END_DIRECTIVE: "enddirective", 114 | HEADER: "header", 115 | CHAR_CONSTANT: "charconst", 116 | INT_CONSTANT: "intconst", 117 | FLOAT_CONSTANT: "floatconst", 118 | IDENT: "ident", 119 | VOID: "void", 120 | INT: "int", 121 | LONG: "long", 122 | SIGNED: "signed", 123 | UNSIGNED: "unsigned", 124 | FLOAT: "float", 125 | DOUBLE: "double", 126 | CHAR: "char", 127 | STRING: "string", 128 | ADD: "'+'", 129 | SUB: "'-'", 130 | MUL: "'*'", 131 | QUO: "'/'", 132 | REM: "'%'", 133 | AND: "'&'", 134 | OR: "'|'", 135 | XOR: "'^'", 136 | SHL: "'<<'", 137 | SHR: "'>>'", 138 | ADD_ASSIGN: "'+='", 139 | SUB_ASSIGN: "'-='", 140 | MUL_ASSIGN: "'*='", 141 | QUO_ASSIGN: "'/='", 142 | REM_ASSIGN: "'%='", 143 | AND_ASSIGN: "'&='", 144 | OR_ASSIGN: "'|='", 145 | XOR_ASSIGN: "'^='", 146 | SHL_ASSIGN: "'<<='", 147 | SHR_ASSIGN: "'>>='", 148 | LAND: "'&&'", 149 | LOR: "'||'", 150 | ARROW: "'->'", 151 | INC: "'++'", 152 | DEC: "'--'", 153 | EQL: "'=='", 154 | LSS: "'<'", 155 | GTR: "'>'", 156 | ASSIGN: "'='", 157 | NOT: "'!'", 158 | BNOT: "'~'", 159 | NEQ: "'!='", 160 | LEQ: "'<='", 161 | GEQ: "'>='", 162 | ELLIPSIS: "'...'", 163 | LPAREN: "'('", 164 | LBRACK: "'['", 165 | LBRACE: "'{'", 166 | COMMA: "','", 167 | PERIOD: "'.'", 168 | RPAREN: "')'", 169 | RBRACK: "']'", 170 | RBRACE: "'}'", 171 | SEMICOLON: "';'", 172 | COLON: "':'", 173 | QUESTION: "'?'", 174 | SIZEOF: "sizeof", 175 | TYPEDEF: "typedef", 176 | BREAK: "break", 177 | CASE: "case", 178 | CONST: "const", 179 | CONTINUE: "continue", 180 | DEFAULT: "default", 181 | ELSE: "else", 182 | FOR: "for", 183 | DO: "do", 184 | WHILE: "while", 185 | GOTO: "goto", 186 | IF: "if", 187 | RETURN: "return", 188 | STRUCT: "struct", 189 | SWITCH: "switch", 190 | STATIC: "static", 191 | } 192 | 193 | var keywordLUT = map[string]TokenKind{ 194 | "for": FOR, 195 | "while": WHILE, 196 | "do": DO, 197 | "if": IF, 198 | "else": ELSE, 199 | "goto": GOTO, 200 | "break": BREAK, 201 | "continue": CONTINUE, 202 | "case": CASE, 203 | "default": DEFAULT, 204 | "switch": SWITCH, 205 | "struct": STRUCT, 206 | "signed": SIGNED, 207 | "unsigned": UNSIGNED, 208 | "typedef": TYPEDEF, 209 | "return": RETURN, 210 | "void": VOID, 211 | "char": CHAR, 212 | "int": INT, 213 | "short": SHORT, 214 | "long": LONG, 215 | "float": FLOAT, 216 | "double": DOUBLE, 217 | "sizeof": SIZEOF, 218 | "static": STATIC, 219 | } 220 | 221 | type TokenKind uint32 222 | 223 | func (tk TokenKind) String() string { 224 | if uint32(tk) >= uint32(len(tokenKindToStr)) { 225 | return "Unknown" 226 | } 227 | ret := tokenKindToStr[tk] 228 | if ret == "" { 229 | return "Unknown" 230 | } 231 | return ret 232 | } 233 | 234 | type FilePos struct { 235 | File string 236 | Line int 237 | Col int 238 | } 239 | 240 | func (pos FilePos) String() string { 241 | return fmt.Sprintf("%s:%d:%d", pos.File, pos.Line, pos.Col) 242 | } 243 | 244 | // Token represents a grouping of characters 245 | // that provide semantic meaning in a C program. 246 | type Token struct { 247 | Kind TokenKind 248 | Val string 249 | Pos FilePos 250 | hs *hideset 251 | } 252 | 253 | func (t *Token) wasExpanded() bool { 254 | return t.hs.len() > 0 255 | } 256 | 257 | func (t *Token) copy() *Token { 258 | ret := *t 259 | return &ret 260 | } 261 | 262 | func (t Token) String() string { 263 | if t.wasExpanded() { 264 | return fmt.Sprintf("%s expanded from macro at %s", t.Val, t.Pos) 265 | } 266 | return fmt.Sprintf("%s at %s", t.Val, t.Pos) 267 | } 268 | -------------------------------------------------------------------------------- /cpp/tokenlist.go: -------------------------------------------------------------------------------- 1 | package cpp 2 | 3 | import "container/list" 4 | 5 | //list of tokens 6 | 7 | type tokenList struct { 8 | l *list.List 9 | } 10 | 11 | func newTokenList() *tokenList { 12 | return &tokenList{list.New()} 13 | } 14 | 15 | func (tl *tokenList) copy() *tokenList { 16 | ret := newTokenList() 17 | ret.appendList(tl) 18 | return ret 19 | } 20 | 21 | func (tl *tokenList) isEmpty() bool { 22 | return tl.l.Len() == 0 23 | } 24 | 25 | func (tl *tokenList) popFront() *Token { 26 | if tl.isEmpty() { 27 | panic("internal error") 28 | } 29 | fronte := tl.l.Front() 30 | ret := fronte.Value.(*Token) 31 | tl.l.Remove(fronte) 32 | return ret 33 | } 34 | 35 | //Makes a copy of all tokens. 36 | func (tl *tokenList) appendList(toAdd *tokenList) { 37 | l := toAdd.l 38 | for e := l.Front(); e != nil; e = e.Next() { 39 | tl.l.PushBack(e.Value.(*Token).copy()) 40 | } 41 | } 42 | 43 | func (tl *tokenList) append(toAdd *Token) { 44 | tl.l.PushBack(toAdd.copy()) 45 | } 46 | 47 | func (tl *tokenList) prepend(toAdd *Token) { 48 | tl.l.PushFront(toAdd.copy()) 49 | } 50 | 51 | //Makes a copy of all tokens. 52 | func (tl *tokenList) prependList(toAdd *tokenList) { 53 | l := toAdd.l 54 | for e := l.Back(); e != nil; e = e.Prev() { 55 | tl.l.PushFront(e.Value.(*Token).copy()) 56 | } 57 | } 58 | 59 | func (tl *tokenList) front() *list.Element { 60 | return tl.l.Front() 61 | } 62 | 63 | func (tl *tokenList) addToHideSets(tok *Token) { 64 | for e := tl.front(); e != nil; e = e.Next() { 65 | e.Value.(*Token).hs = e.Value.(*Token).hs.add(tok.Val) 66 | } 67 | } 68 | 69 | func (tl *tokenList) setHideSets(hs *hideset) { 70 | for e := tl.front(); e != nil; e = e.Next() { 71 | e.Value.(*Token).hs = hs 72 | } 73 | } 74 | 75 | func (tl *tokenList) setPositions(pos FilePos) { 76 | for e := tl.front(); e != nil; e = e.Next() { 77 | e.Value.(*Token).Pos = pos 78 | } 79 | } 80 | 81 | func (tl *tokenList) String() string { 82 | ret := "" 83 | for e := tl.front(); e != nil; e = e.Next() { 84 | t := e.Value.(*Token) 85 | ret += " " + t.Val 86 | } 87 | return ret[1:] 88 | } 89 | -------------------------------------------------------------------------------- /parse/ast.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import "github.com/andrewchambers/cc/cpp" 4 | 5 | type Node interface { 6 | GetPos() cpp.FilePos 7 | } 8 | 9 | type Expr interface { 10 | Node 11 | GetType() CType 12 | } 13 | 14 | type TranslationUnit struct { 15 | TopLevels []Node 16 | AnonymousInits []Node 17 | } 18 | 19 | type Constant struct { 20 | Val int64 21 | Pos cpp.FilePos 22 | Type CType 23 | } 24 | 25 | func (c *Constant) GetType() CType { return c.Type } 26 | func (c *Constant) GetPos() cpp.FilePos { return c.Pos } 27 | 28 | type Initializer struct { 29 | Pos cpp.FilePos 30 | Inits []Node 31 | } 32 | 33 | func (i *Initializer) GetPos() cpp.FilePos { return i.Pos } 34 | 35 | type Return struct { 36 | Pos cpp.FilePos 37 | Ret Expr 38 | } 39 | 40 | func (r *Return) GetPos() cpp.FilePos { return r.Pos } 41 | 42 | type Index struct { 43 | Pos cpp.FilePos 44 | Arr Node 45 | Idx Node 46 | Type CType 47 | } 48 | 49 | func (i *Index) GetType() CType { return i.Type } 50 | func (i *Index) GetPos() cpp.FilePos { return i.Pos } 51 | 52 | type Cast struct { 53 | Pos cpp.FilePos 54 | Operand Expr 55 | Type CType 56 | } 57 | 58 | func (c *Cast) GetType() CType { return c.Type } 59 | func (c *Cast) GetPos() cpp.FilePos { return c.Pos } 60 | 61 | type Block struct { 62 | Pos cpp.FilePos 63 | Body []Node 64 | } 65 | 66 | func (b *Block) GetPos() cpp.FilePos { return b.Pos } 67 | 68 | type EmptyStmt struct { 69 | Pos cpp.FilePos 70 | } 71 | 72 | func (e *EmptyStmt) GetPos() cpp.FilePos { return e.Pos } 73 | 74 | type ExprStmt struct { 75 | Pos cpp.FilePos 76 | Expr Expr 77 | } 78 | 79 | func (e *ExprStmt) GetPos() cpp.FilePos { return e.Pos } 80 | 81 | type Goto struct { 82 | IsBreak bool 83 | IsCont bool 84 | Pos cpp.FilePos 85 | Label string 86 | } 87 | 88 | func (g *Goto) GetPos() cpp.FilePos { return g.Pos } 89 | 90 | type LabeledStmt struct { 91 | Pos cpp.FilePos 92 | AnonLabel string 93 | Label string 94 | Stmt Node 95 | IsCase bool 96 | IsDefault bool 97 | } 98 | 99 | func (l *LabeledStmt) GetPos() cpp.FilePos { return l.Pos } 100 | 101 | type If struct { 102 | Pos cpp.FilePos 103 | Cond Node 104 | Stmt Node 105 | Else Node 106 | LElse string 107 | } 108 | 109 | func (i *If) GetPos() cpp.FilePos { return i.Pos } 110 | 111 | type SwitchCase struct { 112 | V int64 113 | Label string 114 | } 115 | 116 | type Switch struct { 117 | Pos cpp.FilePos 118 | Expr Node 119 | Stmt Node 120 | Cases []SwitchCase 121 | LDefault string 122 | LAfter string 123 | } 124 | 125 | func (sw *Switch) GetPos() cpp.FilePos { return sw.Pos } 126 | 127 | type For struct { 128 | Pos cpp.FilePos 129 | Init Node 130 | Cond Node 131 | Step Node 132 | Body Node 133 | LStart string 134 | LEnd string 135 | } 136 | 137 | func (f *For) GetPos() cpp.FilePos { return f.Pos } 138 | 139 | type While struct { 140 | Pos cpp.FilePos 141 | Cond Node 142 | Body Node 143 | LStart string 144 | LEnd string 145 | } 146 | 147 | func (w *While) GetPos() cpp.FilePos { return w.Pos } 148 | 149 | type DoWhile struct { 150 | Pos cpp.FilePos 151 | Cond Node 152 | Body Node 153 | LStart string 154 | LCond string 155 | LEnd string 156 | } 157 | 158 | func (d *DoWhile) GetPos() cpp.FilePos { return d.Pos } 159 | 160 | type Unop struct { 161 | Op cpp.TokenKind 162 | Pos cpp.FilePos 163 | Operand Node 164 | Type CType 165 | } 166 | 167 | func (u *Unop) GetType() CType { return u.Type } 168 | func (u *Unop) GetPos() cpp.FilePos { return u.Pos } 169 | 170 | type Selector struct { 171 | Op cpp.TokenKind 172 | Pos cpp.FilePos 173 | Type CType 174 | Operand Expr 175 | Sel string 176 | } 177 | 178 | func (s *Selector) GetType() CType { return s.Type } 179 | func (s *Selector) GetPos() cpp.FilePos { return s.Pos } 180 | 181 | type Binop struct { 182 | Op cpp.TokenKind 183 | Pos cpp.FilePos 184 | L Expr 185 | R Expr 186 | Type CType 187 | } 188 | 189 | func (b *Binop) GetType() CType { return b.Type } 190 | func (b *Binop) GetPos() cpp.FilePos { return b.Pos } 191 | 192 | type CFunc struct { 193 | Name string 194 | Pos cpp.FilePos 195 | FuncType *CFuncT 196 | ParamSymbols []*LSymbol 197 | Body []Node 198 | } 199 | 200 | func (f *CFunc) GetType() CType { return f.FuncType } 201 | func (f *CFunc) GetPos() cpp.FilePos { return f.Pos } 202 | 203 | type Call struct { 204 | Pos cpp.FilePos 205 | FuncLike Expr 206 | Args []Expr 207 | Type CType 208 | } 209 | 210 | func (c *Call) GetType() CType { return c.Type } 211 | func (c *Call) GetPos() cpp.FilePos { return c.Pos } 212 | 213 | type DeclList struct { 214 | Pos cpp.FilePos 215 | Storage SClass 216 | Symbols []Symbol 217 | Inits []Expr 218 | } 219 | 220 | func (d *DeclList) GetPos() cpp.FilePos { return d.Pos } 221 | 222 | type String struct { 223 | Pos cpp.FilePos 224 | Val string 225 | Label string 226 | } 227 | 228 | func (s *String) GetType() CType { return &Ptr{CChar} } 229 | func (s *String) GetPos() cpp.FilePos { return s.Pos } 230 | 231 | type Ident struct { 232 | Pos cpp.FilePos 233 | Sym Symbol 234 | } 235 | 236 | func (i *Ident) GetType() CType { 237 | switch sym := i.Sym.(type) { 238 | case *GSymbol: 239 | return sym.Type 240 | case *LSymbol: 241 | return sym.Type 242 | } 243 | panic("unimplemented") 244 | } 245 | 246 | func (i *Ident) GetPos() cpp.FilePos { return i.Pos } 247 | -------------------------------------------------------------------------------- /parse/ctypes.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | type TargetSizeDesc struct { 4 | GetSize func(CType) int 5 | GetAlign func(CType) int 6 | } 7 | 8 | type CType interface{} 9 | 10 | type Array struct { 11 | MemberType CType 12 | Dim int 13 | } 14 | 15 | type Ptr struct { 16 | PointsTo CType 17 | } 18 | 19 | // Struct or union. 20 | type CStruct struct { 21 | Names []string 22 | Types []CType 23 | IsUnion bool 24 | } 25 | 26 | func (s *CStruct) FieldType(n string) CType { 27 | for idx, v := range s.Names { 28 | if v == n { 29 | return s.Types[idx] 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | type CFuncT struct { 36 | RetType CType 37 | ArgTypes []CType 38 | ArgNames []string 39 | IsVarArg bool 40 | } 41 | 42 | type ForwardedType struct { 43 | Type CType 44 | } 45 | 46 | // All the primitive C types. 47 | 48 | type Primitive int 49 | 50 | // *NOTE* order is significant. 51 | const ( 52 | CVoid Primitive = iota 53 | CEnum 54 | // Signed 55 | CChar 56 | CShort 57 | CInt 58 | CLong 59 | CLLong 60 | // Unsigned 61 | CBool 62 | CUChar 63 | CUShort 64 | CUInt 65 | CULong 66 | CULLong 67 | // Floats 68 | CFloat 69 | CDouble 70 | CLDouble 71 | ) 72 | 73 | func IsPtrType(t CType) bool { 74 | _, ok := t.(*Ptr) 75 | return ok 76 | } 77 | 78 | func IsIntType(t CType) bool { 79 | prim, ok := t.(Primitive) 80 | if !ok { 81 | return false 82 | } 83 | return prim >= CEnum && prim < CFloat 84 | } 85 | 86 | func IsSignedIntType(t CType) bool { 87 | prim, ok := t.(Primitive) 88 | if !ok { 89 | return false 90 | } 91 | return prim >= CEnum && prim <= CLLong 92 | } 93 | 94 | func IsScalarType(t CType) bool { 95 | return IsPtrType(t) || IsIntType(t) 96 | } 97 | 98 | func IsArrType(t CType) bool { 99 | _, ok := t.(*Array) 100 | return ok 101 | } 102 | 103 | func IsCFuncType(t CType) bool { 104 | _, ok := t.(*CFuncT) 105 | return ok 106 | } 107 | 108 | func IsStructType(t CType) bool { 109 | _, ok := t.(*CStruct) 110 | return ok 111 | } 112 | 113 | func IsCharType(t CType) bool { 114 | prim, ok := t.(Primitive) 115 | if !ok { 116 | return false 117 | } 118 | return prim == CChar 119 | } 120 | 121 | func IsCharArr(t CType) bool { 122 | return IsArrType(t) && IsCharType(t) 123 | } 124 | -------------------------------------------------------------------------------- /parse/fold.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "fmt" 5 | "github.com/andrewchambers/cc/cpp" 6 | ) 7 | 8 | type ConstantGPtr struct { 9 | Pos cpp.FilePos 10 | PtrLabel string 11 | Offset int64 12 | Type CType 13 | } 14 | 15 | func (c *ConstantGPtr) GetPos() cpp.FilePos { 16 | return c.Pos 17 | } 18 | 19 | func (c *ConstantGPtr) GetType() CType { 20 | return c.Type 21 | } 22 | 23 | // To fold a node means to compute the simplified form which can replace it without 24 | // changing the meaning of the program. 25 | func (p *parser) fold(n Expr) (Expr, error) { 26 | switch n := n.(type) { 27 | case *Constant: 28 | return n, nil 29 | case *String: 30 | return n, nil 31 | case *Unop: 32 | switch n.Op { 33 | case '&': 34 | ident, ok := n.Operand.(*Ident) 35 | if !ok { 36 | // XXX &foo[CONST] is valid. 37 | return nil, fmt.Errorf("'&' requires a valid identifier") 38 | } 39 | gsym, ok := ident.Sym.(*GSymbol) 40 | if !ok { 41 | return nil, fmt.Errorf("'&' requires a static or global identifier") 42 | } 43 | return &ConstantGPtr{ 44 | Pos: n.GetPos(), 45 | Offset: 0, 46 | PtrLabel: gsym.Label, 47 | Type: n.Type, 48 | }, nil 49 | } 50 | default: 51 | } 52 | return nil, fmt.Errorf("not a valid constant value") 53 | } 54 | -------------------------------------------------------------------------------- /parse/parse.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "fmt" 5 | "github.com/andrewchambers/cc/cpp" 6 | "os" 7 | "runtime/debug" 8 | "strconv" 9 | ) 10 | 11 | // Storage class 12 | type SClass int 13 | 14 | const ( 15 | SC_AUTO SClass = iota 16 | SC_REGISTER 17 | SC_STATIC 18 | SC_TYPEDEF 19 | SC_GLOBAL 20 | ) 21 | 22 | type parseErrorBreakOut struct { 23 | err error 24 | } 25 | 26 | type gotoFixup struct { 27 | actualLabel string 28 | g *Goto 29 | } 30 | 31 | type parser struct { 32 | szdesc TargetSizeDesc 33 | 34 | types *scope 35 | structs *scope 36 | decls *scope 37 | 38 | tu *TranslationUnit 39 | 40 | pp *cpp.Preprocessor 41 | curt, nextt *cpp.Token 42 | lcounter int 43 | 44 | breakCounter int 45 | breaks [2048]string 46 | contCounter int 47 | continues [2048]string 48 | 49 | switchCounter int 50 | switchs [2048]*Switch 51 | 52 | // Map of goto labels to anonymous labels. 53 | labels map[string]string 54 | // All gotos found in the current function. 55 | // Needed so we can fix up forward references. 56 | gotos []gotoFixup 57 | } 58 | 59 | func (p *parser) pushScope() { 60 | p.decls = newScope(p.decls) 61 | p.structs = newScope(p.structs) 62 | p.types = newScope(p.types) 63 | } 64 | 65 | func (p *parser) popScope() { 66 | p.decls = p.decls.parent 67 | p.structs = p.structs.parent 68 | p.types = p.types.parent 69 | } 70 | 71 | func (p *parser) pushSwitch(s *Switch) { 72 | p.switchs[p.switchCounter] = s 73 | p.switchCounter += 1 74 | } 75 | 76 | func (p *parser) popSwitch() { 77 | p.switchCounter -= 1 78 | } 79 | 80 | func (p *parser) getSwitch() *Switch { 81 | if p.switchCounter == 0 { 82 | return nil 83 | } 84 | return p.switchs[p.switchCounter-1] 85 | } 86 | 87 | func (p *parser) pushBreak(blabel string) { 88 | p.breaks[p.breakCounter] = blabel 89 | p.breakCounter += 1 90 | } 91 | 92 | func (p *parser) pushCont(clabel string) { 93 | p.continues[p.contCounter] = clabel 94 | p.contCounter += 1 95 | } 96 | 97 | func (p *parser) popBreak() { 98 | p.breakCounter -= 1 99 | if p.breakCounter < 0 { 100 | panic("internal error") 101 | } 102 | } 103 | 104 | func (p *parser) popCont() { 105 | p.contCounter -= 1 106 | if p.contCounter < 0 { 107 | panic("internal error") 108 | } 109 | } 110 | 111 | func (p *parser) pushBreakCont(blabel, clabel string) { 112 | p.pushBreak(blabel) 113 | p.pushCont(clabel) 114 | } 115 | 116 | func (p *parser) popBreakCont() { 117 | p.popBreak() 118 | p.popCont() 119 | } 120 | 121 | func (p *parser) getBreakLabel() string { 122 | if p.breakCounter == 0 { 123 | return "" 124 | } 125 | return p.breaks[p.breakCounter-1] 126 | } 127 | 128 | func (p *parser) getContLabel() string { 129 | if p.contCounter == 0 { 130 | return "" 131 | } 132 | return p.continues[p.contCounter-1] 133 | } 134 | 135 | func (p *parser) nextLabel() string { 136 | p.lcounter += 1 137 | return fmt.Sprintf(".L%d", p.lcounter) 138 | } 139 | 140 | func (p *parser) addAnonymousString(s *String) { 141 | p.tu.AnonymousInits = append(p.tu.AnonymousInits, s) 142 | } 143 | 144 | func Parse(szdesc TargetSizeDesc, pp *cpp.Preprocessor) (tu *TranslationUnit, errRet error) { 145 | p := &parser{} 146 | p.szdesc = szdesc 147 | p.pp = pp 148 | p.types = newScope(nil) 149 | p.decls = newScope(nil) 150 | p.structs = newScope(nil) 151 | p.tu = &TranslationUnit{} 152 | 153 | defer func() { 154 | if e := recover(); e != nil { 155 | peb := e.(parseErrorBreakOut) // Will re-panic if not a breakout. 156 | errRet = peb.err 157 | } 158 | }() 159 | p.next() 160 | p.next() 161 | p.TUnit() 162 | return p.tu, nil 163 | } 164 | 165 | func (p *parser) errorPos(pos cpp.FilePos, m string, vals ...interface{}) { 166 | err := fmt.Errorf(m, vals...) 167 | if os.Getenv("CCDEBUG") == "true" { 168 | err = fmt.Errorf("%s\n%s", err, debug.Stack()) 169 | } 170 | err = cpp.ErrWithLoc(err, pos) 171 | panic(parseErrorBreakOut{err}) 172 | } 173 | 174 | func (p *parser) error(m string, vals ...interface{}) { 175 | err := fmt.Errorf(m, vals...) 176 | if os.Getenv("CCDEBUG") == "true" { 177 | err = fmt.Errorf("%s\n%s", err, debug.Stack()) 178 | } 179 | panic(parseErrorBreakOut{err}) 180 | } 181 | 182 | func (p *parser) expect(k cpp.TokenKind) { 183 | if p.curt.Kind != k { 184 | p.errorPos(p.curt.Pos, "expected %s got %s", k, p.curt.Kind) 185 | } 186 | p.next() 187 | } 188 | 189 | func (p *parser) next() { 190 | p.curt = p.nextt 191 | t, err := p.pp.Next() 192 | if err != nil { 193 | p.error(err.Error()) 194 | } 195 | p.nextt = t 196 | } 197 | 198 | func (p *parser) ensureScalar(n Expr) { 199 | if !IsScalarType(n.GetType()) { 200 | p.errorPos(n.GetPos(), "expected scalar type") 201 | } 202 | } 203 | 204 | func (p *parser) TUnit() { 205 | for p.curt.Kind != cpp.EOF { 206 | toplevel := p.Decl(true) 207 | p.tu.TopLevels = append(p.tu.TopLevels, toplevel) 208 | } 209 | } 210 | 211 | func (p *parser) isDeclStart(t *cpp.Token) bool { 212 | switch t.Kind { 213 | case cpp.IDENT: 214 | _, err := p.decls.lookup(t.Val) 215 | if err != nil { 216 | return true 217 | } 218 | case cpp.STATIC, cpp.VOLATILE, cpp.STRUCT, cpp.CHAR, cpp.INT, cpp.SHORT, cpp.LONG, 219 | cpp.UNSIGNED, cpp.SIGNED, cpp.FLOAT, cpp.DOUBLE: 220 | return true 221 | } 222 | return false 223 | } 224 | 225 | func (p *parser) Stmt() Node { 226 | if p.nextt.Kind == ':' && p.curt.Kind == cpp.IDENT { 227 | return p.parseLabeledStmt() 228 | } 229 | if p.isDeclStart(p.curt) { 230 | return p.Decl(false) 231 | } else { 232 | switch p.curt.Kind { 233 | case cpp.CASE: 234 | return p.Case() 235 | case cpp.DEFAULT: 236 | return p.Default() 237 | case cpp.GOTO: 238 | return p.parseGoto() 239 | case ';': 240 | pos := p.curt.Pos 241 | p.next() 242 | return &EmptyStmt{ 243 | Pos: pos, 244 | } 245 | case cpp.SWITCH: 246 | return p.Switch() 247 | case cpp.RETURN: 248 | return p.Return() 249 | case cpp.WHILE: 250 | return p.While() 251 | case cpp.DO: 252 | return p.DoWhile() 253 | case cpp.FOR: 254 | return p.For() 255 | case cpp.BREAK, cpp.CONTINUE: 256 | return p.BreakCont() 257 | case cpp.IF: 258 | return p.If() 259 | case '{': 260 | return p.Block() 261 | default: 262 | pos := p.curt.Pos 263 | expr := p.Expr() 264 | p.expect(';') 265 | return &ExprStmt{ 266 | Pos: pos, 267 | Expr: expr, 268 | } 269 | } 270 | } 271 | } 272 | 273 | func (p *parser) Switch() Node { 274 | sw := &Switch{} 275 | sw.Pos = p.curt.Pos 276 | sw.LAfter = p.nextLabel() 277 | p.expect(cpp.SWITCH) 278 | p.expect('(') 279 | expr := p.Expr() 280 | sw.Expr = expr 281 | if !IsIntType(expr.GetType()) { 282 | p.errorPos(expr.GetPos(), "switch expression expects an integral type") 283 | } 284 | p.expect(')') 285 | p.pushSwitch(sw) 286 | p.pushBreak(sw.LAfter) 287 | stmt := p.Stmt() 288 | sw.Stmt = stmt 289 | p.popBreak() 290 | p.popSwitch() 291 | return sw 292 | } 293 | 294 | func (p *parser) parseGoto() Node { 295 | pos := p.curt.Pos 296 | p.next() 297 | actualLabel := p.curt.Val 298 | p.expect(cpp.IDENT) 299 | p.expect(';') 300 | ret := &Goto{ 301 | Pos: pos, 302 | Label: "", // To be fixed later. 303 | } 304 | p.gotos = append(p.gotos, gotoFixup{ 305 | actualLabel, 306 | ret, 307 | }) 308 | return ret 309 | } 310 | 311 | func (p *parser) parseLabeledStmt() Node { 312 | pos := p.curt.Pos 313 | label := p.curt.Val 314 | anonlabel := p.nextLabel() 315 | _, ok := p.labels[label] 316 | if ok { 317 | p.errorPos(pos, "redefinition of label %s in function", label) 318 | } 319 | p.labels[label] = anonlabel 320 | p.expect(cpp.IDENT) 321 | p.expect(':') 322 | return &LabeledStmt{ 323 | Pos: pos, 324 | Label: label, 325 | AnonLabel: anonlabel, 326 | Stmt: p.Stmt(), 327 | } 328 | } 329 | 330 | func (p *parser) Case() Node { 331 | pos := p.curt.Pos 332 | p.expect(cpp.CASE) 333 | sw := p.getSwitch() 334 | if sw == nil { 335 | p.errorPos(pos, "'case' outside a switch statement") 336 | } 337 | expr := p.Expr() 338 | if !IsIntType(expr.GetType()) { 339 | p.errorPos(expr.GetPos(), "expected an integral type") 340 | } 341 | v, err := p.fold(expr) 342 | if err != nil { 343 | p.errorPos(expr.GetPos(), err.Error()) 344 | } 345 | p.expect(':') 346 | anonlabel := p.nextLabel() 347 | i := v.(*Constant) 348 | // XXX TODO 349 | swc := SwitchCase{ 350 | V: i.Val, 351 | Label: anonlabel, 352 | } 353 | sw.Cases = append(sw.Cases, swc) 354 | return &LabeledStmt{ 355 | Pos: pos, 356 | AnonLabel: anonlabel, 357 | Stmt: p.Stmt(), 358 | IsCase: true, 359 | } 360 | } 361 | 362 | func (p *parser) Default() Node { 363 | pos := p.curt.Pos 364 | p.expect(cpp.DEFAULT) 365 | sw := p.getSwitch() 366 | if sw == nil { 367 | p.errorPos(pos, "'default' outside a switch statement") 368 | } 369 | p.expect(':') 370 | if sw.LDefault != "" { 371 | p.errorPos(pos, "multiple default statements in switch") 372 | } 373 | anonlabel := p.nextLabel() 374 | sw.LDefault = anonlabel 375 | return &LabeledStmt{ 376 | Pos: pos, 377 | AnonLabel: anonlabel, 378 | Stmt: p.Stmt(), 379 | IsDefault: true, 380 | } 381 | } 382 | 383 | func (p *parser) BreakCont() Node { 384 | pos := p.curt.Pos 385 | label := "" 386 | isbreak := p.curt.Kind == cpp.BREAK 387 | iscont := p.curt.Kind == cpp.CONTINUE 388 | if isbreak { 389 | label = p.getBreakLabel() 390 | if label == "" { 391 | p.errorPos(pos, "break outside of loop/switch") 392 | } 393 | } 394 | if iscont { 395 | label = p.getContLabel() 396 | if label == "" { 397 | p.errorPos(pos, "continue outside of loop/switch") 398 | } 399 | } 400 | p.next() 401 | p.expect(';') 402 | return &Goto{ 403 | Pos: pos, 404 | IsBreak: isbreak, 405 | IsCont: iscont, 406 | Label: label, 407 | } 408 | } 409 | 410 | func (p *parser) Return() Node { 411 | pos := p.curt.Pos 412 | p.expect(cpp.RETURN) 413 | expr := p.Expr() 414 | p.expect(';') 415 | return &Return{ 416 | Pos: pos, 417 | Ret: expr, 418 | } 419 | } 420 | 421 | func (p *parser) If() Node { 422 | ifpos := p.curt.Pos 423 | lelse := p.nextLabel() 424 | p.expect(cpp.IF) 425 | p.expect('(') 426 | expr := p.Expr() 427 | p.ensureScalar(expr) 428 | p.expect(')') 429 | stmt := p.Stmt() 430 | var els Node 431 | if p.curt.Kind == cpp.ELSE { 432 | p.next() 433 | els = p.Stmt() 434 | } 435 | return &If{ 436 | Pos: ifpos, 437 | Cond: expr, 438 | Stmt: stmt, 439 | Else: els, 440 | LElse: lelse, 441 | } 442 | } 443 | 444 | func (p *parser) For() Node { 445 | pos := p.curt.Pos 446 | lstart := p.nextLabel() 447 | lend := p.nextLabel() 448 | var init, cond, step Expr 449 | p.expect(cpp.FOR) 450 | p.expect('(') 451 | if p.curt.Kind != ';' { 452 | init = p.Expr() 453 | } 454 | p.expect(';') 455 | if p.curt.Kind != ';' { 456 | cond = p.Expr() 457 | } 458 | p.expect(';') 459 | if p.curt.Kind != ')' { 460 | step = p.Expr() 461 | } 462 | p.expect(')') 463 | p.pushBreakCont(lend, lstart) 464 | body := p.Stmt() 465 | p.popBreakCont() 466 | return &For{ 467 | Pos: pos, 468 | Init: init, 469 | Cond: cond, 470 | Step: step, 471 | Body: body, 472 | LStart: lstart, 473 | LEnd: lend, 474 | } 475 | } 476 | 477 | func (p *parser) While() Node { 478 | pos := p.curt.Pos 479 | lstart := p.nextLabel() 480 | lend := p.nextLabel() 481 | p.expect(cpp.WHILE) 482 | p.expect('(') 483 | cond := p.Expr() 484 | p.ensureScalar(cond) 485 | p.expect(')') 486 | p.pushBreakCont(lend, lstart) 487 | body := p.Stmt() 488 | p.popBreakCont() 489 | return &While{ 490 | Pos: pos, 491 | Cond: cond, 492 | Body: body, 493 | LStart: lstart, 494 | LEnd: lend, 495 | } 496 | } 497 | 498 | func (p *parser) DoWhile() Node { 499 | pos := p.curt.Pos 500 | lstart := p.nextLabel() 501 | lcond := p.nextLabel() 502 | lend := p.nextLabel() 503 | p.expect(cpp.DO) 504 | p.pushBreakCont(lend, lcond) 505 | body := p.Stmt() 506 | p.popBreakCont() 507 | p.expect(cpp.WHILE) 508 | p.expect('(') 509 | cond := p.Expr() 510 | p.expect(')') 511 | p.expect(';') 512 | return &DoWhile{ 513 | Pos: pos, 514 | Body: body, 515 | Cond: cond, 516 | LStart: lstart, 517 | LCond: lcond, 518 | LEnd: lend, 519 | } 520 | } 521 | 522 | func (p *parser) Block() *Block { 523 | var stmts []Node 524 | pos := p.curt.Pos 525 | p.expect('{') 526 | for p.curt.Kind != '}' { 527 | stmts = append(stmts, p.Stmt()) 528 | } 529 | p.expect('}') 530 | return &Block{ 531 | Pos: pos, 532 | Body: stmts, 533 | } 534 | } 535 | 536 | func (p *parser) FuncBody(f *CFunc) { 537 | p.labels = make(map[string]string) 538 | p.gotos = nil 539 | for p.curt.Kind != '}' { 540 | stmt := p.Stmt() 541 | f.Body = append(f.Body, stmt) 542 | } 543 | for _, fixup := range p.gotos { 544 | anonlabel, ok := p.labels[fixup.actualLabel] 545 | if !ok { 546 | p.errorPos(fixup.g.GetPos(), "goto target %s is undefined", fixup.actualLabel) 547 | } 548 | fixup.g.Label = anonlabel 549 | } 550 | } 551 | 552 | func (p *parser) Decl(isGlobal bool) Node { 553 | firstDecl := true 554 | declPos := p.curt.Pos 555 | var name *cpp.Token 556 | declList := &DeclList{} 557 | sc, ty := p.DeclSpecs() 558 | declList.Storage = sc 559 | isTypedef := sc == SC_TYPEDEF 560 | 561 | if p.curt.Kind == ';' { 562 | p.next() 563 | return declList 564 | } 565 | 566 | for { 567 | name, ty = p.Declarator(ty, false) 568 | if name == nil { 569 | panic("internal error") 570 | } 571 | if firstDecl && isGlobal { 572 | // if declaring a function 573 | if p.curt.Kind == '{' { 574 | if isTypedef { 575 | p.errorPos(name.Pos, "cannot typedef a function") 576 | } 577 | fty, ok := ty.(*CFuncT) 578 | if !ok { 579 | p.errorPos(name.Pos, "expected a function") 580 | } 581 | err := p.decls.define(name.Val, &GSymbol{ 582 | Label: name.Val, 583 | Type: fty, 584 | }) 585 | if err != nil { 586 | p.errorPos(declPos, err.Error()) 587 | } 588 | p.pushScope() 589 | var psyms []*LSymbol 590 | 591 | for idx, name := range fty.ArgNames { 592 | sym := &LSymbol{ 593 | Type: fty.ArgTypes[idx], 594 | } 595 | psyms = append(psyms, sym) 596 | err := p.decls.define(name, sym) 597 | if err != nil { 598 | p.errorPos(declPos, "multiple params with name %s", name) 599 | } 600 | } 601 | f := &CFunc{ 602 | Name: name.Val, 603 | FuncType: fty, 604 | Pos: declPos, 605 | ParamSymbols: psyms, 606 | } 607 | p.expect('{') 608 | p.FuncBody(f) 609 | p.expect('}') 610 | p.popScope() 611 | return f 612 | } 613 | } 614 | var sym Symbol 615 | if isTypedef { 616 | sym = &TSymbol{ 617 | Type: ty, 618 | } 619 | } else if isGlobal { 620 | sym = &GSymbol{ 621 | Label: name.Val, 622 | Type: ty, 623 | } 624 | } else { 625 | sym = &LSymbol{ 626 | Type: ty, 627 | } 628 | } 629 | var err error 630 | if isTypedef { 631 | err = p.types.define(name.Val, sym) 632 | } else { 633 | err = p.decls.define(name.Val, sym) 634 | } 635 | if err != nil { 636 | p.errorPos(name.Pos, err.Error()) 637 | } 638 | declList.Symbols = append(declList.Symbols, sym) 639 | var init Expr 640 | var initPos cpp.FilePos 641 | if p.curt.Kind == '=' { 642 | p.next() 643 | initPos = p.curt.Pos 644 | if isTypedef { 645 | p.errorPos(initPos, "cannot initialize a typedef") 646 | } 647 | init = p.Initializer(ty, true) 648 | } 649 | declList.Inits = append(declList.Inits, init) 650 | if p.curt.Kind != ',' { 651 | break 652 | } 653 | p.next() 654 | firstDecl = false 655 | } 656 | if p.curt.Kind != ';' { 657 | p.errorPos(p.curt.Pos, "expected '=', ',' or ';'") 658 | } 659 | p.expect(';') 660 | return declList 661 | } 662 | 663 | func (p *parser) ParamDecl() (*cpp.Token, CType) { 664 | _, ty := p.DeclSpecs() 665 | return p.Declarator(ty, true) 666 | } 667 | 668 | func isStorageClass(k cpp.TokenKind) (bool, SClass) { 669 | switch k { 670 | case cpp.STATIC: 671 | return true, SC_STATIC 672 | case cpp.EXTERN: 673 | return true, SC_GLOBAL 674 | case cpp.TYPEDEF: 675 | return true, SC_TYPEDEF 676 | case cpp.REGISTER: 677 | return true, SC_REGISTER 678 | } 679 | return false, 0 680 | } 681 | 682 | type dSpec struct { 683 | signedcnt int 684 | unsignedcnt int 685 | charcnt int 686 | intcnt int 687 | shortcnt int 688 | longcnt int 689 | floatcnt int 690 | doublecnt int 691 | } 692 | 693 | type dSpecLutEnt struct { 694 | spec dSpec 695 | ty Primitive 696 | } 697 | 698 | var declSpecLut = [...]dSpecLutEnt{ 699 | {dSpec{ 700 | charcnt: 1, 701 | }, CChar}, 702 | {dSpec{ 703 | signedcnt: 1, 704 | charcnt: 1, 705 | }, CChar}, 706 | {dSpec{ 707 | unsignedcnt: 1, 708 | charcnt: 1, 709 | }, CUChar}, 710 | {dSpec{ 711 | shortcnt: 1, 712 | }, CShort}, 713 | {dSpec{ 714 | signedcnt: 1, 715 | shortcnt: 1, 716 | }, CShort}, 717 | {dSpec{ 718 | intcnt: 1, 719 | shortcnt: 1, 720 | }, CShort}, 721 | {dSpec{ 722 | signedcnt: 1, 723 | intcnt: 1, 724 | shortcnt: 1, 725 | }, CShort}, 726 | {dSpec{ 727 | signedcnt: 1, 728 | intcnt: 1, 729 | shortcnt: 1, 730 | }, CShort}, 731 | {dSpec{ 732 | unsignedcnt: 1, 733 | intcnt: 1, 734 | shortcnt: 1, 735 | }, CUShort}, 736 | {dSpec{ 737 | unsignedcnt: 1, 738 | shortcnt: 1, 739 | }, CUShort}, 740 | {dSpec{ 741 | intcnt: 1, 742 | }, CInt}, 743 | {dSpec{ 744 | intcnt: 1, 745 | }, CInt}, 746 | {dSpec{ 747 | signedcnt: 1, 748 | }, CInt}, 749 | {dSpec{ 750 | signedcnt: 1, 751 | intcnt: 1, 752 | }, CInt}, 753 | {dSpec{ 754 | unsignedcnt: 1, 755 | }, CUInt}, 756 | {dSpec{ 757 | unsignedcnt: 1, 758 | intcnt: 1, 759 | }, CUInt}, 760 | {dSpec{ 761 | longcnt: 1, 762 | }, CLong}, 763 | {dSpec{ 764 | signedcnt: 1, 765 | longcnt: 1, 766 | }, CLong}, 767 | {dSpec{ 768 | longcnt: 1, 769 | intcnt: 1, 770 | }, CLong}, 771 | {dSpec{ 772 | signedcnt: 1, 773 | longcnt: 1, 774 | intcnt: 1, 775 | }, CLong}, 776 | {dSpec{ 777 | unsignedcnt: 1, 778 | longcnt: 1, 779 | }, CULong}, 780 | {dSpec{ 781 | unsignedcnt: 1, 782 | longcnt: 1, 783 | intcnt: 1, 784 | }, CLong}, 785 | {dSpec{ 786 | longcnt: 2, 787 | }, CLLong}, 788 | {dSpec{ 789 | signedcnt: 1, 790 | longcnt: 2, 791 | }, CLLong}, 792 | {dSpec{ 793 | intcnt: 1, 794 | longcnt: 2, 795 | }, CLLong}, 796 | {dSpec{ 797 | intcnt: 1, 798 | signedcnt: 1, 799 | longcnt: 2, 800 | }, CLLong}, 801 | {dSpec{ 802 | unsignedcnt: 1, 803 | longcnt: 2, 804 | }, CULLong}, 805 | {dSpec{ 806 | intcnt: 1, 807 | unsignedcnt: 1, 808 | longcnt: 2, 809 | }, CULLong}, 810 | {dSpec{ 811 | floatcnt: 1, 812 | }, CFloat}, 813 | {dSpec{ 814 | doublecnt: 1, 815 | }, CDouble}, 816 | } 817 | 818 | func (p *parser) DeclSpecs() (SClass, CType) { 819 | dspecpos := p.curt.Pos 820 | scassigned := false 821 | sc := SC_AUTO 822 | var ty CType = CInt 823 | var spec dSpec 824 | nullspec := dSpec{} 825 | loop: 826 | for { 827 | pos := p.curt.Pos 828 | issc, sclass := isStorageClass(p.curt.Kind) 829 | if issc { 830 | if scassigned { 831 | p.errorPos(pos, "only one storage class specifier allowed") 832 | } 833 | scassigned = true 834 | sc = sclass 835 | p.next() 836 | continue 837 | } 838 | switch p.curt.Kind { 839 | case cpp.VOID: 840 | p.next() 841 | case cpp.CHAR: 842 | spec.charcnt += 1 843 | p.next() 844 | case cpp.SHORT: 845 | spec.shortcnt += 1 846 | p.next() 847 | case cpp.INT: 848 | spec.intcnt += 1 849 | p.next() 850 | case cpp.LONG: 851 | spec.longcnt += 1 852 | p.next() 853 | case cpp.FLOAT: 854 | spec.floatcnt += 1 855 | p.next() 856 | case cpp.DOUBLE: 857 | spec.doublecnt += 1 858 | p.next() 859 | case cpp.SIGNED: 860 | spec.signedcnt += 1 861 | p.next() 862 | case cpp.UNSIGNED: 863 | spec.unsignedcnt += 1 864 | p.next() 865 | case cpp.IDENT: 866 | t := p.curt 867 | sym, err := p.types.lookup(t.Val) 868 | if err != nil { 869 | break loop 870 | } 871 | tsym := sym.(*TSymbol) 872 | p.next() 873 | if spec != nullspec { 874 | p.error("TODO...") 875 | } 876 | return sc, tsym.Type 877 | case cpp.STRUCT: 878 | if spec != nullspec { 879 | p.error("TODO...") 880 | } 881 | ty = p.Struct() 882 | return sc, ty 883 | case cpp.UNION: 884 | case cpp.VOLATILE, cpp.CONST: 885 | p.next() 886 | default: 887 | break loop 888 | } 889 | } 890 | 891 | // If we got any type specifiers, look up 892 | // the correct type. 893 | if spec != nullspec { 894 | match := false 895 | for _, te := range declSpecLut { 896 | if te.spec == spec { 897 | ty = te.ty 898 | match = true 899 | break 900 | } 901 | } 902 | if !match { 903 | p.errorPos(dspecpos, "invalid type") 904 | } 905 | } 906 | return sc, ty 907 | } 908 | 909 | // Declarator 910 | // ---------- 911 | // 912 | // A declarator is the part of a Decl that specifies 913 | // the name that is to be introduced into the program. 914 | // 915 | // unsigned int a, *b, **c, *const*d *volatile*e ; 916 | // ^ ^^ ^^^ ^^^^^^^^ ^^^^^^^^^^^ 917 | // 918 | // Direct Declarator 919 | // ----------------- 920 | // 921 | // A direct declarator is missing the pointer prefix. 922 | // 923 | // e.g. 924 | // unsigned int *a[32], b[]; 925 | // ^^^^^ ^^^ 926 | // 927 | // Abstract Declarator 928 | // ------------------- 929 | // 930 | // A delcarator missing an identifier. 931 | 932 | func (p *parser) Declarator(basety CType, abstract bool) (*cpp.Token, CType) { 933 | for p.curt.Kind == cpp.CONST || p.curt.Kind == cpp.VOLATILE { 934 | p.next() 935 | } 936 | switch p.curt.Kind { 937 | case '*': 938 | p.next() 939 | name, ty := p.Declarator(basety, abstract) 940 | return name, &Ptr{ty} 941 | case '(': 942 | forward := &ForwardedType{} 943 | p.next() 944 | name, ty := p.Declarator(forward, abstract) 945 | p.expect(')') 946 | forward.Type = p.DeclaratorTail(basety) 947 | return name, ty 948 | case cpp.IDENT: 949 | name := p.curt 950 | p.next() 951 | return name, p.DeclaratorTail(basety) 952 | default: 953 | if abstract { 954 | return nil, p.DeclaratorTail(basety) 955 | } 956 | p.errorPos(p.curt.Pos, "expected ident, '(' or '*' but got %s", p.curt.Kind) 957 | } 958 | panic("unreachable") 959 | } 960 | 961 | func (p *parser) DeclaratorTail(basety CType) CType { 962 | ret := basety 963 | for { 964 | switch p.curt.Kind { 965 | case '[': 966 | p.next() 967 | var dimn Expr 968 | if p.curt.Kind != ']' { 969 | dimn = p.AssignmentExpr() 970 | } 971 | p.expect(']') 972 | dim, err := p.fold(dimn) 973 | if err != nil { 974 | p.errorPos(dimn.GetPos(), "invalid constant Expr for array dimensions") 975 | } 976 | i, ok := dim.(*Constant) 977 | if !ok || !IsIntType(i.Type) { 978 | p.errorPos(dimn.GetPos(), "Expected an int type for array length") 979 | } 980 | ret = &Array{ 981 | Dim: int(i.Val), 982 | MemberType: ret, 983 | } 984 | case '(': 985 | fret := &CFuncT{} 986 | fret.RetType = basety 987 | p.next() 988 | if p.curt.Kind != ')' { 989 | for { 990 | pnametok, pty := p.ParamDecl() 991 | pname := "" 992 | if pnametok != nil { 993 | pname = pnametok.Val 994 | } 995 | fret.ArgTypes = append(fret.ArgTypes, pty) 996 | fret.ArgNames = append(fret.ArgNames, pname) 997 | if p.curt.Kind == ',' { 998 | p.next() 999 | continue 1000 | } 1001 | break 1002 | } 1003 | } 1004 | p.expect(')') 1005 | ret = fret 1006 | default: 1007 | return ret 1008 | } 1009 | } 1010 | } 1011 | 1012 | func (p *parser) Initializer(ty CType, constant bool) Expr { 1013 | _ = p.curt.Pos 1014 | if IsScalarType(ty) { 1015 | var init Expr 1016 | if p.curt.Kind == '{' { 1017 | p.expect('{') 1018 | init = p.AssignmentExpr() 1019 | p.expect('}') 1020 | } else { 1021 | init = p.AssignmentExpr() 1022 | } 1023 | // XXX ensure types are compatible. 1024 | // XXX Add cast. 1025 | if constant { 1026 | c, err := p.fold(init) 1027 | if err != nil { 1028 | p.errorPos(init.GetPos(), err.Error()) 1029 | } 1030 | return c 1031 | } else { 1032 | return init 1033 | } 1034 | } /* else if IsCharArr(ty) { 1035 | switch p.curt.Kind { 1036 | case cpp.STRING: 1037 | p.expect(cpp.STRING) 1038 | case '{': 1039 | p.expect('{') 1040 | p.expect(cpp.STRING) 1041 | p.expect('}') 1042 | default: 1043 | } 1044 | } else if IsArrType(ty) { 1045 | arr := ty.(*Array) 1046 | p.expect('{') 1047 | var inits []Node 1048 | for p.curt.Kind != '}' { 1049 | inits = append(inits, p.parseInitializer(arr.MemberType, true)) 1050 | if p.curt.Kind == ',' { 1051 | continue 1052 | } 1053 | } 1054 | p.expect('}') 1055 | } 1056 | */ 1057 | panic("unimplemented") 1058 | } 1059 | 1060 | func isAssignmentOperator(k cpp.TokenKind) bool { 1061 | switch k { 1062 | case '=', cpp.ADD_ASSIGN, cpp.SUB_ASSIGN, cpp.MUL_ASSIGN, cpp.QUO_ASSIGN, cpp.REM_ASSIGN, 1063 | cpp.AND_ASSIGN, cpp.OR_ASSIGN, cpp.XOR_ASSIGN, cpp.SHL_ASSIGN, cpp.SHR_ASSIGN: 1064 | return true 1065 | } 1066 | return false 1067 | } 1068 | 1069 | func (p *parser) Expr() Expr { 1070 | var ret Expr 1071 | for { 1072 | ret = p.AssignmentExpr() 1073 | if p.curt.Kind != ',' { 1074 | break 1075 | } 1076 | p.next() 1077 | } 1078 | return ret 1079 | } 1080 | 1081 | func (p *parser) AssignmentExpr() Expr { 1082 | l := p.CondExpr() 1083 | if isAssignmentOperator(p.curt.Kind) { 1084 | pos := p.curt.Pos 1085 | op := p.curt.Kind 1086 | p.next() 1087 | r := p.AssignmentExpr() 1088 | l = &Binop{ 1089 | Pos: pos, 1090 | Op: op, 1091 | L: l, 1092 | R: r, 1093 | Type: CInt, 1094 | } 1095 | } 1096 | return l 1097 | } 1098 | 1099 | // Aka Ternary operator. 1100 | func (p *parser) CondExpr() Expr { 1101 | return p.LogOrExpr() 1102 | } 1103 | 1104 | func (p *parser) LogOrExpr() Expr { 1105 | l := p.LogAndExpr() 1106 | for p.curt.Kind == cpp.LOR { 1107 | pos := p.curt.Pos 1108 | op := p.curt.Kind 1109 | p.next() 1110 | r := p.LogAndExpr() 1111 | l = &Binop{ 1112 | Pos: pos, 1113 | Op: op, 1114 | L: l, 1115 | R: r, 1116 | Type: CInt, 1117 | } 1118 | } 1119 | return l 1120 | } 1121 | 1122 | func (p *parser) LogAndExpr() Expr { 1123 | l := p.OrExpr() 1124 | for p.curt.Kind == cpp.LAND { 1125 | pos := p.curt.Pos 1126 | op := p.curt.Kind 1127 | p.next() 1128 | r := p.OrExpr() 1129 | l = &Binop{ 1130 | Pos: pos, 1131 | Op: op, 1132 | L: l, 1133 | R: r, 1134 | Type: CInt, 1135 | } 1136 | } 1137 | return l 1138 | } 1139 | 1140 | func (p *parser) OrExpr() Expr { 1141 | l := p.XorExpr() 1142 | for p.curt.Kind == '|' { 1143 | pos := p.curt.Pos 1144 | op := p.curt.Kind 1145 | p.next() 1146 | r := p.XorExpr() 1147 | l = &Binop{ 1148 | Pos: pos, 1149 | Op: op, 1150 | L: l, 1151 | R: r, 1152 | Type: CInt, 1153 | } 1154 | } 1155 | return l 1156 | } 1157 | 1158 | func (p *parser) XorExpr() Expr { 1159 | l := p.AndExpr() 1160 | for p.curt.Kind == '^' { 1161 | pos := p.curt.Pos 1162 | op := p.curt.Kind 1163 | p.next() 1164 | r := p.AndExpr() 1165 | l = &Binop{ 1166 | Pos: pos, 1167 | Op: op, 1168 | L: l, 1169 | R: r, 1170 | Type: CInt, 1171 | } 1172 | } 1173 | return l 1174 | } 1175 | 1176 | func (p *parser) AndExpr() Expr { 1177 | l := p.EqlExpr() 1178 | for p.curt.Kind == '&' { 1179 | pos := p.curt.Pos 1180 | op := p.curt.Kind 1181 | p.next() 1182 | r := p.EqlExpr() 1183 | l = &Binop{ 1184 | Pos: pos, 1185 | Op: op, 1186 | L: l, 1187 | R: r, 1188 | Type: CInt, 1189 | } 1190 | } 1191 | return l 1192 | } 1193 | 1194 | func (p *parser) EqlExpr() Expr { 1195 | l := p.RelExpr() 1196 | for p.curt.Kind == cpp.EQL || p.curt.Kind == cpp.NEQ { 1197 | pos := p.curt.Pos 1198 | op := p.curt.Kind 1199 | p.next() 1200 | r := p.RelExpr() 1201 | l = &Binop{ 1202 | Pos: pos, 1203 | Op: op, 1204 | L: l, 1205 | R: r, 1206 | Type: CInt, 1207 | } 1208 | } 1209 | return l 1210 | } 1211 | 1212 | func (p *parser) RelExpr() Expr { 1213 | l := p.ShiftExpr() 1214 | for p.curt.Kind == '>' || p.curt.Kind == '<' || p.curt.Kind == cpp.LEQ || p.curt.Kind == cpp.GEQ { 1215 | pos := p.curt.Pos 1216 | op := p.curt.Kind 1217 | p.next() 1218 | r := p.ShiftExpr() 1219 | l = &Binop{ 1220 | Pos: pos, 1221 | Op: op, 1222 | L: l, 1223 | R: r, 1224 | Type: CInt, 1225 | } 1226 | } 1227 | return l 1228 | } 1229 | 1230 | func (p *parser) ShiftExpr() Expr { 1231 | l := p.AddExpr() 1232 | for p.curt.Kind == cpp.SHL || p.curt.Kind == cpp.SHR { 1233 | pos := p.curt.Pos 1234 | op := p.curt.Kind 1235 | p.next() 1236 | r := p.AddExpr() 1237 | l = &Binop{ 1238 | Pos: pos, 1239 | Op: op, 1240 | L: l, 1241 | R: r, 1242 | Type: CInt, 1243 | } 1244 | } 1245 | return l 1246 | } 1247 | 1248 | func (p *parser) AddExpr() Expr { 1249 | l := p.MulExpr() 1250 | for p.curt.Kind == '+' || p.curt.Kind == '-' { 1251 | pos := p.curt.Pos 1252 | op := p.curt.Kind 1253 | p.next() 1254 | r := p.MulExpr() 1255 | l = &Binop{ 1256 | Pos: pos, 1257 | Op: op, 1258 | L: l, 1259 | R: r, 1260 | Type: CInt, 1261 | } 1262 | } 1263 | return l 1264 | } 1265 | 1266 | func (p *parser) MulExpr() Expr { 1267 | l := p.CastExpr() 1268 | for p.curt.Kind == '*' || p.curt.Kind == '/' || p.curt.Kind == '%' { 1269 | pos := p.curt.Pos 1270 | op := p.curt.Kind 1271 | p.next() 1272 | r := p.CastExpr() 1273 | l = &Binop{ 1274 | Pos: pos, 1275 | Op: op, 1276 | L: l, 1277 | R: r, 1278 | Type: CInt, 1279 | } 1280 | } 1281 | return l 1282 | } 1283 | 1284 | func (p *parser) CastExpr() Expr { 1285 | // Cast 1286 | if p.curt.Kind == '(' { 1287 | if p.isDeclStart(p.nextt) { 1288 | pos := p.curt.Pos 1289 | p.expect('(') 1290 | ty := p.TypeName() 1291 | p.expect(')') 1292 | operand := p.UnaryExpr() 1293 | return &Cast{ 1294 | Pos: pos, 1295 | Operand: operand, 1296 | Type: ty, 1297 | } 1298 | } 1299 | } 1300 | return p.UnaryExpr() 1301 | } 1302 | 1303 | func (p *parser) TypeName() CType { 1304 | _, ty := p.DeclSpecs() 1305 | _, ty = p.Declarator(ty, true) 1306 | return ty 1307 | } 1308 | 1309 | func (p *parser) UnaryExpr() Expr { 1310 | switch p.curt.Kind { 1311 | case cpp.INC, cpp.DEC: 1312 | p.next() 1313 | p.UnaryExpr() 1314 | case '*', '+', '-', '!', '~', '&': 1315 | pos := p.curt.Pos 1316 | op := p.curt.Kind 1317 | p.next() 1318 | operand := p.CastExpr() 1319 | ty := operand.GetType() 1320 | if op == '&' { 1321 | ty = &Ptr{ 1322 | PointsTo: ty, 1323 | } 1324 | } else if op == '*' { 1325 | ptr, ok := ty.(*Ptr) 1326 | if !ok { 1327 | p.errorPos(pos, "dereferencing requires a pointer type") 1328 | } 1329 | ty = ptr.PointsTo 1330 | } 1331 | return &Unop{ 1332 | Pos: pos, 1333 | Op: op, 1334 | Operand: operand, 1335 | Type: ty, 1336 | } 1337 | default: 1338 | return p.PostExpr() 1339 | } 1340 | panic("unreachable") 1341 | } 1342 | 1343 | func (p *parser) PostExpr() Expr { 1344 | l := p.PrimaryExpr() 1345 | loop: 1346 | for { 1347 | switch p.curt.Kind { 1348 | case '[': 1349 | var ty CType 1350 | arr, isArr := l.GetType().(*Array) 1351 | ptr, isPtr := l.GetType().(*Ptr) 1352 | if !isArr && !isPtr { 1353 | p.errorPos(p.curt.Pos, "Can only index into array or pointer types") 1354 | } 1355 | if isArr { 1356 | ty = arr.MemberType 1357 | } 1358 | if isPtr { 1359 | ty = ptr.PointsTo 1360 | } 1361 | p.next() 1362 | idx := p.Expr() 1363 | p.expect(']') 1364 | l = &Index{ 1365 | Arr: l, 1366 | Idx: idx, 1367 | Type: ty, 1368 | } 1369 | case '.': 1370 | pos := p.curt.Pos 1371 | strct, isStruct := l.GetType().(*CStruct) 1372 | p.next() 1373 | if !isStruct { 1374 | p.errorPos(l.GetPos(), "expected a struct") 1375 | } 1376 | sel := p.curt 1377 | p.expect(cpp.IDENT) 1378 | ty := strct.FieldType(sel.Val) 1379 | if ty == nil { 1380 | p.errorPos(pos, "struct does not have field %s", sel.Val) 1381 | } 1382 | l = &Selector{ 1383 | Op: '.', 1384 | Pos: pos, 1385 | Operand: l, 1386 | Type: ty, 1387 | Sel: sel.Val, 1388 | } 1389 | case cpp.ARROW: 1390 | pos := p.curt.Pos 1391 | p.next() 1392 | pty, isPtr := l.GetType().(*Ptr) 1393 | if !isPtr { 1394 | p.errorPos(l.GetPos(), "expected a pointer") 1395 | } 1396 | sty, isPStruct := pty.PointsTo.(*CStruct) 1397 | if !isPStruct { 1398 | p.errorPos(l.GetPos(), "expected a pointer to a struct") 1399 | } 1400 | sel := p.curt 1401 | p.expect(cpp.IDENT) 1402 | ty := sty.FieldType(sel.Val) 1403 | if ty == nil { 1404 | p.errorPos(pos, "struct does not have field %s", sel.Val) 1405 | } 1406 | l = &Selector{ 1407 | Op: cpp.ARROW, 1408 | Pos: pos, 1409 | Operand: l, 1410 | Type: ty, 1411 | Sel: sel.Val, 1412 | } 1413 | case '(': 1414 | parenpos := p.curt.Pos 1415 | var fty *CFuncT 1416 | switch ty := l.GetType().(type) { 1417 | case *Ptr: 1418 | functy, ok := ty.PointsTo.(*CFuncT) 1419 | if !ok { 1420 | p.errorPos(l.GetPos(), "expected a function pointer") 1421 | } 1422 | fty = functy 1423 | case *CFuncT: 1424 | fty = ty 1425 | default: 1426 | p.errorPos(l.GetPos(), "expected a func or func pointer") 1427 | } 1428 | var args []Expr 1429 | p.next() 1430 | if p.curt.Kind != ')' { 1431 | for { 1432 | args = append(args, p.AssignmentExpr()) 1433 | if p.curt.Kind == ',' { 1434 | p.next() 1435 | continue 1436 | } 1437 | break 1438 | } 1439 | } 1440 | p.expect(')') 1441 | return &Call{ 1442 | Pos: parenpos, 1443 | FuncLike: l, 1444 | Args: args, 1445 | Type: fty.RetType, 1446 | } 1447 | case cpp.INC: 1448 | p.next() 1449 | case cpp.DEC: 1450 | p.next() 1451 | default: 1452 | break loop 1453 | } 1454 | } 1455 | return l 1456 | } 1457 | 1458 | func constantToExpr(t *cpp.Token) (Expr, error) { 1459 | switch t.Kind { 1460 | case cpp.INT_CONSTANT: 1461 | v, err := strconv.ParseInt(t.Val, 0, 64) 1462 | return &Constant{ 1463 | Val: v, 1464 | Pos: t.Pos, 1465 | Type: CInt, 1466 | }, err 1467 | default: 1468 | return nil, fmt.Errorf("internal error - %s", t.Kind) 1469 | } 1470 | } 1471 | 1472 | func (p *parser) PrimaryExpr() Expr { 1473 | switch p.curt.Kind { 1474 | case cpp.IDENT: 1475 | sym, err := p.decls.lookup(p.curt.Val) 1476 | if err != nil { 1477 | p.errorPos(p.curt.Pos, "undefined symbol %s", p.curt.Val) 1478 | } 1479 | p.next() 1480 | return &Ident{ 1481 | Sym: sym, 1482 | } 1483 | case cpp.INT_CONSTANT: 1484 | t := p.curt 1485 | p.next() 1486 | n, err := constantToExpr(t) 1487 | if err != nil { 1488 | p.errorPos(t.Pos, err.Error()) 1489 | } 1490 | return n 1491 | case cpp.CHAR_CONSTANT: 1492 | p.next() 1493 | case cpp.STRING: 1494 | s := p.curt 1495 | p.next() 1496 | l := p.nextLabel() 1497 | rstr := &String{ 1498 | Pos: s.Pos, 1499 | Val: s.Val, 1500 | Label: l, 1501 | } 1502 | p.addAnonymousString(rstr) 1503 | return rstr 1504 | case '(': 1505 | p.next() 1506 | expr := p.Expr() 1507 | p.expect(')') 1508 | return expr 1509 | default: 1510 | p.errorPos(p.curt.Pos, "expected an identifier, constant, string or Expr") 1511 | } 1512 | panic("unreachable") 1513 | } 1514 | 1515 | func (p *parser) Struct() CType { 1516 | p.expect(cpp.STRUCT) 1517 | var ret *CStruct 1518 | sname := "" 1519 | npos := p.curt.Pos 1520 | if p.curt.Kind == cpp.IDENT { 1521 | sname = p.curt.Val 1522 | p.next() 1523 | sym, err := p.structs.lookup(sname) 1524 | if err != nil && p.curt.Kind != '{' { 1525 | p.errorPos(npos, err.Error()) 1526 | } 1527 | if err == nil { 1528 | ret = sym.(*TSymbol).Type.(*CStruct) 1529 | } 1530 | } 1531 | if p.curt.Kind == '{' { 1532 | p.expect('{') 1533 | ret = &CStruct{} 1534 | for { 1535 | if p.curt.Kind == '}' { 1536 | break 1537 | } 1538 | _, basety := p.DeclSpecs() 1539 | for { 1540 | name, ty := p.Declarator(basety, false) 1541 | ret.Names = append(ret.Names, name.Val) 1542 | ret.Types = append(ret.Types, ty) 1543 | if p.curt.Kind == ',' { 1544 | p.next() 1545 | continue 1546 | } 1547 | break 1548 | } 1549 | p.expect(';') 1550 | } 1551 | p.expect('}') 1552 | if sname != "" { 1553 | // TODO: 1554 | // If ret is nil, is this a predefine? 1555 | // Do we need an incomplete type? 1556 | err := p.structs.define(sname, &TSymbol{ 1557 | Type: ret, 1558 | }) 1559 | if err != nil { 1560 | p.errorPos(npos, err.Error()) 1561 | } 1562 | } 1563 | } 1564 | return ret 1565 | } 1566 | -------------------------------------------------------------------------------- /parse/scope.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import "fmt" 4 | 5 | type scope struct { 6 | parent *scope 7 | kv map[string]Symbol 8 | } 9 | 10 | func (s *scope) lookup(k string) (Symbol, error) { 11 | sym, ok := s.kv[k] 12 | if ok { 13 | return sym, nil 14 | } 15 | if s.parent != nil { 16 | return s.parent.lookup(k) 17 | } 18 | return nil, fmt.Errorf("%s is not defined", k) 19 | } 20 | 21 | func (s *scope) define(k string, v Symbol) error { 22 | _, ok := s.kv[k] 23 | if ok { 24 | return fmt.Errorf("redefinition of %s", k) 25 | } 26 | s.kv[k] = v 27 | return nil 28 | } 29 | 30 | func (s *scope) String() string { 31 | str := "" 32 | if s.parent != nil { 33 | str += s.parent.String() + "\n" 34 | } 35 | str += fmt.Sprintf("%v", s.kv) 36 | return str 37 | } 38 | 39 | func newScope(parent *scope) *scope { 40 | ret := &scope{} 41 | ret.parent = parent 42 | ret.kv = make(map[string]Symbol) 43 | return ret 44 | } 45 | 46 | type Symbol interface{} 47 | 48 | type GSymbol struct { 49 | Label string 50 | Type CType 51 | } 52 | 53 | type LSymbol struct { 54 | Type CType 55 | } 56 | 57 | type TSymbol struct { 58 | Type CType 59 | } 60 | -------------------------------------------------------------------------------- /report/report.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/andrewchambers/cc/cpp" 7 | "os" 8 | ) 9 | 10 | func ReportError(err error) { 11 | if err == nil { 12 | return 13 | } 14 | fmt.Fprintln(os.Stderr, err) 15 | fmt.Fprintln(os.Stderr, "") 16 | errLoc, ok := err.(cpp.ErrorLoc) 17 | if !ok { 18 | return 19 | } 20 | pos := errLoc.Pos 21 | f, err := os.Open(pos.File) 22 | if err != nil { 23 | return 24 | } 25 | b := bufio.NewReader(f) 26 | lineno := 1 27 | for { 28 | done := false 29 | line, err := b.ReadString('\n') 30 | if err != nil { 31 | done = true 32 | } 33 | if lineno == pos.Line /* || lineno == pos.Line - 1 || lineno == pos.Line + 1 */ { 34 | for _, v := range line { 35 | switch v { 36 | case '\t': 37 | fmt.Fprintf(os.Stderr, " ") 38 | default: 39 | fmt.Fprintf(os.Stderr, "%c", v) 40 | } 41 | } 42 | } 43 | if lineno == pos.Line { 44 | linelen := 0 45 | for _, v := range line { 46 | switch v { 47 | case '\t': 48 | linelen += 4 49 | case '\n': 50 | // nothing. 51 | default: 52 | linelen += 1 53 | } 54 | } 55 | for i := 0; i < linelen; i++ { 56 | if i+1 == pos.Col { 57 | fmt.Fprintf(os.Stderr, "%c", '^') 58 | } else { 59 | fmt.Fprintf(os.Stderr, "%c", ' ') 60 | } 61 | } 62 | fmt.Fprintln(os.Stderr, "") 63 | } 64 | lineno += 1 65 | if done { 66 | break 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | go test ./... 3 | go install github.com/andrewchambers/cc/cmd/x64cc 4 | go run test/runner.go 5 | -------------------------------------------------------------------------------- /test/runner.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "regexp" 12 | "strings" 13 | "text/template" 14 | "time" 15 | ) 16 | 17 | var ( 18 | filter = flag.String("filter", ".*", "A regex filtering which tests to run") 19 | cfg = Config{ 20 | CompileCmd: "x64cc -o {{.Out}} {{.In}}", 21 | AssembleCmd: "as {{.In}} -o {{.Out}}", 22 | LinkCmd: "gcc {{.In}} -o {{.Out}}", 23 | } 24 | ) 25 | 26 | type Config struct { 27 | CompileCmd string 28 | AssembleCmd string 29 | LinkCmd string 30 | } 31 | 32 | func (c Config) Compile(in, out string) error { 33 | return RunWithInOutTemplate(in, out, c.CompileCmd, 5*time.Second) 34 | } 35 | 36 | func (c Config) Assemble(in, out string) error { 37 | return RunWithInOutTemplate(in, out, c.AssembleCmd, 5*time.Second) 38 | } 39 | 40 | func (c Config) Link(in, out string) error { 41 | return RunWithInOutTemplate(in, out, c.LinkCmd, 5*time.Second) 42 | } 43 | 44 | func RunWithInOutTemplate(in, out, templ string, timeout time.Duration) error { 45 | data := struct{ In, Out string }{In: in, Out: out} 46 | t := template.New("gencmdline") 47 | t, err := t.Parse(templ) 48 | if err != nil { 49 | return err 50 | } 51 | var b bytes.Buffer 52 | err = t.Execute(&b, data) 53 | if err != nil { 54 | return err 55 | } 56 | cmdline := b.String() 57 | return RunWithTimeout(cmdline, timeout) 58 | } 59 | 60 | // True on success, else fail. 61 | func RunWithTimeout(command string, timeout time.Duration) error { 62 | args := strings.Split(command, " ") 63 | if len(args) == 0 { 64 | return fmt.Errorf("malformed command %s", command) 65 | } 66 | bin := args[0] 67 | args = args[1:] 68 | c := exec.Command(bin, args...) 69 | rc := make(chan error) 70 | go func() { 71 | err := c.Run() 72 | rc <- err 73 | }() 74 | t := time.NewTicker(timeout) 75 | defer t.Stop() 76 | select { 77 | case <-t.C: 78 | return fmt.Errorf("%s timed out", bin) 79 | case err := <-rc: 80 | return err 81 | } 82 | } 83 | 84 | // Tests which are expected to run and return an error code true or false. 85 | func ExecuteTests(tdir string) error { 86 | fmt.Println("execute tests in", tdir) 87 | passcount := 0 88 | runcount := 0 89 | tests, err := ioutil.ReadDir(tdir) 90 | if err != nil { 91 | panic(err) 92 | } 93 | for _, t := range tests { 94 | if !strings.HasSuffix(t.Name(), ".c") { 95 | continue 96 | } 97 | tc := filepath.Join(tdir, t.Name()) 98 | m, err := regexp.MatchString(*filter, tc) 99 | if err != nil { 100 | panic(err) 101 | } 102 | if !m { 103 | continue 104 | } 105 | sname := tc + ".s" 106 | oname := tc + ".o" 107 | bname := tc + ".bin" 108 | runcount += 1 109 | err = cfg.Compile(tc, sname) 110 | if err != nil { 111 | fmt.Printf("FAIL: %s compile - %s\n", tc, err) 112 | continue 113 | } 114 | err = cfg.Assemble(sname, oname) 115 | if err != nil { 116 | fmt.Printf("FAIL: %s assemble - %s\n", tc, err) 117 | continue 118 | } 119 | err = cfg.Link(oname, bname) 120 | if err != nil { 121 | fmt.Printf("FAIL: %s link - %s\n", tc, err) 122 | continue 123 | } 124 | err = RunWithTimeout(bname, 5*time.Second) 125 | if err != nil { 126 | fmt.Printf("FAIL: %s execute - %s\n", tc, err) 127 | continue 128 | } 129 | fmt.Printf("PASS: %s\n", tc) 130 | passcount += 1 131 | } 132 | if passcount != runcount { 133 | return fmt.Errorf("passed %d/%d", passcount, runcount) 134 | } 135 | return nil 136 | } 137 | 138 | func main() { 139 | flag.Parse() 140 | pass := true 141 | for _, tdir := range []string{"test/testcases/execute", "test/testcases/bugs"} { 142 | err := ExecuteTests(tdir) 143 | if err != nil { 144 | fmt.Printf("%s FAIL: %s\n", tdir, err) 145 | pass = false 146 | } 147 | } 148 | if !pass { 149 | os.Exit(1) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/testcases/bugs/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.s 3 | *.bin 4 | -------------------------------------------------------------------------------- /test/testcases/bugs/0007.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int x; 3 | x = -1; 4 | return x > 2; 5 | } 6 | -------------------------------------------------------------------------------- /test/testcases/execute/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.s 3 | *.bin 4 | -------------------------------------------------------------------------------- /test/testcases/execute/0001-sanity.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/testcases/execute/0002-operators.c: -------------------------------------------------------------------------------- 1 | 2 | int x; 3 | 4 | // TODO: 5 | // Should we generate test cases for these? 6 | // Should we do table based testing? 7 | // Test prec. 8 | // Test all types. 9 | // Test short circuits. 10 | 11 | int main() { 12 | x = 0; 13 | x = x + 2; // 2 14 | x = x - 1; // 1 15 | x = x * 6; // 6 16 | x = x / 2; // 3 17 | x = x % 2; // 1 18 | x = x << 2; // 4 19 | x = x >> 1; // 2 20 | x = x | 255; // 255 21 | x = x & 3; // 3 22 | x = x ^ 1; // 2 23 | x = -x; // -2 24 | x = x + !!x; // -1 25 | x = x + (x > 2); // -1 26 | x = x + (x < 2); // 0 27 | // XXX <= >= != == 28 | return x; 29 | } 30 | -------------------------------------------------------------------------------- /test/testcases/execute/0003-globals.c: -------------------------------------------------------------------------------- 1 | 2 | int x; 3 | int arr[20]; 4 | int *p = &x; 5 | int **p2 = &p; 6 | 7 | int main() { 8 | x = 1; 9 | arr[2] = 1; 10 | **p2 = x - arr[2]; 11 | return x; 12 | } 13 | -------------------------------------------------------------------------------- /test/testcases/execute/0004-if.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | if (0) { 4 | return 1; 5 | } else { 6 | if (1) { 7 | if (1) 8 | return 0; 9 | } else { 10 | return 1; 11 | } 12 | } 13 | return 1; 14 | } 15 | -------------------------------------------------------------------------------- /test/testcases/execute/0005-for.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 5; 3 | 4 | int main() { 5 | for(x = 0; x < 10 ; x = x + 1) 6 | ; 7 | return x - 10; 8 | } 9 | -------------------------------------------------------------------------------- /test/testcases/execute/0006-while.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 0; 3 | 4 | int main() { 5 | while(1) { 6 | x = x + 1; 7 | if (x == 10) 8 | return x - 10; 9 | } 10 | } -------------------------------------------------------------------------------- /test/testcases/execute/0007-dowhile.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 0; 3 | 4 | int main() { 5 | do 6 | x = x + 1; 7 | while(x < 10); 8 | 9 | do { 10 | x = x + 1; 11 | } while(x < 20); 12 | 13 | return x - 20; 14 | } 15 | -------------------------------------------------------------------------------- /test/testcases/execute/0008-breakcont.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 0; 3 | 4 | int main() { 5 | while(1) 6 | break; 7 | while(1) { 8 | if (x == 5) { 9 | break; 10 | } 11 | x = x + 1; 12 | continue; 13 | } 14 | for (;;) { 15 | if (x == 10) { 16 | break; 17 | } 18 | x = x + 1; 19 | continue; 20 | } 21 | do { 22 | if (x == 15) { 23 | break; 24 | } 25 | x = x + 1; 26 | continue; 27 | } while(1); 28 | return x - 15; 29 | } -------------------------------------------------------------------------------- /test/testcases/execute/0009-goto.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | start: 4 | goto next; 5 | return 1; 6 | success: 7 | return 0; 8 | next: 9 | goto success; 10 | return 1; 11 | } -------------------------------------------------------------------------------- /test/testcases/execute/0010-locals.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | int x; 4 | int *p; 5 | int y[10]; 6 | p = &y[6]; 7 | *p = 5; 8 | x = 10; 9 | y[3] = 5; 10 | return x - y[3] - *p; 11 | } 12 | -------------------------------------------------------------------------------- /test/testcases/execute/0011-switch.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 0; 3 | 4 | int main() { 5 | switch(x) 6 | case 0: 7 | ; 8 | switch(x) 9 | case 0: 10 | switch(x) { 11 | case 0: 12 | goto next; 13 | default: 14 | return 1; 15 | } 16 | return 1; 17 | next: 18 | switch(x) 19 | case 1: 20 | return 1; 21 | switch(x) { 22 | { 23 | x = 1 + 1; 24 | foo: 25 | case 1: 26 | return 1; 27 | } 28 | } 29 | switch(x) { 30 | case 0: 31 | return x; 32 | case 1: 33 | return 1; 34 | default: 35 | return 1; 36 | } 37 | } -------------------------------------------------------------------------------- /test/testcases/execute/0012-char.c: -------------------------------------------------------------------------------- 1 | 2 | char * p = "hello1"; 3 | 4 | int main() { 5 | return p[0] - 104; 6 | } 7 | -------------------------------------------------------------------------------- /test/testcases/execute/0013-typedef.c: -------------------------------------------------------------------------------- 1 | 2 | typedef int t; 3 | 4 | t x = 0; 5 | 6 | int main() { 7 | t z; 8 | z = 1; 9 | return x + z - 1; 10 | } -------------------------------------------------------------------------------- /test/testcases/execute/0014-struct1.c: -------------------------------------------------------------------------------- 1 | 2 | struct s { 3 | int x; 4 | int y; 5 | }; 6 | 7 | int main() { 8 | struct s v; 9 | v.x = 1; 10 | v.y = 1; 11 | return v.x - v.y; 12 | } 13 | -------------------------------------------------------------------------------- /test/testcases/execute/0014-struct2.c: -------------------------------------------------------------------------------- 1 | 2 | struct s { 3 | int x; 4 | struct { 5 | int y; 6 | int z; 7 | } nest; 8 | }; 9 | 10 | int main() { 11 | struct s v; 12 | v.x = 1; 13 | v.nest.y = 2; 14 | v.nest.z = 3; 15 | if (v.x + v.nest.y + v.nest.z != 6) 16 | return 1; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /test/testcases/execute/0014-struct3.c: -------------------------------------------------------------------------------- 1 | 2 | struct s1 { 3 | int y; 4 | int z; 5 | }; 6 | 7 | struct s2 { 8 | struct s1 *p; 9 | }; 10 | 11 | int main() { 12 | struct s1 nested; 13 | struct s2 v; 14 | v.p = &nested; 15 | v.p->y = 1; 16 | v.p->z = 2; 17 | if (nested.y != 1) 18 | return 1; 19 | if (nested.z != 2) 20 | return 2; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /test/testcases/execute/0015-calls.c: -------------------------------------------------------------------------------- 1 | 2 | int foo() { 3 | return 0; 4 | } 5 | 6 | int main() { 7 | return foo(); 8 | } 9 | -------------------------------------------------------------------------------- /test/testcases/execute/0016-calls.c: -------------------------------------------------------------------------------- 1 | 2 | int foo(int a, int b) { 3 | return a + b; 4 | } 5 | 6 | int main() { 7 | return foo(1, 2) - 3; 8 | } 9 | -------------------------------------------------------------------------------- /test/testcases/execute/0017-calls.c: -------------------------------------------------------------------------------- 1 | 2 | int x; 3 | 4 | int foo(int *p1) { 5 | *p1 = 0; 6 | return 0; 7 | } 8 | 9 | int main() { 10 | x = 6; 11 | foo(&x); 12 | return x; 13 | } 14 | -------------------------------------------------------------------------------- /test/testcases/execute/0018-cast.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | long int y; 4 | long int *p; 5 | y = (long int)&y; 6 | p = (long int *)y; 7 | *p = 0; 8 | return (int)y; 9 | } 10 | -------------------------------------------------------------------------------- /test/testcases/execute/0019-cast.c: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | char c; 4 | c = -1; 5 | if ((int)c != -1) { 6 | return 1; 7 | } 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /test/testcases/execute/0020-inits.c: -------------------------------------------------------------------------------- 1 | 2 | int x = 0; 3 | 4 | int y = {2}; 5 | 6 | int main() { 7 | if (x) return 1; 8 | if (y != 2) return 1; 9 | return 0; 10 | } --------------------------------------------------------------------------------