xplshn
·
2025-08-16
config.go
Go
1package config
2
3import (
4 "fmt"
5 "os"
6 "strings"
7
8 "modernc.org/libqbe"
9)
10
11type Feature int
12
13const (
14 FeatExtrn Feature = iota
15 FeatAsm
16 FeatBEsc
17 FeatCEsc
18 FeatBOps
19 FeatCOps
20 FeatCComments
21 FeatTyped
22 FeatShortDecl
23 FeatBxDeclarations
24 FeatAllowUninitialized
25 FeatStrictDecl
26 FeatNoDirectives
27 FeatContinue
28 FeatCount
29)
30
31type Warning int
32
33const (
34 WarnCEsc Warning = iota
35 WarnBEsc
36 WarnBOps
37 WarnCOps
38 WarnUnrecognizedEscape
39 WarnTruncatedChar
40 WarnLongCharConst
41 WarnCComments
42 WarnOverflow
43 WarnPedantic
44 WarnUnreachableCode
45 WarnImplicitDecl
46 WarnType
47 WarnExtra
48 WarnCount
49)
50
51type Info struct {
52 Name string
53 Enabled bool
54 Description string
55}
56
57type Config struct {
58 Features map[Feature]Info
59 Warnings map[Warning]Info
60 FeatureMap map[string]Feature
61 WarningMap map[string]Warning
62 StdName string
63 TargetArch string
64 QbeTarget string
65 WordSize int
66 WordType string
67 StackAlignment int
68}
69
70func NewConfig() *Config {
71 cfg := &Config{
72 Features: make(map[Feature]Info),
73 Warnings: make(map[Warning]Info),
74 FeatureMap: make(map[string]Feature),
75 WarningMap: make(map[string]Warning),
76 }
77
78 features := map[Feature]Info{
79 FeatExtrn: {"extrn", true, "Allow the 'extrn' keyword."},
80 FeatAsm: {"asm", true, "Allow `__asm__` blocks for inline assembly."},
81 FeatBEsc: {"b-esc", false, "Recognize B-style '*' character escapes."},
82 FeatCEsc: {"c-esc", true, "Recognize C-style '\\' character escapes."},
83 FeatBOps: {"b-ops", false, "Recognize B-style assignment operators like '=+'."},
84 FeatCOps: {"c-ops", true, "Recognize C-style assignment operators like '+='."},
85 FeatCComments: {"c-comments", true, "Recognize C-style '//' line comments."},
86 FeatTyped: {"typed", true, "Enable the Bx opt-in & backwards-compatible type system."},
87 FeatShortDecl: {"short-decl", true, "Enable Bx-style short declaration `:=`."},
88 FeatBxDeclarations: {"bx-decl", true, "Enable Bx-style `auto name = val` declarations."},
89 FeatAllowUninitialized: {"allow-uninitialized", true, "Allow declarations without an initializer (`var;` or `auto var;`)."},
90 FeatStrictDecl: {"strict-decl", false, "Require all declarations to be initialized."},
91 FeatContinue: {"continue", true, "Allow the Bx keyword `continue` to be used."},
92 FeatNoDirectives: {"no-directives", false, "Disable `// [b]:` directives."},
93 }
94
95 warnings := map[Warning]Info{
96 WarnCEsc: {"c-esc", true, "Warn on usage of C-style '\\' escapes."},
97 WarnBEsc: {"b-esc", true, "Warn on usage of B-style '*' escapes."},
98 WarnBOps: {"b-ops", true, "Warn on usage of B-style assignment operators like '=+'."},
99 WarnCOps: {"c-ops", true, "Warn on usage of C-style assignment operators like '+='."},
100 WarnUnrecognizedEscape: {"u-esc", true, "Warn on unrecognized character escape sequences."},
101 WarnTruncatedChar: {"truncated-char", true, "Warn when a character escape value is truncated."},
102 WarnLongCharConst: {"long-char-const", true, "Warn when a multi-character constant is too long for a word."},
103 WarnCComments: {"c-comments", false, "Warn on usage of non-standard C-style '//' comments."},
104 WarnOverflow: {"overflow", true, "Warn when an integer constant is out of range for its type."},
105 WarnPedantic: {"pedantic", false, "Issue all warnings demanded by the strict standard."},
106 WarnUnreachableCode: {"unreachable-code", true, "Warn about code that will never be executed."},
107 WarnImplicitDecl: {"implicit-decl", true, "Warn about implicit function or variable declarations."},
108 WarnType: {"type", true, "Warn about type mismatches in expressions and assignments."},
109 WarnExtra: {"extra", true, "Enable extra miscellaneous warnings."},
110 }
111
112 cfg.Features, cfg.Warnings = features, warnings
113 for ft, info := range features {
114 cfg.FeatureMap[info.Name] = ft
115 }
116 for wt, info := range warnings {
117 cfg.WarningMap[info.Name] = wt
118 }
119
120 return cfg
121}
122
123// SetTarget configures the compiler for a specific architecture and QBE target.
124func (c *Config) SetTarget(goos, goarch, qbeTarget string) {
125 if qbeTarget == "" {
126 c.QbeTarget = libqbe.DefaultTarget(goos, goarch)
127 fmt.Fprintf(os.Stderr, "gbc: info: no target specified, defaulting to host target '%s'\n", c.QbeTarget)
128 } else {
129 c.QbeTarget = qbeTarget
130 fmt.Fprintf(os.Stderr, "gbc: info: using specified target '%s'\n", c.QbeTarget)
131 }
132
133 c.TargetArch = goarch
134
135 switch c.QbeTarget {
136 case "amd64_sysv", "amd64_apple", "arm64", "arm64_apple", "rv64":
137 c.WordSize, c.WordType, c.StackAlignment = 8, "l", 16
138 case "arm", "rv32":
139 c.WordSize, c.WordType, c.StackAlignment = 4, "w", 8
140 default:
141 fmt.Fprintf(os.Stderr, "gbc: warning: unrecognized or unsupported QBE target '%s'.\n", c.QbeTarget)
142 fmt.Fprintf(os.Stderr, "gbc: warning: defaulting to 64-bit properties. Compilation may fail.\n")
143 c.WordSize, c.WordType, c.StackAlignment = 8, "l", 16
144 }
145}
146
147func (c *Config) SetFeature(ft Feature, enabled bool) {
148 if info, ok := c.Features[ft]; ok {
149 info.Enabled = enabled
150 c.Features[ft] = info
151 }
152}
153
154func (c *Config) IsFeatureEnabled(ft Feature) bool { return c.Features[ft].Enabled }
155
156func (c *Config) SetWarning(wt Warning, enabled bool) {
157 if info, ok := c.Warnings[wt]; ok {
158 info.Enabled = enabled
159 c.Warnings[wt] = info
160 }
161}
162
163func (c *Config) IsWarningEnabled(wt Warning) bool { return c.Warnings[wt].Enabled }
164
165func (c *Config) ApplyStd(stdName string) error {
166 c.StdName = stdName
167 isPedantic := c.IsWarningEnabled(WarnPedantic)
168
169 type stdSettings struct {
170 feature Feature
171 bValue bool
172 bxValue bool
173 }
174
175 settings := []stdSettings{
176 {FeatAllowUninitialized, true, !isPedantic},
177 {FeatBOps, true, false},
178 {FeatBEsc, true, false},
179 {FeatCOps, !isPedantic, true},
180 {FeatCEsc, !isPedantic, true},
181 {FeatCComments, !isPedantic, true},
182 {FeatExtrn, !isPedantic, true},
183 {FeatAsm, !isPedantic, true},
184 {FeatTyped, false, true},
185 {FeatShortDecl, false, true},
186 {FeatBxDeclarations, false, true},
187 {FeatStrictDecl, false, isPedantic},
188 }
189
190 switch stdName {
191 case "B":
192 for _, s := range settings {
193 c.SetFeature(s.feature, s.bValue)
194 }
195 c.SetWarning(WarnBOps, false)
196 c.SetWarning(WarnBEsc, false)
197 c.SetWarning(WarnCOps, true)
198 c.SetWarning(WarnCEsc, true)
199 c.SetWarning(WarnCComments, true)
200 case "Bx":
201 for _, s := range settings {
202 c.SetFeature(s.feature, s.bxValue)
203 }
204 c.SetWarning(WarnBOps, true)
205 c.SetWarning(WarnBEsc, true)
206 c.SetWarning(WarnCOps, false)
207 c.SetWarning(WarnCEsc, false)
208 c.SetWarning(WarnCComments, false)
209 default:
210 return fmt.Errorf("unsupported standard '%s'. Supported: 'B', 'Bx'", stdName)
211 }
212 return nil
213}
214
215func (c *Config) applyFlag(flag string) {
216 trimmed := strings.TrimPrefix(flag, "-")
217 isNo := strings.HasPrefix(trimmed, "Wno-") || strings.HasPrefix(trimmed, "Fno-")
218 enable := !isNo
219
220 var name string
221 var isWarning bool
222
223 switch {
224 case strings.HasPrefix(trimmed, "W"):
225 name = strings.TrimPrefix(trimmed, "W")
226 if isNo {
227 name = strings.TrimPrefix(name, "no-")
228 }
229 isWarning = true
230 case strings.HasPrefix(trimmed, "F"):
231 name = strings.TrimPrefix(trimmed, "F")
232 if isNo {
233 name = strings.TrimPrefix(name, "no-")
234 }
235 default:
236 name = trimmed
237 isWarning = true
238 }
239
240 if name == "all" && isWarning {
241 for i := Warning(0); i < WarnCount; i++ {
242 if i != WarnPedantic {
243 c.SetWarning(i, enable)
244 }
245 }
246 return
247 }
248
249 if name == "pedantic" && isWarning {
250 c.SetWarning(WarnPedantic, true)
251 return
252 }
253
254 if isWarning {
255 if w, ok := c.WarningMap[name]; ok {
256 c.SetWarning(w, enable)
257 }
258 } else {
259 if f, ok := c.FeatureMap[name]; ok {
260 c.SetFeature(f, enable)
261 }
262 }
263}
264
265func (c *Config) ProcessFlags(visitFlag func(fn func(name string))) {
266 visitFlag(func(name string) {
267 if name == "Wall" || name == "Wno-all" || name == "pedantic" {
268 c.applyFlag("-" + name)
269 }
270 })
271 visitFlag(func(name string) {
272 if name != "Wall" && name != "Wno-all" && name != "pedantic" {
273 c.applyFlag("-" + name)
274 }
275 })
276}
277
278func (c *Config) ProcessDirectiveFlags(flagStr string) {
279 for _, flag := range strings.Fields(flagStr) {
280 c.applyFlag(flag)
281 }
282}