Browse Source

[PATCH 4/4] Signatures for all callables. Makes type checking possible and help better structured.

Beoran 4 years ago
parent
commit
62ef94fdc0
3 changed files with 137 additions and 42 deletions
  1. 19 21
      builtin.go
  2. 92 7
      signature.go
  3. 26 14
      vm.go

+ 19 - 21
builtin.go

@@ -222,22 +222,9 @@ func to(vm *VM, args ...Value) []Value {
 		return Fail(NewErrorValuef("Not a block: %v", last))
 	}
 	param := rest[0:len(rest)-1]
-	sign := &Signature{}
-	for i := 1 ; 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 Fail(NewErrorValuef("Not a word value: %v", name))
-		}
-		if tv, ok = typ.(TypeValue); !ok {
-			return Fail(NewErrorValuef("Not a type value: %v", typ))
-		}
-		sign.Parameters[i].Type = tv
-		sign.Parameters[i].Name = nv
+	sign, err := NewSignatureWithNames(param...)
+	if err != nil {
+		return Fail(NewErrorValuef("Not a word value: %v", name))
 	}
 	
 	// To must register one level up.
@@ -388,7 +375,11 @@ func help(vm *VM, val ...Value) [] Value {
 	
 	if helper, isHelper := target.(Helper) ; isHelper {
 		help := helper.Help()
-		fmt.Printf("%s: %s.\n", targetName, help)
+		if call, isCall := target.(Callable); isCall { 
+			fmt.Printf("%s %s: %s.\n", targetName, call.Signature().String(), help)
+		} else {
+			fmt.Printf("%s: %s.\n", targetName, help)
+		}
 		return Ok(StringValue(help))
 	}
 	
@@ -515,9 +506,9 @@ func openDoor(vm *VM, val ...Value) [] Value {
 }
 
 func (vm *VM) RegisterBuiltins() {
-	vm.RegisterBuiltinWithHelp("addi", addi, `[Int Int] -> Int: adds two integers together`)
-	vm.RegisterBuiltinWithHelp("addf", addf, `[Int Int] -> Int: adds two floats together`)
-	vm.RegisterBuiltinWithHelp("andb", andb, `[Bool Bool] -> Bool: returns true if all it's arguments are true`)
+	vm.RegisterBuiltinWithHelp("addi", addi, `adds two integers together`).Takes(IntTypeValue, IntTypeValue).Returns(BoolTypeValue)
+	vm.RegisterBuiltinWithHelp("addf", addf, `adds two floats together`).Takes(IntTypeValue, IntTypeValue).Returns(IntTypeValue)
+	vm.RegisterBuiltinWithHelp("andb", andb, `returns true if all it's arguments are true`).Takes(BoolTypeValue, BoolTypeValue).Returns(BoolTypeValue)
 	vm.RegisterBuiltin("cover", cover)
 	vm.RegisterBuiltin("fetchl", fetchl)
 	vm.RegisterBuiltin("fetchm", fetchm)
@@ -556,7 +547,14 @@ func (vm *VM) RegisterBuiltins() {
 			Over("mulf", 0, FloatTypeValue, FloatTypeValue),
 			Over("muli", 0, IntTypeValue, IntTypeValue),
 			Over("mulf", 0, FloatTypeValue, IntTypeValue),
-			Over("mulf", 0, FloatTypeValue, IntTypeValue))
+			Over("mulf", 0, IntTypeValue, FloatTypeValue))
+	
+	vm.AddOverloads("add", 
+			Over("addf", 0, FloatTypeValue, FloatTypeValue),
+			Over("addi", 0, IntTypeValue, IntTypeValue),
+			Over("addf", 0, FloatTypeValue, IntTypeValue),
+			Over("addf", 0, IntTypeValue, FloatTypeValue))
+	
 			
 	vm.SetHelp("mul", "	 Num: Multiplies two numbers. Cover for muli and mulf.")	
 	vm.AddOverloads("fetch", 

+ 92 - 7
signature.go

@@ -1,5 +1,7 @@
 package muesli
 
+import "fmt"
+
 /*
 ParametersPerSignature is the amount of parameters that will be
 checked when type checking a signature.	Limited mosty to allow hashability, 
@@ -7,15 +9,33 @@ that is, Signature is a map key.
 */
 const ParametersPerSignature = 32
 
-/* Parameter describes the name and type of a parameter of a command. */
-type Parameter struct {
+/*
+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
 }
 
-// Signature describes the types of the arguments that a callable takes. */
+/* 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 [ParametersPerSignature]Parameter
+    Returns [ReturnsPerSignature]Returned
 }
 
 func (s Signature) String() string {
@@ -25,21 +45,86 @@ func (s Signature) String() string {
         typ := param.Type
 		if typ != ZeroTypeValue { 
 			res = res + sep + typ.String()
-			sep = " "
+			sep = ", "
 		}
 	}
-	res = res + "]"
+	res = res + "] -> ["
+    sep = ""
+    for _, param := range s.Returns {
+        typ := param.Type
+		if typ != ZeroTypeValue { 
+			res = res + sep + typ.String()
+			sep = ", "
+		}
+	}
+	res = res + "]"    
 	return res
 }
 
-func NewSignature(types ... TypeValue) Signature {
-    signature := Signature{}
+func (s Signature) StringWithNames() string {
+	res := "["
+	sep := ""
+	for _, param := range s.Parameters {
+        typ := param.Type
+		if typ != ZeroTypeValue { 
+			res = res + sep + string(param.Name) + " " + typ.String()
+			sep = ", "
+		}
+	}
+	res = res + "] -> ["
+    sep = ""
+    for _, param := range s.Returns {
+        typ := param.Type
+		if typ != ZeroTypeValue { 
+			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))
+    }
+}
+
+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))
     }
+}
+
+func NewSignature(types ... TypeValue) Signature {
+    signature := Signature{}
+    signature.SetParameters(types...)
     return signature
 }
 
+func NewSignatureWithNames(param ...Value) (Signature, error) {
+    sign := Signature{}
+	for i := 1 ; 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[i].Type = tv
+		sign.Parameters[i].Name = nv
+	}
+	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 { 

+ 26 - 14
vm.go

@@ -73,6 +73,15 @@ func ClearHelp(helper Helper, extra string) string {
 	return helper.SetHelp("")
 }
 
+func (bc * BasicCallable) Takes(arguments ...TypeValue) *BasicCallable {
+	bc.signature.SetParameters(arguments...)
+	return bc
+}
+
+func (bc * BasicCallable) Returns(arguments ...TypeValue) {
+	bc.signature.SetReturns(arguments...)
+}
+
 
 func (val BasicCallable) String() string {
 	return val.Name
@@ -153,24 +162,23 @@ func (cv BasicCallable) Position() *Position {
 type DefinedValue struct {	
 	BasicCallable
 	Body *BlockValue
-	Signature *Signature
 }
 
-func NewDefinedValue(name string, signature *Signature, body *BlockValue) *DefinedValue {
+func NewDefinedValue(name string, signature Signature, body *BlockValue) *DefinedValue {
 	result := &DefinedValue{}
 	result.Name = name
 	result.Body = body
-	result.Signature = signature
+	result.signature = signature
 	return result
 }
 
 func (defined *DefinedValue) Call(vm *VM, arguments ...Value) []Value {
-	vm.Trace("Call defined value: %v %v %v", defined, defined.Signature, arguments)
+	vm.Trace("Call defined value: %v %v %v", defined, defined.signature, arguments)
     for i , arg := range arguments {
-		if i >= len(defined.Signature.Parameters) {
+		if i >= len(defined.signature.Parameters) {
 			break
 		}
-		param := defined.Signature.Parameters[i]
+		param := defined.signature.Parameters[i]
 		expectedType := param.Type
 		if !expectedType.IsMatch(arg.Type()) {
 			return Fail(NewErrorValuef("Argument %d type mismatch: %s<->%s", i, expectedType, arg.Type()))
@@ -187,14 +195,15 @@ func (defined *DefinedValue) Call(vm *VM, arguments ...Value) []Value {
 func (defined *DefinedValue) Help() string {
 	help := defined.BasicCallable.Help()
 	extra := "["
-	for _, parameter := range defined.Signature.Parameters {
+	for _, parameter := range defined.signature.Parameters {
 		extra = fmt.Sprintf("%s %s %s", extra, parameter.Name, parameter.Type)
 	}
 	extra = extra + "]:"
 	return extra + help
 }
 
-
+// Assert that DefinedValue is callable.
+var _ Callable = &DefinedValue{}
 
 /* An overload is an overloaded value that can be called. */
 type Overload struct {
@@ -553,6 +562,7 @@ func (builtin * BuiltinValue) Call(vm *VM, arguments ...Value) []Value {
 	return handler.Call(vm, arguments...)
 }
 
+	
 /*
 func (vm *VM) CallDefined(ast *Ast, arguments ...Value) []Value {	
 	arr := vm.RunChildren(*ast, arguments...)
@@ -629,7 +639,7 @@ func (vm * VM) ScopeUp(level int) *Scope {
 	return scope
 }
 
-// RegisterUp registers in klevel scopes up from the current scope, 
+// RegisterUp registers in level scopes up from the current scope,
 // or at toplevel if the level is greater than the total depth
 func (vm *VM) RegisterUp(name string, value Value, level int) Value {
 	scope := vm.ScopeUp(level)	
@@ -646,18 +656,20 @@ func (vm *VM) RegisterCover(name string, level int) *CoverValue {
 	return value
 }
 
-func (vm *VM) RegisterBuiltin(name string, handler Handler) Value {
+func (vm *VM) RegisterBuiltin(name string, handler Handler) *BuiltinValue {
 	value := NewBuiltinValue(name, handler)
-	return vm.Register(name, value)
+	vm.Register(name, value)
+	return value
 }
 
-func (vm *VM) RegisterDefined(name string, signature *Signature, block *BlockValue, level int) Value {
+func (vm *VM) RegisterDefined(name string, signature Signature, block *BlockValue, level int) *DefinedValue {
 	value := NewDefinedValue(name, signature, block)
-	return vm.RegisterUp(name, value, level)
+	vm.RegisterUp(name, value, level)
+	return value
 }
 
 // RegisterBuiltinWithHelp
-func (vm *VM) RegisterBuiltinWithHelp(name string, handler Handler, help string) Value {
+func (vm *VM) RegisterBuiltinWithHelp(name string, handler Handler, help string) *BuiltinValue {
 	res := vm.RegisterBuiltin(name, handler)
 	vm.SetHelp(name, help)
 	return res