github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/examples/userdata/regexlib/regexlib.go (about) 1 // Package regexlib is an example of how to make a go library for lua. It 2 // allows using Go regular expressions in Lua code. To use in a runtime r, add 3 // the following Go code: 4 // regexlib.LibLoader.Run(r) 5 // Then in Lua code e.g. 6 // regex = require"regex" 7 // ptn = regex.new("[0-9]+") 8 // match = ptn:find("hello there 123 yippee") 9 package regexlib 10 11 import ( 12 "fmt" 13 "regexp" 14 15 "github.com/arnodel/golua/lib/packagelib" 16 rt "github.com/arnodel/golua/runtime" 17 ) 18 19 var regexMetaKey = rt.StringValue("regexMeta") 20 21 // LibLoader defines the name of the package and how to load it. Given a runtime 22 // r, call: 23 // regexlib.LibLoader.Run(r) 24 // To load the package into the runtime (note that packagelib needs to be loaded 25 // first). 26 var LibLoader = packagelib.Loader{ 27 Load: load, 28 Name: "regex", 29 } 30 31 // This function is the Load function of the LibLoader defined above. It sets 32 // up a package (which is a lua table and returns it). 33 func load(r *rt.Runtime) (rt.Value, func()) { 34 // First build a table of methods. 35 regexMethods := rt.NewTable() 36 r.SetEnvGoFunc(regexMethods, "find", regexFind, 2, false) 37 38 // Build the metatable 39 regexMeta := rt.NewTable() 40 r.SetEnv(regexMeta, "__index", rt.TableValue(regexMethods)) 41 r.SetEnvGoFunc(regexMeta, "__tostring", regexToString, 1, false) 42 r.SetRegistry(regexMetaKey, rt.TableValue(regexMeta)) 43 44 // Make a new table 45 pkg := rt.NewTable() 46 47 // Add the "new" function to it 48 r.SetEnvGoFunc(pkg, "new", newRegex, 1, false) 49 50 // Return the package table 51 return rt.TableValue(pkg), nil 52 } 53 54 // Creates a new regex userdata from a string. 55 func newRegex(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 56 var s string 57 err := c.Check1Arg() 58 if err == nil { 59 s, err = c.StringArg(0) 60 } 61 if err != nil { 62 return nil, err 63 } 64 re, compErr := regexp.Compile(string(s)) 65 if compErr != nil { 66 return nil, compErr 67 } 68 regexMeta := t.Registry(regexMetaKey) 69 return c.PushingNext(t.Runtime, t.NewUserDataValue(re, regexMeta.AsTable())), nil 70 } 71 72 // Hepler function that turns a Lua value to a Go regexp. 73 func valueToRegex(v rt.Value) (re *regexp.Regexp, ok bool) { 74 var u *rt.UserData 75 u, ok = v.TryUserData() 76 if ok { 77 re, ok = u.Value().(*regexp.Regexp) 78 } 79 return 80 } 81 82 // Helper function that extracts a regexp arg from a continuation. 83 func regexArg(c *rt.GoCont, n int) (*regexp.Regexp, error) { 84 re, ok := valueToRegex(c.Arg(n)) 85 if ok { 86 return re, nil 87 } 88 return nil, fmt.Errorf("#%d must be a regex", n+1) 89 } 90 91 // This implements the 'find' method of a regexp. 92 func regexFind(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 93 var ( 94 re *regexp.Regexp 95 s string 96 ) 97 // Check there are two arguments. 98 err := c.CheckNArgs(2) 99 if err == nil { 100 // Ok, then get the first argument as a regexp. 101 re, err = regexArg(c, 0) 102 } 103 if err == nil { 104 // Ok, then get the second argument as a string. 105 s, err = c.StringArg(1) 106 } 107 if err != nil { 108 // Fail if an error occurred above 109 return nil, err 110 } 111 // Find the pattern in the string and return it. 112 match := re.FindString(string(s)) 113 return c.PushingNext(t.Runtime, rt.StringValue(match)), nil 114 } 115 116 // Implementation of the regex's '__tostring' metamethod. 117 func regexToString(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 118 var re *regexp.Regexp 119 err := c.Check1Arg() 120 if err == nil { 121 re, err = regexArg(c, 0) 122 } 123 if err != nil { 124 return nil, err 125 } 126 s := rt.StringValue(fmt.Sprintf("regex(%q)", re.String())) 127 return c.PushingNext(t.Runtime, s), nil 128 }