diff 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
line wrap: on
line diff
--- a/icf/icf.go	Mon Mar 09 03:35:47 2026 +0500
+++ b/icf/icf.go	Tue Mar 10 10:22:02 2026 +0500
@@ -17,9 +17,10 @@
 }
 
 type Config struct {
-	vars     map[string]string
-	abstract map[string][]Directive
-	Blocks   []ParsedBlock
+	vars         map[string]string
+	abstract     map[string][]Directive
+	abstractMixin map[string]string // mixin name for each abstract block
+	Blocks       []ParsedBlock
 }
 
 type ParsedBlock struct {
@@ -103,8 +104,9 @@
 
 	// --- Pass 3: separate abstract from concrete, apply var substitution ---
 	c := &Config{
-		vars:     vars,
-		abstract: make(map[string][]Directive),
+		vars:          vars,
+		abstract:      make(map[string][]Directive),
+		abstractMixin: make(map[string]string),
 	}
 
 	for _, rb := range raws {
@@ -118,7 +120,11 @@
 		}
 
 		if strings.HasPrefix(rb.id, "@") {
-			c.abstract[rb.id[1:]] = dirs
+			name := rb.id[1:]
+			c.abstract[name] = dirs
+			if rb.mixin != "" {
+				c.abstractMixin[name] = rb.mixin
+			}
 		} else {
 			c.Blocks = append(c.Blocks, ParsedBlock{
 				ID:         rb.id,
@@ -185,7 +191,7 @@
 func (c *Config) ResolveBlock(b ParsedBlock, caps map[string]string) []Directive {
 	var merged []Directive
 	if b.Mixin != "" {
-		merged = append(merged, c.abstract[b.Mixin]...)
+		merged = append(merged, c.resolveAbstract(b.Mixin, make(map[string]bool))...)
 	}
 	merged = append(merged, b.Directives...)
 
@@ -206,6 +212,22 @@
 	return out
 }
 
+// resolveAbstract recursively expands an abstract block and its mixin chain.
+// visited guards against circular references.
+func (c *Config) resolveAbstract(name string, visited map[string]bool) []Directive {
+	if visited[name] {
+		return nil
+	}
+	visited[name] = true
+
+	var out []Directive
+	if parent, ok := c.abstractMixin[name]; ok {
+		out = append(out, c.resolveAbstract(parent, visited)...)
+	}
+	out = append(out, c.abstract[name]...)
+	return out
+}
+
 // makeSubst returns a function that substitutes $VAR in s,
 // checking caps first, then vars.
 func makeSubst(vars map[string]string) func(s string, caps map[string]string) string {