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 }