github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/program/program.go (about) 1 // Package program contains high-level orchestration and state of the input and 2 // output program during transpilation. 3 package program 4 5 import ( 6 "bytes" 7 "fmt" 8 "go/format" 9 "go/token" 10 "os" 11 12 goast "go/ast" 13 14 "strings" 15 16 "github.com/Konstantin8105/c4go/ast" 17 "github.com/Konstantin8105/c4go/preprocessor" 18 "github.com/Konstantin8105/c4go/util" 19 ) 20 21 // StructRegistry is a map of Struct for struct types and union type 22 type StructRegistry map[string]*Struct 23 24 // HasType method check if type exists 25 func (sr StructRegistry) HasType(typename string) bool { 26 _, exists := sr[typename] 27 28 return exists 29 } 30 31 // Program contains all of the input, output and transpition state of a C 32 // program to a Go program. 33 type Program struct { 34 // All of the Go import paths required for this program. 35 imports []string 36 37 // These are for the output Go AST. 38 FileSet *token.FileSet 39 File *goast.File 40 41 // One a type is defined it will be ignored if a future type of the same 42 // name appears. 43 typesAlreadyDefined []string 44 45 // Contains the current function name during the transpilation. 46 Function *ast.FunctionDecl 47 48 functionDefinitions map[string]DefinitionFunction 49 builtInFunctionDefinitionsHaveBeenLoaded bool 50 51 // These are used to setup the runtime before the application begins. An 52 // example would be to setup globals with stdin file pointers on certain 53 // platforms. 54 startupStatements []goast.Stmt 55 56 // This is used to generate globally unique names for temporary variables 57 // and other generated code. See GetNextIdentifier(). 58 nextUniqueIdentifier int 59 60 // The definitions for defined structs. 61 // TODO: This field should be protected through proper getters and setters. 62 Structs StructRegistry 63 Unions StructRegistry 64 65 // If verbose is on progress messages will be printed immediately as code 66 // comments (so that they do not interfere with the program output). 67 Verbose bool 68 69 // Contains the messages (for example, "// Warning") generated when 70 // transpiling the AST. These messages, which are code comments, are 71 // appended to the very top of the output file. See AddMessage(). 72 messages []string 73 74 // messagePosition - position of slice messages, added like a comment 75 // in output Go code 76 messagePosition int 77 78 // A map of all the global variables (variables that exist outside of a 79 // function) and their types. 80 GlobalVariables map[string]string 81 82 // EnumConstantToEnum - a map with key="EnumConstant" and value="enum type" 83 // clang don`t show enum constant with enum type, 84 // so we have to use hack for repair the type 85 EnumConstantToEnum map[string]string 86 87 // EnumTypedefName - a map with key="Name of typedef enum" and 88 // value="exist ot not" 89 EnumTypedefName map[string]bool 90 91 // TypedefType - map for type alias, for example: 92 // C : typedef int INT; 93 // Map: key = INT, value = int 94 // Important: key and value are C types 95 TypedefType map[string]string 96 97 // commentLine - a map with: 98 // key - filename 99 // value - last comment inserted in Go code 100 commentLine map[string]commentPos 101 102 // preprocessor file 103 PreprocessorFile preprocessor.FilePP 104 105 // UnsafeConvertValueToPointer - simplification for convert value to pointer 106 UnsafeConvertValueToPointer map[string]bool 107 108 // UnsafeConvertPointerArith - simplification for pointer arithmetic 109 UnsafeConvertPointerArith map[string]bool 110 111 // IsHaveVaList 112 IsHaveVaList bool 113 114 DoNotAddComments bool 115 116 // for binding parse FunctionDecl one time 117 Binding bool 118 } 119 120 type commentPos struct { 121 pos int // index in comments slice 122 line int // line position 123 } 124 125 // NewProgram creates a new blank program. 126 func NewProgram() (p *Program) { 127 defer func() { 128 // Need for "stdbool.h" 129 p.TypedefType["_Bool"] = "int" 130 // Initialization c4go implementation of CSTD structs 131 p.initializationStructs() 132 }() 133 return &Program{ 134 imports: []string{}, 135 typesAlreadyDefined: []string{}, 136 startupStatements: []goast.Stmt{}, 137 Structs: StructRegistry(map[string]*Struct{ 138 // Structs without implementations inside system C headers 139 // Example node for adding: 140 // &ast.TypedefDecl{ ... Type:"struct __locale_struct *" ... } 141 }), 142 Unions: make(StructRegistry), 143 Verbose: false, 144 messages: []string{}, 145 GlobalVariables: map[string]string{}, 146 EnumConstantToEnum: map[string]string{}, 147 EnumTypedefName: map[string]bool{}, 148 TypedefType: map[string]string{}, 149 commentLine: map[string]commentPos{}, 150 functionDefinitions: map[string]DefinitionFunction{}, 151 builtInFunctionDefinitionsHaveBeenLoaded: false, 152 UnsafeConvertValueToPointer: map[string]bool{}, 153 UnsafeConvertPointerArith: map[string]bool{}, 154 } 155 } 156 157 // AddMessage adds a message (such as a warning or error) comment to the output 158 // file. Usually the message is generated from one of the Generate functions in 159 // the ast package. 160 // 161 // It is expected that the message already have the comment ("//") prefix. 162 // 163 // The message will not be appended if it is blank. This is because the Generate 164 // functions return a blank string conditionally when there is no error. 165 // 166 // The return value will be true if a message was added, otherwise false. 167 func (p *Program) AddMessage(message string) bool { 168 if message == "" { 169 return false 170 } 171 172 p.messages = append(p.messages, message) 173 174 // Compactizarion warnings stack 175 if len(p.messages) > 1 { 176 var ( 177 new = len(p.messages) - 1 178 last = len(p.messages) - 2 179 ) 180 // Warning collapsing for minimize warnings 181 warning := "// Warning" 182 if strings.HasPrefix(p.messages[last], warning) { 183 l := p.messages[last][len(warning):] 184 if strings.HasSuffix(p.messages[new], l) { 185 p.messages[last] = p.messages[new] 186 p.messages = p.messages[0:new] 187 } 188 } 189 } 190 191 return true 192 } 193 194 // GetMessageComments - get messages "Warnings", "Error" like a comment 195 // Location of comments only NEAR of error or warning and 196 // don't show directly location 197 func (p *Program) GetMessageComments() (_ *goast.CommentGroup) { 198 var group goast.CommentGroup 199 if p.messagePosition < len(p.messages) { 200 for i := p.messagePosition; i < len(p.messages); i++ { 201 group.List = append(group.List, &goast.Comment{ 202 Text: p.messages[i], 203 }) 204 } 205 p.messagePosition = len(p.messages) 206 } 207 return &group 208 } 209 210 // GetComments - return comments 211 func (p *Program) GetComments(n ast.Position) (out []*goast.Comment) { 212 if p.DoNotAddComments { 213 return 214 } 215 beginLine := p.commentLine[n.File] 216 if n.Line < beginLine.line { 217 return 218 } 219 comms := p.PreprocessorFile.GetComments() 220 for i := beginLine.pos; i < len(comms); i++ { 221 if comms[i].File != n.File { 222 continue 223 } 224 if comms[i].Line <= beginLine.line { 225 continue 226 } 227 if comms[i].Line > n.Line { 228 break 229 } 230 // add comment 231 out = append(out, &goast.Comment{ 232 Text: comms[i].Comment, 233 }) 234 beginLine.pos = i 235 if comms[i].Comment[1] == '*' { 236 out = append(out, &goast.Comment{ 237 Text: "// ", 238 }) 239 } 240 } 241 beginLine.line = n.Line 242 p.commentLine[n.File] = beginLine 243 return 244 } 245 246 // GetStruct returns a struct object (representing struct type or union type) or 247 // nil if doesn't exist. This method can get struct or union in the same way and 248 // distinguish only by the IsUnion field. `name` argument is the C like 249 // `struct a_struct`, it allow pointer type like `union a_union *`. Pointer 250 // types used in a DeclRefExpr in the case a deferenced structure by using `->` 251 // operator to access to a field like this: a_struct->member . 252 // 253 // This method is used in collaboration with the field 254 // "c4go/program".*Struct.IsUnion to simplify the code like in function 255 // "c4go/transpiler".transpileMemberExpr() where the same *Struct value returned 256 // by this method is used in the 2 cases, in the case where the value has a 257 // struct type and in the case where the value has an union type. 258 func (p *Program) GetStruct(name string) *Struct { 259 if name == "" { 260 return nil 261 } 262 263 // That allow to get struct from pointer type 264 if last := len(name) - 1; name[last] == '*' { 265 name = name[:last] 266 } 267 268 name = strings.TrimSpace(name) 269 270 res, ok := p.Structs[name] 271 if ok { 272 return res 273 } 274 res, ok = p.Unions[name] 275 if ok { 276 return res 277 } 278 279 return nil 280 } 281 282 // IsTypeAlreadyDefined will return true if the typeName has already been 283 // defined. 284 // 285 // A type could be defined: 286 // 287 // 1. Initially. That is, before the transpilation starts (hard-coded). 288 // 2. By calling DefineType throughout the transpilation. 289 func (p *Program) IsTypeAlreadyDefined(typeName string) bool { 290 return util.InStrings(typeName, p.typesAlreadyDefined) 291 } 292 293 // DefineType will record a type as having already been defined. The purpose for 294 // this is to not generate Go for a type more than once. C allows variables and 295 // other entities (such as function prototypes) to be defined more than once in 296 // some cases. An example of this would be static variables or functions. 297 func (p *Program) DefineType(typeName string) { 298 p.typesAlreadyDefined = append(p.typesAlreadyDefined, typeName) 299 } 300 301 // UndefineType undefine defined type 302 func (p *Program) UndefineType(typeName string) { 303 check_again: 304 for i := range p.typesAlreadyDefined { 305 if typeName == p.typesAlreadyDefined[i] { 306 if len(p.typesAlreadyDefined) == 1 { 307 p.typesAlreadyDefined = make([]string, 0) 308 } else if i == len(p.typesAlreadyDefined)-1 { 309 p.typesAlreadyDefined = p.typesAlreadyDefined[:len(p.typesAlreadyDefined)-1] 310 } else { 311 p.typesAlreadyDefined = append( 312 p.typesAlreadyDefined[:i], 313 p.typesAlreadyDefined[i+1:]...) 314 } 315 goto check_again 316 } 317 } 318 } 319 320 // GetNextIdentifier generates a new globally unique identifier name. This can 321 // be used for variables and functions in generated code. 322 // 323 // The value of prefix is only useful for readability in the code. If the prefix 324 // is an empty string then the prefix "__temp" will be used. 325 func (p *Program) GetNextIdentifier(prefix string) string { 326 if prefix == "" { 327 prefix = "temp" 328 } 329 330 identifierName := fmt.Sprintf("%s%d", prefix, p.nextUniqueIdentifier) 331 p.nextUniqueIdentifier++ 332 333 return identifierName 334 } 335 336 type nilWalker struct { 337 } 338 339 func (n nilWalker) Visit(node goast.Node) (w goast.Visitor) { 340 fmt.Fprintf(os.Stdout, "\n---------\n") 341 fmt.Fprintf(os.Stdout, "Node: %#v\n", node) 342 switch v := node.(type) { 343 case *goast.IndexExpr: 344 fmt.Fprintf(os.Stdout, "IndexExpr\n") 345 fmt.Fprintf(os.Stdout, "\tx = %#v\n", v.X) 346 fmt.Fprintf(os.Stdout, "\tindex = %#v\n", v.Index) 347 if v.Index == nil { 348 goast.Print(token.NewFileSet(), v) 349 panic("") 350 } 351 352 case *goast.GenDecl: 353 fmt.Fprintf(os.Stdout, "%#v\n", v) 354 for i, s := range v.Specs { 355 fmt.Fprintf(os.Stdout, "Spec%d: %#v\n", i, s) 356 if vs, ok := s.(*goast.ValueSpec); ok { 357 for j := range vs.Names { 358 fmt.Fprintf(os.Stdout, "IDS : %#v\n", vs.Names[j]) 359 } 360 } 361 } 362 } 363 return n 364 } 365 366 type simpleDefer struct { 367 } 368 369 func (s simpleDefer) Visit(node goast.Node) (w goast.Visitor) { 370 // var s int32 = func() int32 { 371 // if int32(sstr_s[0]) == int32(sstr_bufs[sstr_n]) { 372 // return 1 373 // } 374 // return 0 375 // }() 376 377 // from : 378 // { 379 // ... 380 // li = func() int32 { 381 // if booled { 382 // return result1 383 // } 384 // return result2 385 // }() 386 // ... 387 // } 388 // to : 389 // { 390 // ... 391 // if booled { 392 // li = result1 393 // } else { 394 // li = result2 395 // } 396 // ... 397 // } 398 if eb, ok := node.(*goast.BlockStmt); ok && 0 < len(eb.List) { 399 for i := range eb.List { 400 if eb.List[i] == nil { 401 continue 402 } 403 es, ok := eb.List[i].(*goast.ExprStmt) 404 if !ok { 405 continue 406 } 407 be, ok := es.X.(*goast.BinaryExpr) 408 if !ok { 409 continue 410 } 411 412 valueName := be.X 413 if be.Op != token.ASSIGN { 414 continue 415 } 416 cl, ok := be.Y.(*goast.CallExpr) 417 if !ok { 418 continue 419 } 420 fl, ok := cl.Fun.(*goast.FuncLit) 421 if !ok { 422 continue 423 } 424 b := fl.Body 425 if 2 != len(b.List) { 426 continue 427 } 428 ifd, ok := b.List[0].(*goast.IfStmt) 429 if !ok { 430 continue 431 } 432 433 condition := ifd.Cond 434 435 ifbod := ifd.Body 436 if 1 != len(ifbod.List) { 437 continue 438 } 439 440 ret1, ok := ifbod.List[0].(*goast.ReturnStmt) 441 if !ok { 442 continue 443 } 444 445 result1 := ret1.Results 446 447 ret2, ok := b.List[1].(*goast.ReturnStmt) 448 if !ok { 449 continue 450 } 451 452 result2 := ret2.Results 453 454 eb.List[i] = &goast.IfStmt{ 455 Cond: condition, 456 Body: &goast.BlockStmt{ 457 List: []goast.Stmt{ 458 &goast.AssignStmt{ 459 Lhs: []goast.Expr{valueName}, 460 Tok: token.ASSIGN, 461 Rhs: result1, 462 }, 463 }, 464 }, 465 Else: &goast.BlockStmt{ 466 List: []goast.Stmt{ 467 &goast.AssignStmt{ 468 Lhs: []goast.Expr{valueName}, 469 Tok: token.ASSIGN, 470 Rhs: result2, 471 }, 472 }, 473 }, 474 } 475 } 476 } 477 478 // from : 479 // if ... { 480 // { 481 // ... 482 // } 483 // } else { 484 // { 485 // ... 486 // } 487 // } 488 // to : 489 // if ... { 490 // ... 491 // } else { 492 // ... 493 // } 494 if fb, ok := node.(*goast.IfStmt); ok { 495 if len(fb.Body.List) == 1 { 496 if ib, ok := fb.Body.List[0].(*goast.BlockStmt); ok { 497 fb.Body = ib 498 } 499 } 500 if fb.Else != nil { 501 if b1, ok := fb.Else.(*goast.BlockStmt); ok && 1 == len(b1.List) { 502 if b2, ok := b1.List[0].(*goast.BlockStmt); ok { 503 fb.Else = b2 504 } 505 } 506 } 507 } 508 509 // from : 510 // return func() int32 { 511 // if int32(sstr_s[0]) == int32(sstr_bufs[sstr_n]) { 512 // return 1 513 // } 514 // return 0 515 // }() 516 // or : 517 // return func() int32 { 518 // ... 519 // }() 520 // to : 521 // if int32(sstr_s[0]) == int32(sstr_bufs[sstr_n]) { 522 // return 1 523 // } 524 // return 0 525 // or : 526 // ... 527 if eb, ok := node.(*goast.BlockStmt); ok && 0 < len(eb.List) { 528 if ret, ok := eb.List[len(eb.List)-1].(*goast.ReturnStmt); ok && 1 == len(ret.Results) { 529 if c, ok := ret.Results[0].(*goast.CallExpr); ok { 530 if fl, ok := c.Fun.(*goast.FuncLit); ok { 531 if 1 < len(eb.List) { 532 eb.List = eb.List[:len(eb.List)-1] 533 } else { 534 eb.List = []goast.Stmt{} 535 } 536 eb.List = append(eb.List, fl.Body.List...) 537 } 538 } 539 } 540 } 541 542 // from : 543 // { 544 // ..... 545 // func() []byte { 546 // tempVarUnary := sstr_s 547 // defer func() { 548 // sstr_s = f(sstr_s, int(-1)) 549 // }() 550 // return tempVarUnary 551 // }() 552 // ..... 553 // } 554 // to : 555 // { 556 // ..... 557 // sstr_s = f(sstr_s, int(-1)) 558 // ..... 559 // } 560 if eb, ok := node.(*goast.BlockStmt); ok { 561 for i := range eb.List { 562 es, ok := eb.List[i].(*goast.ExprStmt) 563 if !ok { 564 continue 565 } 566 cl, ok := es.X.(*goast.CallExpr) 567 if !ok { 568 continue 569 } 570 fl, ok := cl.Fun.(*goast.FuncLit) 571 if !ok { 572 continue 573 } 574 ft := fl.Type // .(*goast.FuncType) 575 if 1 != len(ft.Results.List) { 576 continue 577 } 578 579 if 3 != len(fl.Body.List) { 580 continue 581 } 582 583 body := fl.Body.List 584 585 as, ok := body[0].(*goast.AssignStmt) 586 if !ok { 587 continue 588 } 589 if 1 != len(as.Lhs) { 590 continue 591 } 592 in, ok := as.Lhs[0].(*goast.Ident) 593 if !ok { 594 continue 595 } 596 if in.Name != "tempVarUnary" { 597 continue 598 } 599 600 rt, ok := body[2].(*goast.ReturnStmt) 601 if !ok { 602 continue 603 } 604 if 1 != len(rt.Results) { 605 continue 606 } 607 id, ok := rt.Results[0].(*goast.Ident) 608 if !ok { 609 continue 610 } 611 if id.Name != "tempVarUnary" { 612 continue 613 } 614 615 def, ok := body[1].(*goast.DeferStmt) 616 if !ok { 617 continue 618 } 619 620 fl, ok = def.Call.Fun.(*goast.FuncLit) 621 if !ok { 622 continue 623 } 624 625 body = fl.Body.List 626 if 1 != len(body) { 627 continue 628 } 629 630 eb.List[i] = body[0] 631 } 632 } 633 634 // from: 635 // func f4() { 636 // { 637 // var i int32 638 // for ; i < 10; i++ { 639 // } 640 // } 641 // } 642 // to : 643 // func f4() { 644 // var i int32 645 // for ; i < 10; i++ { 646 // } 647 // } 648 if fd, ok := node.(*goast.FuncDecl); ok && len(fd.Body.List) == 1 { 649 if ib, ok := fd.Body.List[0].(*goast.BlockStmt); ok { 650 fd.Body = ib // internal body 651 } 652 } 653 // from: 654 // { 655 // { 656 // var i int32 657 // for ; i < 10; i++ { 658 // } 659 // } 660 // } 661 // to : 662 // { 663 // var i int32 664 // for ; i < 10; i++ { 665 // } 666 // } 667 if eb, ok := node.(*goast.BlockStmt); ok && len(eb.List) == 1 { 668 if ib, ok := eb.List[0].(*goast.BlockStmt); ok { 669 eb = ib // internal body 670 } 671 } 672 // Simplification from : 673 // var cc int32 = int32(uint8((func() []byte { 674 // defer func() { 675 // func() []byte { 676 // tempVarUnary := ss 677 // defer func() { 678 // ss = ss[0+1:] 679 // }() 680 // return tempVarUnary 681 // }() 682 // }() 683 // return ss 684 // }())[0])) 685 // 686 // to: 687 // var cc int32 = int32(uint8((func() []byte { 688 // defer func() { 689 // ss = ss[0+1:] 690 // }() 691 // return ss 692 // }())[0])) 693 if f0, ok := node.(*goast.FuncLit); ok && f0.Body != nil { 694 if len(f0.Body.List) == 2 { 695 if df, ok := f0.Body.List[0].(*goast.DeferStmt); ok { 696 cl := df.Call 697 if fl, ok := cl.Fun.(*goast.FuncLit); ok && len(fl.Body.List) == 1 { 698 if es, ok := fl.Body.List[0].(*goast.ExprStmt); ok { 699 if cl, ok := es.X.(*goast.CallExpr); ok { 700 if fl, ok := cl.Fun.(*goast.FuncLit); ok && len(fl.Body.List) == 3 { 701 if _, ok := fl.Body.List[0].(*goast.AssignStmt); ok { 702 if df, ok := fl.Body.List[1].(*goast.DeferStmt); ok { 703 if _, ok := fl.Body.List[2].(*goast.ReturnStmt); ok { 704 f0.Body.List[0] = df 705 } 706 } 707 } 708 } 709 } 710 } 711 } 712 } 713 } 714 } 715 716 return s 717 } 718 719 // String generates the whole output Go file as a string. This will include the 720 // messages at the top of the file and all the rendered Go code. 721 func (p *Program) String() string { 722 var buf bytes.Buffer 723 724 buf.WriteString(fmt.Sprintf(`// 725 // Package - transpiled by c4go 726 // 727 // If you have found any issues, please raise an issue at: 728 // https://github.com/Konstantin8105/c4go/ 729 // 730 731 `)) 732 733 // Simplification from : 734 // var cc int32 = int32(uint8((func() []byte { 735 // defer func() { 736 // func() []byte { 737 // tempVarUnary := ss 738 // defer func() { 739 // ss = ss[0+1:] 740 // }() 741 // return tempVarUnary 742 // }() 743 // }() 744 // return ss 745 // }())[0])) 746 // 747 // to: 748 // var cc int32 = int32(uint8((func() []byte { 749 // defer func() { 750 // ss = ss[0+1:] 751 // }() 752 // return ss 753 // }())[0])) 754 goast.Walk(new(simpleDefer), p.File) 755 756 // Only for debugging 757 // goast.Walk(new(nilWalker), p.File) 758 759 // First write all the messages. The double newline afterwards is important 760 // so that the package statement has a newline above it so that the warnings 761 // are not part of the documentation for the package. 762 buf.WriteString(strings.Join(p.messages, "\n") + "\n\n") 763 764 if err := format.Node(&buf, p.FileSet, p.File); err != nil { 765 // Printing the entire AST will generate a lot of output. However, it is 766 // the only way to debug this type of error. Hopefully the error 767 // (printed immediately afterwards) will give a clue. 768 // 769 // You may see an error like: 770 // 771 // panic: format.Node internal error (692:23: expected selector or 772 // type assertion, found '[') 773 // 774 // This means that when Go was trying to convert the Go AST to source 775 // code it has come across a value or attribute that is illegal. 776 // 777 // The line number it is referring to (in this case, 692) is not helpful 778 // as it references the internal line number of the Go code which you 779 // will never see. 780 // 781 // The "[" means that there is a bracket in the wrong place. Almost 782 // certainly in an identifer, like: 783 // 784 // noarch.IntTo[]byte("foo") 785 // 786 // The "[]" which is obviously not supposed to be in the function name 787 // is causing the syntax error. However, finding the original code that 788 // produced this can be tricky. 789 // 790 // The first step is to filter down the AST output to probably lines. 791 // In the error message it said that there was a misplaced "[" so that's 792 // what we will search for. Using the original command (that generated 793 // thousands of lines) we will add two grep filters: 794 // 795 // go test ... | grep "\[" | grep -v '{$' 796 // # | | 797 // # | ^ This excludes lines that end with "{" 798 // # | which almost certainly won't be what 799 // # | we are looking for. 800 // # | 801 // # ^ This is the character we are looking for. 802 // 803 // Hopefully in the output you should see some lines, like (some lines 804 // removed for brevity): 805 // 806 // 9083 . . . . . . . . . . Name: "noarch.[]byteTo[]int" 807 // 9190 . . . . . . . . . Name: "noarch.[]intTo[]byte" 808 // 809 // These two lines are clearly the error because a name should not look 810 // like this. 811 // 812 // Looking at the full output of the AST (thousands of lines) and 813 // looking at those line numbers should give you a good idea where the 814 // error is coming from; by looking at the parents of the bad lines. 815 _ = goast.Print(p.FileSet, p.File) 816 817 panic(err) 818 } 819 820 // Add comments at the end C file 821 for file, beginLine := range p.commentLine { 822 for i := range p.PreprocessorFile.GetComments() { 823 if p.PreprocessorFile.GetComments()[i].File == file { 824 if beginLine.line < p.PreprocessorFile.GetComments()[i].Line { 825 buf.WriteString( 826 fmt.Sprintln( 827 p.PreprocessorFile.GetComments()[i].Comment)) 828 } 829 } 830 } 831 } 832 833 // simplify Go code. Example : 834 // Before: 835 // func compare(a interface { 836 // }, b interface { 837 // }) (c4goDefaultReturn int) { 838 // After : 839 // func compare(a interface {}, b interface {}) (c4goDefaultReturn int) { 840 reg := util.GetRegex("interface( )?{(\r*)\n(\t*)}") 841 s := string(reg.ReplaceAll(buf.Bytes(), []byte("interface {}"))) 842 843 sp := strings.Split(s, "\n") 844 for i := range sp { 845 if strings.HasSuffix(sp[i], "-= 1") { 846 sp[i] = strings.TrimSuffix(sp[i], "-= 1") + "--" 847 } 848 if strings.HasSuffix(sp[i], "+= 1") { 849 sp[i] = strings.TrimSuffix(sp[i], "+= 1") + "++" 850 } 851 } 852 853 return strings.Join(sp, "\n") 854 } 855 856 // IncludeHeaderIsExists return true if C #include header is inside list 857 func (p *Program) IncludeHeaderIsExists(includeHeader string) bool { 858 for _, inc := range p.PreprocessorFile.GetIncludeFiles() { 859 if strings.HasSuffix(inc.HeaderName, includeHeader) { 860 return true 861 } 862 } 863 return false 864 }