resolve.mx raw

   1  package rewrite
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/syntax"
   5  	"git.smesh.lol/moxie/pkg/token"
   6  	"git.smesh.lol/moxie/pkg/types"
   7  )
   8  
   9  // Package-level vars referenced by the resolve functions.
  10  var ConstValMap map[string]int64
  11  var PkgSrc []byte
  12  var ImportRegistry map[string]*types.TCPackage
  13  
  14  func EnsureImportRegistry() {
  15  	if ImportRegistry == nil {
  16  		ImportRegistry = map[string]*types.TCPackage{}
  17  	}
  18  }
  19  
  20  // irParseInt64 is a local copy of the emit helper.
  21  func irParseInt64(s string) int64 {
  22  	var n int64
  23  	for i := 0; i < len(s); i++ {
  24  		c := s[i]
  25  		if c < '0' || c > '9' {
  26  			break
  27  		}
  28  		n = n*10 + int64(c-'0')
  29  	}
  30  	return n
  31  }
  32  
  33  // typeBaseName is a local copy of the types helper.
  34  func typeBaseName(t syntax.Type) string {
  35  	if t == nil {
  36  		return ""
  37  	}
  38  	switch t := t.(type) {
  39  	case *types.Named:
  40  		if tn := t.Obj(); tn != nil {
  41  			return tn.Name()
  42  		}
  43  	case *types.Pointer:
  44  		return typeBaseName(t.Elem())
  45  	case *types.Basic:
  46  		return t.Name()
  47  	}
  48  	return ""
  49  }
  50  
  51  func ResolveNameInline(e syntax.Expr, scope *types.Scope) syntax.Type {
  52  	if e == nil {
  53  		return nil
  54  	}
  55  	switch e := e.(type) {
  56  	case *syntax.Name:
  57  		var obj Object
  58  		if scope != nil {
  59  			_, obj = scope.LookupParent(e.Value)
  60  		} else {
  61  			_, obj = types.Universe.LookupParent(e.Value)
  62  		}
  63  		if obj != nil {
  64  			if tn, ok := obj.(*types.TypeName); ok {
  65  				return tn.Type()
  66  			}
  67  			if fn, ok := obj.(*types.TCFunc); ok {
  68  				if sig := fn.Signature(); sig != nil {
  69  					return sig
  70  				}
  71  			}
  72  		}
  73  	case *syntax.SelectorExpr:
  74  		pkgName, ok := e.X.(*syntax.Name)
  75  		if ok && scope != nil {
  76  			_, pkgObj := scope.LookupParent(pkgName.Value)
  77  			if pn, ok2 := pkgObj.(*types.PkgName); ok2 && pn.Imported() != nil {
  78  				typeObj := pn.Imported().Scope().Lookup(e.Sel.Value)
  79  				if typeObj != nil {
  80  					if tn, ok3 := typeObj.(*types.TypeName); ok3 {
  81  						return tn.Type()
  82  					}
  83  					if fn, ok3 := typeObj.(*types.TCFunc); ok3 {
  84  						if sig := fn.Signature(); sig != nil {
  85  							return sig
  86  						}
  87  					}
  88  				}
  89  			}
  90  		}
  91  		// Fallback: check ImportRegistry directly for external package types
  92  		if pkgName, ok := e.X.(*syntax.Name); ok {
  93  			var irKeys []string
  94  			for k := range ImportRegistry {
  95  				irKeys = append(irKeys, k)
  96  			}
  97  			for i := 1; i < len(irKeys); i++ {
  98  				for j := i; j > 0 && irKeys[j] < irKeys[j-1]; j-- {
  99  					irKeys[j], irKeys[j-1] = irKeys[j-1], irKeys[j]
 100  				}
 101  			}
 102  			for _, k := range irKeys {
 103  				pkg := ImportRegistry[k]
 104  				if pkg.Name() == pkgName.Value {
 105  					typeObj := pkg.Scope().Lookup(e.Sel.Value)
 106  					if typeObj != nil {
 107  						if tn, ok2 := typeObj.(*types.TypeName); ok2 {
 108  							return tn.Type()
 109  						}
 110  					}
 111  					break
 112  				}
 113  			}
 114  		}
 115  	case *syntax.Operation:
 116  		if e.Y == nil && e.Op == Mul {
 117  			base := ResolveNameInline(e.X, scope)
 118  			if base == nil {
 119  				base = types.Typ[types.Int8]
 120  			}
 121  			return types.NewPointer(base)
 122  		}
 123  	case *types.SliceType:
 124  		elem := ResolveNameInline(e.Elem, scope)
 125  		if elem != nil {
 126  			if b, ok := elem.(*types.Basic); ok && b.Kind() == Uint8 {
 127  				return types.Typ[types.TCString]
 128  			}
 129  			return types.NewSlice(elem)
 130  		}
 131  	case *syntax.ArrayType:
 132  		elem := ResolveNameInline(e.Elem, scope)
 133  		if elem != nil {
 134  			n := int64(-1)
 135  			if lit, ok := e.Len.(*syntax.BasicLit); ok {
 136  				n = irParseInt64(lit.Value)
 137  			} else if e.Len != nil {
 138  				cv := EvalConstExpr(e.Len, scope, -1)
 139  				if cv != nil {
 140  					if ci, ok := cv.(*types.ConstInt); ok {
 141  						n = ci.V
 142  					}
 143  				}
 144  				if n == -1 && ConstValMap != nil {
 145  					n = ResolveArrayLenFromSrc(e.pos, ConstValMap)
 146  				}
 147  			}
 148  			return types.NewArray(elem, n)
 149  		}
 150  	case *syntax.MapType:
 151  		key := ResolveNameInline(e.Key, scope)
 152  		val := ResolveNameInline(e.Value, scope)
 153  		if key != nil && val != nil {
 154  			return types.NewTCMap(key, val)
 155  		}
 156  	case *syntax.StructType:
 157  		var fields []*types.TCVar
 158  		var tags []string
 159  		for i, field := range e.FieldList {
 160  			typ := ResolveNameInline(field.Type, scope)
 161  			fname := ""
 162  			if field.Name != nil {
 163  				fname = field.Name.Value
 164  			} else {
 165  				fname = typeBaseName(typ)
 166  			}
 167  			fields = append(fields, NewTCField(nil, fname, typ, field.Name == nil))
 168  			tag := ""
 169  			if i < len(e.TagList) && e.TagList[i] != nil {
 170  				tag = e.TagList[i].Value
 171  			}
 172  			tags = append(tags, tag)
 173  		}
 174  		return types.NewTCStruct(fields, tags)
 175  	case *syntax.FuncType:
 176  		return ResolveFuncInline(e, scope)
 177  	case *syntax.InterfaceType:
 178  		return ResolveInterfaceInline(e, scope)
 179  	case *syntax.ChanType:
 180  		elem := ResolveNameInline(e.Elem, scope)
 181  		if elem == nil {
 182  			elem = types.NewTCStruct(nil, nil)
 183  		}
 184  		var dir types.TCChanDir
 185  		switch e.Dir {
 186  		case syntax.SendOnly:
 187  			dir = types.TCSendOnly
 188  		case syntax.RecvOnly:
 189  			dir = types.TCRecvOnly
 190  		default:
 191  			dir = types.TCSendRecv
 192  		}
 193  		return types.NewTCChan(dir, elem)
 194  	case *syntax.DotsType:
 195  		elem := ResolveNameInline(e.Elem, scope)
 196  		if elem != nil {
 197  			if b, ok := elem.(*types.Basic); ok && b.Kind() == Uint8 {
 198  				return types.Typ[types.TCString]
 199  			}
 200  			return types.NewSlice(elem)
 201  		}
 202  	}
 203  	return nil
 204  }
 205  
 206  func ResolveInterfaceInline(e *syntax.InterfaceType, scope *types.Scope) *types.TCInterface {
 207  	var methods []*types.IfaceMethod
 208  	var embeds []syntax.Type
 209  	for _, f := range e.MethodList {
 210  		if f.Name == nil {
 211  			if f.Type != nil {
 212  				embed := ResolveNameInline(f.Type, scope)
 213  				if embed != nil {
 214  					embeds = append(embeds, embed)
 215  				}
 216  			}
 217  			continue
 218  		}
 219  		ft, ok := f.Type.(*syntax.FuncType)
 220  		if !ok {
 221  			continue
 222  		}
 223  		sig := ResolveFuncInline(ft, scope)
 224  		if sig != nil {
 225  			methods = append(methods, types.NewTCIfaceMethod(f.Name.Value, sig))
 226  		}
 227  	}
 228  	iface := types.NewTCInterface(methods, embeds)
 229  	iface.Complete()
 230  	return iface
 231  }
 232  
 233  func ResolveFuncInline(ft *syntax.FuncType, scope *types.Scope) *types.Signature {
 234  	if ft == nil {
 235  		return nil
 236  	}
 237  	var params []*types.TCVar
 238  	for _, p := range ft.ParamList {
 239  		typ := ResolveNameInline(p.Type, scope)
 240  		pname := ""
 241  		if p.Name != nil {
 242  			pname = p.Name.Value
 243  		}
 244  		params = append(params, types.NewTCVar(nil, pname, typ))
 245  	}
 246  	var results []*types.TCVar
 247  	for _, r := range ft.ResultList {
 248  		typ := ResolveNameInline(r.Type, scope)
 249  		rname := ""
 250  		if r.Name != nil {
 251  			rname = r.Name.Value
 252  		}
 253  		results = append(results, types.NewTCVar(nil, rname, typ))
 254  	}
 255  	variadic := false
 256  	if len(ft.ParamList) > 0 {
 257  		if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*syntax.DotsType); ok {
 258  			variadic = true
 259  		}
 260  	}
 261  	var pTuple *types.Tuple
 262  	if len(params) > 0 {
 263  		pTuple = types.NewTuple(params...)
 264  	}
 265  	var rTuple *types.Tuple
 266  	if len(results) > 0 {
 267  		rTuple = types.NewTuple(results...)
 268  	}
 269  	return types.NewSignature(nil, pTuple, rTuple, variadic)
 270  }
 271  
 272  func ResolveFuncInlineWithRecv(ft *syntax.FuncType, recv *syntax.Field, scope *types.Scope) *types.Signature {
 273  	if ft == nil {
 274  		return nil
 275  	}
 276  	var recvVar *types.TCVar
 277  	if recv != nil {
 278  		recvTyp := ResolveNameInline(recv.Type, scope)
 279  		recvName := ""
 280  		if recv.Name != nil {
 281  			recvName = recv.Name.Value
 282  		}
 283  		recvVar = types.NewTCVar(nil, recvName, recvTyp)
 284  	}
 285  	params := ResolveFieldList(ft.ParamList, scope)
 286  	results := ResolveFieldList(ft.ResultList, scope)
 287  	variadic := false
 288  	if len(ft.ParamList) > 0 {
 289  		if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*syntax.DotsType); ok {
 290  			variadic = true
 291  		}
 292  	}
 293  	return types.NewSignature(recvVar, params, results, variadic)
 294  }
 295  
 296  func ResolveRecvType(recv *syntax.Field, scope *types.Scope) syntax.Type {
 297  	if recv == nil {
 298  		return nil
 299  	}
 300  	return ResolveNameInline(recv.Type, scope)
 301  }
 302  
 303  func ResolveFieldList(fields []*syntax.Field, scope *types.Scope) *types.Tuple {
 304  	if len(fields) == 0 {
 305  		return nil
 306  	}
 307  	var vars []*types.TCVar
 308  	for _, f := range fields {
 309  		typ := ResolveNameInline(f.Type, scope)
 310  		pname := ""
 311  		if f.Name != nil {
 312  			pname = f.Name.Value
 313  		}
 314  		vars = append(vars, types.NewTCVar(nil, pname, typ))
 315  	}
 316  	return types.NewTuple(vars...)
 317  }
 318  
 319  func InferTypeFromExpr(e syntax.Expr, scope *types.Scope) syntax.Type {
 320  	switch e := e.(type) {
 321  	case *syntax.BasicLit:
 322  		switch e.Kind {
 323  		case token.StringLit:
 324  			return types.Typ[types.TCString]
 325  		case token.IntLit:
 326  			return types.Typ[types.Int32]
 327  		case token.FloatLit:
 328  			return types.Typ[types.Float64]
 329  		}
 330  	case *syntax.Name:
 331  		if e.Value == "true" || e.Value == "false" {
 332  			return types.Typ[types.Bool]
 333  		}
 334  		return ResolveNameInline(e, scope)
 335  	case *syntax.CallExpr:
 336  		ft := ResolveNameInline(e.Fun, scope)
 337  		if sig, ok := ft.(*types.Signature); ok && sig != nil {
 338  			res := sig.Results()
 339  			if res != nil && res.Len() == 1 {
 340  				return res.At(0).Type()
 341  			}
 342  			if res != nil && res.Len() > 1 {
 343  				return res
 344  			}
 345  		}
 346  		return ft
 347  	case *syntax.CompositeLit:
 348  		t := ResolveNameInline(e.Type, scope)
 349  		if arr, ok := t.(*types.Array); ok && arr.Len() < 0 {
 350  			return types.NewArray(arr.Elem(), int64(len(e.ElemList)))
 351  		}
 352  		return t
 353  	case *syntax.Operation:
 354  		if e.Y == nil && e.Op == And {
 355  			return types.NewPointer(InferTypeFromExpr(e.X, scope))
 356  		}
 357  	}
 358  	return nil
 359  }
 360  
 361  func EvalConstExpr(e syntax.Expr, scope *types.Scope, iotaVal int64) types.ConstVal {
 362  	if e == nil {
 363  		return nil
 364  	}
 365  	switch e := e.(type) {
 366  	case *syntax.BasicLit:
 367  		return types.EvalBasicLitLocal(e)
 368  	case *syntax.Name:
 369  		if e.Value == "iota" && iotaVal >= 0 {
 370  			return &types.ConstInt{V:iotaVal}
 371  		}
 372  		if scope != nil {
 373  			_, obj := scope.LookupParent(e.Value)
 374  			if c, ok := obj.(*types.TCConst); ok && c.Val() != nil {
 375  				return c.Val()
 376  			}
 377  		}
 378  		if ConstValMap != nil {
 379  			if v, ok := ConstValMap[e.Value]; ok {
 380  				return &types.ConstInt{V:v}
 381  			}
 382  		}
 383  	case *syntax.Operation:
 384  		if e.Y == nil {
 385  			xr := EvalConstExpr(e.X, scope, iotaVal)
 386  			if xr == nil {
 387  				return nil
 388  			}
 389  			return evalUnaryLocal(e.Op, xr)
 390  		}
 391  		xr := EvalConstExpr(e.X, scope, iotaVal)
 392  		yr := EvalConstExpr(e.Y, scope, iotaVal)
 393  		if xr == nil || yr == nil {
 394  			return nil
 395  		}
 396  		return evalBinaryLocal(e.Op, xr, yr)
 397  	case *syntax.ParenExpr:
 398  		return EvalConstExpr(e.X, scope, iotaVal)
 399  	case *syntax.CallExpr:
 400  		if id, ok := e.Fun.(*syntax.Name); ok && id.Value == "len" && len(e.ArgList) == 1 {
 401  			if lit, ok2 := e.ArgList[0].(*syntax.BasicLit); ok2 && lit.Kind == token.StringLit {
 402  				lv := types.EvalBasicLitLocal(lit)
 403  				if cs, ok3 := lv.(*types.ConstStr); ok3 {
 404  					return &types.ConstInt{V:int64(len(cs.S))}
 405  				}
 406  			}
 407  			inner := EvalConstExpr(e.ArgList[0], scope, iotaVal)
 408  			if cs, ok2 := inner.(*types.ConstStr); ok2 {
 409  				return &types.ConstInt{V:int64(len(cs.S))}
 410  			}
 411  		}
 412  		if sel, ok := e.Fun.(*syntax.SelectorExpr); ok {
 413  			if pkg, ok2 := sel.X.(*syntax.Name); ok2 && pkg.Value == "unsafe" {
 414  				switch sel.Sel.Value {
 415  				case "Sizeof", "Alignof", "Offsetof":
 416  					return &types.ConstInt{V:8}
 417  				}
 418  			}
 419  		}
 420  		if len(e.ArgList) != 1 {
 421  			return nil
 422  		}
 423  		inner := EvalConstExpr(e.ArgList[0], scope, iotaVal)
 424  		if inner == nil {
 425  			return nil
 426  		}
 427  		targetType := ResolveNameInline(e.Fun, scope)
 428  		if targetType == nil {
 429  			return inner
 430  		}
 431  		return convertConstLocal(inner, targetType)
 432  	case *syntax.SelectorExpr:
 433  		pkgName, ok := e.X.(*syntax.Name)
 434  		if !ok {
 435  			return nil
 436  		}
 437  		var imported *types.TCPackage
 438  		if scope != nil {
 439  			_, obj := scope.LookupParent(pkgName.Value)
 440  			if pn, ok2 := obj.(*types.PkgName); ok2 && pn.Imported() != nil {
 441  				imported = pn.Imported()
 442  			}
 443  		}
 444  		if imported == nil {
 445  			EnsureImportRegistry()
 446  			imported = ImportRegistry[pkgName.Value]
 447  		}
 448  		if imported == nil {
 449  			return nil
 450  		}
 451  		member := imported.Scope().Lookup(e.Sel.Value)
 452  		if member == nil {
 453  			return nil
 454  		}
 455  		if k, ok := member.(*types.TCConst); ok && k.Val() != nil {
 456  			return k.Val()
 457  		}
 458  		return nil
 459  	}
 460  	return nil
 461  }
 462  
 463  func ResolveArrayLenFromSrc(p token.Pos, cmap map[string]int64) int64 {
 464  	line := p.Line()
 465  	col := p.Col()
 466  	if line == 0 || col == 0 || PkgSrc == nil {
 467  		return -1
 468  	}
 469  	off := 0
 470  	curLine := uint32(1)
 471  	for off < len(PkgSrc) && curLine < line {
 472  		if PkgSrc[off] == '\n' {
 473  			curLine++
 474  		}
 475  		off++
 476  	}
 477  	off += int32(col) - 1
 478  	if off >= len(PkgSrc) || PkgSrc[off] != '[' {
 479  		return -1
 480  	}
 481  	off++
 482  	for off < len(PkgSrc) && PkgSrc[off] == ' ' {
 483  		off++
 484  	}
 485  	start := off
 486  	for off < len(PkgSrc) {
 487  		c := PkgSrc[off]
 488  		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' {
 489  			off++
 490  		} else {
 491  			break
 492  		}
 493  	}
 494  	if off == start {
 495  		return -1
 496  	}
 497  	name := string(PkgSrc[start:off])
 498  	if v, ok := cmap[name]; ok {
 499  		if off < len(PkgSrc) && PkgSrc[off] == '+' {
 500  			off++
 501  			for off < len(PkgSrc) && PkgSrc[off] == ' ' {
 502  				off++
 503  			}
 504  			numStart := off
 505  			for off < len(PkgSrc) && PkgSrc[off] >= '0' && PkgSrc[off] <= '9' {
 506  				off++
 507  			}
 508  			if off > numStart {
 509  				addend := int64(0)
 510  				for i := numStart; i < off; i++ {
 511  					addend = addend*10 + int64(PkgSrc[i]-'0')
 512  				}
 513  				return v + addend
 514  			}
 515  		}
 516  		return v
 517  	}
 518  	n := int64(0)
 519  	isNum := true
 520  	for i := start; i < off; i++ {
 521  		c := PkgSrc[i]
 522  		if c >= '0' && c <= '9' {
 523  			n = n*10 + int64(c-'0')
 524  		} else {
 525  			isNum = false
 526  			break
 527  		}
 528  	}
 529  	if isNum && off > start {
 530  		return n
 531  	}
 532  	return -1
 533  }
 534  
 535  func ResolveArrayLenFromConstMap(e syntax.Expr, cmap map[string]int64) int64 {
 536  	line := e.Pos().Line()
 537  	col := e.Pos().Col()
 538  	if line == 0 || col == 0 || PkgSrc == nil {
 539  		return -1
 540  	}
 541  	curLine := uint32(1)
 542  	off := 0
 543  	for off < len(PkgSrc) && curLine < line {
 544  		if PkgSrc[off] == '\n' {
 545  			curLine++
 546  		}
 547  		off++
 548  	}
 549  	off += int32(col) - 1
 550  	if off >= len(PkgSrc) {
 551  		return -1
 552  	}
 553  	start := off
 554  	for off < len(PkgSrc) {
 555  		c := PkgSrc[off]
 556  		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' {
 557  			off++
 558  		} else {
 559  			break
 560  		}
 561  	}
 562  	if off == start {
 563  		return -1
 564  	}
 565  	name := string(PkgSrc[start:off])
 566  	if v, ok := cmap[name]; ok {
 567  		return v
 568  	}
 569  	return -1
 570  }
 571