package muesli import "fmt" /* ParametersPerSignature is the amount of parameters that will be checked when type checking a signature. Limited mosty to allow hashability, that is, Signature is a map key. */ const ParametersPerSignature = 32 /* ReturnsPerSignature is the amount of parameters that will be checked when type checking a signature. Limited mosty to allow hashability, that is, Signature is a map key. */ const ReturnsPerSignature = 8 /* NamedWithType describes something that is named and that has a type. */ type NamedWithType struct { Name WordValue Type TypeValue } /* Parameter describes the name and type of a parameter of a command. */ type Parameter NamedWithType /* Returned describes the name and type of a value returned by a command. * It is in essence the same as a Parameter, the type is redefined to * avoid confounding the two. */ type Returned NamedWithType /* Signature describes the types of the arguments that a callable takes, as well as the returned values.*/ type Signature struct { // Parameters the callable takes. Parameters [ParametersPerSignature]Parameter // Returns results of the given types and names. Returns [ReturnsPerSignature]Returned // RequiresAtLeast is the required amount of parameters. // The callable may accept more than this amount, but not less. RequiresAtLeast int // ReturnsAtLeast is the minimal amount of return values for the // callable. The callable may return more than this amount, but not less. ReturnsAtLeast int } func (s Signature) String() string { res := "[" sep := "" for _, param := range s.Parameters { typ := param.Type if typ != ZeroType { res = res + sep + typ.String() sep = ", " } } res = res + "] -> [" sep = "" for _, param := range s.Returns { typ := param.Type if typ != ZeroType { res = res + sep + typ.String() sep = ", " } } res = res + "]" return res } func (s Signature) StringWithNames() string { res := "[" sep := "" for _, param := range s.Parameters { typ := param.Type if typ != ZeroType { res = res + sep + string(param.Name) + " " + typ.String() sep = ", " } } res = res + "] -> [" sep = "" for _, param := range s.Returns { typ := param.Type if typ != ZeroType { res = res + sep + string(param.Name) + " " + typ.String() sep = ", " } } res = res + "]" return res } func (signature * Signature) SetReturns(types ... TypeValue) { for i := 0 ; i < len(types) && i < len(signature.Returns) ; i ++ { signature.Returns[i].Type = types[i] signature.Returns[i].Name = WordValue(fmt.Sprintf("res%d", i)) } signature.ReturnsAtLeast = len(types) } func (signature * Signature) SetParameters(types ... TypeValue) { for i := 0 ; i < len(types) && i < len(signature.Parameters) ; i ++ { signature.Parameters[i].Type = types[i] signature.Parameters[i].Name = WordValue(fmt.Sprintf("arg%d", i)) } signature.RequiresAtLeast = len(types) } func NewSignature(types ... TypeValue) Signature { signature := Signature{} signature.SetParameters(types...) return signature } func NewSignatureWithNames(param ...Value) (Signature, error) { sign := Signature{} for i, j := 1, 0 ; i < len(param) ; i += 2 { name := param[i-1] typ := param[i] var ok bool var nv WordValue var tv TypeValue if nv, ok = name.(WordValue); !ok { return sign, fmt.Errorf("Not a word value: %v", name) } if tv, ok = typ.(TypeValue); !ok { return sign, fmt.Errorf("Not a type value: %v", typ) } sign.Parameters[j].Type = tv sign.Parameters[j].Name = nv j++ sign.RequiresAtLeast++ } return sign, nil } // NoSignature is the default signature which means the VM will not do // any argument type checking when the callable is called. func NoSignature() Signature { return Signature{} } func CalculateSignature(arguments ...Value) Signature { signature := Signature{} for i := 0; i < len(signature.Parameters); i++ { if i < len(arguments) { signature.Parameters[i].Type = arguments[i].Type() } else { signature.Parameters[i].Type = AnyType } } signature.RequiresAtLeast = len(arguments) return signature } func (tv TypeValue) IsMatch(other TypeValue) bool { if tv == AnyType || other == AnyType { return true } if tv == ZeroType || other == ZeroType { return true } return tv == other } func (signature Signature) IsMatch(other Signature) bool { for i, param := range signature.Parameters { t1 := param.Type t2 := other.Parameters[i].Type if !t1.IsMatch(t2) { return false } } return true } // TypeCheck checks if the arguments match the signature. // Returns nil if so, or an error if not. func (signature Signature) TypeCheck(arguments ...Value) error { if len(arguments) < signature.RequiresAtLeast { return fmt.Errorf("Too few arguments, expected %d: for %d", signature.RequiresAtLeast, len(arguments)) } for i , arg := range arguments { if i >= len(signature.Parameters) { break } param := signature.Parameters[i] expectedType := param.Type if arg == nil { return fmt.Errorf("Nil argument %d, expected %s: %v for %v", i, expectedType, arguments, signature) } if !expectedType.IsMatch(arg.Type()) { return fmt.Errorf("Argument %d type mismatch: %s<->%s", i, expectedType, arg.Type()) } } return nil }