github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/transpiler/functions.go (about) 1 // This file contains functions for declaring function prototypes, expressions 2 // that call functions, returning from function and the coordination of 3 // processing the function bodies. 4 5 package transpiler 6 7 import ( 8 "bytes" 9 "fmt" 10 "os" 11 "strings" 12 "text/template" 13 14 "github.com/Konstantin8105/c4go/ast" 15 "github.com/Konstantin8105/c4go/program" 16 "github.com/Konstantin8105/c4go/types" 17 "github.com/Konstantin8105/c4go/util" 18 19 goast "go/ast" 20 "go/parser" 21 "go/token" 22 ) 23 24 // getFunctionBody returns the function body as a CompoundStmt. If the function 25 // is a prototype or forward declaration (meaning it has no body) then nil is 26 // returned. 27 func getFunctionBody(n *ast.FunctionDecl) *ast.CompoundStmt { 28 // It's possible that the last node is the CompoundStmt (after all the 29 // parameter declarations) - but I don't know this for certain so we will 30 // look at all the children for now. 31 for _, c := range n.Children() { 32 if b, ok := c.(*ast.CompoundStmt); ok { 33 return b 34 } 35 } 36 37 return nil 38 } 39 40 // transpileFunctionDecl transpiles the function prototype. 41 // 42 // The function prototype may also have a body. If it does have a body the whole 43 // function will be transpiled into Go. 44 // 45 // If there is no function body we register the function internally (actually 46 // either way the function is registered internally) but we do not do anything 47 // because Go does not use or have any use for forward declarations of 48 // functions. 49 func transpileFunctionDecl(n *ast.FunctionDecl, p *program.Program) ( 50 decls []goast.Decl, err error) { 51 defer func() { 52 if err != nil { 53 err = fmt.Errorf("cannot transpileFunctionDecl. %v", err) 54 } 55 }() 56 57 // This is set at the start of the function declaration so when the 58 // ReturnStmt comes alone it will know what the current function is, and 59 // therefore be able to lookup what the real return type should be. I'm sure 60 // there is a much better way of doing this. 61 p.Function = n 62 defer func() { 63 // Reset the function name when we go out of scope. 64 p.Function = nil 65 }() 66 67 n.Name = util.ConvertFunctionNameFromCtoGo(n.Name) 68 69 // Always register the new function. Only from this point onwards will 70 // we be allowed to refer to the function. 71 define := func() (err error) { 72 var pr string 73 var f, r []string 74 pr, _, f, r, err = util.ParseFunction(n.Type) 75 if err != nil { 76 err = fmt.Errorf("cannot get function definition : %v", err) 77 return 78 } 79 if len(pr) != 0 { 80 p.AddMessage(p.GenerateWarningMessage( 81 fmt.Errorf("prefix of type '%s' is not empty", n.Type), n)) 82 } 83 84 p.AddFunctionDefinition(program.DefinitionFunction{ 85 Name: n.Name, 86 ReturnType: r[0], 87 ArgumentTypes: f, 88 Substitution: "", 89 IncludeFile: n.Pos.File, 90 }) 91 92 return 93 } 94 95 if p.GetFunctionDefinition(n.Name) == nil { 96 if err = define(); err != nil { 97 return 98 } 99 } 100 101 if p.Binding { 102 // probably a few function in result with same names 103 decls, err = bindingFunctionDecl(n, p) 104 return 105 } 106 107 if n.IsExtern { 108 return 109 } 110 111 // Test if the function has a body. This is identified by a child node that 112 // is a CompoundStmt (since it is not valid to have a function body without 113 // curly brackets). 114 functionBody := getFunctionBody(n) 115 if functionBody == nil { 116 return 117 } 118 119 if err = define(); err != nil { 120 return 121 } 122 123 // If the function has a direct substitute in Go we do not want to 124 // output the C definition of it. 125 f := p.GetFunctionDefinition(n.Name) 126 127 p.SetHaveBody(n.Name) 128 body, pre, post, err := transpileToBlockStmt(functionBody, p) 129 if err != nil || len(pre) > 0 || len(post) > 0 { 130 p.AddMessage(p.GenerateWarningMessage( 131 fmt.Errorf("not correct result in function %s body: err = %v", 132 n.Name, err), n)) 133 err = nil // Error is ignored 134 } 135 136 if p.IncludeHeaderIsExists("stdlib.h") && n.Name == "main" { 137 body.List = append([]goast.Stmt{&goast.DeferStmt{ 138 Call: util.NewCallExpr("noarch.AtexitRun"), 139 }}, body.List...) 140 p.AddImport("github.com/Konstantin8105/c4go/noarch") 141 } 142 143 // if functionBody != nil { 144 145 // If verbose mode is on we print the name of the function as a comment 146 // immediately to stdout. This will appear at the top of the program but 147 // make it much easier to diagnose when the transpiler errors. 148 if p.Verbose { 149 fmt.Fprintf(os.Stdout, "// Function: %s(%s)\n", f.Name, 150 strings.Join(f.ArgumentTypes, ", ")) 151 } 152 153 var fieldList = &goast.FieldList{} 154 fieldList, err = getFieldList(p, n, f.ArgumentTypes) 155 if err != nil { 156 return 157 } 158 159 // return type 160 161 t, err := types.ResolveType(p, f.ReturnType) 162 if err != nil { 163 err = fmt.Errorf("ReturnType: %s. %v", f.ReturnType, err) 164 p.AddMessage(p.GenerateWarningMessage(err, n)) 165 err = nil 166 } 167 168 if p.Function != nil && p.Function.Name == "main" { 169 // main() function does not have a return type. 170 t = "" 171 172 // This collects statements that will be placed at the top of 173 // (before any other code) in main(). 174 prependStmtsInMain := []goast.Stmt{} 175 176 // In Go, the main() function does not take the system arguments. 177 // Instead they are accessed through the os package. We create new 178 // variables in the main() function (if needed), immediately after 179 // the __init() for these variables. 180 if len(fieldList.List) > 0 { 181 p.AddImport("os") 182 183 prependStmtsInMain = append( 184 prependStmtsInMain, 185 &goast.AssignStmt{ 186 Lhs: []goast.Expr{fieldList.List[0].Names[0]}, 187 Tok: token.DEFINE, 188 Rhs: []goast.Expr{ 189 &goast.CallExpr{ 190 Fun: goast.NewIdent("int32"), 191 Args: []goast.Expr{ 192 util.NewCallExpr("len", goast.NewIdent("os.Args")), 193 }, 194 }, 195 }, 196 }, 197 ) 198 } 199 200 if len(fieldList.List) > 1 { 201 prependStmtsInMain = append( 202 prependStmtsInMain, 203 &goast.AssignStmt{ 204 Lhs: []goast.Expr{fieldList.List[1].Names[0]}, 205 Tok: token.DEFINE, 206 Rhs: []goast.Expr{&goast.CompositeLit{Type: util.NewTypeIdent("[][]byte")}}, 207 }, 208 &goast.RangeStmt{ 209 Key: goast.NewIdent("_"), 210 Value: util.NewIdent("argvSingle"), 211 Tok: token.DEFINE, 212 X: goast.NewIdent("os.Args"), 213 Body: &goast.BlockStmt{ 214 List: []goast.Stmt{ 215 &goast.AssignStmt{ 216 Lhs: []goast.Expr{fieldList.List[1].Names[0]}, 217 Tok: token.ASSIGN, 218 Rhs: []goast.Expr{util.NewCallExpr( 219 "append", 220 fieldList.List[1].Names[0], 221 util.NewCallExpr("[]byte", util.NewIdent("argvSingle")), 222 )}, 223 }, 224 }, 225 }, 226 }) 227 } 228 229 // Prepend statements for main(). 230 body.List = append(prependStmtsInMain, body.List...) 231 232 // The main() function does not have arguments or a return value. 233 fieldList = &goast.FieldList{} 234 } 235 236 // Each function MUST have "ReturnStmt", 237 // except function without return type 238 var addReturnName bool 239 if len(body.List) > 0 { 240 last := body.List[len(body.List)-1] 241 if _, ok := last.(*goast.ReturnStmt); !ok && t != "" { 242 body.List = append(body.List, &goast.ReturnStmt{}) 243 addReturnName = true 244 } 245 } 246 247 // For functions without return type - no need add return at 248 // the end of body 249 if p.GetFunctionDefinition(n.Name).ReturnType == "void" { 250 if len(body.List) > 0 { 251 if _, ok := (body.List[len(body.List)-1]).(*goast.ReturnStmt); ok { 252 body.List = body.List[:len(body.List)-1] 253 } 254 } 255 } 256 257 decls = append(decls, &goast.FuncDecl{ 258 Name: util.NewIdent(n.Name), 259 Type: util.NewFuncType(fieldList, t, addReturnName), 260 Body: body, 261 }) 262 //} 263 264 err = nil 265 return 266 } 267 268 // convert from: 269 // |-FunctionDecl 0x55677bd48ee0 <line:924:7, col:63> col:12 InitWindow 'void (int, int, const char *)' 270 // | |-ParmVarDecl 0x55677bd48d08 <col:23, col:27> col:27 width 'int' 271 // | |-ParmVarDecl 0x55677bd48d80 <col:34, col:38> col:38 height 'int' 272 // | `-ParmVarDecl 0x55677bd48df8 <col:46, col:58> col:58 title 'const char *' 273 // 274 // Go equal code: 275 // 276 // // Initwindow is binding of function "InitWindow" 277 // func InitWindow(width int32, height int32, title string) { 278 // cwidth := (C.int)(width) 279 // cheight := (C.int)(height) 280 // ctitle := C.CString(title) 281 // defer C.free(unsafe.Pointer(ctitle)) 282 // // run function 283 // C.InitWindow(cwidth, cheight, ctitle) 284 // } 285 func bindingFunctionDecl(n *ast.FunctionDecl, p *program.Program) ( 286 decls []goast.Decl, err error) { 287 defer func() { 288 if err != nil { 289 err = fmt.Errorf("cannot bindingFunctionDecl func `%s`. %v", n.Name, err) 290 } 291 }() 292 293 // generate names 294 n.Name = strings.TrimSpace(n.Name) 295 cName := n.Name 296 goName := func() string { 297 rs := []rune(n.Name) 298 name := strings.ToUpper(string(rs[0])) 299 if 1 < len(rs) { 300 name += string(rs[1:]) 301 } 302 return name 303 }() 304 // parse variables 305 type variable struct { 306 cName, cType string 307 valid bool 308 cgoType string 309 goName, goType string 310 } 311 var args []variable 312 for i := range n.ChildNodes { 313 switch n := n.ChildNodes[i].(type) { 314 case *ast.ParmVarDecl: 315 var cgoType, goType string 316 ok, cgoType, goType := cTypeToGoType(n.Type) 317 args = append(args, variable{ 318 goName: n.Name, 319 cType: n.Type, 320 valid: ok, 321 cgoType: cgoType, 322 goType: goType, 323 }) 324 default: 325 err = fmt.Errorf("not valid type:%T", n) 326 return 327 } 328 } 329 for i := range args { 330 args[i].cName = "c" + args[i].goName 331 } 332 333 f := p.GetFunctionDefinition(n.Name) 334 335 var fieldList = &goast.FieldList{} 336 fieldList, err = getFieldList(p, n, f.ArgumentTypes) 337 if err != nil { 338 return 339 } 340 341 // fix only for binding 342 for i := range fieldList.List { 343 name := fieldList.List[i].Type 344 id, ok := name.(*goast.Ident) 345 if !ok { 346 continue 347 } 348 if id.Name == "[]byte" || id.Name == "[] byte" { 349 fieldList.List[i].Type = goast.NewIdent("string") 350 } 351 } 352 353 t, err := types.ResolveType(p, f.ReturnType) 354 if err != nil { 355 err = fmt.Errorf("ReturnType: %s. %v", f.ReturnType, err) 356 p.AddMessage(p.GenerateWarningMessage(err, n)) 357 err = nil 358 } 359 360 var fd goast.FuncDecl 361 fd.Name = goast.NewIdent(goName) 362 363 body := new(goast.BlockStmt) 364 365 for i := range args { 366 if cType := args[i].cType; cType == "char *" { 367 // for type `char *` 368 // 369 // bs := []byte(*text) 370 // if len(bs) == 0 { 371 // bs = []byte{byte(0)} 372 // } 373 // if 0 < len(bs) && bs[len(bs)-1] != byte(0) { // minimalize allocation 374 // bs = append(bs, byte(0)) // for next input symbols 375 // } 376 // ctext := (*C.char)(unsafe.Pointer(&bs[0])) 377 // defer func() { 378 // *text = string(bs) 379 // // no need : C.free(unsafe.Pointer(ctext)) 380 // }() 381 src := `package main 382 func main() { 383 {{ .GoName }}_bs := []byte(* {{ .GoName }}) 384 if len({{ .GoName }}_bs) == 0 { 385 {{ .GoName }}_bs = []byte{byte(0)} 386 } 387 if 0 < len({{ .GoName }}_bs) && {{ .GoName }}_bs[len({{ .GoName }}_bs)-1] != byte(0) { 388 {{ .GoName }}_bs = append({{ .GoName }}_bs, byte(0)) 389 } 390 {{ .CName }} := (*C.char)(unsafe.Pointer(&{{ .GoName }}_bs[0])) 391 defer func() { 392 *{{ .GoName }} = string({{ .GoName }}_bs) 393 // no need : C.free(unsafe.Pointer(ctext)) 394 }() 395 }` 396 tmpl := template.Must(template.New("").Parse(src)) 397 var source bytes.Buffer 398 err = tmpl.Execute(&source, struct{ CName, GoName string }{ 399 CName: args[i].cName, GoName: args[i].goName, 400 }) 401 if err != nil { 402 err = fmt.Errorf("cannot execute template \"%s\" for data : %v", 403 source.String(), err) 404 return 405 } 406 407 // Create the AST by parsing src. 408 fset := token.NewFileSet() // positions are relative to fset 409 f, err := parser.ParseFile(fset, "", source.String(), 0) 410 if err != nil { 411 err = fmt.Errorf("cannot parse source \"%s\" : %v", 412 source.String(), err) 413 p.AddMessage(p.GenerateWarningMessage(err, n)) 414 err = nil // ignore error 415 continue 416 } 417 if 0 < len(f.Decls) { 418 if fd, ok := f.Decls[0].(*goast.FuncDecl); ok { 419 body.List = append(body.List, fd.Body.List...) 420 } 421 } 422 continue 423 } 424 if !args[i].valid { 425 // func Rect(r Rectangle, s int) { 426 // var cr C.struct_Rectangle 427 // cr.x = C.int(r.x) 428 // cr.y = C.int(r.y) 429 // cs = C.int(s) 430 // C.Rect(cr, cs) 431 // } 432 st := p.GetStruct(args[i].cType) 433 if st == nil { 434 tname := p.TypedefType[args[i].cType] 435 st = p.GetStruct(tname) 436 if st != nil { 437 args[i].cType = tname 438 } 439 } 440 if st != nil && 441 !strings.Contains(args[i].cType, "*") && 442 !strings.Contains(args[i].cType, "[") { 443 body.List = append(body.List, &goast.DeclStmt{Decl: &goast.GenDecl{ 444 Tok: token.VAR, 445 Specs: []goast.Spec{&goast.ValueSpec{ 446 Names: []*goast.Ident{goast.NewIdent(args[i].cName)}, 447 Type: &goast.SelectorExpr{ 448 X: goast.NewIdent("C"), 449 Sel: goast.NewIdent("struct_" + args[i].cType), 450 }, 451 }}, 452 }}) 453 for fname, ftype := range st.Fields { 454 ft := fmt.Sprintf("%v", ftype) 455 if strings.Contains(ft, "*") || 456 strings.Contains(ft, "[") { 457 err = fmt.Errorf("field type is pointer: `%s`", ft) 458 p.AddMessage(p.GenerateWarningMessage(err, n)) 459 err = nil // ignore error 460 } 461 _, cgot, _ := cTypeToGoType(ft) 462 body.List = append(body.List, &goast.AssignStmt{ 463 Lhs: []goast.Expr{&goast.SelectorExpr{ 464 X: goast.NewIdent(args[i].cName), 465 Sel: goast.NewIdent(fname), 466 }}, 467 Tok: token.ASSIGN, 468 Rhs: []goast.Expr{&goast.CallExpr{ 469 Fun: goast.NewIdent(cgot), 470 Args: []goast.Expr{&goast.SelectorExpr{ 471 X: goast.NewIdent(args[i].goName), 472 Sel: goast.NewIdent(fname), 473 }}, 474 }}, 475 }) 476 } 477 continue 478 } 479 if cType := args[i].cType; 2 < len(cType) && 480 cType[len(cType)-1] == '*' && 481 strings.Count(cType, "*") == 1 && 482 strings.Count(cType, "[") == 0 { 483 cType = cType[:len(cType)-1] 484 if ok, cgoType, goType := cTypeToGoType(cType); ok { 485 // if active == nil { 486 // active = new(int32) 487 // } 488 var ifs goast.IfStmt 489 ifs.Cond = &goast.BinaryExpr{ 490 X: goast.NewIdent(args[i].goName), 491 Op: token.EQL, 492 Y: goast.NewIdent("nil"), 493 } 494 ifs.Body = &goast.BlockStmt{ 495 List: []goast.Stmt{ 496 &goast.AssignStmt{ 497 Lhs: []goast.Expr{ 498 goast.NewIdent(args[i].goName), 499 }, 500 Tok: token.ASSIGN, 501 Rhs: []goast.Expr{ 502 &goast.CallExpr{ 503 Fun: goast.NewIdent("new"), 504 Args: []goast.Expr{ 505 goast.NewIdent(goType), 506 }, 507 }, 508 }, 509 }, 510 }, 511 } 512 body.List = append(body.List, &ifs) 513 // cactive := C.int(*active) 514 body.List = append(body.List, &goast.AssignStmt{ 515 Lhs: []goast.Expr{ 516 goast.NewIdent(args[i].cName), 517 }, 518 Tok: token.DEFINE, 519 Rhs: []goast.Expr{ 520 &goast.CallExpr{ 521 Fun: goast.NewIdent(cgoType), 522 Args: []goast.Expr{ 523 &goast.StarExpr{ 524 X: goast.NewIdent(args[i].goName), 525 }, 526 }, 527 }, 528 }, 529 }) 530 // defer func() { 531 // *active = int32(cactive) 532 // }() 533 body.List = append(body.List, &goast.DeferStmt{ 534 Call: &goast.CallExpr{ 535 Fun: &goast.FuncLit{ 536 Type: &goast.FuncType{}, 537 Body: &goast.BlockStmt{ 538 List: []goast.Stmt{ 539 &goast.AssignStmt{ 540 Lhs: []goast.Expr{ 541 &goast.StarExpr{ 542 X: goast.NewIdent(args[i].goName), 543 }, 544 }, 545 Tok: token.ASSIGN, 546 Rhs: []goast.Expr{ 547 goast.NewIdent(args[i].cName), 548 }, 549 }, 550 }, 551 }, 552 }, 553 }, 554 }) 555 continue 556 } 557 } 558 // fmt.Frintln(os.Stdout, "Not implemented: ", args[i].cType, args[i].cName) 559 err = fmt.Errorf("cannot parse C type: `%s` and name `%s`", 560 args[i].cType, args[i].cName) 561 p.AddMessage(p.GenerateWarningMessage(err, n)) 562 err = nil // ignore error 563 continue 564 } 565 body.List = append(body.List, &goast.AssignStmt{ 566 Lhs: []goast.Expr{goast.NewIdent(args[i].cName)}, 567 Tok: token.DEFINE, 568 Rhs: []goast.Expr{&goast.CallExpr{ 569 Fun: goast.NewIdent(args[i].cgoType), 570 Args: []goast.Expr{goast.NewIdent(args[i].goName)}}, 571 }, 572 }) 573 // free memory 574 if strings.Contains(args[i].cType, "*") || 575 strings.Contains(args[i].cType, "[") { 576 body.List = append(body.List, &goast.DeferStmt{ 577 Call: &goast.CallExpr{ 578 Fun: &goast.SelectorExpr{ 579 X: goast.NewIdent("C"), 580 Sel: goast.NewIdent("free"), 581 }, 582 Args: []goast.Expr{ 583 goast.NewIdent(fmt.Sprintf("unsafe.Pointer(%s)", args[i].cName)), 584 }, 585 }, 586 }) 587 } 588 } 589 590 ce := &goast.CallExpr{ 591 Fun: &goast.SelectorExpr{ 592 X: goast.NewIdent("C"), 593 Sel: goast.NewIdent(cName), 594 }, 595 } 596 for i := range args { 597 ce.Args = append(ce.Args, goast.NewIdent(args[i].cName)) 598 } 599 600 runC := false 601 if t == "" { 602 body.List = append(body.List, &goast.ExprStmt{X: ce}) 603 runC = true 604 } 605 st := p.GetStruct(t) 606 if st == nil { 607 t2 := p.TypedefType[t] 608 st = p.GetStruct(t2) 609 if st != nil { 610 t = t2 611 } 612 } 613 if !runC && st != nil { 614 // func Rect() Rectangle { 615 // cResult := C.Rect() 616 // var goRes Rectangle 617 // goRes.x = int32(cResult.x) 618 // goRes.y = int32(cResult.y) 619 // return goRes 620 // } 621 cResult := "cResult" 622 body.List = append(body.List, &goast.AssignStmt{ 623 Lhs: []goast.Expr{goast.NewIdent(cResult)}, 624 Tok: token.DEFINE, 625 Rhs: []goast.Expr{ce}, 626 }) 627 goRes := "goRes" 628 body.List = append(body.List, &goast.DeclStmt{Decl: &goast.GenDecl{ 629 Tok: token.VAR, 630 Specs: []goast.Spec{&goast.ValueSpec{ 631 Names: []*goast.Ident{goast.NewIdent(goRes)}, 632 Type: goast.NewIdent(t), 633 }}, 634 }}) 635 for fname, ftype := range st.Fields { 636 ft := fmt.Sprintf("%v", ftype) 637 if strings.Contains(ft, "*") || 638 strings.Contains(ft, "[") { 639 err = fmt.Errorf("field type is pointer: `%s`", ft) 640 p.AddMessage(p.GenerateWarningMessage(err, n)) 641 err = nil // ignore error 642 return 643 } 644 _, _, goType := cTypeToGoType(fmt.Sprintf("%v", ft)) 645 body.List = append(body.List, &goast.AssignStmt{ 646 Lhs: []goast.Expr{&goast.SelectorExpr{ 647 X: goast.NewIdent(goRes), 648 Sel: goast.NewIdent(fname), 649 }}, 650 Tok: token.ASSIGN, 651 Rhs: []goast.Expr{&goast.CallExpr{ 652 Fun: goast.NewIdent(goType), 653 Args: []goast.Expr{&goast.SelectorExpr{ 654 X: goast.NewIdent(cResult), 655 Sel: goast.NewIdent(fname), 656 }}, 657 }}, 658 }) 659 } 660 body.List = append(body.List, &goast.ReturnStmt{ 661 Results: []goast.Expr{goast.NewIdent(goRes)}}) 662 runC = true 663 } 664 if !runC { 665 body.List = append(body.List, &goast.ReturnStmt{ 666 Results: []goast.Expr{ce}}) 667 } 668 669 addReturnName := false 670 671 decls = append(decls, &goast.FuncDecl{ 672 Name: util.NewIdent(goName), 673 Type: util.NewFuncType(fieldList, t, addReturnName), 674 Body: body, 675 }) 676 677 return 678 } 679 680 // C/C++ type CGO type Go type 681 // C.char, C.schar (signed char), C.uchar (unsigned char), C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), C.long, C.ulong (unsigned long), C.longlong (long long), C.ulonglong (unsigned long long), C.float, C.double, C.complexfloat (complex float), and C.complexdouble (complex double) 682 // {"bool", "C.int32", "bool"}, 683 var table = [][3]string{ 684 {"char", "C.char", "byte"}, 685 {"signed char", "C.schar", "int8"}, 686 {"unsigned char", "C.uchar", "byte"}, 687 {"short", "C.short", "int16"}, 688 {"unsigned short", "C.ushort", "uint16"}, 689 {"int", "C.int", "int"}, 690 {"unsigned int", "C.uint", "uint"}, 691 {"long", "C.long", "int64"}, 692 {"unsigned long", "C.ulong", "uint64"}, 693 {"long long", "C.longlong", "int64"}, 694 {"unsigned long long", "C.ulonglong", "uint64"}, 695 {"float", "C.float", "float32"}, 696 {"double", "C.double", "float64"}, 697 {"const char *", "C.CString", "string"}, 698 // {"char *", "C.CString", "string"}, 699 // {"char []", "C.CString", "string"}, 700 {"_Bool", "C.bool", "bool"}, 701 } 702 703 func cTypeToGoType(cType string) (ok bool, cgoType, goType string) { 704 cType = strings.TrimSpace(cType) 705 for i := range table { 706 if cType == table[i][0] { 707 return true, table[i][1], table[i][2] 708 } 709 } 710 return false, cType, cType 711 } 712 713 // getFieldList returns the parameters of a C function as a Go AST FieldList. 714 func getFieldList(p *program.Program, f *ast.FunctionDecl, fieldTypes []string) ( 715 _ *goast.FieldList, err error) { 716 defer func() { 717 if err != nil { 718 err = fmt.Errorf("error in function field list. err = %v", err) 719 } 720 }() 721 r := []*goast.Field{} 722 for i := range fieldTypes { 723 if len(f.Children()) <= i { 724 err = fmt.Errorf("not correct type/children: %d, %d", 725 len(f.Children()), len(fieldTypes)) 726 return 727 } 728 n := f.Children()[i] 729 if v, ok := n.(*ast.ParmVarDecl); ok { 730 t, err := types.ResolveType(p, fieldTypes[i]) 731 if err != nil { 732 err = fmt.Errorf("FieldList type: %s. %v", fieldTypes[i], err) 733 p.AddMessage(p.GenerateWarningMessage(err, f)) 734 err = nil // ignore error 735 t = "C4GO_UNDEFINE_TYPE" 736 } 737 738 if t == "" { 739 continue 740 } 741 r = append(r, &goast.Field{ 742 Names: []*goast.Ident{util.NewIdent(v.Name)}, 743 Type: goast.NewIdent(t), 744 }) 745 } 746 } 747 748 // for function argument: ... 749 if strings.Contains(f.Type, "...") { 750 r = append(r, &goast.Field{ 751 Names: []*goast.Ident{util.NewIdent("c4goArgs")}, 752 Type: &goast.Ellipsis{ 753 Ellipsis: 1, 754 Elt: &goast.InterfaceType{ 755 Interface: 1, 756 Methods: &goast.FieldList{ 757 Opening: 1, 758 }, 759 Incomplete: false, 760 }, 761 }, 762 }) 763 } 764 765 return &goast.FieldList{ 766 List: r, 767 }, nil 768 } 769 770 func transpileReturnStmt(n *ast.ReturnStmt, p *program.Program) ( 771 _ goast.Stmt, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 772 defer func() { 773 if err != nil { 774 err = fmt.Errorf("cannot transpileReturnStmt. err = %v", err) 775 } 776 }() 777 // There may not be a return value. Then we don't have to both ourselves 778 // with all the rest of the logic below. 779 if len(n.Children()) == 0 { 780 return &goast.ReturnStmt{}, nil, nil, nil 781 } 782 783 var eType string 784 var e goast.Expr 785 e, eType, preStmts, postStmts, err = atomicOperation(n.Children()[0], p) 786 if err != nil { 787 return nil, nil, nil, err 788 } 789 if e == nil { 790 return nil, nil, nil, fmt.Errorf("expr is nil") 791 } 792 793 f := p.GetFunctionDefinition(p.Function.Name) 794 795 t, err := types.CastExpr(p, e, eType, f.ReturnType) 796 if p.AddMessage(p.GenerateWarningMessage(err, n)) { 797 t = util.NewNil() 798 } 799 800 results := []goast.Expr{t} 801 802 // main() function is not allowed to return a result. Use os.Exit if 803 // non-zero. 804 if p.Function != nil && p.Function.Name == "main" { 805 litExpr, isLiteral := e.(*goast.BasicLit) 806 if !isLiteral || (isLiteral && litExpr.Value != "0") { 807 p.AddImport("github.com/Konstantin8105/c4go/noarch") 808 return util.NewExprStmt(&goast.CallExpr{ 809 Fun: goast.NewIdent("noarch.Exit"), 810 Args: []goast.Expr{ 811 &goast.CallExpr{ 812 Fun: goast.NewIdent("int32"), 813 Args: results, 814 }, 815 }, 816 }), preStmts, postStmts, nil 817 } 818 results = []goast.Expr{} 819 } 820 821 return &goast.ReturnStmt{ 822 Results: results, 823 }, preStmts, postStmts, nil 824 }