subst.mx raw
1 package iskra
2
3 import "bytes"
4
5 type TokenSubst struct {
6 Dict *Dict
7 Replace map[uint32]uint32 // old token index -> new token index
8 }
9
10 func (s *TokenSubst) Apply(seq []uint32) []uint32 {
11 out := []uint32{:0:len(seq)}
12 for _, idx := range seq {
13 if newIdx, ok := s.Replace[idx]; ok {
14 out = append(out, newIdx)
15 } else {
16 out = append(out, idx)
17 }
18 }
19 return out
20 }
21
22 func (s *TokenSubst) Decode(seq []uint32) []byte {
23 return s.Dict.Decode(seq)
24 }
25
26 func BuildTokenSubst(d *Dict, templateAST, targetAST string, templateIRName, targetIRName string) *TokenSubst {
27 ts := &TokenSubst{
28 Dict: d,
29 Replace: map[uint32]uint32{},
30 }
31
32 // Map the function name (verb): @"old.Func" -> @"new.Func"
33 oldQuoted := "@\"" | templateIRName | "\""
34 newQuoted := "@\"" | targetIRName | "\""
35 oldIdx, hasOld := d.Index[oldQuoted]
36 if hasOld {
37 newIdx := d.Add([]byte(newQuoted), TokVerb)
38 ts.Replace[oldIdx] = newIdx
39 }
40
41 // Unquoted form: @old.Func -> @new.Func
42 oldUnquoted := "@" | templateIRName
43 newUnquoted := "@" | targetIRName
44 oldIdx2, hasOld2 := d.Index[oldUnquoted]
45 if hasOld2 {
46 newIdx2 := d.Add([]byte(newUnquoted), TokVerb)
47 ts.Replace[oldIdx2] = newIdx2
48 }
49
50 // Map parameter names (nouns): %old_param -> %new_param
51 tplSt := ExtractSymbols(templateAST)
52 tgtSt := ExtractSymbols(targetAST)
53 tplParams := substExtractParams(tplSt)
54 tgtParams := substExtractParams(tgtSt)
55
56 minP := len(tplParams)
57 if len(tgtParams) < minP {
58 minP = len(tgtParams)
59 }
60 for i := 0; i < minP; i++ {
61 if tplParams[i] != tgtParams[i] {
62 // Map all % prefixed forms of this param
63 for _, suffix := range []string{"", "."} {
64 oldP := "%" | tplParams[i] | suffix
65 newP := "%" | tgtParams[i] | suffix
66 oi, ok := d.Index[oldP]
67 if ok {
68 ni := d.Add([]byte(newP), TokNoun)
69 ts.Replace[oi] = ni
70 }
71 }
72 }
73 }
74
75 return ts
76 }
77
78 // Map the quoted function name in assembly: "old/pkg.Func" -> "new/pkg.Func"
79 func (s *TokenSubst) AddASMNameMapping(templateIRName, targetIRName string) {
80 oldQ := "\"" | templateIRName | "\""
81 newQ := "\"" | targetIRName | "\""
82 oi, ok := s.Dict.Index[oldQ]
83 if ok {
84 ni := s.Dict.Add([]byte(newQ), TokLiteral)
85 s.Replace[oi] = ni
86 }
87 }
88
89 func substExtractParams(st *SymbolTable) []string {
90 var params []string
91 for _, sym := range st.Symbols {
92 if sym.Kind == SymParam {
93 params = append(params, sym.Name)
94 }
95 }
96 return params
97 }
98
99 // Legacy text-based substitution kept for backward compatibility.
100 type IRSubst struct {
101 OldFuncName string
102 NewFuncName string
103 ParamMap map[string]string
104 StripDebug bool
105 }
106
107 func (s *IRSubst) Apply(ir []byte) []byte {
108 lines := bytes.Split(ir, []byte("\n"))
109 out := []byte{:0:len(ir)}
110 prevEmpty := false
111
112 for _, line := range lines {
113 if s.StripDebug && isDebugLine(line) {
114 continue
115 }
116
117 line = s.substFuncName(line)
118 line = s.substParams(line)
119
120 if s.StripDebug {
121 line = stripDebugRefs(line)
122 }
123
124 empty := len(bytes.TrimSpace(line)) == 0
125 if empty && prevEmpty {
126 continue
127 }
128 prevEmpty = empty
129
130 out = append(out, line...)
131 out = append(out, '\n')
132 }
133 return out
134 }
135
136 func (s *IRSubst) substFuncName(line []byte) []byte {
137 old := []byte("@\"" | s.OldFuncName | "\"")
138 new_ := []byte("@\"" | s.NewFuncName | "\"")
139 if bytes.Contains(line, old) {
140 return bytes.Replace(line, old, new_, -1)
141 }
142 old = []byte("@" | s.OldFuncName)
143 new_ = []byte("@" | s.NewFuncName)
144 return bytes.Replace(line, old, new_, -1)
145 }
146
147 func (s *IRSubst) substParams(line []byte) []byte {
148 for old, new_ := range s.ParamMap {
149 line = bytes.Replace(line, []byte("%"|old|"."), []byte("%"|new_|"."), -1)
150 }
151 return line
152 }
153
154 func isDebugLine(line []byte) bool {
155 trimmed := bytes.TrimSpace(line)
156 if bytes.HasPrefix(trimmed, []byte("#dbg_")) {
157 return true
158 }
159 if bytes.HasPrefix(trimmed, []byte("!")) && bytes.IndexByte(trimmed, '=') > 0 {
160 return true
161 }
162 return false
163 }
164
165 func stripDebugRefs(line []byte) []byte {
166 for {
167 idx := bytes.Index(line, []byte("!dbg !"))
168 if idx < 0 {
169 break
170 }
171 end := idx + 6
172 for end < len(line) && line[end] >= '0' && line[end] <= '9' {
173 end++
174 }
175 start := idx
176 if start > 0 && line[start-1] == ' ' {
177 start--
178 }
179 if start > 0 && line[start-1] == ',' {
180 start--
181 }
182 var rebuilt []byte
183 rebuilt = append(rebuilt, line[:start]...)
184 rebuilt = append(rebuilt, line[end:]...)
185 line = rebuilt
186 }
187 return line
188 }
189
190 func BuildIRSubst(templateAST, targetAST string, templateIRName, targetIRName string, stripDebug bool) *IRSubst {
191 tplSt := ExtractSymbols(templateAST)
192 tgtSt := ExtractSymbols(targetAST)
193
194 paramMap := map[string]string{}
195 tplParams := substExtractParams(tplSt)
196 tgtParams := substExtractParams(tgtSt)
197
198 minP := len(tplParams)
199 if len(tgtParams) < minP {
200 minP = len(tgtParams)
201 }
202 for i := 0; i < minP; i++ {
203 if tplParams[i] != tgtParams[i] {
204 paramMap[tplParams[i]] = tgtParams[i]
205 }
206 }
207
208 return &IRSubst{
209 OldFuncName: templateIRName,
210 NewFuncName: targetIRName,
211 ParamMap: paramMap,
212 StripDebug: stripDebug,
213 }
214 }
215