adapter.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package stdlib
  11. import (
  12. "fmt"
  13. "go/ast"
  14. "go/doc"
  15. goparser "go/parser"
  16. "go/token"
  17. "path/filepath"
  18. "reflect"
  19. "runtime"
  20. "strings"
  21. "devt.de/krotik/ecal/parser"
  22. )
  23. /*
  24. ECALFunctionAdapter models a bridge adapter between an ECAL function to a Go function.
  25. */
  26. type ECALFunctionAdapter struct {
  27. funcval reflect.Value
  28. }
  29. /*
  30. Run executes this function.
  31. */
  32. func (ea *ECALFunctionAdapter) Run(instanceID string, vs parser.Scope,
  33. is map[string]interface{}, args []interface{}) (ret interface{}, err error) {
  34. defer func() {
  35. if r := recover(); r != nil {
  36. err = fmt.Errorf("Error: %v", r)
  37. }
  38. }()
  39. funcType := ea.funcval.Type()
  40. // Build arguments
  41. fargs := make([]reflect.Value, 0, len(args))
  42. for i, arg := range args {
  43. if i == funcType.NumIn() {
  44. return nil, fmt.Errorf("Too many parameters - got %v expected %v",
  45. len(args), funcType.NumIn())
  46. }
  47. expectedType := funcType.In(i)
  48. // Try to convert into correct number types
  49. if float64Arg, ok := arg.(float64); ok {
  50. switch expectedType.Kind() {
  51. case reflect.Int:
  52. arg = int(float64Arg)
  53. case reflect.Int8:
  54. arg = int8(float64Arg)
  55. case reflect.Int16:
  56. arg = int16(float64Arg)
  57. case reflect.Int32:
  58. arg = int32(float64Arg)
  59. case reflect.Int64:
  60. arg = int64(float64Arg)
  61. case reflect.Uint:
  62. arg = uint(float64Arg)
  63. case reflect.Uint8:
  64. arg = uint8(float64Arg)
  65. case reflect.Uint16:
  66. arg = uint16(float64Arg)
  67. case reflect.Uint32:
  68. arg = uint32(float64Arg)
  69. case reflect.Uint64:
  70. arg = uint64(float64Arg)
  71. case reflect.Uintptr:
  72. arg = uintptr(float64Arg)
  73. case reflect.Float32:
  74. arg = float32(float64Arg)
  75. }
  76. }
  77. givenType := reflect.TypeOf(arg)
  78. // Check that the right types were given
  79. if givenType != expectedType &&
  80. !(expectedType.Kind() == reflect.Interface &&
  81. givenType.Kind() == reflect.Interface &&
  82. givenType.Implements(expectedType)) &&
  83. expectedType != reflect.TypeOf([]interface{}{}) {
  84. return nil, fmt.Errorf("Parameter %v should be of type %v but is of type %v",
  85. i+1, expectedType, givenType)
  86. }
  87. fargs = append(fargs, reflect.ValueOf(arg))
  88. }
  89. // Call the function
  90. vals := ea.funcval.Call(fargs)
  91. // Convert result value
  92. results := make([]interface{}, 0, len(vals))
  93. for i, v := range vals {
  94. res := v.Interface()
  95. if i == len(vals)-1 {
  96. // If the last item is an error then it is not part of the resutls
  97. // (it will be wrapped into a proper runtime error later)
  98. if funcType.Out(i) == reflect.TypeOf((*error)(nil)).Elem() {
  99. if res != nil {
  100. err = res.(error)
  101. }
  102. break
  103. }
  104. }
  105. // Convert result if it is a primitive type
  106. switch v.Kind() {
  107. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  108. res = float64(v.Int())
  109. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  110. res = float64(v.Uint())
  111. case reflect.Float32, reflect.Float64:
  112. res = v.Float()
  113. }
  114. results = append(results, res)
  115. }
  116. ret = results
  117. // Return a single value if results contains only a single item
  118. if len(results) == 1 {
  119. ret = results[0]
  120. }
  121. return ret, err
  122. }
  123. /*
  124. DocString returns the docstring of the wrapped function.
  125. */
  126. func (ea *ECALFunctionAdapter) DocString() (string, error) {
  127. ffunc := runtime.FuncForPC(ea.funcval.Pointer())
  128. fileName, _ := ffunc.FileLine(0)
  129. funcNameSplit := strings.Split(ffunc.Name(), ".")
  130. funcName := funcNameSplit[len(funcNameSplit)-1]
  131. fset := token.NewFileSet()
  132. res := ""
  133. parsedAst, err := goparser.ParseFile(fset, fileName, nil, goparser.ParseComments)
  134. if err == nil {
  135. pkg := &ast.Package{
  136. Name: "Any",
  137. Files: make(map[string]*ast.File),
  138. }
  139. pkg.Files[fileName] = parsedAst
  140. importPath, _ := filepath.Abs("/")
  141. myDoc := doc.New(pkg, importPath, doc.AllDecls)
  142. for _, theFunc := range myDoc.Funcs {
  143. if theFunc.Name == funcName {
  144. res = theFunc.Doc
  145. break
  146. }
  147. }
  148. }
  149. return res, err
  150. }