github.com/gotranspile/cxgo@v0.3.7/parse.go (about)

     1  package cxgo
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/gotranspile/cxgo/libs"
    11  	"github.com/gotranspile/cxgo/libs/libcc"
    12  	"modernc.org/cc/v3"
    13  )
    14  
    15  type SourceConfig struct {
    16  	Predef           string
    17  	Define           []Define
    18  	Include          []string
    19  	SysInclude       []string
    20  	IgnoreIncludeDir bool
    21  }
    22  
    23  func Parse(c *libs.Env, root, fname string, sconf SourceConfig) (*cc.AST, error) {
    24  	path := filepath.Dir(fname)
    25  	if root == "" {
    26  		root = path
    27  	}
    28  	srcs := []cc.Source{{Name: fname}}
    29  	if sconf.Predef != "" {
    30  		srcs = []cc.Source{
    31  			{Name: "predef.h", Value: sconf.Predef}, // FIXME: this should preappend to the file content instead
    32  			{Name: fname},
    33  		}
    34  	}
    35  	var (
    36  		inc []string
    37  		sys []string
    38  	)
    39  	inc = append(inc, sconf.Include...)
    40  	sys = append(sys, sconf.SysInclude...)
    41  	if !sconf.IgnoreIncludeDir {
    42  		inc = append(inc,
    43  			filepath.Join(root, "includes"),
    44  			filepath.Join(root, "include"),
    45  		)
    46  		sys = append(sys, []string{
    47  			filepath.Join(root, "includes"),
    48  			filepath.Join(root, "include"),
    49  		}...)
    50  	}
    51  	inc = append(inc,
    52  		path,
    53  		"@",
    54  	)
    55  	return ParseSource(c, ParseConfig{
    56  		Sources:     srcs,
    57  		WorkDir:     path,
    58  		Includes:    inc,
    59  		SysIncludes: sys,
    60  		Predefines:  true,
    61  		Define:      sconf.Define,
    62  	})
    63  }
    64  
    65  func addIncludeOverridePath(inc []string) []string {
    66  	return append(inc[:len(inc):len(inc)], libs.IncludePath)
    67  }
    68  
    69  var (
    70  	tokLBrace = cc.String("(")
    71  	tokRBrace = cc.String(")")
    72  )
    73  
    74  type Define struct {
    75  	Name  string `yaml:"name" json:"name"`
    76  	Value string `yaml:"value" json:"value"`
    77  }
    78  
    79  type ParseConfig struct {
    80  	WorkDir     string
    81  	Includes    []string
    82  	SysIncludes []string
    83  	Predefines  bool
    84  	Define      []Define
    85  	Sources     []cc.Source
    86  }
    87  
    88  func ParseSource(env *libs.Env, c ParseConfig) (*cc.AST, error) {
    89  	var srcs []cc.Source
    90  	if len(c.Define) != 0 {
    91  		var buf bytes.Buffer
    92  		for _, d := range c.Define {
    93  			buf.WriteString("#define ")
    94  			buf.WriteString(strings.TrimSpace(d.Name))
    95  			if d.Value != "" {
    96  				buf.WriteByte(' ')
    97  				buf.WriteString(strings.TrimSpace(d.Value))
    98  			}
    99  			buf.WriteByte('\n')
   100  		}
   101  		srcs = append(srcs, cc.Source{Name: "cxgo_config_defines.h", Value: buf.String()})
   102  	}
   103  	if c.Predefines {
   104  		srcs = append(srcs, cc.Source{Name: "cxgo_predef.h", Value: fmt.Sprintf(gccPredefine, "int")})
   105  	}
   106  	srcs = append(srcs, c.Sources...)
   107  	includes := addIncludeOverridePath(c.Includes)
   108  	sysIncludes := addIncludeOverridePath(c.SysIncludes)
   109  	return cc.Translate(&cc.Config{
   110  		Config3: cc.Config3{
   111  			WorkingDir: c.WorkDir,
   112  			Filesystem: cc.Overlay(cc.LocalFS(), newIncludeFS(env)),
   113  		},
   114  		ABI: libcc.NewABI(env.Env),
   115  		PragmaHandler: func(p cc.Pragma, toks []cc.Token) {
   116  			if len(toks) == 0 {
   117  				return
   118  			}
   119  			name := toks[0].Value.String()
   120  			toks = toks[1:]
   121  			switch name {
   122  			case "push_macro":
   123  				if len(toks) != 3 {
   124  					return
   125  				} else if toks[0].Value != tokLBrace || toks[2].Value != tokRBrace {
   126  					return
   127  				}
   128  				def := toks[1].Value.String()
   129  				def, err := strconv.Unquote(def)
   130  				if err != nil {
   131  					return
   132  				}
   133  				p.PushMacro(def)
   134  			case "pop_macro":
   135  				if len(toks) != 3 {
   136  					return
   137  				} else if toks[0].Value != tokLBrace || toks[2].Value != tokRBrace {
   138  					return
   139  				}
   140  				def := toks[1].Value.String()
   141  				def, err := strconv.Unquote(def)
   142  				if err != nil {
   143  					return
   144  				}
   145  				p.PopMacro(def)
   146  			}
   147  		},
   148  	}, includes, sysIncludes, srcs)
   149  }