check.go 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package zygo
  2. import (
  3. "fmt"
  4. )
  5. // FunctionCallNameTypeCheck type checks a function call.
  6. func (env *Zlisp) FunctionCallNameTypeCheck(f *SexpFunction, nargs *int) error {
  7. if f.inputTypes != nil {
  8. //Q("FunctionCallNameTypeCheck sees inputTypes: '%v'", f.inputTypes.SexpString(nil))
  9. } else {
  10. return nil // no type checking requested
  11. }
  12. if f.varargs {
  13. return nil // name/type checking for vargarg not currently implemented.
  14. }
  15. // our call arguments prepared -- will be pushed to the datastack
  16. finalArgs := make([]Sexp, f.inputTypes.NumKeys)
  17. // pop everything off the stack, will push finalArgs later
  18. exprs, err := env.datastack.PopExpressions(*nargs)
  19. if err != nil {
  20. return err
  21. }
  22. // map the named submitted args, for fast lookup by name
  23. submittedByName := make(map[string]Sexp)
  24. // prep submittedByName
  25. for i := 0; i < *nargs; i++ {
  26. switch sym := exprs[i].(type) {
  27. case *SexpSymbol:
  28. if sym.colonTail {
  29. //Q("in env.CallFunction, have symbol.colonTail: exprs[%v]='%#v'", i, sym)
  30. typ, err := f.inputTypes.HashGet(env, sym)
  31. if err != nil {
  32. return fmt.Errorf("%s takes no argument '%s'", f.name, sym.name)
  33. }
  34. if i == (*nargs)-1 {
  35. return fmt.Errorf("named parameter '%s' not followed by value", sym.name)
  36. }
  37. val := exprs[i+1]
  38. i++
  39. _, already := submittedByName[sym.name]
  40. if already {
  41. return fmt.Errorf("duplicate named parameter '%s'", sym.name)
  42. }
  43. submittedByName[sym.name] = val
  44. valtyp := val.Type()
  45. if typ != nil && typ != valtyp {
  46. return fmt.Errorf("type mismatch for parameter '%s': expected '%s', got '%s'",
  47. sym.name, typ.SexpString(nil), valtyp.SexpString(nil))
  48. }
  49. } else {
  50. //Q("in env.CallFunction, exprs[%v]='%v'/type=%T", i, exprs[i].SexpString(nil), exprs[i])
  51. }
  52. default:
  53. //Q("in env.CallFunction, exprs[%v]='%v'/type=%T", i, exprs[i].SexpString(nil), exprs[i])
  54. }
  55. }
  56. // simplify name matching for now with this rule: all by name, or none.
  57. haveByName := len(submittedByName)
  58. if haveByName > 0 {
  59. if haveByName != f.inputTypes.NumKeys {
  60. return fmt.Errorf("named arguments count %v != expected %v", haveByName, f.inputTypes.NumKeys)
  61. }
  62. // prep finalArgs in the order dictated
  63. for i, key := range f.inputTypes.KeyOrder {
  64. switch sy := key.(type) {
  65. case *SexpSymbol:
  66. // search for sy.name in our submittedByName args
  67. a, found := submittedByName[sy.name]
  68. if found {
  69. //Q("%s call: matching %v-th argument named '%s': passing value '%s'",
  70. // f.name, i, sy.name, a.SexpString(nil))
  71. finalArgs[i] = a
  72. }
  73. default:
  74. return fmt.Errorf("unsupported argument-name type %T", key)
  75. }
  76. }
  77. } else {
  78. // not using named parameters, restore the arguments to the stack as they were.
  79. finalArgs = exprs
  80. }
  81. *nargs = len(finalArgs)
  82. return env.datastack.PushExpressions(finalArgs)
  83. }