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 }