On the wait.lua modified(Generation of post)

Posted by Lzkd on Wed 03 Dec 2014 02:12 AM — 17 posts, 61,285 views.

#0
Hello everyone

My English is very bad.I transformed my native language in use translation software.So the expression may not be accurate, please forgive me.
We are a group of using MUSHclient to play MUD.Some people think that there is not enough perfect place wail.lua, and make improvement.

This modified wait.lua, had been used by many people, that good.We get happy people from MUSHclient, so I hope that Mush is getting better and better.Now, we submit the amendment, thanks to Nick. And MUSHclient, I hope more and more powerful.


------------------------------
When the implementation time of thread, there will be some problems.The code on the next page.
Amended on Wed 03 Dec 2014 02:50 AM by Lzkd
#1

function subthread()  --子线程 (child thread)
    wait.make (function()
        while true do
             Note("----------sub-thread-----------")
             wait.time(2)
        end
    end)
end
function mainfun()  --主线程 (the main thread)
    wait.make (function()
        subthread() --调用子线程 (call the child thread)
        for i=1,5 do
             Note("----------main-----------")
             wait.time(2)
        end
        Note("----------close-----------")
    end)
end

The problem is that, after the closure of main thread, thread can't be closed. A lot of the time this is very hate.
--------------------------------------------
The modified wait.lua can do this.

function subthread()  --子线程 (child thread)
    local st=wait.subwait:new()
    wait.make (function()
        while true do
             Note("----------sub-thread-----------")
             st:time(2)
        end
    end)
    return st
end
function mainfun()  --主线程 (the main thread)
    wait.make (function()
        local st=subthread() --调用子线程 (call the child thread)
        for i=1,5 do
             Note("----------main-----------")
             wait.time(2)
        end
        st:close()   --关闭子线程 (close the child thread)
        Note("----------close-----------")
    end)
end


The main thread can be closed to create their own thread.




[EDIT] Comments translated by Nick using Google Translate.
Amended on Wed 03 Dec 2014 04:32 AM by Nick Gammon
#2
In addition, there are some changes.
1.Add a wait for multiple triggers returned by the function.


    l,w=wait.mulregexp({
                        {"^\s{4}(这里没有.*出路|这里.*的出口是 [a-z\d]+).*。"},  --单行触发器 (a single line trigger)
                        {"^\s{4}这里.*的出口是 [a-z\d]+.*\n.*。",true,2}        --多行触发器 (multi-line trigger)
                       },
                       8,nil)


The above function will create a single trigger, a multi line 2 lines of the trigger, a timer, a coroutine object.
This function is no longer try regular on a row matching two rows of data.




[EDIT] Comments translated by Nick using Google Translate.
Amended on Wed 03 Dec 2014 04:32 AM by Nick Gammon
#3
2. The coroutine package for a more simple suspension mechanism using. In order to be able to interrupt function flow in time of need.


function wakefun()
    -- Wake-called "pausename" and pass the value "this is some input" to pause position
    wait.wake("pausename","this is some input")  
end

function mainfun()
    wait.make (function ()
        local text
        while true do
            Note("---test---") 
            wait.time(2)
            --Here pause, wait function wait.wake wake, the pause position named "pausename".
            text=wait.pause("pausename")    
            Note(text)
        end
    end)
end


It can be used in a particular situation.




[EDIT] Comments translated by Nick using Google Translate.
Amended on Wed 03 Dec 2014 04:34 AM by Nick Gammon
#4
All the modified wait.lua

Too long, cannot be posted. Treatment with the other way.

http://pan.baidu.com/s/1pJ2YfZP

This link, you can download to modify the wait.lua, do not need to register.
Amended on Wed 03 Dec 2014 02:43 AM by Lzkd
Australia Forum Administrator #5
Interesting. I'll take a look at it.

Can you translate the comments into English? The file has not converted into Unicode properly, and I cannot translate them.
#6
Nick Gammon said:

