Mercurial Hosting > luan
changeset 1716:b82767112d8e
add String.regex
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 24 Jul 2022 23:43:03 -0600 |
parents | ad44e849c60c |
children | c637a2a1023d |
files | conv.txt scripts/test.luan src/luan/LuanJavaFunction.java src/luan/host/https.luan src/luan/host/init.luan src/luan/host/main.luan src/luan/modules/Boot.luan src/luan/modules/Io.luan src/luan/modules/RegexLuan.java src/luan/modules/String.luan src/luan/modules/StringLuan.java src/luan/modules/host/Hosting.luan src/luan/modules/http/Http.luan src/luan/modules/http/Http_test.luan src/luan/modules/http/Server.luan src/luan/modules/http/tools/Run.luan src/luan/modules/lucene/Lucene.luan src/luan/modules/mmake.luan website/src/manual.html.luan |
diffstat | 19 files changed, 510 insertions(+), 63 deletions(-) [+] |
line wrap: on
line diff
--- a/conv.txt Sat Jul 23 21:53:04 2022 -0600 +++ b/conv.txt Sun Jul 24 23:43:03 2022 -0600 @@ -1,3 +1,14 @@ +String.find +String.gmatch +String.gsub +String.match +String.matches + +String.contains +String.starts_with +String.ends_with + + rename_to Mail.Sender
--- a/scripts/test.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/scripts/test.luan Sun Jul 24 23:43:03 2022 -0600 @@ -17,7 +17,7 @@ local error = Luan.error local range = Luan.range or error() local trim = String.trim or error() -local find = String.find or error() +local contains = String.contains or error() local init = Http_test.init or error() local get_page = Http_test.get_page or error() local run_page = Http_test.run_page or error() @@ -38,7 +38,7 @@ init() Http.request.parameters.cmd = "'ab'..'cd'" page = run_page(require("luan:http/tools/Shell.luan").respond) -find(page,"abcd") or error "failed" +contains(page,"abcd") or error "failed" ]] -- lucene @@ -84,7 +84,7 @@ init() Http.request.parameters.name = "bob" page = get_page "/examples/hi2.html" -find(page,"bob") or error "failed" +contains(page,"bob") or error "failed" print "done"
--- a/src/luan/LuanJavaFunction.java Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/LuanJavaFunction.java Sun Jul 24 23:43:03 2022 -0600 @@ -31,7 +31,6 @@ private LuanJavaFunction(JavaMethod method,Object obj) { this.method = method; - this.obj = obj; this.rtnConverter = getRtnConverter(method); this.takesLuan = takesLuan(method); this.argConverters = getArgConverters(takesLuan,method); @@ -41,6 +40,16 @@ } else { this.varArgCls = null; } + this.obj = obj; + } + + public LuanJavaFunction(LuanJavaFunction fn,Object obj) { + this.method = fn.method; + this.rtnConverter = fn.rtnConverter; + this.takesLuan = fn.takesLuan; + this.argConverters = fn.argConverters; + this.varArgCls = fn.varArgCls; + this.obj = obj; } @Override public String toString() {
--- a/src/luan/host/https.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/host/https.luan Sun Jul 24 23:43:03 2022 -0600 @@ -8,8 +8,7 @@ local uri = Io.uri or error() local output_of = Io.output_of or error() local String = require "luan:String.luan" -local regex_quote = String.regex_quote or error() -local matches = String.matches or error() +local starts_with = String.starts_with or error() local Http = require "luan:http/Http.luan" local Hosted = require "luan:host/Hosted.luan" local Logging = require "luan:logging/Logging.luan" @@ -148,9 +147,9 @@ nginx_file.delete() local_cer_file.delete() local_ca_file.delete() - local ptn = [[^]]..regex_quote(domain)..[[\.]] + local ptn = domain.."." for _, file in ipairs(dir.children()) do - if matches(file.name(),ptn) then + if starts_with(file.name(),ptn) then file.delete() end end
--- a/src/luan/host/init.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/host/init.luan Sun Jul 24 23:43:03 2022 -0600 @@ -2,8 +2,6 @@ local error = Luan.error local do_file = Luan.do_file or error() local Package = require "luan:Package.luan" -local String = require "luan:String.luan" -local gsub = String.gsub or error() local Number = require "luan:Number.luan" local long = Number.long or error()
--- a/src/luan/host/main.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/host/main.luan Sun Jul 24 23:43:03 2022 -0600 @@ -7,9 +7,8 @@ local Rpc = require "luan:Rpc.luan" local Thread = require "luan:Thread.luan" local String = require "luan:String.luan" -local regex_quote = String.regex_quote or error() local lower = String.lower or error() -local matches = String.matches or error() +local starts_with = String.starts_with or error() local Hosted = require "luan:host/Hosted.luan" local Logging = require "luan:logging/Logging.luan" local logger = Logging.logger "main" @@ -113,7 +112,7 @@ end local function security(site_dir,file) - matches( file.to_string(), "^"..regex_quote(site_dir.to_string()) ) or error "security violation" + starts_with( file.to_string(), site_dir.to_string() ) or error "security violation" end function fns.copy_file(domain,password,dir,name,content)
--- a/src/luan/modules/Boot.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/Boot.luan Sun Jul 24 23:43:03 2022 -0600 @@ -8,8 +8,8 @@ local load = BasicLuan.load local type = BasicLuan.type local StringLuan = require "java:luan.modules.StringLuan" -local match = StringLuan.match -- String.match -local matches = StringLuan.matches -- String.matches +local contains = StringLuan.contains -- String.contains +local RegexLuan = require "java:luan.modules.RegexLuan" local IoLuan = require "java:luan.modules.IoLuan" local LuanUrl = require "java:luan.modules.url.LuanUrl" local LuanJava = require "java:luan.Luan" @@ -49,6 +49,21 @@ Boot.local_metatable = local_metatable +local function regex(pattern) + local regex = RegexLuan.new(pattern) + return { + java = regex + find = regex.find + gmatch = regex.gmatch + gsub = regex.gsub + match = regex.match + matches = regex.matches + set = regex.set + } +end +Boot.regex = regex + + local function new_LuanIn(io) local this = {} this.java = io @@ -230,8 +245,10 @@ Boot.schemes = schemes +local uri_regex = regex("(?s)^([^:]+):(.*)$") + local function uri(name,options) - local scheme, location = match( name, "(?s)^([^:]+):(.*)$" ) + local scheme, location = uri_regex.match(name) scheme or error( "invalid Io.uri name '"..name.."', missing scheme" ) local opener = schemes[scheme] or error( "invalid scheme '"..scheme.."' in '"..name.."'" ) return opener(location,options) @@ -249,7 +266,7 @@ function Boot.load_file(file) if type(file) == "string" then - if not matches(file,":") then + if not contains(file,":") then file = "file:"..file end local u = uri(file)
--- a/src/luan/modules/Io.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/Io.luan Sun Jul 24 23:43:03 2022 -0600 @@ -53,7 +53,6 @@ local unpack = Table.unpack or error() local String = require "luan:String.luan" local encode = String.encode or error() -local matches = String.matches or error() -- do not change
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/RegexLuan.java Sun Jul 24 23:43:03 2022 -0600 @@ -0,0 +1,147 @@ +package luan.modules; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import luan.Luan; +import luan.LuanMutable; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; + + +public final class RegexLuan implements LuanMutable { + public Pattern pattern; + private boolean immutable = false; + + public RegexLuan(String s) { + this.pattern = Pattern.compile(s); + } + + @Override public boolean isImmutable() { + return immutable; + } + + @Override public void makeImmutable() { + if(immutable) + return; + immutable = true; + } + + public void set(String s) throws LuanException { + if( immutable ) + throw new LuanException("regex is immutable"); + this.pattern = Pattern.compile(s); + } + + public Object[] find(String s,Integer init) throws LuanException { + int start = StringLuan.start(s,init,0); + Matcher m = pattern.matcher(s); + if( !m.find(start) ) + return null; + int n = m.groupCount(); + Object[] rtn = new Object[2+n]; + rtn[0] = m.start() + 1; + rtn[1] = m.end(); + for( int i=0; i<n; i++ ) { + rtn[2+i] = m.group(i+1); + } + return rtn; + } + + public LuanFunction gmatch(String s) throws LuanException { + Utils.checkNotNull(s); + final Matcher m = pattern.matcher(s); + return new LuanFunction() { + @Override public Object call(Luan luan,Object[] args) { + if( !m.find() ) + return null; + final int n = m.groupCount(); + if( n == 0 ) + return m.group(); + String[] rtn = new String[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = m.group(i+1); + } + return rtn; + } + }; + } + + public Object[] gsub(Luan luan,String s,Object repl,Integer n) throws LuanException { + Utils.checkNotNull(s); + int max = n==null ? Integer.MAX_VALUE : n; + final Matcher m = pattern.matcher(s); + if( repl instanceof String ) { + String replacement = (String)repl; + int i = 0; + StringBuffer sb = new StringBuffer(); + while( i<max && m.find() ) { + m.appendReplacement(sb,replacement); + i++; + } + m.appendTail(sb); + return new Object[]{ sb.toString(), i }; + } + if( repl instanceof LuanTable ) { + LuanTable t = (LuanTable)repl; + int i = 0; + StringBuffer sb = new StringBuffer(); + while( i<max && m.find() ) { + String match = m.groupCount()==0 ? m.group() : m.group(1); + Object val = t.get(luan,match); + if( val != null ) { + String replacement = luan.luanToString(val); + m.appendReplacement(sb,replacement); + } + i++; + } + m.appendTail(sb); + return new Object[]{ sb.toString(), i }; + } + if( repl instanceof LuanFunction ) { + LuanFunction fn = (LuanFunction)repl; + int i = 0; + StringBuffer sb = new StringBuffer(); + while( i<max && m.find() ) { + Object[] args; + final int count = m.groupCount(); + if( count == 0 ) { + args = new String[]{m.group()}; + } else { + args = new String[count]; + for( int j=0; j<count; j++ ) { + args[j] = m.group(j+1); + } + } + Object val = Luan.first( fn.call(luan,args) ); + if( val != null ) { + String replacement = luan.luanToString(val); + m.appendReplacement(sb,replacement); + } + i++; + } + m.appendTail(sb); + return new Object[]{ sb.toString(), i }; + } + throw new LuanException( "bad argument #3 to 'gsub' (string/function/table expected)" ); + } + + public String[] match(String s,Integer init) throws LuanException { + int start = StringLuan.start(s,init,0); + Matcher m = pattern.matcher(s); + if( !m.find(start) ) + return null; + int n = m.groupCount(); + if( n == 0 ) + return new String[]{m.group()}; + String[] rtn = new String[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = m.group(i+1); + } + return rtn; + } + + public boolean matches(String s) { + return pattern.matcher(s).find(); + } +}
--- a/src/luan/modules/String.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/String.luan Sun Jul 24 23:43:03 2022 -0600 @@ -1,12 +1,15 @@ require "java" local StringLuan = require "java:luan.modules.StringLuan" local Pattern = require "java:java.util.regex.Pattern" +local Boot = require "luan:Boot.luan" local String = {} String.char = StringLuan.char_ +String.contains = StringLuan.contains String.digest_message = StringLuan.digest_message String.encode = StringLuan.encode +String.ends_with = StringLuan.ends_with String.find = StringLuan.find String.format = StringLuan.format String.gmatch = StringLuan.gmatch @@ -14,11 +17,12 @@ String.lower = StringLuan.lower String.match = StringLuan.match String.matches = StringLuan.matches -String.regex_compile = Pattern.compile +String.regex = Boot.regex String.regex_quote = Pattern.quote String.rep = StringLuan.rep String.reverse = StringLuan.reverse String.split = StringLuan.split +String.starts_with = StringLuan.starts_with String.sub = StringLuan.sub String.to_binary = StringLuan.to_binary String.to_number = StringLuan.to_number
--- a/src/luan/modules/StringLuan.java Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/StringLuan.java Sun Jul 24 23:43:03 2022 -0600 @@ -92,14 +92,13 @@ return s.substring(start,end); } - public static Object[] find(String s,Object pattern,Integer init,Boolean plain) throws LuanException { + public static Object[] find(String s,String pattern,Integer init,Boolean plain) { int start = start(s,init,0); if( Boolean.TRUE.equals(plain) ) { - String ptn = (String)pattern; - int i = s.indexOf(ptn,start); - return i == -1 ? null : new Integer[]{i+1,i+ptn.length()}; + int i = s.indexOf(pattern,start); + return i == -1 ? null : new Integer[]{i+1,i+pattern.length()}; } - Matcher m = pattern(pattern).matcher(s); + Matcher m = Pattern.compile(pattern).matcher(s); if( !m.find(start) ) return null; int n = m.groupCount(); @@ -112,9 +111,9 @@ return rtn; } - public static String[] match(String s,Object pattern,Integer init) throws LuanException { + public static String[] match(String s,String pattern,Integer init) { int start = start(s,init,0); - Matcher m = pattern(pattern).matcher(s); + Matcher m = Pattern.compile(pattern).matcher(s); if( !m.find(start) ) return null; int n = m.groupCount(); @@ -127,9 +126,10 @@ return rtn; } - public static LuanFunction gmatch(String s,Object pattern) throws LuanException { + public static LuanFunction gmatch(String s,String pattern) throws LuanException { Utils.checkNotNull(s); - final Matcher m = pattern(pattern).matcher(s); + Utils.checkNotNull(pattern,2); + final Matcher m = Pattern.compile(pattern).matcher(s); return new LuanFunction() { @Override public Object call(Luan luan,Object[] args) { if( !m.find() ) @@ -146,10 +146,10 @@ }; } - public static Object[] gsub(Luan luan,String s,Object pattern,Object repl,Integer n) throws LuanException { + public static Object[] gsub(Luan luan,String s,String pattern,Object repl,Integer n) throws LuanException { Utils.checkNotNull(s); int max = n==null ? Integer.MAX_VALUE : n; - final Matcher m = pattern(pattern).matcher(s); + final Matcher m = Pattern.compile(pattern).matcher(s); if( repl instanceof String ) { String replacement = (String)repl; int i = 0; @@ -226,24 +226,14 @@ return null; } - private static Pattern pattern(Object pattern) throws LuanException { - if( pattern instanceof Pattern ) { - return (Pattern)pattern; - } else if( pattern instanceof String ) { - return Pattern.compile((String)pattern); - } else { - throw new LuanException( "bad argument #2 (string or compiled pattern expected)" ); - } - } - - public static boolean matches(String s,Object pattern) throws LuanException { + public static boolean matches(String s,String pattern) throws LuanException { Utils.checkNotNull(s); - return pattern(pattern).matcher(s).find(); + return Pattern.compile(pattern).matcher(s).find(); } public static String[] split(String s,String pattern,Integer limit) throws LuanException { Utils.checkNotNull(s); - Utils.checkNotNull(pattern,1); + Utils.checkNotNull(pattern,2); int n = limit==null ? -1 : limit; return s.split(pattern,n); } @@ -254,4 +244,22 @@ return BinaryLuan.to_hex( BinaryLuan.digest_message( algorithm, input.getBytes() ) ); } + public static boolean contains(String s,String s2) throws LuanException { + Utils.checkNotNull(s); + Utils.checkNotNull(s2,2); + return s.contains(s2); + } + + public static boolean starts_with(String s,String s2) throws LuanException { + Utils.checkNotNull(s); + Utils.checkNotNull(s2,2); + return s.startsWith(s2); + } + + public static boolean ends_with(String s,String s2) throws LuanException { + Utils.checkNotNull(s); + Utils.checkNotNull(s2,2); + return s.endsWith(s2); + } + }
--- a/src/luan/modules/host/Hosting.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/host/Hosting.luan Sun Jul 24 23:43:03 2022 -0600 @@ -8,7 +8,8 @@ local print = Io.print or error() local Rpc = require "luan:Rpc.luan" local String = require "luan:String.luan" -local matches = String.matches or error() +local contains = String.contains or error() +local starts_with = String.starts_with or error() local substring = String.sub or error() local split = String.split or error() local Logging = require "luan:logging/Logging.luan" @@ -43,7 +44,7 @@ end for _, here_child in ipairs(here.children()) do local name = here_child.name() - if not matches(name,[[^\.]]) then + if not starts_with(name,".") then process(there,there.children[name],here_child) there.children[name] = nil end @@ -73,7 +74,7 @@ my_file.is_file() or error("'"..file.."' is not a file") local my_file_string = my_file.to_string() local my_dir_string = my_dir.to_string().."/" - matches( my_file_string, [[^\Q]]..my_dir_string..[[\E]] ) or error "file must be in dir" + contains( my_file_string, my_dir_string ) or error "file must be in dir" my_file_string = substring(my_file_string,#my_dir_string+1) local path = {split( my_file_string, "/" )} path[#path] = nil
--- a/src/luan/modules/http/Http.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/http/Http.luan Sun Jul 24 23:43:03 2022 -0600 @@ -19,7 +19,6 @@ local Package = require "luan:Package.luan" local String = require "luan:String.luan" local lower = String.lower or error() -local matches = String.matches or error() local trim = String.trim or error() local Time = require "luan:Time.luan" local time_format = Time.format or error()
--- a/src/luan/modules/http/Http_test.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/http/Http_test.luan Sun Jul 24 23:43:03 2022 -0600 @@ -4,7 +4,7 @@ local Package = require "luan:Package.luan" local Io = require "luan:Io.luan" local String = require "luan:String.luan" -local matches = String.matches or error() +local ends_with = String.ends_with or error() local Http = require "luan:http/Http.luan" @@ -15,7 +15,7 @@ function Http_test.get_page(path) Http.request.path = path - if Http_test.welcome_file ~= nil and matches(path,"/$") then + if Http_test.welcome_file ~= nil and ends_with(path,"/") then path = path .. Http_test.welcome_file end local old_out = Io.stdout
--- a/src/luan/modules/http/Server.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/http/Server.luan Sun Jul 24 23:43:03 2022 -0600 @@ -1,9 +1,8 @@ local Luan = require "luan:Luan.luan" local error = Luan.error local String = require "luan:String.luan" -local gsub = String.gsub or error() -local match = String.match or error() -local matches = String.matches or error() +local regex = String.regex or error() +local contains = String.contains or error() local Io = require "luan:Io.luan" local uri = Io.uri or error() local Package = require "luan:Package.luan" @@ -31,10 +30,10 @@ local Server = {} function Server.init_dir(dir) - if not matches(dir,":") then + if not contains(dir,":") then dir = "file:"..dir end - dir = gsub(dir,"/$","") -- remove trailing '/' if any + dir = regex("/$").gsub(dir,"") -- remove trailing '/' if any Http.dir = dir Http.is_serving = true function Io.schemes.site(path) @@ -63,10 +62,12 @@ Thread.fork(Rpc.serve) end +local file_regex = regex("^file:(.*)$") + function Server.serve(dir,port) port = port or 8080 Server.init_dir(dir) - local dir_path = match(Http.dir,"^file:(.*)$") or error "server dir must be scheme 'file:'" + local dir_path = file_regex.match(Http.dir) or error "server dir must be scheme 'file:'" local file_handler = FileHandler.new(dir_path) local luan_handler = LuanHandler.new() local handler = ListHandler.new( luan_handler, file_handler )
--- a/src/luan/modules/http/tools/Run.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/http/tools/Run.luan Sun Jul 24 23:43:03 2022 -0600 @@ -4,14 +4,16 @@ local Io = require "luan:Io.luan" local print = Io.print or error() local String = require "luan:String.luan" -local gmatch = String.gmatch or error() +local regex = String.regex or error() local Http = require "luan:http/Http.luan" local Run = {} +local line_regex = regex("([^\n]*)\n|([^\n]+)$") + local function lines(s) - local matcher = gmatch(s,"([^\n]*)\n|([^\n]+)$") + local matcher = line_regex.gmatch(s) return function() local m1, m2 = matcher() return m1 or m2
--- a/src/luan/modules/lucene/Lucene.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/lucene/Lucene.luan Sun Jul 24 23:43:03 2022 -0600 @@ -15,7 +15,7 @@ local Io = require "luan:Io.luan" local uri = Io.uri or error() local String = require "luan:String.luan" -local matches = String.matches or error() +local starts_with = String.starts_with or error() local Rpc = require "luan:Rpc.luan" local LuceneIndex = require "java:luan.modules.lucene.LuceneIndex" local NumberFieldParser = require "java:goodjava.lucene.queryparser.NumberFieldParser" @@ -64,7 +64,7 @@ local function get_file(f) type(f)=="table" or error "index_dir must be table" - f.to_uri_string and matches(f.to_uri_string(),"^file:") or error "index_dir must be file" + f.to_uri_string and starts_with(f.to_uri_string(),"file:") or error "index_dir must be file" return f.java.file or error() end
--- a/src/luan/modules/mmake.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/src/luan/modules/mmake.luan Sun Jul 24 23:43:03 2022 -0600 @@ -5,7 +5,12 @@ local print = Io.print local output_to = Io.output_to local String = require "luan:String.luan" +local regex = String.regex +local substring = String.sub +local ends_with = String.ends_with local Time = require "luan:Time.luan" +local time_now = Time.now +local time_format = Time.format local compiler = Table.concat( { "javac -g -encoding UTF8", ... }, " " ) @@ -13,7 +18,7 @@ local function header() %> -# Makefile created on <%=Time.format(Time.now())%> by Mmake +# Makefile created on <%=time_format(time_now())%> by Mmake .SUFFIXES: .java .class @@ -29,8 +34,8 @@ local dirs = {} for _, file in ipairs(dir.children()) do local name = file.name() - if String.matches(name,[[\.java$]]) then - javas[#javas+1] = String.sub(name,1,-6) + if ends_with(name,".java") then + javas[#javas+1] = substring(name,1,-6) end if file.is_directory() and mmake(file) then dirs[#dirs+1] = name @@ -41,8 +46,9 @@ end local out = dir.child("Makefile").text_writer() output_to(out,header) + local r = regex([[\$]]) for _, s in ipairs(javas) do - s = String.gsub(s,[[\$]],[[\$\$]]) + s = r.gsub(s,[[\$\$]]) out.write( "\\\n\t\t", s , ".class" ) end for _, s in ipairs(dirs) do
--- a/website/src/manual.html.luan Sat Jul 23 21:53:04 2022 -0600 +++ b/website/src/manual.html.luan Sun Jul 24 23:43:03 2022 -0600 @@ -2415,6 +2415,16 @@ <% end } + ["String.contains"] = { + title = "<code>String.contains (s, s2)</code>" + content = function() +%> +<p> +Returns a boolean indicating whether the <code>s</code> contains <code>s2</code>. +</p> +<% + end + } ["String.encode"] = { title = "<code>String.encode (s)</code>" content = function() @@ -2425,6 +2435,16 @@ <% end } + ["String.ends_with"] = { + title = "<code>String.ends_with (s, s2)</code>" + content = function() +%> +<p> +Returns a boolean indicating whether the <code>s</code> ends with <code>s2</code>. +</p> +<% + end + } ["String.find"] = { title = "<code>String.find (s, pattern [, init [, plain]])</code>" content = function() @@ -2634,6 +2654,16 @@ <% end } + ["String.regex"] = { + title = "<code>String.regex (s)</code>" + content = function() +%> +<p> +Returns a <a href="#regex_table">regex</a> table for the pattern <code>s</code>. +</p> +<% + end + } ["String.regex_quote"] = { title = "<code>String.regex_quote (s)</code>" content = function() @@ -2678,6 +2708,16 @@ <% end } + ["String.starts_with"] = { + title = "<code>String.starts_with (s, s2)</code>" + content = function() +%> +<p> +Returns a boolean indicating whether the <code>s</code> starts with <code>s2</code>. +</p> +<% + end + } ["String.sub"] = { title = "<code>String.sub (s, i [, j])</code>" content = function() @@ -2785,6 +2825,205 @@ } } } + regex_table = { + title = "Regular Expressions" + content = function() +%> +<p> +Regular expressions are handled using a regex table generated by <a href="#String.regex">String.regex</a>. +</p> + +<p> +Pattern matching is based on the Java <a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Pattern</a> class. +</p> +<% + end + subs = { + ["regex.find"] = { + title = "<code>regex.find (s [, init])</code>" + content = function() +%> +<p> +Looks for the first match of +the regex in the string <code>s</code>. +If it finds a match, then <code>find</code> returns the indices of <code>s</code> +where this occurrence starts and ends; +otherwise, it returns <b>nil</b>. +A third, optional numerical argument <code>init</code> specifies +where to start the search; +its default value is 1 and can be negative. +</p> + +<p> +If the regex has captures, +then in a successful match +the captured values are also returned, +after the two indices. +</p> +<% + end + } + ["regex.gmatch"] = { + title = "<code>regex.gmatch (s)</code>" + content = function() +%> +<p> +Returns an iterator function that, +each time it is called, +returns the next captures from the regex +over the string <code>s</code>. +If the regex specifies no captures, +then the whole match is produced in each call. +</p> + +<p> +As an example, the following loop +will iterate over all the words from string <code>s</code>, +printing one per line: +</p> +<pre> + local r = String.regex[[\w+]] + local s = "hello world from Lua" + for w in r.gmatch(s) do + print(w) + end +</pre> + +<p> +The next example collects all pairs <code>key=value</code> from the +given string into a table: +</p> +<pre> + local t = {} + local r = String.regex[[(\w+)=(\w+)]] + local s = "from=world, to=Lua" + for k, v in r.gmatch(s) do + t[k] = v + end +</pre> + +<p> +For this function, a caret '<code>^</code>' at the start of a pattern does not +work as an anchor, as this would prevent the iteration. +</p> +<% + end + } + ["regex.gsub"] = { + title = "<code>regex.gsub (s, repl [, n])</code>" + content = function() +%> +<p> +Returns a copy of <code>s</code> +in which all (or the first <code>n</code>, if given) +occurrences of the regex have been +replaced by a replacement string specified by <code>repl</code>, +which can be a string, a table, or a function. +<code>gsub</code> also returns, as its second value, +the total number of matches that occurred. +The name <code>gsub</code> comes from <em>Global SUBstitution</em>. +</p> + +<p> +If <code>repl</code> is a string, then its value is used for replacement. +The character <code>\</code> works as an escape character. +Any sequence in <code>repl</code> of the form <code>$<em>d</em></code>, +with <em>d</em> between 1 and 9, +stands for the value of the <em>d</em>-th captured substring. +The sequence <code>$0</code> stands for the whole match. +</p> + +<p> +If <code>repl</code> is a table, then the table is queried for every match, +using the first capture as the key. +</p> + +<p> +If <code>repl</code> is a function, then this function is called every time a +match occurs, with all captured substrings passed as arguments, +in order. +</p> + +<p> +In any case, +if the regex specifies no captures, +then it behaves as if the whole regex was inside a capture. +</p> + +<p> +If the value returned by the table query or by the function call +is not <b>nil</b>, +then it is used as the replacement string; +otherwise, if it is <b>nil</b>, +then there is no replacement +(that is, the original match is kept in the string). +</p> + +<p> +Here are some examples: +</p> +<pre> + local r = String.regex[[(\w+)]] + local x = r.gsub("hello world", "$1 $1") + --> x="hello hello world world" + + local r = String.regex[[(\w+)]] + local x = r.gsub("hello world", "$0 $0", 1) + --> x="hello hello world" + + local r = String.regex[[(\w+)\s*(\w+)]] + local x = r.gsub("hello world from Luan", "$2 $1") + --> x="world hello Luan from" + + local r = String.regex[[\$(.*?)\$]] + local x = r.gsub("4+5 = $return 4+5$", function(s) + return load(s)() + end) + --> x="4+5 = 9" + + local r = String.regex[[\$(\w+)]] + local t = {name="lua", version="5.3"} + local x = r.gsub("$name-$version.tar.gz", t) + --> x="lua-5.3.tar.gz" +</pre> +<% + end + } + ["regex.match"] = { + title = "<code>regex.match (s [, init])</code>" + content = function() +%> +<p> +Looks for the first <em>match</em> of +the regex in the string <code>s</code>. +If it finds one, then <code>match</code> returns +the captures from the regex; +otherwise it returns <b>nil</b>. +If the regex specifies no captures, +then the whole match is returned. +A third, optional numerical argument <code>init</code> specifies +where to start the search; +its default value is 1 and can be negative. +</p> +<% + end + } + ["regex.matches"] = { + title = "<code>regex.matches (s)</code>" + content = function() +%> +<p> +Returns a boolean indicating whether the regex can be found in string <code>s</code>. +This function is equivalent to +</p> +<pre> + return regex.match(s) ~= nil +</pre> +<% + end + } + } + } binary_lib = { title = "Binary Manipulation" content = function() @@ -3375,6 +3614,14 @@ display: inline-block; width: 100px; } + code { + font-size: 16px; + font-weight: bold; + } + div[toc] code { + font-size: inherit; + font-weight: inherit; + } </style> </head> <body>