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  }