Interesting. I'll take a look at it.

Can you translate the comments into English? The file has not converted into Unicode properly, and I cannot translate them.



Okay, I do this.But may be a little late.My time here, I need to go out. When I come back, I will operate.
In addition, I want to know. Do you need me to translate, is post in Chinese, or links to download the wait.lua Chinese.
Amended on Wed 03 Dec 2014 03:25 AM by Lzkd
Australia Forum Administrator #7
I am not sure what happened to it, but the comments just look like garbage:


------------------------------------------
--¶à´¥·¢¶¨Ê±Æ÷³¬Ê±»Øµ÷Óõĺ¯Êý
--
--return  ·µ»ØÐ½¨µÄ¶ÔÏó
------------------------------------------
function timer_mulresume (name)



If you can convert them to UTF-8 I can probably translate them with Google Translate.

Quote:

... or links to download the wait.lua Chinese ...


A link will do provided it is proper Chinese and I can use Google Translate on it.
Australia Forum Administrator #8
Never mind. I worked it out.


iconv -f GB2312 -t utf-8 < wait.lua > wait.lua.fixed



------------------------------------------
--多触发回调用的函数
--
--return  返回新建的对象
------------------------------------------


Now I can translate that:


------------------------------------------
-- Multi-trigger function call back
--
--return return the new object
------------------------------------------
Amended on Wed 03 Dec 2014 03:58 AM by Nick Gammon
Australia Forum Administrator #9
Here is the wait.lua you supplied with comments translated:


-- wait.lua
-- ----------------------------------------------------------
-- 'wait' stuff - lets you build pauses into scripts.
-- See forum thread:
--   http://www.gammon.com.au/forum/?id=4957
--
--Modified by simon
--Date 2013.12.10
--Email:hapopen@gmail.com
--Fix: 2014.1.20
-- ----------------------------------------------------------

--[[

Example of an alias 'send to script':


require "wait"

wait.make (function ()  --- coroutine below here

  repeat
    Send "cast heal"
    line, wildcards = 
       wait.regexp ("^(You heal .*|You lose your concentration)$")

  until string.find (line, "heal")

  -- wait a second for luck
  wait.time (1) 

end)  -- end of coroutine

--]]

require "check"

module (..., package.seeall)

-- ----------------------------------------------------------
-- table of outstanding threads that are waiting
-- ----------------------------------------------------------
local threads = {}

-- ----------------------------------------------------------
-- wait.timer_resume: called by a timer to resume a thread
-- ----------------------------------------------------------
function timer_resume (name)
  local thread = threads [name]
  if thread then
    threads [name] = nil
    local ok, err = coroutine.resume (thread)
    if not ok then
       ColourNote ("deeppink", "black", "Error raised in timer function (in wait module).")
       ColourNote ("darkorange", "black", debug.traceback (thread))
       error (err)
    end -- if
  end -- if
end -- function timer_resume 

-- ----------------------------------------------------------
-- wait.trigger_resume: called by a trigger to resume a thread
-- ----------------------------------------------------------
function trigger_resume (name, line, wildcards, styles)
  local thread = threads [name]
  if thread then
    threads [name] = nil
    local ok, err = coroutine.resume (thread, line, wildcards, styles)
    if not ok then
       ColourNote ("deeppink", "black", "Error raised in trigger function (in wait module)")
       ColourNote ("darkorange", "black", debug.traceback (thread))
       error (err)
    end -- if
  end -- if
end -- function trigger_resume 

-- ----------------------------------------------------------
-- convert x seconds to hours, minutes, seconds (for AddTimer)
-- ----------------------------------------------------------
local function convert_seconds (seconds)
  local hours = math.floor (seconds / 3600)
  seconds = seconds - (hours * 3600)
  local minutes = math.floor (seconds / 60)
  seconds = seconds - (minutes * 60)
  return hours, minutes, seconds
end -- function convert_seconds 

