github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/transpiler/transpiler.go (about) 1 // Package transpiler handles the conversion between the Clang AST and the Go 2 // AST. 3 package transpiler 4 5 import ( 6 "errors" 7 "fmt" 8 goast "go/ast" 9 "go/parser" 10 "go/token" 11 "runtime/debug" 12 "strings" 13 "unicode" 14 15 "github.com/Konstantin8105/c4go/ast" 16 "github.com/Konstantin8105/c4go/program" 17 "github.com/Konstantin8105/c4go/types" 18 "github.com/Konstantin8105/c4go/util" 19 ) 20 21 var AddOutsideStruct bool 22 23 // TranspileAST iterates through the Clang AST and builds a Go AST 24 func TranspileAST(fileName, packageName string, withOutsideStructs bool, 25 p *program.Program, root ast.Node, clangFlags []string) ( 26 source string, // result Go source 27 err error) { 28 // Start by parsing an empty file. 29 p.FileSet = token.NewFileSet() 30 packageSignature := fmt.Sprintf("package %v", packageName) 31 f, err := parser.ParseFile(p.FileSet, fileName, packageSignature, 0) 32 p.File = f 33 AddOutsideStruct = withOutsideStructs 34 35 if err != nil { 36 return 37 } 38 39 // replace if type name and variable name 40 { 41 var replacer func(ast.Node) 42 replacer = func(node ast.Node) { 43 if node == nil { 44 return 45 } 46 var vName *string 47 var vType *string 48 switch v := node.(type) { 49 case *ast.DeclRefExpr: 50 vName = &v.Name 51 vType = &v.Type 52 case *ast.VarDecl: 53 vName = &v.Name 54 vType = &v.Type 55 case *ast.ParmVarDecl: 56 vName = &v.Name 57 vType = &v.Type 58 } 59 60 // examples: 61 // vName vType 62 // `wb` `wb` 63 // `wb` `wb *` 64 // `wb` `struct wb` 65 // `wb` `struct wb *` 66 // `wb` `struct wb*` 67 // `wb` `struct wb [10]` 68 // not ok: 69 // `wb` `struct wba` 70 postfix := "_c4go_postfix" 71 if vType != nil && vName != nil && 72 len(strings.TrimSpace(*vName)) > 0 && 73 strings.Contains(*vType, *vName) { 74 75 for _, pr := range []string{*vName, "struct " + *vName, "union " + *vName} { 76 if pr == *vType { 77 *vName += postfix 78 break 79 } 80 if len(*vType) > len(pr) && pr == (*vType)[:len(pr)] && len(pr) > 0 { 81 letter := (*vType)[len(pr)] 82 if unicode.IsLetter(rune(letter)) { 83 continue 84 } 85 if unicode.IsNumber(rune(letter)) { 86 continue 87 } 88 if letter == '*' || letter == '[' || letter == ' ' { 89 *vName += postfix 90 break 91 } 92 } 93 } 94 } 95 for i := range node.Children() { 96 replacer(node.Children()[i]) 97 } 98 } 99 replacer(root) 100 } 101 102 // Now begin building the Go AST. 103 decls, err := transpileToNode(root, p) 104 if err != nil { 105 p.AddMessage(p.GenerateWarningMessage( 106 fmt.Errorf("error of transpiling: err = %v", err), root)) 107 err = nil // Error is ignored 108 } 109 p.File.Decls = append(p.File.Decls, decls...) 110 111 // only for "stdbool.h" 112 if p.IncludeHeaderIsExists("stdbool.h") { 113 p.File.Decls = append(p.File.Decls, &goast.GenDecl{ 114 Tok: token.TYPE, 115 Specs: []goast.Spec{ 116 &goast.TypeSpec{ 117 Name: goast.NewIdent("_Bool"), 118 Type: goast.NewIdent("int32"), 119 }, 120 }, 121 }) 122 } 123 124 // add functions from CSTD 125 std := p.GetCstdFunction() 126 127 // add convertion value to slice 128 GetUnsafeConvertDecls(p) 129 130 // checking implementation for all called functions 131 bindHeader, bindCode := generateBinding(p, clangFlags) 132 133 // Add the imports after everything else so we can ensure that they are all 134 // placed at the top. 135 for _, quotedImportPath := range p.Imports() { 136 importSpec := &goast.ImportSpec{ 137 Path: &goast.BasicLit{ 138 Kind: token.IMPORT, 139 Value: quotedImportPath, 140 }, 141 } 142 importDecl := &goast.GenDecl{ 143 Tok: token.IMPORT, 144 } 145 146 importDecl.Specs = append(importDecl.Specs, importSpec) 147 p.File.Decls = append([]goast.Decl{importDecl}, p.File.Decls...) 148 } 149 150 // generate Go source 151 source = p.String() 152 153 // add functions from CSTD 154 source += std 155 156 // inject binding code 157 if len(bindCode) > 0 { 158 index := strings.Index(source, "package") 159 index += strings.Index(source[index:], "\n") 160 src := source[:index] 161 src += "\n" 162 src += bindHeader 163 src += "\n" 164 src += source[index:] 165 src += "\n" 166 src += bindCode 167 source = src 168 } 169 170 // only for "stdarg.h" 171 if (p.IncludeHeaderIsExists("stdarg.h") && p.IsHaveVaList) || strings.Contains(source, "va_list") { 172 source += getVaListStruct() 173 } 174 175 // generate pointer arithmetic functions 176 source += getPointerArithFunctions(p) 177 178 return 179 } 180 181 func transpileToExpr(node ast.Node, p *program.Program, exprIsStmt bool) ( 182 expr goast.Expr, 183 exprType string, 184 preStmts []goast.Stmt, 185 postStmts []goast.Stmt, 186 err error) { 187 defer func() { 188 if err != nil { 189 err = fmt.Errorf("cannot transpileToExpr. err = %v", err) 190 } 191 }() 192 if node == nil { 193 err = fmt.Errorf("not acceptable nil node") 194 return 195 } 196 defer func() { 197 preStmts = nilFilterStmts(preStmts) 198 postStmts = nilFilterStmts(postStmts) 199 }() 200 201 switch n := node.(type) { 202 case *ast.StringLiteral: 203 expr, exprType, err = transpileStringLiteral(p, n, false) 204 return 205 206 case *ast.FloatingLiteral: 207 expr, exprType, err = transpileFloatingLiteral(n), "double", nil 208 209 case *ast.PredefinedExpr: 210 expr, exprType, err = transpilePredefinedExpr(n, p) 211 212 case *ast.BinaryConditionalOperator: 213 expr, exprType, preStmts, postStmts, err = transpileBinaryConditionalOperator(n, p) 214 215 case *ast.ConditionalOperator: 216 expr, exprType, preStmts, postStmts, err = transpileConditionalOperator(n, p) 217 218 case *ast.ArraySubscriptExpr: 219 expr, exprType, preStmts, postStmts, err = transpileArraySubscriptExpr(n, p) 220 221 case *ast.BinaryOperator: 222 expr, exprType, preStmts, postStmts, err = transpileBinaryOperator(n, p, exprIsStmt) 223 224 case *ast.UnaryOperator: 225 expr, exprType, preStmts, postStmts, err = transpileUnaryOperator(n, p) 226 227 case *ast.MemberExpr: 228 expr, exprType, preStmts, postStmts, err = transpileMemberExpr(n, p) 229 230 case *ast.ImplicitCastExpr: 231 expr, exprType, preStmts, postStmts, err = transpileImplicitCastExpr(n, p, exprIsStmt) 232 233 case *ast.DeclRefExpr: 234 expr, exprType, err = transpileDeclRefExpr(n, p) 235 236 case *ast.IntegerLiteral: 237 expr, exprType, err = transpileIntegerLiteral(n), "int", nil 238 239 case *ast.ParenExpr: 240 expr, exprType, preStmts, postStmts, err = transpileParenExpr(n, p) 241 242 case *ast.CStyleCastExpr: 243 expr, exprType, preStmts, postStmts, err = transpileCStyleCastExpr(n, p, exprIsStmt) 244 245 case *ast.CharacterLiteral: 246 expr, exprType, err = transpileCharacterLiteral(n), "char", nil 247 248 case *ast.CallExpr: 249 expr, exprType, preStmts, postStmts, err = transpileCallExpr(n, p) 250 251 case *ast.CompoundAssignOperator: 252 return transpileCompoundAssignOperator(n, p, exprIsStmt) 253 254 case *ast.UnaryExprOrTypeTraitExpr: 255 return transpileUnaryExprOrTypeTraitExpr(n, p) 256 257 case *ast.InitListExpr: 258 expr, exprType, err = transpileInitListExpr(n, p) 259 260 case *ast.CompoundLiteralExpr: 261 expr, exprType, err = transpileCompoundLiteralExpr(n, p) 262 263 case *ast.StmtExpr: 264 return transpileStmtExpr(n, p) 265 266 case *ast.ImplicitValueInitExpr: 267 return transpileImplicitValueInitExpr(n, p) 268 269 case *ast.OffsetOfExpr: 270 expr, exprType, err = transpileOffsetOfExpr(n, p) 271 272 case *ast.VAArgExpr: 273 expr, exprType, preStmts, postStmts, err = transpileVAArgExpr(n, p) 274 275 case *ast.ConstantExpr: 276 switch len(n.Children()) { 277 case 0: 278 // ignore 279 case 1: 280 expr, exprType, preStmts, postStmts, err = transpileToExpr(n.Children()[0], p, exprIsStmt) 281 default: 282 expr, exprType, preStmts, postStmts, err = transpileToExpr( 283 n.Children()[len(n.Children())-1], 284 p, 285 exprIsStmt, 286 ) 287 // old code: 288 // 289 // err = fmt.Errorf("ConstantExpr: %v. has many nodes", err) 290 // 291 // ConstantExpr 0x1fa9498 <line:324:10, col:15> 'int' 292 // |-value: Int 1 293 // `-BinaryOperator 0x1fa9420 <col:10, col:15> 'int' '==' 294 } 295 296 case *ast.VisibilityAttr: 297 // ignore 298 299 case *ast.WeakAttr: 300 // ignore 301 302 default: 303 p.AddMessage(p.GenerateWarningMessage( 304 fmt.Errorf("cannot transpile to expr in transpileToExpr : %T : %#v", node, node), node)) 305 expr = util.NewNil() 306 } 307 308 // Real return is through named arguments. 309 return 310 } 311 312 func transpileToStmts(node ast.Node, p *program.Program) ( 313 stmts []goast.Stmt, err error) { 314 315 if node == nil { 316 return 317 } 318 319 stmt, preStmts, postStmts, err := transpileToStmt(node, p) 320 if err != nil { 321 p.AddMessage(p.GenerateWarningMessage( 322 fmt.Errorf("error in DeclStmt: %v", err), node)) 323 err = nil // Error is ignored 324 } 325 326 stmts = combineStmts(preStmts, stmt, postStmts) 327 stmts = nilFilterStmts(stmts) 328 return 329 } 330 331 func transpileToStmt(node ast.Node, p *program.Program) ( 332 stmt goast.Stmt, preStmts []goast.Stmt, postStmts []goast.Stmt, err error) { 333 if node == nil { 334 return 335 } 336 337 defer func() { 338 if err != nil { 339 err = fmt.Errorf("cannot transpileToStmt : %v", err) 340 p.AddMessage(p.GenerateWarningMessage(err, node)) 341 err = nil // Error is ignored 342 } 343 }() 344 defer func() { 345 preStmts = nilFilterStmts(preStmts) 346 postStmts = nilFilterStmts(postStmts) 347 }() 348 defer func() { 349 com := p.GetComments(node.Position()) 350 for i := range com { 351 preStmts = append(preStmts, &goast.ExprStmt{ 352 X: goast.NewIdent(com[i].Text), 353 }) 354 } 355 cg := p.GetMessageComments() 356 for i := range cg.List { 357 preStmts = append(preStmts, &goast.ExprStmt{ 358 X: goast.NewIdent(cg.List[i].Text), 359 }) 360 } 361 }() 362 363 var expr goast.Expr 364 365 switch n := node.(type) { 366 // case *ast.DefaultStmt: 367 // stmt, err = transpileDefaultStmt(n, p) 368 // return 369 // 370 // case *ast.CaseStmt: 371 // stmt, preStmts, postStmts, err = transpileCaseStmt(n, p) 372 // return 373 374 case *ast.SwitchStmt: 375 stmt, preStmts, postStmts, err = transpileSwitchStmt(n, p) 376 return 377 378 case *ast.BreakStmt: 379 stmt = &goast.BranchStmt{ 380 Tok: token.BREAK, 381 } 382 return 383 384 case *ast.WhileStmt: 385 return transpileWhileStmt(n, p) 386 387 case *ast.DoStmt: 388 return transpileDoStmt(n, p) 389 390 case *ast.ContinueStmt: 391 stmt, err = transpileContinueStmt(n, p) 392 return 393 394 case *ast.IfStmt: 395 stmt, preStmts, postStmts, err = transpileIfStmt(n, p) 396 return 397 398 case *ast.ForStmt: 399 return transpileForStmt(n, p) 400 401 case *ast.ReturnStmt: 402 return transpileReturnStmt(n, p) 403 404 case *ast.CompoundStmt: 405 stmt, preStmts, postStmts, err = transpileCompoundStmt(n, p) 406 return 407 408 case *ast.BinaryOperator: 409 if n.Operator == "," { 410 stmt, preStmts, err = transpileBinaryOperatorComma(n, p) 411 return 412 } 413 414 case *ast.LabelStmt: 415 stmt, preStmts, postStmts, err = transpileLabelStmt(n, p) 416 return 417 418 case *ast.GotoStmt: 419 stmt, err = transpileGotoStmt(n, p) 420 return 421 422 case *ast.GCCAsmStmt: 423 // Go does not support inline assembly. See: 424 // https://github.com/Konstantin8105/c4go/issues/228 425 p.AddMessage(p.GenerateWarningMessage( 426 errors.New("cannot transpile asm, will be ignored"), n)) 427 428 stmt = &goast.EmptyStmt{} 429 return 430 case *ast.DeclStmt: 431 var stmts []goast.Stmt 432 stmts, err = transpileDeclStmt(n, p) 433 if err != nil { 434 p.AddMessage(p.GenerateWarningMessage( 435 fmt.Errorf("error in DeclStmt: %v", err), n)) 436 err = nil // Error is ignored 437 return 438 } 439 switch len(stmts) { 440 case 0: 441 return 442 case 1: 443 stmt = stmts[0] 444 default: 445 stmt = stmts[0] 446 postStmts = stmts[1:] 447 } 448 return 449 } 450 451 // We do not care about the return type. 452 var theType string 453 expr, theType, preStmts, postStmts, err = transpileToExpr(node, p, true) 454 if err != nil { 455 return 456 } 457 458 // nil is happen, when we remove function `free` of <stdlib.h> 459 // see function CallExpr in transpiler 460 if expr == (*goast.CallExpr)(nil) { 461 return 462 } 463 464 // CStyleCastExpr.Kind == ToVoid 465 var foundToVoid bool 466 if theType == types.ToVoid { 467 foundToVoid = true 468 } 469 if v, ok := node.(*ast.CStyleCastExpr); ok && v.Kind == ast.CStyleCastExprToVoid { 470 foundToVoid = true 471 } 472 if len(node.Children()) > 0 { 473 if v, ok := node.Children()[0].(*ast.CStyleCastExpr); ok && 474 v.Kind == ast.CStyleCastExprToVoid { 475 foundToVoid = true 476 } 477 } 478 if foundToVoid { 479 stmt = &goast.AssignStmt{ 480 Lhs: []goast.Expr{goast.NewIdent("_")}, 481 Tok: token.ASSIGN, 482 Rhs: []goast.Expr{expr}, 483 } 484 return 485 } 486 487 // For all other cases 488 if expr == nil { 489 err = fmt.Errorf("expr is nil") 490 return 491 } 492 stmt = util.NewExprStmt(expr) 493 494 return 495 } 496 497 func transpileToNode(node ast.Node, p *program.Program) ( 498 decls []goast.Decl, err error) { 499 defer func() { 500 if err != nil { 501 if _, ok := node.(*ast.RecordDecl); !ok { 502 // ignore error for all case except RecordDecl 503 p.AddMessage(p.GenerateWarningMessage(err, node)) 504 err = nil // Error is ignored 505 } 506 } 507 }() 508 509 if node == nil { 510 return 511 } 512 513 defer func() { 514 decls = nilFilterDecl(decls) 515 }() 516 517 defer func() { 518 if r := recover(); r != nil { 519 err = fmt.Errorf("transpileToNode: error - panic : %#v. %s", r, string(debug.Stack())) 520 } 521 }() 522 523 if n, ok := node.(*ast.TranslationUnitDecl); ok { 524 return transpileTranslationUnitDecl(p, n) 525 } 526 527 if !AddOutsideStruct && 528 !p.PreprocessorFile.IsUserSource(node.Position().File) { 529 if fd, ok := node.(*ast.FunctionDecl); ok { 530 if getFunctionBody(fd) != nil { 531 return 532 } 533 } else { 534 return 535 } 536 } 537 538 defer func() { 539 if len(decls) > 0 && err == nil { 540 for i := range decls { 541 if decls[i] == nil { 542 continue 543 } 544 545 var ( 546 doc *goast.CommentGroup 547 name string 548 found bool 549 ) 550 551 if p.Function != nil { 552 continue 553 } 554 555 switch decls[i].(type) { 556 case *goast.GenDecl: 557 if decls[i].(*goast.GenDecl).Doc == nil { 558 decls[i].(*goast.GenDecl).Doc = &goast.CommentGroup{} 559 } 560 doc = decls[i].(*goast.GenDecl).Doc 561 found = true 562 563 // try to find name 564 name = "c4go_name_is_not_found" 565 specs := decls[i].(*goast.GenDecl).Specs 566 if len(specs) > 0 { 567 switch v := specs[0].(type) { 568 case *goast.TypeSpec: 569 if v.Name != nil { 570 name = v.Name.Name 571 } 572 573 case *goast.ValueSpec: 574 if len(v.Names) > 0 { 575 if v.Names[0] != nil { 576 name = v.Names[0].Name 577 } 578 } 579 580 default: 581 // ignored 582 } 583 } 584 585 case *goast.FuncDecl: 586 if decls[i].(*goast.FuncDecl).Doc == nil { 587 decls[i].(*goast.FuncDecl).Doc = &goast.CommentGroup{} 588 } 589 if decls[i].(*goast.FuncDecl).Name == nil { 590 decls[i].(*goast.FuncDecl).Name = goast.NewIdent("c4go_noname") 591 } 592 doc = decls[i].(*goast.FuncDecl).Doc 593 name = decls[i].(*goast.FuncDecl).Name.Name 594 found = true 595 596 default: 597 // ignore that goast.Decl 598 found = false 599 continue 600 } 601 602 if !found { 603 continue 604 } 605 606 com := p.GetComments(node.Position()) 607 msg := p.GetMessageComments().List 608 doc.List = append(doc.List, com...) 609 doc.List = append(doc.List, msg...) 610 611 // location of file 612 location := node.Position().GetSimpleLocation() 613 location = program.PathSimplification(location) 614 doc.List = append([]*goast.Comment{{ 615 Text: fmt.Sprintf("// %s - transpiled function from %s", 616 name, location), 617 }}, doc.List...) 618 619 break 620 } 621 } 622 }() 623 624 switch n := node.(type) { 625 case *ast.FunctionDecl: 626 decls, err = transpileFunctionDecl(n, p) 627 628 case *ast.CXXRecordDecl: 629 if !strings.Contains(n.RecordDecl.Kind, "class") { 630 decls, err = transpileToNode(n.RecordDecl, p) 631 } else { 632 decls, err = transpileCXXRecordDecl(p, n.RecordDecl) 633 } 634 635 case *ast.TypedefDecl: 636 decls, err = transpileTypedefDecl(p, n) 637 638 case *ast.RecordDecl: 639 decls, err = transpileRecordDecl(p, n) 640 641 case *ast.VarDecl: 642 decls, _, err = transpileVarDecl(p, n) 643 644 case *ast.EnumDecl: 645 decls, err = transpileEnumDecl(p, n) 646 647 case *ast.LinkageSpecDecl: 648 // ignore 649 650 case *ast.EmptyDecl: 651 if len(n.Children()) == 0 { 652 // ignore if length is zero, for avoid 653 // mistake warning 654 } else { 655 p.AddMessage(p.GenerateWarningMessage( 656 fmt.Errorf("EmptyDecl is not transpiled"), n)) 657 } 658 err = nil 659 return 660 661 default: 662 err = fmt.Errorf("cannot transpile to node: %#v", node) 663 } 664 665 return 666 } 667 668 func transpileStmts(nodes []ast.Node, p *program.Program) (stmts []goast.Stmt, err error) { 669 for _, s := range nodes { 670 if s == nil { 671 continue 672 } 673 stmt, preStmts, postStmts, err := transpileToStmt(s, p) 674 if err != nil { 675 p.AddMessage(p.GenerateWarningMessage( 676 fmt.Errorf("transpileToStmts: %v", err), nodes[0])) 677 err = nil // Error is ignored 678 continue 679 } 680 stmts = append(stmts, combineStmts(preStmts, stmt, postStmts)...) 681 } 682 683 return stmts, nil 684 }