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