-- ----------------------------------------------------------
-- wait.time: we call this to wait in a script
-- ----------------------------------------------------------
function time (seconds)
  local id = "wait_timer_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")

  local hours, minutes, seconds = convert_seconds (seconds)

  check (AddTimer (id, hours, minutes, seconds, "",
                  bit.bor (timer_flag.Enabled,
                           timer_flag.OneShot,
                           timer_flag.Temporary,
                           timer_flag.ActiveWhenClosed,
                           timer_flag.Replace), 
                   "wait.timer_resume"))

  return coroutine.yield ()
end -- function time

-- ----------------------------------------------------------
-- wait.regexp: we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function regexp (regexp, timeout, flags, multi, multi_lines)
  if type(regexp) == "table" then
    local s="("
    for k,v in pairs(regexp) do
      if k~=1 then
        s=s.."|"
      end
      s=s..v
    end
    s=s..")"
    regexp=s
  end
  local id = "wait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")
            
  check (AddTriggerEx (id, regexp, 
            "-- added by wait.regexp",  
            bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
                     trigger_flag.Enabled, 
                     trigger_flag.RegularExpression,
                     trigger_flag.Temporary,
                     trigger_flag.Replace,
                     trigger_flag.OneShot),
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait.trigger_resume", 
            12, 100))  -- send to script (in case we have to delete the timer)
  if (multi ~= nil) and multi then
    SetTriggerOption (id, "multi_line", multi)
    if (multi_lines ~= nil) then
      SetTriggerOption (id, "lines_to_match", multi_lines)
    end
  end
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.ActiveWhenClosed,
                            timer_flag.Replace), 
                   "wait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))  

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end -- function regexp 

-- ----------------------------------------------------------
-- wait.match: we call this to wait for a trigger (not a regexp)
-- ----------------------------------------------------------
function match (match, timeout, flags)
  return regexp (MakeRegularExpression (match), timeout, flags)
end -- function match 

-- ----------------------------------------------------------
-- wait.make: makes a coroutine and resumes it
-- ----------------------------------------------------------
function make (f)
  assert (type (f) == "function", "wait.make requires a function")
  assert (GetOption ("enable_timers") == 1, "Timers not enabled")
  assert (GetOption ("enable_triggers") == 1, "Triggers not enabled")
  coroutine.wrap (f) () -- make coroutine, resume it
end -- make


------------------------------------------
-- Multi-function timer expires trigger call back
--
-- Returns the newly created object
------------------------------------------
function timer_mulresume (name)
  DeleteTriggerGroup(name)
  local thread = threads [name]
  if thread then
    threads [name] = nil
    local ok, err = coroutine.resume (thread)
    if not ok then
       ColourNote ("deeppink", "black", "Error raised in timer function (in wait module).")
       ColourNote ("darkorange", "black", debug.traceback (thread))
       error (err)
    end -- if
  end -- if
end -- function timer_mulresume 

------------------------------------------
-- Multi-trigger function call back
--
-- return the new object
------------------------------------------
function trigger_mulresume (name, line, wildcards, styles)
  local gp=GetTriggerOption(name,"group")
  DeleteTriggerGroup(gp)
  DeleteTimer(gp)  
  local thread = threads [gp]
  if thread then
    threads [gp] = nil
    local ok, err = coroutine.resume (thread, line, wildcards, styles)
    if not ok then
       ColourNote ("deeppink", "black", "Error raised in trigger function (in wait module)")
       ColourNote ("darkorange", "black", debug.traceback (thread))
       error (err)
    end -- if
  end -- if
end -- function trigger_mulresume 

