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  }