repos / gbc

GBC - Go B Compiler
git clone https://github.com/xplshn/gbc.git

gbc / pkg / config
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}