------------------------------------------
-- This function waits for a trigger multiple regular return. 
-- After a trigger to return any other triggers will also fail.
--@regexps   List of regular expressions. 
--           {Regular expressions, whether multi-line trigger, trigger rows}
--@timeout   Timeout
--@flags     Trigger characteristic mark
--
--Returns the newly created object
------------------------------------------
function mulregexp (regexps, timeout, flags)
  local gp = "wait_group_" .. GetUniqueNumber ()
  threads [gp] = assert (coroutine.running (), "Must be in coroutine")
  local id
  for k,v in pairs(regexps) do
      id = "wait_trigger_" .. GetUniqueNumber ()
      check (AddTriggerEx (id, v[1], 
		"-- added by wait.mulregexp",  
		bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
			 trigger_flag.Enabled, 
			 trigger_flag.RegularExpression,
			 trigger_flag.Temporary,
			 trigger_flag.Replace,
			 trigger_flag.OneShot),
		custom_colour.NoChange, 
		0, "",  -- wildcard number, sound file name
		"wait.trigger_mulresume", 
		12, 100))  -- send to script (in case we have to delete the timer)
      SetTriggerOption (id, "group", gp)
      if (v[2] ~= nil) and v[2] then
	SetTriggerOption (id, "multi_line", v[2])
	if (v[3] ~= nil) then
	  SetTriggerOption (id, "lines_to_match", v[3])
	end
      end
  end
            
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (gp, hours, minutes, seconds, 
		   "-- added by wait.mulregexp",  
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.ActiveWhenClosed,
                            timer_flag.Replace), 
                   "wait.timer_mulresume"))

    check (SetTimerOption (gp, "send_to", "12"))  -- send to script

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end
------------------------------------------
--Restore a specified process
--@sign  Pause logo
--@text  Returns a string to suspend location
--
--Returns the newly created object
------------------------------------------
function wake (sign,text)
  local name="wait_pause_"..sign
  local thread = threads [name]
  if thread then
    threads [name] = nil
    local ok, err = coroutine.resume (thread,text)
    if not ok then
       ColourNote ("deeppink", "black", "Error raised in timer function (in wait module).")
       ColourNote ("darkorange", "black", debug.traceback (thread))
       error (err)
    end -- if
  end -- if
end
------------------------------------------
-- Suspend a process until wake is called
--@sign  pause location identification wordmark
--
--return the new object
------------------------------------------
function pause (sign)
  local id = "wait_pause_"..sign
  threads [id] = assert (coroutine.running (), "Must be in coroutine")
  return coroutine.yield ()  -- return line, wildcards
end

subwait={}
------------------------------------------
-- Delete the array elements specified in the table, delete only one
--@arr     table
--@element element
--
--returns the new table
------------------------------------------
function table_del(arr, element) 
   if arr==nil then
      return nil
   end
   if #arr==0 then
      return {}
   end
   for k, value in pairs(arr) do
      if value == element then 
         table.remove(arr, k)
         return arr 
      end 
   end   
   return arr
end 
------------------------------------------
--Start a new thread object and returns a new thread
--
--Returns the newly created object
------------------------------------------
function subwait:new ()
    local o = {list={["timer"]={},["trigger"]={},["group"]={}}} 
    setmetatable(o, self)
    self.__index = self
    return o
end
------------------------------------------
--Close thread object
--
--return  No
------------------------------------------
function subwait:close ()
    for key,value in pairs(self.list["timer"]) do
	DeleteTimer(value)
	threads[value]=nil
    end
    self.list["timer"]={}
    for key,value in pairs(self.list["trigger"]) do
	DeleteTrigger(value)
	DeleteTimer(value)
	threads[value]=nil
    end
    self.list["trigger"]={}
    for key,value in pairs(self.list["group"]) do
        DeleteTriggerGroup(value)
	DeleteTimer(value)
	threads[value]=nil
    end
    self.list["group"]={}
end
------------------------------------------
--Child thread waits
--@seconds
--
--return  No
------------------------------------------
function subwait:time (seconds)
  local id = "subwait_timer_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")

  local hours, minutes, seconds = convert_seconds (seconds)

  check (AddTimer (id, hours, minutes, seconds, "",
                  bit.bor (timer_flag.Enabled,
                           timer_flag.OneShot,
                           timer_flag.Temporary,
                           timer_flag.ActiveWhenClosed,
                           timer_flag.Replace), 
                   "wait.timer_resume"))
  table.insert(self.list["timer"],id)
  --return coroutine.yield ()
  coroutine.yield () 
  self.list["timer"]=table_del(self.list["timer"],id) 
  return
 
