github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/analyzer/const.go (about)

     1  package analyzer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/nevalang/neva/internal/compiler"
     8  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
     9  )
    10  
    11  var (
    12  	ErrEmptyConst         = errors.New("Constant must either have value or reference to another constant")
    13  	ErrEntityNotConst     = errors.New("Constant refers to an entity that is not constant")
    14  	ErrResolveConstType   = errors.New("Cannot resolve constant type")
    15  	ErrUnionConst         = errors.New("Constant cannot have type union")
    16  	ErrConstSeveralValues = errors.New("Constant cannot have several values at once")
    17  )
    18  
    19  //nolint:funlen
    20  func (a Analyzer) analyzeConst(
    21  	constant src.Const,
    22  	scope src.Scope,
    23  ) (src.Const, *compiler.Error) {
    24  	if constant.Message == nil && constant.Ref == nil {
    25  		return src.Const{}, &compiler.Error{
    26  			Err:      ErrEmptyConst,
    27  			Location: &scope.Location,
    28  			Meta:     &constant.Meta,
    29  		}
    30  	}
    31  
    32  	if constant.Message == nil { // is ref
    33  		entity, location, err := scope.Entity(*constant.Ref)
    34  		if err != nil {
    35  			return src.Const{}, &compiler.Error{
    36  				Err:      err,
    37  				Location: &location,
    38  				Meta:     entity.Meta(),
    39  			}
    40  		}
    41  
    42  		if entity.Kind != src.ConstEntity {
    43  			return src.Const{}, &compiler.Error{
    44  				Err:      fmt.Errorf("%w: entity kind %v", ErrEntityNotConst, entity.Kind),
    45  				Location: &location,
    46  				Meta:     entity.Meta(),
    47  			}
    48  		}
    49  
    50  		return a.analyzeConst(entity.Const, scope)
    51  	}
    52  
    53  	resolvedType, err := a.analyzeTypeExpr(constant.Message.TypeExpr, scope)
    54  	if err != nil {
    55  		return src.Const{}, compiler.Error{
    56  			Err:      ErrResolveConstType,
    57  			Location: &scope.Location,
    58  			Meta:     &constant.Meta,
    59  		}.Wrap(err)
    60  	}
    61  
    62  	if resolvedType.Lit != nil && resolvedType.Lit.Union != nil {
    63  		return src.Const{}, &compiler.Error{
    64  			Err:      ErrUnionConst,
    65  			Location: &scope.Location,
    66  			Meta:     &constant.Meta,
    67  		}
    68  	}
    69  
    70  	var typeExprStrRepr string
    71  	if inst := resolvedType.Inst; inst != nil {
    72  		typeExprStrRepr = inst.Ref.String()
    73  	} else if lit := resolvedType.Lit; lit != nil {
    74  		if lit.Enum != nil {
    75  			typeExprStrRepr = "enum"
    76  		} else if lit.Struct != nil {
    77  			typeExprStrRepr = "struct"
    78  		}
    79  	}
    80  
    81  	switch typeExprStrRepr {
    82  	case "bool":
    83  		if constant.Message.Bool == nil {
    84  			return src.Const{}, &compiler.Error{
    85  				Err:      fmt.Errorf("Boolean value is missing in boolean contant: %v", constant),
    86  				Location: &scope.Location,
    87  				Meta:     &constant.Meta,
    88  			}
    89  		}
    90  		if constant.Message.Int != nil ||
    91  			constant.Message.Float != nil ||
    92  			constant.Message.Str != nil ||
    93  			constant.Message.List != nil ||
    94  			constant.Message.MapOrStruct != nil ||
    95  			constant.Message.Enum != nil {
    96  			return src.Const{}, &compiler.Error{
    97  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant),
    98  				Location: &scope.Location,
    99  				Meta:     &constant.Meta,
   100  			}
   101  		}
   102  	case "int":
   103  		if constant.Message.Int == nil {
   104  			return src.Const{}, &compiler.Error{
   105  				Err:      fmt.Errorf("Integer value is missing in integer contant: %v", constant),
   106  				Location: &scope.Location,
   107  				Meta:     &constant.Meta,
   108  			}
   109  		}
   110  		if constant.Message.Bool != nil ||
   111  			constant.Message.Float != nil ||
   112  			constant.Message.Str != nil ||
   113  			constant.Message.List != nil ||
   114  			constant.Message.MapOrStruct != nil ||
   115  			constant.Message.Enum != nil {
   116  			return src.Const{}, &compiler.Error{
   117  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   118  				Location: &scope.Location,
   119  				Meta:     &constant.Meta,
   120  			}
   121  		}
   122  	case "float":
   123  		// Float is special case. Constant can have float type expression but integer literal.
   124  		// We must pass this case. Desugarer will turn integer literal into float.
   125  		if constant.Message.Float == nil && constant.Message.Int == nil {
   126  			return src.Const{}, &compiler.Error{
   127  				Err:      fmt.Errorf("Float or integer value is missing in float contant: %v", constant),
   128  				Location: &scope.Location,
   129  				Meta:     &constant.Meta,
   130  			}
   131  		}
   132  		if constant.Message.Float != nil && constant.Message.Int != nil {
   133  			return src.Const{}, &compiler.Error{
   134  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   135  				Location: &scope.Location,
   136  				Meta:     &constant.Meta,
   137  			}
   138  		}
   139  		if constant.Message.Bool != nil ||
   140  			constant.Message.Str != nil ||
   141  			constant.Message.List != nil ||
   142  			constant.Message.MapOrStruct != nil ||
   143  			constant.Message.Enum != nil {
   144  			return src.Const{}, &compiler.Error{
   145  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   146  				Location: &scope.Location,
   147  				Meta:     &constant.Meta,
   148  			}
   149  		}
   150  	case "string":
   151  		if constant.Message.Str == nil {
   152  			return src.Const{}, &compiler.Error{
   153  				Err:      fmt.Errorf("String value is missing in string contant: %v", constant),
   154  				Location: &scope.Location,
   155  				Meta:     &constant.Meta,
   156  			}
   157  		}
   158  		if constant.Message.Bool != nil ||
   159  			constant.Message.Int != nil ||
   160  			constant.Message.Float != nil ||
   161  			constant.Message.List != nil ||
   162  			constant.Message.MapOrStruct != nil ||
   163  			constant.Message.Enum != nil {
   164  			return src.Const{}, &compiler.Error{
   165  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   166  				Location: &scope.Location,
   167  				Meta:     &constant.Meta,
   168  			}
   169  		}
   170  	case "list":
   171  		if constant.Message.List == nil {
   172  			return src.Const{}, &compiler.Error{
   173  				Err:      fmt.Errorf("List value is missing in list contant: %v", constant),
   174  				Location: &scope.Location,
   175  				Meta:     &constant.Meta,
   176  			}
   177  		}
   178  		if constant.Message.Bool != nil ||
   179  			constant.Message.Int != nil ||
   180  			constant.Message.Float != nil ||
   181  			constant.Message.MapOrStruct != nil ||
   182  			constant.Message.Enum != nil {
   183  			return src.Const{}, &compiler.Error{
   184  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   185  				Location: &scope.Location,
   186  				Meta:     &constant.Meta,
   187  			}
   188  		}
   189  	case "map", "struct":
   190  		if constant.Message.MapOrStruct == nil {
   191  			return src.Const{}, &compiler.Error{
   192  				Err:      fmt.Errorf("Map or struct value is missing in map or struct contant: %v", constant),
   193  				Location: &scope.Location,
   194  				Meta:     &constant.Meta,
   195  			}
   196  		}
   197  		if constant.Message.Bool != nil ||
   198  			constant.Message.Int != nil ||
   199  			constant.Message.Float != nil ||
   200  			constant.Message.List != nil ||
   201  			constant.Message.Enum != nil {
   202  			return src.Const{}, &compiler.Error{
   203  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   204  				Location: &scope.Location,
   205  				Meta:     &constant.Meta,
   206  			}
   207  		}
   208  	case "enum":
   209  		if constant.Message.Enum == nil {
   210  			return src.Const{}, &compiler.Error{
   211  				Err:      fmt.Errorf("Enum value is missing in enum contant: %v", constant),
   212  				Location: &scope.Location,
   213  				Meta:     &constant.Meta,
   214  			}
   215  		}
   216  		if constant.Message.Bool != nil ||
   217  			constant.Message.Int != nil ||
   218  			constant.Message.Float != nil ||
   219  			constant.Message.List != nil ||
   220  			constant.Message.MapOrStruct != nil {
   221  			return src.Const{}, &compiler.Error{
   222  				Err:      fmt.Errorf("%w: %v", ErrConstSeveralValues, constant.Message),
   223  				Location: &scope.Location,
   224  				Meta:     &constant.Meta,
   225  			}
   226  		}
   227  	}
   228  
   229  	valueCopy := *constant.Message
   230  	valueCopy.TypeExpr = resolvedType
   231  
   232  	return src.Const{
   233  		Message: &valueCopy,
   234  		Meta:    constant.Meta,
   235  	}, nil
   236  }