tc_universe.mx raw

   1  package main
   2  
   3  // Universe is the outermost scope containing all predeclared identifiers.
   4  // All package scopes have Universe as their ultimate ancestor.
   5  var Universe *Scope
   6  
   7  // Predeclared types. Indices match BasicKind values.
   8  var Typ [UntypedNil + 1]*Basic
   9  
  10  var universeError *TypeName
  11  
  12  func initUniverse() {
  13  	if Universe != nil {
  14  		return
  15  	}
  16  	Universe = NewScope(nil)
  17  
  18  	// Initialize Basic types in Typ table.
  19  	infos := [...]struct {
  20  		kind BasicKind
  21  		info BasicInfo
  22  		name string
  23  	}{
  24  		{Invalid, 0, "invalid type"},
  25  
  26  		{Bool, IsBoolean, "bool"},
  27  
  28  		{Int8, IsInteger | IsOrdered | IsNumeric, "int8"},
  29  		{Int16, IsInteger | IsOrdered | IsNumeric, "int16"},
  30  		{Int32, IsInteger | IsOrdered | IsNumeric, "int32"},
  31  		{Int64, IsInteger | IsOrdered | IsNumeric, "int32"},
  32  		{Uint8, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint8"},
  33  		{Uint16, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint16"},
  34  		{Uint32, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint32"},
  35  		{Uint64, IsInteger | IsUnsigned | IsOrdered | IsNumeric, "uint32"},
  36  
  37  		{Float32, IsFloat | IsOrdered | IsNumeric, "float32"},
  38  		{Float64, IsFloat | IsOrdered | IsNumeric, "float64"},
  39  
  40  		// string and []byte share kind TCString in Moxie.
  41  		{TCString, IsString | IsOrdered, "string"},
  42  
  43  		{UnsafePointer, 0, "Pointer"}, // lives in unsafe package
  44  
  45  		{UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
  46  		{UntypedInt, IsInteger | IsNumeric | IsUntyped, "untyped int32"},
  47  		{UntypedRune, IsInteger | IsNumeric | IsUntyped, "untyped rune"},
  48  		{UntypedFloat, IsFloat | IsNumeric | IsUntyped, "untyped float"},
  49  		{UntypedString, IsString | IsUntyped, "untyped string"},
  50  		{UntypedNil, IsUntyped, "untyped nil"},
  51  	}
  52  	for i, info := range infos {
  53  		Typ[i] = &Basic{kind: info.kind, info: info.info, name: info.name}
  54  	}
  55  
  56  	// In Moxie, int32 = int32 and uint32 = uint32. We expose "int32" and "uint32"
  57  	// as named aliases to int32/uint32 in the universe scope.
  58  	defTypeName("bool", Typ[Bool])
  59  	defTypeName("int8", Typ[Int8])
  60  	defTypeName("int16", Typ[Int16])
  61  	defTypeName("int32", Typ[Int32])
  62  	defTypeName("int32", Typ[Int64])
  63  	defTypeName("uint8", Typ[Uint8])
  64  	defTypeName("uint16", Typ[Uint16])
  65  	defTypeName("uint32", Typ[Uint32])
  66  	defTypeName("uint32", Typ[Uint64])
  67  	defTypeName("float32", Typ[Float32])
  68  	defTypeName("float64", Typ[Float64])
  69  	defTypeName("string", Typ[TCString])
  70  
  71  	// int32 = int32, uint32 = uint32 (Moxie always 32-bit)
  72  	intType := NewNamed(NewTypeName(nil, "int32", nil), Typ[Int32])
  73  	uintType := NewNamed(NewTypeName(nil, "uint32", nil), Typ[Uint32])
  74  	defObj(intType.obj)
  75  	defObj(uintType.obj)
  76  	intType.obj.typ = intType
  77  	uintType.obj.typ = uintType
  78  
  79  	// byte = uint8
  80  	byteType := NewNamed(NewTypeName(nil, "byte", nil), Typ[Uint8])
  81  	defObj(byteType.obj)
  82  	byteType.obj.typ = byteType
  83  
  84  	// rune = int32
  85  	runeType := NewNamed(NewTypeName(nil, "rune", nil), Typ[Int32])
  86  	defObj(runeType.obj)
  87  	runeType.obj.typ = runeType
  88  
  89  	// uintptr (allowed only with import "unsafe")
  90  	uintptrType := NewNamed(NewTypeName(nil, "uintptr", nil), Typ[Uint64])
  91  	defObj(uintptrType.obj)
  92  	uintptrType.obj.typ = uintptrType
  93  
  94  	// error interface
  95  	errMeth := &IfaceMethod{name: "Error", sig: NewSignature(nil, nil, NewTuple(NewTCVar(nil, "", Typ[TCString])), false)}
  96  	errIface := NewTCInterface([]*IfaceMethod{errMeth}, nil)
  97  	errIface.Complete()
  98  	universeError = NewTypeName(nil, "error", errIface)
  99  	defObj(universeError)
 100  
 101  	// complex64/128 are not valid in Moxie programs but appear in stdlib source.
 102  	// Register them as invalid basic types so the checker doesn't report "undefined".
 103  	complex64Type := NewNamed(NewTypeName(nil, "complex64", nil), Typ[Invalid])
 104  	defObj(complex64Type.obj)
 105  	complex128Type := NewNamed(NewTypeName(nil, "complex128", nil), Typ[Invalid])
 106  	defObj(complex128Type.obj)
 107  
 108  	// any is an alias for interface{} (Go 1.18+)
 109  	anyIface := NewTCInterface(nil, nil)
 110  	anyIface.Complete()
 111  	anyObj := NewTypeName(nil, "any", anyIface)
 112  	defObj(anyObj)
 113  
 114  	// comparable is a builtin interface satisfied by comparable types.
 115  	comparableIface := NewTCInterface(nil, nil)
 116  	comparableIface.Complete()
 117  	comparableObj := NewTypeName(nil, "comparable", comparableIface)
 118  	defObj(comparableObj)
 119  
 120  	// predeclared constants
 121  	defConst("true", Typ[UntypedBool], untypedBool(true))
 122  	defConst("false", Typ[UntypedBool], untypedBool(false))
 123  	defConst("iota", Typ[UntypedInt], untypedInt(0))
 124  	defNil()
 125  
 126  	// predeclared builtins
 127  	builtinNames := [17]string{
 128  		BuiltinAppend:  "append",
 129  		BuiltinCap:     "cap",
 130  		BuiltinClear:   "clear",
 131  		BuiltinClose:   "close",
 132  		BuiltinComplex: "complex",
 133  		BuiltinCopy:    "copy",
 134  		BuiltinDelete:  "delete",
 135  		BuiltinImag:    "imag",
 136  		BuiltinLen:     "len",
 137  		BuiltinMake:    "make",
 138  		BuiltinNew:     "new",
 139  		BuiltinPanic:   "panic",
 140  		BuiltinPrint:   "print",
 141  		BuiltinPrintln: "println",
 142  		BuiltinReal:    "real",
 143  		BuiltinRecover: "recover",
 144  		BuiltinSpawn:   "spawn",
 145  	}
 146  	for id, name := range builtinNames {
 147  		Universe.Insert(newBuiltin(name, BuiltinID(id)))
 148  	}
 149  
 150  	// Go 1.21 builtins.
 151  	Universe.Insert(newBuiltin("min", BuiltinMin))
 152  	Universe.Insert(newBuiltin("max", BuiltinMax))
 153  }
 154  
 155  func defTypeName(name string, typ Type) {
 156  	tn := NewTypeName(nil, name, typ)
 157  	Universe.Insert(tn)
 158  }
 159  
 160  func defObj(obj Object) {
 161  	Universe.Insert(obj)
 162  }
 163  
 164  func defConst(name string, typ Type, val ConstVal) {
 165  	Universe.Insert(NewTCConst(nil, name, typ, val))
 166  }
 167  
 168  func defNil() {
 169  	Universe.Insert(NewTCConst(nil, "nil", Typ[UntypedNil], untypedNil()))
 170  }
 171  
 172  // Placeholder constant values. These wrap go/constant values. In B4 they
 173  // become native Moxie values.
 174  type constBool struct{ v bool }
 175  type constInt struct{ v int32 }
 176  type constFloat struct {
 177  	v   float64
 178  	lit string
 179  }
 180  type constStr struct{ s string }
 181  type constNil struct{}
 182  
 183  func untypedBool(v bool) ConstVal     { return constBool{v} }
 184  func untypedInt(v int32) ConstVal     { return constInt{v} }
 185  func untypedFloat(v float64) ConstVal { return constFloat{v: v} }
 186  func untypedStr(s string) ConstVal    { return constStr{s} }
 187  func untypedNil() ConstVal            { return constNil{} }
 188  
 189  func (c constBool) String() string { if c.v { return "true" }; return "false" }
 190  func (c constInt) String() string {
 191  	n := c.v
 192  	if n == 0 {
 193  		return "0"
 194  	}
 195  	neg := n < 0
 196  	if neg {
 197  		n = -n
 198  		if n < 0 {
 199  			return "-9223372036854775808"
 200  		}
 201  	}
 202  	buf := []byte{:0:20}
 203  	for n > 0 {
 204  		buf = append(buf, byte('0'+n%10))
 205  		n /= 10
 206  	}
 207  	if neg {
 208  		buf = append(buf, '-')
 209  	}
 210  	for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
 211  		buf[i], buf[j] = buf[j], buf[i]
 212  	}
 213  	return string(buf)
 214  }
 215  func (c constFloat) String() string {
 216  	if c.lit != "" {
 217  		return normalizeLLVMFloat(c.lit)
 218  	}
 219  	if c.v == 0.0 {
 220  		return "0.0"
 221  	}
 222  	return "0.0"
 223  }
 224  func (c constStr) String() string { return c.s }
 225  func (c constNil) String() string { return "nil" }
 226  
 227  func normalizeLLVMFloat(s string) string {
 228  	if len(s) == 0 {
 229  		return "0.0"
 230  	}
 231  	start := 0
 232  	if s[0] == '-' || s[0] == '+' {
 233  		start = 1
 234  	}
 235  	hasDot := false
 236  	ePos := -1
 237  	for i := start; i < len(s); i++ {
 238  		if s[i] == '.' {
 239  			hasDot = true
 240  		}
 241  		if s[i] == 'e' || s[i] == 'E' {
 242  			ePos = i
 243  			break
 244  		}
 245  	}
 246  	if !hasDot {
 247  		if ePos >= 0 {
 248  			return s[:ePos] + ".0" + s[ePos:]
 249  		}
 250  		return s + ".0"
 251  	}
 252  	dotIdx := -1
 253  	for i := start; i < len(s); i++ {
 254  		if s[i] == '.' {
 255  			dotIdx = i
 256  			break
 257  		}
 258  	}
 259  	if dotIdx == start {
 260  		s = s[:start] + "0" + s[start:]
 261  		dotIdx++
 262  	}
 263  	afterDot := dotIdx + 1
 264  	if afterDot >= len(s) || s[afterDot] == 'e' || s[afterDot] == 'E' {
 265  		s = s[:afterDot] + "0" + s[afterDot:]
 266  	}
 267  	return s
 268  }
 269  
 270  // Predeclared returns the universe-scope object with the given name, or nil.
 271  func Predeclared(name string) Object {
 272  	return Universe.Lookup(name)
 273  }
 274