end
------------------------------------------
--Child thread waits for a trigger
--
--return  Text data triggered
------------------------------------------
function subwait:regexp (regexp, timeout, flags, multi, multi_lines)
  if type(regexp) == "table" then
    local s="("
    for k,v in pairs(regexp) do
      if k~=1 then
        s=s.."|"
      end
      s=s..v
    end
    s=s..")"
    regexp=s
  end
  local id = "subwait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")
            
  check (AddTriggerEx (id, regexp, 
            "-- added by subwait.regexp",  
            bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
                     trigger_flag.Enabled, 
                     trigger_flag.RegularExpression,
                     trigger_flag.Temporary,
                     trigger_flag.Replace,
                     trigger_flag.OneShot),
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait.trigger_resume", 
            12, 100))  -- send to script (in case we have to delete the timer)
  if (multi ~= nil) and multi then
    SetTriggerOption (id, "multi_line", multi)
    if (multi_lines ~= nil) then
      SetTriggerOption (id, "lines_to_match", multi_lines)
    end
  end
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.ActiveWhenClosed,
                            timer_flag.Replace), 
                   "wait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))  

  end -- if having a timeout

  --return coroutine.yield ()  -- return line, wildcards
  table.insert(self.list["trigger"],id)
  local line, wildcards, styles
  line, wildcards, styles=coroutine.yield () 
  self.list["trigger"]=table_del(self.list["trigger"],id) 
  return line, wildcards, styles
end
------------------------------------------
-- This function waits for multiple regular trigger returned. 
-- After a trigger to return any other triggers will also fail.
--@regexps    list of regular expressions.
--            {Regular expressions, whether multi-line trigger, trigger rows}
--@timeout   timeout
--@flags      trigger characteristic mark
--
--return  return the new object
------------------------------------------
function subwait:mulregexp (regexps, timeout, flags)
  local gp = "wait_group_" .. GetUniqueNumber ()
  threads [gp] = assert (coroutine.running (), "Must be in coroutine")
  local id
  for k,v in pairs(regexps) do
      id = "wait_trigger_" .. GetUniqueNumber ()
      check (AddTriggerEx (id, v[1], 
		"-- added by wait.mulregexp",  
		bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
			 trigger_flag.Enabled, 
			 trigger_flag.RegularExpression,
			 trigger_flag.Temporary,
			 trigger_flag.Replace,
			 trigger_flag.OneShot),
		custom_colour.NoChange, 
		0, "",  -- wildcard number, sound file name
		"wait.trigger_mulresume", 
		12, 100))  -- send to script (in case we have to delete the timer)
      SetTriggerOption (id, "group", gp)
      if (v[2] ~= nil) and v[2] then
	SetTriggerOption (id, "multi_line", v[2])
	if (v[3] ~= nil) then
	  SetTriggerOption (id, "lines_to_match", v[3])
	end
      end
  end
            
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (gp, hours, minutes, seconds, 
		   "-- added by wait.mulregexp",  
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.ActiveWhenClosed,
                            timer_flag.Replace), 
                   "wait.timer_mulresume"))

    check (SetTimerOption (gp, "send_to", "12"))  -- send to script

  end -- if having a timeout

  --return coroutine.yield ()  -- return line, wildcards
  table.insert(self.list["group"],gp)
  local line, wildcards, styles
  line, wildcards, styles=coroutine.yield () 
  self.list["group"]=table_del(self.list["group"],gp) 
  return line, wildcards, styles
end
Australia Forum Administrator #10
You appear to have omitted a recent change:


 -- ----------------------------------------------------------
 function make (f)
   assert (type (f) == "function", "wait.make requires a function")
