comparison icf/icf.go @ 6:54ab94198677

abstract mixins + building sctipt + documentation
author Atarwn Gard <a@qwa.su>
date Tue, 10 Mar 2026 10:22:02 +0500
parents dacc92aae6d5
children 2ffb8028ccbb
comparison
equal deleted inserted replaced
5:07b6f06899e0 6:54ab94198677
15 Key string 15 Key string
16 Args []string 16 Args []string
17 } 17 }
18 18
19 type Config struct { 19 type Config struct {
20 vars map[string]string 20 vars map[string]string
21 abstract map[string][]Directive 21 abstract map[string][]Directive
22 Blocks []ParsedBlock 22 abstractMixin map[string]string // mixin name for each abstract block
23 Blocks []ParsedBlock
23 } 24 }
24 25
25 type ParsedBlock struct { 26 type ParsedBlock struct {
26 ID string 27 ID string
27 Mixin string 28 Mixin string
101 } 102 }
102 flush() 103 flush()
103 104
104 // --- Pass 3: separate abstract from concrete, apply var substitution --- 105 // --- Pass 3: separate abstract from concrete, apply var substitution ---
105 c := &Config{ 106 c := &Config{
106 vars: vars, 107 vars: vars,
107 abstract: make(map[string][]Directive), 108 abstract: make(map[string][]Directive),
109 abstractMixin: make(map[string]string),
108 } 110 }
109 111
110 for _, rb := range raws { 112 for _, rb := range raws {
111 dirs := make([]Directive, len(rb.directives)) 113 dirs := make([]Directive, len(rb.directives))
112 for i, rd := range rb.directives { 114 for i, rd := range rb.directives {
116 } 118 }
117 dirs[i] = Directive{Key: rd.key, Args: args} 119 dirs[i] = Directive{Key: rd.key, Args: args}
118 } 120 }
119 121
120 if strings.HasPrefix(rb.id, "@") { 122 if strings.HasPrefix(rb.id, "@") {
121 c.abstract[rb.id[1:]] = dirs 123 name := rb.id[1:]
124 c.abstract[name] = dirs
125 if rb.mixin != "" {
126 c.abstractMixin[name] = rb.mixin
127 }
122 } else { 128 } else {
123 c.Blocks = append(c.Blocks, ParsedBlock{ 129 c.Blocks = append(c.Blocks, ParsedBlock{
124 ID: rb.id, 130 ID: rb.id,
125 Mixin: rb.mixin, 131 Mixin: rb.mixin,
126 Directives: dirs, 132 Directives: dirs,
183 // ResolveBlock merges mixin directives (lower priority) with block directives, 189 // ResolveBlock merges mixin directives (lower priority) with block directives,
184 // then substitutes capture variables. 190 // then substitutes capture variables.
185 func (c *Config) ResolveBlock(b ParsedBlock, caps map[string]string) []Directive { 191 func (c *Config) ResolveBlock(b ParsedBlock, caps map[string]string) []Directive {
186 var merged []Directive 192 var merged []Directive
187 if b.Mixin != "" { 193 if b.Mixin != "" {
188 merged = append(merged, c.abstract[b.Mixin]...) 194 merged = append(merged, c.resolveAbstract(b.Mixin, make(map[string]bool))...)
189 } 195 }
190 merged = append(merged, b.Directives...) 196 merged = append(merged, b.Directives...)
191 197
192 if len(caps) == 0 { 198 if len(caps) == 0 {
193 return merged 199 return merged
204 } 210 }
205 } 211 }
206 return out 212 return out
207 } 213 }
208 214
215 // resolveAbstract recursively expands an abstract block and its mixin chain.
216 // visited guards against circular references.
217 func (c *Config) resolveAbstract(name string, visited map[string]bool) []Directive {
218 if visited[name] {
219 return nil
220 }
221 visited[name] = true
222
223 var out []Directive
224 if parent, ok := c.abstractMixin[name]; ok {
225 out = append(out, c.resolveAbstract(parent, visited)...)
226 }
227 out = append(out, c.abstract[name]...)
228 return out
229 }
230
209 // makeSubst returns a function that substitutes $VAR in s, 231 // makeSubst returns a function that substitutes $VAR in s,
210 // checking caps first, then vars. 232 // checking caps first, then vars.
211 func makeSubst(vars map[string]string) func(s string, caps map[string]string) string { 233 func makeSubst(vars map[string]string) func(s string, caps map[string]string) string {
212 return func(s string, caps map[string]string) string { 234 return func(s string, caps map[string]string) string {
213 if !strings.Contains(s, "$") { 235 if !strings.Contains(s, "$") {