github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/transpiler/union.go (about) 1 package transpiler 2 3 import ( 4 "bytes" 5 "fmt" 6 "html/template" 7 "strings" 8 9 goast "go/ast" 10 "go/format" 11 "go/parser" 12 "go/token" 13 14 "github.com/Konstantin8105/c4go/ast" 15 "github.com/Konstantin8105/c4go/program" 16 ) 17 18 func transpileUnion(name string, size int, fields []*goast.Field) ( 19 _ []goast.Decl, err error) { 20 21 defer func() { 22 if err != nil { 23 err = fmt.Errorf("cannot transpileUnion : err = %v", err) 24 if strings.Contains(err.Error(), "\n") { 25 err = fmt.Errorf("%v", strings.Replace(err.Error(), "\n", "||", -1)) 26 } 27 } 28 }() 29 30 type field struct { 31 Name string 32 TypeField string 33 } 34 35 type union struct { 36 Name string 37 Size int 38 Fields []field 39 } 40 41 src := `package main 42 43 import( 44 "unsafe" 45 "reflect" 46 ) 47 48 type {{ .Name }} struct{ 49 memory unsafe.Pointer 50 } 51 52 func (unionVar * {{ .Name }}) copy() ( {{ .Name }}){ 53 var buffer [{{ .Size }}]byte 54 for i := range buffer{ 55 buffer[i] = (*((*[{{ .Size }}]byte)(unionVar.memory)))[i] 56 } 57 var newUnion {{ .Name }} 58 newUnion.memory = unsafe.Pointer(&buffer) 59 return newUnion 60 } 61 62 {{ range .Fields }} 63 func (unionVar * {{ $.Name }}) {{ .Name }}() (*{{ .TypeField }}){ 64 if unionVar.memory == nil{ 65 var buffer [{{ $.Size }}]byte 66 unionVar.memory = unsafe.Pointer(&buffer) 67 } 68 return (*{{ .TypeField }})(unionVar.memory) 69 } 70 {{ end }} 71 ` 72 // Generate structure of union 73 var un union 74 defer func() { 75 if err != nil { 76 err = fmt.Errorf("%v. Details of union: %#v", err, un) 77 } 78 }() 79 un.Name = name 80 un.Size = size 81 for i := range fields { 82 var f field 83 f.Name = fields[i].Names[0].Name 84 85 var buf bytes.Buffer 86 err = format.Node(&buf, token.NewFileSet(), fields[i].Type) 87 if err != nil { 88 err = fmt.Errorf("cannot parse type '%s' : %v", fields[i].Type, err) 89 return 90 } 91 f.TypeField = buf.String() 92 93 if f.TypeField == "" { 94 goast.Print(token.NewFileSet(), fields[i].Type) 95 f.TypeField = "interface{}" 96 } 97 98 un.Fields = append(un.Fields, f) 99 } 100 101 tmpl := template.Must(template.New("").Parse(src)) 102 var source bytes.Buffer 103 err = tmpl.Execute(&source, un) 104 if err != nil { 105 err = fmt.Errorf("cannot execute template \"%s\" for data %v : %v", 106 source.String(), un, err) 107 return 108 } 109 110 // Create the AST by parsing src. 111 fset := token.NewFileSet() // positions are relative to fset 112 f, err := parser.ParseFile(fset, "", source.String(), 0) 113 if err != nil { 114 err = fmt.Errorf("cannot parse source \"%s\" : %v", 115 source.String(), err) 116 return 117 } 118 119 return f.Decls[1:], nil 120 } 121 122 func isUnionMemberExpr(p *program.Program, n *ast.MemberExpr) (IsUnion bool) { 123 if len(n.Children()) > 0 { 124 _, t, _, _, _ := transpileToExpr(n.Children()[0], p, false) 125 if p.IsUnion(t) { 126 IsUnion = true 127 } 128 } 129 return 130 }