-
-  -- More friendly failure, suggested by Fiendish
-  local errors = {}
-  if GetOption ("enable_timers") ~= 1 then
-    table.insert (errors, "TIMERS")
-  end -- if timers disabled
-  if GetOption ("enable_triggers") ~= 1 then
-    table.insert (errors, "TRIGGERS")
-  end  -- if triggers disabled
-  if #errors ~= 0 then
-    ColourNote("white", "red",
-               "One of your scripts (in '" ..
-               (GetPluginInfo(GetPluginID(), 1) or "World") ..
-                "') just did something that requires " ..
-                table.concat (errors, " and ") ..
-                " to be enabled, but they aren't. " ..
-                "Please check your configuration settings.")
-    return nil, "Trigger/Timers not enabled"  -- bad return
-  end  -- if have errors
+  assert (GetOption ("enable_timers") == 1, "Timers not enabled")
+  assert (GetOption ("enable_triggers") == 1, "Triggers not enabled")
   coroutine.wrap (f) () -- make coroutine, resume it
-  return true  -- good return
 end -- make






You don't need to test for non-nil as nil is considered false:


  if (multi ~= nil) and multi then


Can be:


if multi then






 if (multi_lines ~= nil) then


Can be:


if multi_lines then
#11
Nick Gammon said:

Never mind. I worked it out.


iconv -f GB2312 -t utf-8 < wait.lua > wait.lua.fixed



------------------------------------------
--多触发回调用的函数
--
--return  返回新建的对象
------------------------------------------


Now I can translate that:


------------------------------------------
-- Multi-trigger function call back
--
--return return the new object
------------------------------------------



http://pan.baidu.com/s/1bnH9H8r

I finished, it seems a step slow.
#12
A description provides the wait.lua modifier.
I don't know whether it will help things, stick out together.

-------------------

Wait.time (seconds) the old module function
Wait.regexp (regexp, timeout, flags, multi, multi_lines) the old module function, modified to support multiple lines
Wait.match (match, timeout, flags) the old module function
Wait.make (f) the old module function
Wait.mulregexp (regexps, timeout, flags) increase new function. Allow multiple rows multiple triggers
Wait.pause (sign) new increasing function. Pause function process
Wait.wake (sign, text) increase new function. Resume function process

-------

The following is the trigger function simulation thread
Wait.subwait:new () new increasing function. The new thread
Wait.subwait:close () new increasing function. Zi Xiancheng closed
Wait.subwait:time (seconds) new increasing function. Zi Xiancheng from time to time to wait
Wait.subwait:regexp (regexp, timeout, flags, multi, multi_lines) increase new function. Regular Zi Xiancheng trigger
Wait.subwait:mulregexp (regexps, timeout, flags) increase new function. Multiple triggers Zi Xiancheng in waiting for a function
Amended on Wed 03 Dec 2014 04:23 AM by Lzkd
Australia Forum Administrator #13
I've made a branch (wait_improvements - https://github.com/nickgammon/mushclient/tree/wait_improvements) with a view to analyzing your changes easily:

https://github.com/nickgammon/mushclient/commit/d7113244bdcb
Amended on Wed 03 Dec 2014 04:26 AM by Nick Gammon
Australia Forum Administrator #14
I am guessing "Zi Xiancheng" means "child thread".
Australia Forum Administrator #15

    for k,v in pairs(regexp) do
      if k~=1 then
        s=s.."|"
      end


That should be ipairs
Australia Forum Administrator #16
I've done some cleanups. For example you can replace:


  if type(regexp) == "table" then
    local s="("
    for k,v in pairs(regexp) do
      if k~=1 then
        s=s.."|"
      end
      s=s..v
    end
    s=s..")"
    regexp=s
  end


By:


  if type(regexp) == "table" then
    regexp = "(" .. table.concat (regexp, "|") .. ")"
  end


See if it still works properly.

https://github.com/nickgammon/mushclient/commit/23de39a1b7f449ecf