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  )