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 }