github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/transpiler/enum.go (about)

     1  // This file contains transpiling for enums.
     2  
     3  package transpiler
     4  
     5  import (
     6  	"fmt"
     7  	"go/token"
     8  	"strconv"
     9  	"strings"
    10  
    11  	goast "go/ast"
    12  
    13  	"github.com/Konstantin8105/c4go/ast"
    14  	"github.com/Konstantin8105/c4go/program"
    15  	"github.com/Konstantin8105/c4go/util"
    16  )
    17  
    18  func transpileEnumConstantDecl(p *program.Program, n *ast.EnumConstantDecl) (
    19  	*goast.ValueSpec, []goast.Stmt, []goast.Stmt) {
    20  	var value goast.Expr = util.NewIdent("iota")
    21  	valueType := "int"
    22  	preStmts := []goast.Stmt{}
    23  	postStmts := []goast.Stmt{}
    24  
    25  	if len(n.Children()) > 0 {
    26  		var err error
    27  		value, _, preStmts, postStmts, err = transpileToExpr(n.Children()[0], p, false)
    28  		if err != nil {
    29  			panic(err)
    30  		}
    31  	}
    32  
    33  	return &goast.ValueSpec{
    34  		Names:  []*goast.Ident{util.NewIdent(n.Name)},
    35  		Type:   util.NewTypeIdent(valueType),
    36  		Values: []goast.Expr{value},
    37  		Doc:    p.GetMessageComments(),
    38  	}, preStmts, postStmts
    39  }
    40  
    41  func transpileEnumDecl(p *program.Program, n *ast.EnumDecl) (
    42  	decls []goast.Decl, err error) {
    43  	defer func() {
    44  		if err != nil {
    45  			err = fmt.Errorf("cannot transpileEnumDecl. %v", err)
    46  		}
    47  	}()
    48  
    49  	n.Name = strings.TrimPrefix(n.Name, "enum ")
    50  	n.Name = util.GenerateCorrectType(n.Name)
    51  
    52  	// For case `enum` without name
    53  	if n.Name == "" {
    54  		return transpileEnumDeclWithType(p, n, "int32")
    55  	}
    56  
    57  	// For case `enum` with name
    58  
    59  	// Create alias of enum for int
    60  	decls = append(decls, &goast.GenDecl{
    61  		Tok: token.TYPE,
    62  		Specs: []goast.Spec{
    63  			&goast.TypeSpec{
    64  				Name:   util.NewIdent(n.Name),
    65  				Assign: 1,
    66  				// by defaults enum in C is INT
    67  				Type: util.NewTypeIdent("int32"),
    68  			},
    69  		},
    70  	})
    71  
    72  	// Registration new type in program.Program
    73  	if !p.IsTypeAlreadyDefined(n.Name) {
    74  		p.DefineType(n.Name)
    75  	}
    76  
    77  	var d []goast.Decl
    78  	d, err = transpileEnumDeclWithType(p, n, n.Name)
    79  	decls = append(decls, d...)
    80  	return
    81  }
    82  
    83  func transpileEnumDeclWithType(p *program.Program, n *ast.EnumDecl, enumType string) (
    84  	decls []goast.Decl, err error) {
    85  	defer func() {
    86  		if err != nil {
    87  			err = fmt.Errorf("cannot transpileEnumDeclWithName. %v", err)
    88  		}
    89  	}()
    90  	preStmts := []goast.Stmt{}
    91  	postStmts := []goast.Stmt{}
    92  
    93  	// initialization decls
    94  	d := &goast.GenDecl{
    95  		Tok: token.CONST,
    96  	}
    97  
    98  	// create all EnumConstant like just constants
    99  	var counter int
   100  	var i int
   101  	for _, children := range n.Children() {
   102  		child, ok := children.(*ast.EnumConstantDecl)
   103  		if !ok {
   104  			p.AddMessage(p.GenerateWarningMessage(
   105  				fmt.Errorf("unsupported type `%T` in enum", child), child))
   106  			continue
   107  		}
   108  		var (
   109  			e       *goast.ValueSpec
   110  			newPre  []goast.Stmt
   111  			newPost []goast.Stmt
   112  			val     *goast.ValueSpec
   113  		)
   114  
   115  		// specific case:
   116  		//
   117  		// EnumConstantDecl referenced _ISalpha 'int'
   118  		// `-ParenExpr 'int'
   119  		//   `-ConditionalOperator 'int'
   120  		//     |-BinaryOperator 'int' '<'
   121  		//     | |-ParenExpr 'int'
   122  		//     | | `-IntegerLiteral 'int' 2
   123  		//     | `-IntegerLiteral 'int' 8
   124  		//     |-ParenExpr 'int'
   125  		//     | `-BinaryOperator 'int' '<<'
   126  		//     |   |-ParenExpr 'int'
   127  		//     |   | `-BinaryOperator 'int' '<<'
   128  		//     |   |   |-IntegerLiteral 'int' 1
   129  		//     |   |   `-ParenExpr 0x3b752a8 'int'
   130  		//     |   |     `-IntegerLiteral 'int' 2
   131  		//     |   `-IntegerLiteral 'int' 8
   132  		//     `-ParenExpr 'int'
   133  		//       `-BinaryOperator 'int' '>>'
   134  		//         |-ParenExpr 'int'
   135  		//         | `-BinaryOperator 'int' '<<'
   136  		//         |   |-IntegerLiteral 'int' 1
   137  		//         |   `-ParenExpr 'int'
   138  		//         |     `-IntegerLiteral 'int' 2
   139  		//         `-IntegerLiteral 'int' 8
   140  		//
   141  		// specific for clang 8:
   142  		// EnumConstantDecl  _ISalpha 'int'
   143  		// `-ConstantExpr 'int'
   144  		//   `-ParenExpr 'int'
   145  		//     `-ConditionalOperator 'int'
   146  		//       ...
   147  		//
   148  		//	_ISalpha = func() int32 {
   149  		//		if 2 < 8 {
   150  		//			return 1 << uint64(2) << uint64(8)
   151  		//		}
   152  		//		return 1 << uint64(2) >> uint64(8)
   153  		//	}()
   154  		if len(child.Children()) == 1 {
   155  			if ce, ok := child.Children()[0].(*ast.ConstantExpr); ok && len(ce.ChildNodes) > 0 {
   156  				child.ChildNodes[0] = ce.ChildNodes[0]
   157  			}
   158  			if par, ok := child.Children()[0].(*ast.ParenExpr); ok {
   159  				if cond, ok := par.Children()[0].(*ast.ConditionalOperator); ok {
   160  					if bin, ok := cond.Children()[0].(*ast.BinaryOperator); ok && bin.Operator == "<" {
   161  						if par, ok := bin.Children()[0].(*ast.ParenExpr); ok {
   162  							xint, xok := par.Children()[0].(*ast.IntegerLiteral)
   163  							yint, yok := bin.Children()[1].(*ast.IntegerLiteral)
   164  							if xok && yok {
   165  								xv, xerr := strconv.Atoi(xint.Value)
   166  								yv, yerr := strconv.Atoi(yint.Value)
   167  								if xerr == nil && yerr == nil {
   168  									child.Children()[0] = cond.Children()[2]
   169  									if xv < yv {
   170  										child.Children()[0] = cond.Children()[1]
   171  									}
   172  								}
   173  							}
   174  						}
   175  					}
   176  				}
   177  			}
   178  		}
   179  		val, newPre, newPost = transpileEnumConstantDecl(p, child)
   180  
   181  		if len(newPre) > 0 || len(newPost) > 0 {
   182  			p.AddMessage(p.GenerateWarningMessage(
   183  				fmt.Errorf("check - added in code : (%d)(%d)",
   184  					len(newPre), len(newPost)), n))
   185  		}
   186  
   187  		preStmts, postStmts = combinePreAndPostStmts(
   188  			preStmts, postStmts, newPre, newPost)
   189  
   190  	remove_parent_expr:
   191  		if v, ok := val.Values[0].(*goast.ParenExpr); ok {
   192  			val.Values[0] = v.X
   193  			goto remove_parent_expr
   194  		}
   195  
   196  		sign := 1
   197  		if unary, ok := val.Values[0].(*goast.UnaryExpr); ok {
   198  			if unary.Op == token.SUB {
   199  				sign = -1
   200  			}
   201  			val.Values[0] = unary.X
   202  		}
   203  
   204  		switch v := val.Values[0].(type) {
   205  		case *goast.Ident:
   206  			e = &goast.ValueSpec{
   207  				Names: []*goast.Ident{{Name: child.Name}},
   208  				Values: []goast.Expr{&goast.BasicLit{
   209  					Kind:  token.INT,
   210  					Value: strconv.Itoa(counter),
   211  				}},
   212  				Type: val.Type,
   213  				Doc:  p.GetMessageComments(),
   214  			}
   215  			counter++
   216  
   217  		case *goast.BasicLit:
   218  			var value int
   219  			value, err = strconv.Atoi(v.Value)
   220  			if err != nil {
   221  				e = val
   222  				counter++
   223  				break
   224  			}
   225  			if sign == -1 {
   226  				e = &goast.ValueSpec{
   227  					Names: []*goast.Ident{{Name: child.Name}},
   228  					Values: []goast.Expr{util.NewUnaryExpr(
   229  						&goast.BasicLit{
   230  							Kind:  token.INT,
   231  							Value: v.Value,
   232  						},
   233  						token.SUB,
   234  					)},
   235  					Type: val.Type,
   236  					Doc:  p.GetMessageComments(),
   237  				}
   238  			} else {
   239  				e = &goast.ValueSpec{
   240  					Names: []*goast.Ident{{Name: child.Name}},
   241  					Values: []goast.Expr{&goast.BasicLit{
   242  						Kind:  token.INT,
   243  						Value: v.Value,
   244  					}},
   245  					Type: val.Type,
   246  					Doc:  p.GetMessageComments(),
   247  				}
   248  			}
   249  			counter = value * sign
   250  			counter++
   251  
   252  		case *goast.BinaryExpr:
   253  			// do nothing
   254  			e = val
   255  
   256  		default:
   257  			e = val
   258  			p.AddMessage(p.GenerateWarningMessage(
   259  				fmt.Errorf("add support of continues counter for Go type : %#v",
   260  					v), n))
   261  		}
   262  
   263  		valSpec := &goast.ValueSpec{
   264  			Names:  e.Names,
   265  			Values: e.Values,
   266  		}
   267  
   268  		if i == 0 {
   269  			valSpec.Type = goast.NewIdent(enumType)
   270  		}
   271  
   272  		d.Specs = append(d.Specs, valSpec)
   273  		i++
   274  
   275  		if enumType != "int" {
   276  			// registration of enum constants
   277  			p.EnumConstantToEnum[child.Name] = "enum " + enumType
   278  		}
   279  	}
   280  	d.Lparen = 1
   281  	decls = append(decls, d)
   282  	err = nil
   283  	return
   284  }