123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- /*
- * ECAL
- *
- * Copyright 2020 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- package stdlib
- import (
- "fmt"
- "go/ast"
- "go/doc"
- goparser "go/parser"
- "go/token"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
- "devt.de/krotik/ecal/parser"
- )
- /*
- ECALFunctionAdapter models a bridge adapter between an ECAL function to a Go function.
- */
- type ECALFunctionAdapter struct {
- funcval reflect.Value
- }
- /*
- Run executes this function.
- */
- func (ea *ECALFunctionAdapter) Run(instanceID string, vs parser.Scope,
- is map[string]interface{}, args []interface{}) (ret interface{}, err error) {
- defer func() {
- if r := recover(); r != nil {
- err = fmt.Errorf("Error: %v", r)
- }
- }()
- funcType := ea.funcval.Type()
- // Build arguments
- fargs := make([]reflect.Value, 0, len(args))
- for i, arg := range args {
- if i == funcType.NumIn() {
- return nil, fmt.Errorf("Too many parameters - got %v expected %v",
- len(args), funcType.NumIn())
- }
- expectedType := funcType.In(i)
- // Try to convert into correct number types
- if float64Arg, ok := arg.(float64); ok {
- switch expectedType.Kind() {
- case reflect.Int:
- arg = int(float64Arg)
- case reflect.Int8:
- arg = int8(float64Arg)
- case reflect.Int16:
- arg = int16(float64Arg)
- case reflect.Int32:
- arg = int32(float64Arg)
- case reflect.Int64:
- arg = int64(float64Arg)
- case reflect.Uint:
- arg = uint(float64Arg)
- case reflect.Uint8:
- arg = uint8(float64Arg)
- case reflect.Uint16:
- arg = uint16(float64Arg)
- case reflect.Uint32:
- arg = uint32(float64Arg)
- case reflect.Uint64:
- arg = uint64(float64Arg)
- case reflect.Uintptr:
- arg = uintptr(float64Arg)
- case reflect.Float32:
- arg = float32(float64Arg)
- }
- }
- givenType := reflect.TypeOf(arg)
- // Check that the right types were given
- if givenType != expectedType &&
- !(expectedType.Kind() == reflect.Interface &&
- givenType.Kind() == reflect.Interface &&
- givenType.Implements(expectedType)) &&
- expectedType != reflect.TypeOf([]interface{}{}) {
- return nil, fmt.Errorf("Parameter %v should be of type %v but is of type %v",
- i+1, expectedType, givenType)
- }
- fargs = append(fargs, reflect.ValueOf(arg))
- }
- // Call the function
- vals := ea.funcval.Call(fargs)
- // Convert result value
- results := make([]interface{}, 0, len(vals))
- for i, v := range vals {
- res := v.Interface()
- if i == len(vals)-1 {
- // If the last item is an error then it is not part of the resutls
- // (it will be wrapped into a proper runtime error later)
- if funcType.Out(i) == reflect.TypeOf((*error)(nil)).Elem() {
- if res != nil {
- err = res.(error)
- }
- break
- }
- }
- // Convert result if it is a primitive type
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- res = float64(v.Int())
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- res = float64(v.Uint())
- case reflect.Float32, reflect.Float64:
- res = v.Float()
- }
- results = append(results, res)
- }
- ret = results
- // Return a single value if results contains only a single item
- if len(results) == 1 {
- ret = results[0]
- }
- return ret, err
- }
- /*
- DocString returns the docstring of the wrapped function.
- */
- func (ea *ECALFunctionAdapter) DocString() (string, error) {
- ffunc := runtime.FuncForPC(ea.funcval.Pointer())
- fileName, _ := ffunc.FileLine(0)
- funcNameSplit := strings.Split(ffunc.Name(), ".")
- funcName := funcNameSplit[len(funcNameSplit)-1]
- fset := token.NewFileSet()
- res := ""
- parsedAst, err := goparser.ParseFile(fset, fileName, nil, goparser.ParseComments)
- if err == nil {
- pkg := &ast.Package{
- Name: "Any",
- Files: make(map[string]*ast.File),
- }
- pkg.Files[fileName] = parsedAst
- importPath, _ := filepath.Abs("/")
- myDoc := doc.New(pkg, importPath, doc.AllDecls)
- for _, theFunc := range myDoc.Funcs {
- if theFunc.Name == funcName {
- res = theFunc.Doc
- break
- }
- }
- }
- return res, err
- }
|