github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/load/load.go (about)

     1  package load
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hirochachacha/plua/internal/compiler_pool"
    10  	"github.com/hirochachacha/plua/object"
    11  	"github.com/hirochachacha/plua/object/fnutil"
    12  )
    13  
    14  var luaPath string
    15  
    16  func init() {
    17  	luaPath = os.Getenv("LUA_luaPath_5_3")
    18  	if luaPath == "" {
    19  		luaPath = os.Getenv("LUA_luaPath")
    20  		if luaPath == "" {
    21  			luaPath = defaultPath
    22  		}
    23  	}
    24  }
    25  
    26  func searchpath(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    27  	ap := fnutil.NewArgParser(th, args)
    28  
    29  	name, err := ap.ToGoString(0)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	path, err := ap.ToGoString(1)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	sep, err := ap.OptGoString(2, ".")
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	rep, err := ap.OptGoString(3, dsep)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	var errmsg []string
    50  
    51  	name = strings.Replace(name, sep, rep, -1)
    52  	for _, p := range strings.Split(path, psep) {
    53  		fpath := strings.Replace(p, mark, name, -1)
    54  		_, err := os.Stat(fpath)
    55  		if err == nil {
    56  			return []object.Value{object.String(fpath)}, nil
    57  		}
    58  
    59  		errmsg = append(errmsg, fmt.Sprintf("no file '%s'", fpath))
    60  	}
    61  
    62  	return []object.Value{nil, object.String("\n\t" + strings.Join(errmsg, "\n\t"))}, nil
    63  }
    64  
    65  func preloadSearcher(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    66  	ap := fnutil.NewArgParser(th, args)
    67  
    68  	modname, err := ap.ToString(0)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	preload := th.Preload()
    74  
    75  	t := preload.Get(modname)
    76  	if t == nil {
    77  		return []object.Value{object.String(fmt.Sprintf("\n\tno field package.preload['%s']", modname))}, nil
    78  	}
    79  
    80  	return []object.Value{t}, nil
    81  }
    82  
    83  func makeSearchers(m object.Table) []object.Value {
    84  	luaSearcher := func(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    85  		ap := fnutil.NewArgParser(th, args)
    86  
    87  		modname, err := ap.ToString(0)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  
    92  		loadpath, ok := object.ToString(m.Get(object.String("path")))
    93  		if !ok {
    94  			return nil, object.NewRuntimeError("'package.path' must be a string")
    95  		}
    96  
    97  		rets, err := searchpath(th, modname, loadpath)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  
   102  		switch len(rets) {
   103  		case 0:
   104  			return nil, nil
   105  		case 1:
   106  			fpath := string(rets[0].(object.String))
   107  
   108  			p, err := compiler_pool.CompileFile(fpath, 0)
   109  			if err != nil {
   110  				return nil, err
   111  			}
   112  
   113  			return []object.Value{th.NewClosure(p), object.String(fpath)}, nil
   114  		case 2:
   115  			errmsg := rets[1].(object.String)
   116  
   117  			return []object.Value{errmsg}, nil
   118  		default:
   119  			panic("unreachable")
   120  		}
   121  	}
   122  
   123  	return []object.Value{object.GoFunction(preloadSearcher), object.GoFunction(luaSearcher)}
   124  }
   125  
   126  func makeRequire(m object.Table) object.GoFunction {
   127  	Require := func(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   128  		ap := fnutil.NewArgParser(th, args)
   129  
   130  		modname, err := ap.ToString(0)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  
   135  		loaded := th.Loaded()
   136  		if val := loaded.Get(modname); val != nil {
   137  			return []object.Value{val}, nil
   138  		}
   139  
   140  		var errbuf bytes.Buffer
   141  
   142  		searchers, ok := m.Get(object.String("searchers")).(object.Table)
   143  		if !ok {
   144  			return nil, object.NewRuntimeError("'package.searchers' must be a table")
   145  		}
   146  
   147  		slen := searchers.Len()
   148  		for i := 1; i <= slen; i++ {
   149  			val := searchers.Get(object.Integer(i))
   150  			if val == nil {
   151  				break
   152  			}
   153  
   154  			if searcher, ok := val.(object.GoFunction); ok {
   155  				rets, err := searcher(th, modname)
   156  				if err != nil {
   157  					return nil, err
   158  				}
   159  				if len(rets) > 0 {
   160  					var fpath object.Value
   161  					if len(rets) > 1 {
   162  						fpath = rets[1]
   163  					}
   164  
   165  					if object.ToType(rets[0]) == object.TFUNCTION {
   166  						rets, err := th.Call(rets[0], modname, fpath)
   167  						if err != nil {
   168  							return nil, err
   169  						}
   170  
   171  						// treat self require
   172  						if val := loaded.Get(modname); val != nil {
   173  							return []object.Value{val}, nil
   174  						}
   175  
   176  						if len(rets) == 0 || rets[0] == nil {
   177  							loaded.Set(modname, object.True)
   178  
   179  							return []object.Value{object.True}, nil
   180  						}
   181  
   182  						if rets[0] == object.False {
   183  							return []object.Value{object.False}, nil
   184  						}
   185  
   186  						loaded.Set(modname, rets[0])
   187  
   188  						return []object.Value{rets[0]}, nil
   189  					} else if s, ok := object.ToGoString(rets[0]); ok {
   190  						errbuf.WriteString(s)
   191  					}
   192  				}
   193  			}
   194  		}
   195  
   196  		return nil, object.NewRuntimeError(fmt.Sprintf("module '%s' not found:%s", modname, errbuf.String()))
   197  	}
   198  
   199  	return object.GoFunction(Require)
   200  }
   201  
   202  func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   203  	m := th.NewTableSize(0, 7)
   204  
   205  	m.Set(object.String("preload"), th.Preload())
   206  	m.Set(object.String("path"), object.String(luaPath))
   207  	m.Set(object.String("cpath"), object.String("")) // stub for test
   208  	m.Set(object.String("config"), object.String(config))
   209  	m.Set(object.String("loaded"), th.Loaded())
   210  
   211  	m.Set(object.String("searchpath"), object.GoFunction(searchpath))
   212  
   213  	m.Set(object.String("searchers"), th.NewTableArray(makeSearchers(m)))
   214  
   215  	th.Globals().Set(object.String("require"), makeRequire(m))
   216  
   217  	return []object.Value{m}, nil
   218  }