// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // func-import.go package expr import ( "errors" "fmt" "io" "os" "path" "path/filepath" "strings" ) const ENV_EXPR_PATH = "EXPR_PATH" func importFunc(ctx ExprContext, name string, args []any) (result any, err error) { return importGeneral(ctx, name, args) } func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) { enable(ctx, control_export_all) return importGeneral(ctx, name, args) } func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) { var dirList []string dirList = addEnvImportDirs(dirList) dirList = addPresetImportDirs(ctx, dirList) result, err = doImport(ctx, name, dirList, NewArrayIterator(args)) return } func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { if !(IsString(paramValue) /*|| isList(paramValue)*/) { err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue) } return } func addEnvImportDirs(dirList []string) []string { if dirSpec, exists := os.LookupEnv(ENV_EXPR_PATH); exists { dirs := strings.Split(dirSpec, ":") if dirList == nil { dirList = dirs } else { dirList = append(dirList, dirs...) } } return dirList } func addPresetImportDirs(ctx ExprContext, dirList []string) []string { if dirSpec, exists := getControlString(ctx, ControlImportPath); exists { dirs := strings.Split(dirSpec, ":") if dirList == nil { dirList = dirs } else { dirList = append(dirList, dirs...) } } return dirList } func isFile(filePath string) bool { info, err := os.Stat(filePath) return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular() } func searchAmongPath(filename string, dirList []string) (filePath string) { for _, dir := range dirList { if fullPath := path.Join(dir, filename); isFile(fullPath) { filePath = fullPath break } } return } func isPathRelative(filePath string) bool { unixPath := filepath.ToSlash(filePath) return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../") } func makeFilepath(filename string, dirList []string) (filePath string, err error) { if path.IsAbs(filename) || isPathRelative(filename) { if isFile(filename) { filePath = filename } } else { filePath = searchAmongPath(filename, dirList) } if len(filePath) == 0 { err = fmt.Errorf("source file %q not found", filename) } return } func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) { var v any var sourceFilepath string for v, err = it.Next(); err == nil; v, err = it.Next() { if err = checkStringParamExpected(name, v, it.Index()); err != nil { break } if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil { break } var file *os.File if file, err = os.Open(sourceFilepath); err == nil { defer file.Close() var expr *ast scanner := NewScanner(file, DefaultTranslations()) parser := NewParser(ctx) if expr, err = parser.parseGeneral(scanner, true, true); err == nil { result, err = expr.eval(ctx, false) } if err != nil { break } } else { break } } if err != nil { if err == io.EOF { err = nil } else { result = nil } } return } func ImportImportFuncs(ctx ExprContext) { ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{ newFuncParamFlag(paramFilepath, pfRepeat), }) ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{ newFuncParamFlag(paramFilepath, pfRepeat), }) } func init() { registerImport("import", ImportImportFuncs, "Functions import() and include()") }