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  }