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