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  }