github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/sourcecode/typesystem/typesystem.go (about) 1 // Package typesystem implements type-system with generics and structural subtyping. 2 // For convenience these structures have json tags (like `src` package). 3 // This is not clean architecture but it's very handy for LSP. 4 package typesystem 5 6 import ( 7 "github.com/nevalang/neva/internal/compiler/sourcecode/core" 8 ) 9 10 type Def struct { 11 // Body can refer to these. Must be replaced with arguments while resolving 12 Params []Param `json:"params,omitempty"` 13 // Empty body means base type 14 BodyExpr *Expr `json:"bodyExpr,omitempty"` 15 // Meta can be used to store anything that can be useful for typesystem user. It is ignored by the typesystem itself. 16 Meta ExprMeta `json:"meta,omitempty"` 17 } 18 19 func (def Def) String() string { 20 var params string 21 22 params += "<" 23 for i, param := range def.Params { 24 params += param.Name 25 params += " " + param.Constr.String() 26 if i < len(def.Params)-1 { 27 params += ", " 28 } 29 } 30 params += ">" 31 32 return params + " = " + def.BodyExpr.String() 33 } 34 35 type Param struct { 36 Name string `json:"name,omitempty"` // Must be unique among other type's parameters 37 Constr Expr `json:"constr,omitempty"` // Expression that must be resolved supertype of corresponding argument 38 } 39 40 // Instantiation or literal. Lit or Inst must be not nil, but not both 41 type Expr struct { 42 Lit *LitExpr `json:"lit,omitempty"` 43 Inst *InstExpr `json:"inst,omitempty"` 44 Meta ExprMeta `json:"meta,omitempty"` // This field must be ignored by the typesystem and only used outside 45 } 46 47 // ExprMeta can contain any meta information that typesystem user might need e.g. source code text representation. 48 type ExprMeta any 49 50 // String formats expression in a TS manner 51 func (expr Expr) String() string { 52 if expr.Inst == nil && expr.Lit == nil { 53 return "empty" 54 } 55 56 var str string 57 58 switch expr.Lit.Type() { 59 case EnumLitType: 60 str += "{" 61 for i, el := range expr.Lit.Enum { 62 str += " " + el 63 if i == len(expr.Lit.Enum)-1 { 64 str += " " 65 } else { 66 str += "," 67 } 68 } 69 return str + "}" 70 case StructLitType: 71 str += "{" 72 count := 0 73 for fieldName, fieldExpr := range expr.Lit.Struct { 74 str += " " + fieldName + " " + fieldExpr.String() 75 if count < len(expr.Lit.Struct)-1 { 76 str += "," 77 } else { 78 str += " " 79 } 80 count++ 81 } 82 return str + "}" 83 case UnionLitType: 84 for i, el := range expr.Lit.Union { 85 str += el.String() 86 if i < len(expr.Lit.Union)-1 { 87 str += " | " 88 } 89 } 90 return str 91 } 92 93 if len(expr.Inst.Args) == 0 { 94 return expr.Inst.Ref.String() 95 } 96 97 str = expr.Inst.Ref.String() 98 str += "<" 99 100 for i, arg := range expr.Inst.Args { 101 str += arg.String() 102 if i < len(expr.Inst.Args)-1 { 103 str += ", " 104 } 105 } 106 str += ">" 107 108 return str 109 } 110 111 // Instantiation expression 112 type InstExpr struct { 113 Ref core.EntityRef `json:"ref,omitempty"` // Must be in the scope 114 Args []Expr `json:"args,omitempty"` // Every ref's parameter must have subtype argument 115 } 116 117 // Literal expression. Only one field must be initialized 118 type LitExpr struct { 119 Struct map[string]Expr `json:"struct,omitempty"` 120 Enum []string `json:"enum,omitempty"` 121 Union []Expr `json:"union,omitempty"` 122 } 123 124 func (lit *LitExpr) Empty() bool { 125 return lit == nil || 126 lit.Struct == nil && 127 lit.Enum == nil && 128 lit.Union == nil 129 } 130 131 // Always call Validate before 132 func (lit *LitExpr) Type() LiteralType { 133 switch { 134 case lit == nil: 135 return EmptyLitType 136 case lit.Struct != nil: 137 return StructLitType 138 case lit.Enum != nil: 139 return EnumLitType 140 case lit.Union != nil: 141 return UnionLitType 142 } 143 return EmptyLitType // for inst or invalid lit 144 } 145 146 type LiteralType uint8 147 148 const ( 149 EmptyLitType LiteralType = iota 150 StructLitType 151 EnumLitType 152 UnionLitType 153 )