diff options
-rw-r--r-- | lua/pl/List.lua | 2 | ||||
-rw-r--r-- | lua/pl/dir.lua | 137 | ||||
-rw-r--r-- | lua/pl/init.lua | 12 | ||||
-rw-r--r-- | lua/pl/lexer.lua | 36 | ||||
-rw-r--r-- | lua/pl/luabalanced.lua | 5 | ||||
-rw-r--r-- | lua/pl/path.lua | 21 | ||||
-rw-r--r-- | lua/pl/seq.lua | 12 | ||||
-rw-r--r-- | lua/pl/utils.lua | 5 |
8 files changed, 132 insertions, 98 deletions
diff --git a/lua/pl/List.lua b/lua/pl/List.lua index f34360c..99fedfe 100644 --- a/lua/pl/List.lua +++ b/lua/pl/List.lua @@ -339,7 +339,7 @@ local function tostring_q(val) end --- how our list should be rendered as a string. Uses join(). --- @see pl.List:join +-- @see List:join function List:__tostring() return '{'..self:join(',',tostring_q)..'}' end diff --git a/lua/pl/dir.lua b/lua/pl/dir.lua index 15a0386..b45bc94 100644 --- a/lua/pl/dir.lua +++ b/lua/pl/dir.lua @@ -101,7 +101,8 @@ function dir.getdirectories(dir) return _listfiles(dir,false) end -local function quote_if_necessary (f) +local function quote_argument (f) + f = path.normcase(f) if f:find '%s' then return '"'..f..'"' else @@ -112,56 +113,83 @@ end local alien,no_alien,kernel,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile +local function execute_command(cmd,parms) + if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end + local err = path.is_windows and ' > ' or ' 2> ' + cmd = cmd..' '..parms..err..cmd_tmpfile + --print(cmd) + local ret = os.execute(cmd) == 0 + if not ret then + return false,(utils.readfile(cmd_tmpfile):gsub('\n(.*)','')) + else + return true + end +end + +local function find_alien_copyfile () + if not alien and not no_alien then + res,alien = pcall(require,'alien') + no_alien = not res + if no_alien then alien = nil end + if alien then + -- register the Win32 CopyFile and MoveFile functions + local copySpec = {'string','string','int',ret='int',abi='stdcall'} + kernel = alien.load('kernel32.dll') + CopyFile = kernel.CopyFileA + CopyFile:types(copySpec) + local moveSpec = {'string','string',ret='int',abi='stdcall'} + MoveFile = kernel.MoveFileA + MoveFile:types(moveSpec) + GetLastError = kernel.GetLastError + GetLastError:types{ret ='int', abi='stdcall'} + win32_errors = { + ERROR_FILE_NOT_FOUND = 2, + ERROR_PATH_NOT_FOUND = 3, + ERROR_ACCESS_DENIED = 5, + ERROR_WRITE_PROTECT = 19, + ERROR_BAD_UNIT = 20, + ERROR_NOT_READY = 21, + ERROR_WRITE_FAULT = 29, + ERROR_READ_FAULT = 30, + ERROR_SHARING_VIOLATION = 32, + ERROR_LOCK_VIOLATION = 33, + ERROR_HANDLE_DISK_FULL = 39, + ERROR_BAD_NETPATH = 53, + ERROR_NETWORK_BUSY = 54, + ERROR_DEV_NOT_EXIST = 55, + ERROR_FILE_EXISTS = 80, + ERROR_OPEN_FAILED = 110, + ERROR_INVALID_NAME = 123, + ERROR_BAD_PATHNAME = 161, + ERROR_ALREADY_EXISTS = 183, + } + end + end +end + +local function two_arguments (f1,f2) + return quote_argument(f1)..' '..quote_argument(f2) +end + local function file_op (is_copy,src,dest,flag) - local null + if flag == 1 and path.exists(dest) then + return false,"cannot overwrite destination" + end if is_windows then local res -- if we haven't tried to load Alien before, then do so - if not alien and not no_alien then - res,alien = pcall(require,'alien') - no_alien = not res - if no_alien then alien = nil end - if alien then - -- register the Win32 CopyFile and MoveFile functions - local copySpec = {'string','string','int',ret='int',abi='stdcall'} - kernel = alien.load('kernel32.dll') - CopyFile = kernel.CopyFileA - CopyFile:types(copySpec) - local moveSpec = {'string','string',ret='int',abi='stdcall'} - MoveFile = kernel.MoveFileA - MoveFile:types(moveSpec) - GetLastError = kernel.GetLastError - GetLastError:types{ret ='int', abi='stdcall'} - win32_errors = { - ERROR_FILE_NOT_FOUND = 2, - ERROR_PATH_NOT_FOUND = 3, - ERROR_ACCESS_DENIED = 5, - ERROR_WRITE_PROTECT = 19, - ERROR_BAD_UNIT = 20, - ERROR_NOT_READY = 21, - ERROR_WRITE_FAULT = 29, - ERROR_READ_FAULT = 30, - ERROR_SHARING_VIOLATION = 32, - ERROR_LOCK_VIOLATION = 33, - ERROR_HANDLE_DISK_FULL = 39, - ERROR_BAD_NETPATH = 53, - ERROR_NETWORK_BUSY = 54, - ERROR_DEV_NOT_EXIST = 55, - ERROR_FILE_EXISTS = 80, - ERROR_OPEN_FAILED = 110, - ERROR_INVALID_NAME = 123, - ERROR_BAD_PATHNAME = 161, - ERROR_ALREADY_EXISTS = 183, - } - end - end - if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end + find_alien_copyfile() -- fallback if there's no Alien, just use DOS commands *shudder* + -- 'rename' involves a copy and then deleting the source. if not CopyFile then src = path.normcase(src) dest = path.normcase(dest) cmd = is_copy and 'copy' or 'rename' - null = ' > '..cmd_tmpfile + local res, err = execute_command('copy',two_arguments(src,dest)) + if not res then return nil,err end + if not is_copy then + return execute_command('del',quote_argument(src)) + end else if path.isdir(dest) then dest = path.join(dest,path.basename(src)) @@ -178,23 +206,8 @@ local function file_op (is_copy,src,dest,flag) end end else -- for Unix, just use cp for now - if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end - cmd = is_copy and 'cp' or 'mv' - null = ' 2> '..cmd_tmpfile - end - if flag == 1 and path.exists(dest) then - return false,"cannot overwrite destination" - end - src = quote_if_necessary(src) - dest = quote_if_necessary(dest) - -- let's make this as quiet a call as we can... - cmd = cmd..' '..src..' '..dest..null - --print(cmd) - local ret = os.execute(cmd) == 0 - if not ret then - return false,(utils.readfile(cmd_tmpfile):gsub('\n(.*)','')) - else - return true + return execute_command(is_copy and 'cp' or 'mv', + two_arguments(src,dest)) end end @@ -377,15 +390,17 @@ end function dir.dirtree( d ) assert( d and d ~= "", "directory parameter is missing or empty" ) local exists, isdir = path.exists, path.isdir + local sep = path.sep - if sub( d, -1 ) == "/" then + local last = sub ( d, -1 ) + if last == sep or last == '/' then d = sub( d, 1, -2 ) end local function yieldtree( dir ) for entry in ldir( dir ) do if entry ~= "." and entry ~= ".." then - entry = dir .. "/" .. entry + entry = dir .. sep .. entry if exists(entry) then -- Just in case a symlink is broken. local is_dir = isdir(entry) yield( entry, is_dir ) diff --git a/lua/pl/init.lua b/lua/pl/init.lua index 3cceb28..b7f84a2 100644 --- a/lua/pl/init.lua +++ b/lua/pl/init.lua @@ -1,11 +1,17 @@ --- entry point for loading all PL libraries only on demand! +-------------- +-- entry point for loading all PL libraries only on demand. +-- Requiring 'pl' means that whenever a module is accesssed (e.g. utils.split) +-- then that module is dynamically loaded. The submodules are all brought into +-- the global space. +-- @class module +-- @name pl local modules = { utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true, input=true,seq=true,lexer=true,stringx=true, config=true,pretty=true,data=true,func=true,text=true, operator=true,lapp=true,array2d=true, - comprehension=true,luabalanced=true, + comprehension=true, test = true, app = true, file = true, class = true, List = true, Map = true, Set = true, OrderedMap = true, MultiMap = true, Date = true, @@ -30,7 +36,7 @@ setmetatable(_G,{ -- either way, we load the required module and make it globally available. if found then -- e..g pretty.dump causes pl.pretty to become available as 'pretty' - rawset(_G,name,require('pl.'..name)) + rawset(_G,name,require('pl.'..name)) return _G[name] elseif _hook then return _hook(t,name) diff --git a/lua/pl/lexer.lua b/lua/pl/lexer.lua index 7a3aead..d4d674b 100644 --- a/lua/pl/lexer.lua +++ b/lua/pl/lexer.lua @@ -1,21 +1,21 @@ --- Lexical scanner for creating a sequence of tokens from text. <br> --- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the --- string <code>s</code>. This iterator returns two values, a token type string --- (such as 'string' for quoted string, 'iden' for identifier) and the value of the +-- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the +-- string <code>s</code>. This iterator returns two values, a token type string +-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the -- token. -- <p> -- Versions specialized for Lua and C are available; these also handle block comments --- and classify keywords as 'keyword' tokens. For example: +-- and classify keywords as 'keyword' tokens. For example: -- <pre class=example> -- > s = 'for i=1,n do' -- > for t,v in lexer.lua(s) do print(t,v) end -- keyword for --- iden i --- = = +-- iden i +-- = = -- number 1 --- , , +-- , , -- iden n --- keyword do +-- keyword do -- </pre> -- See the Guide for further <a href="../../index.html#lexer">discussion</a> <br> -- @class module @@ -90,15 +90,6 @@ local function cdump(tok) return yield('comment',tok) end --- handling line comments - want to trim off the excess whitespace (and linefeed) --- and make it a separate space token. Needed because these patterns grab --- upto and including the line end. -local function cdump_line(tok) - local s1 = tok:find '%s*$' - yield("comment",tok:sub(1,s1-1)) - return yield("space",tok:sub(s1)) -end - local function wsdump (tok) return yield("space",tok) end @@ -142,7 +133,6 @@ function lexer.scan (s,matches,filter,options) if filter.space then filter[wsdump] = true end if filter.comments then filter[cdump] = true - filter[cdump_line] = true end end if not matches then @@ -205,11 +195,11 @@ function lexer.scan (s,matches,filter,options) end if idx > sz then if file then - repeat -- next non-empty line + --repeat -- next non-empty line line = line + 1 s = file:read() if not s then return end - until not s:match '^%s*$' + --until not s:match '^%s*$' s = s .. '\n' idx ,sz = 1,#s break @@ -311,7 +301,7 @@ function lexer.lua(s,filter,options) {STRING3,sdump}, {STRING1,sdump}, {STRING2,sdump}, - {'^%-%-.-\n',cdump_line}, + {'^%-%-.-\n',cdump}, {'^%[%[.+%]%]',sdump_l}, {'^%-%-%[%[.+%]%]',cdump}, {'^==',tdump}, @@ -342,7 +332,7 @@ function lexer.cpp(s,filter,options) ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true, ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true, ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true, - ["double"] = true, ["while"] = true, ["new"] = true, + ["double"] = true, ["while"] = true, ["new"] = true, ["namespace"] = true, ["try"] = true, ["catch"] = true, ["switch"] = true, ["case"] = true, ["extern"] = true, ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true, @@ -415,7 +405,7 @@ function lexer.get_separated_list(tok,endtoken,delim) if not token then return nil,'EOS' end -- end of stream is an error! if is_end(token,value) and level == 1 then append(parm_values,tl) - break + break elseif token == '(' then level = level + 1 tappend(tl,'(') diff --git a/lua/pl/luabalanced.lua b/lua/pl/luabalanced.lua index 1bb051a..96edee2 100644 --- a/lua/pl/luabalanced.lua +++ b/lua/pl/luabalanced.lua @@ -1,4 +1,4 @@ --- luabalanced.lua +--- luabalanced.lua -- Extracted delimited Lua sequences from strings.[1] -- Inspired by Damian Conway's Text::Balanced[2] in Perl. -- @@ -6,7 +6,8 @@ -- [2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm -- -- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license). --- +-- @class module +-- @name pl.luabalanced local M = {} diff --git a/lua/pl/path.lua b/lua/pl/path.lua index 9a0daf1..4269b75 100644 --- a/lua/pl/path.lua +++ b/lua/pl/path.lua @@ -31,7 +31,7 @@ else if res then attributes = lfs.attributes currentdir = lfs.currentdir - link_attrib = lfs.symlinkattributes + link_attrib = lfs.symlinkattributes else error("pl.path requires LuaFileSystem") end @@ -119,6 +119,18 @@ else end local sep,dirsep = path.sep,path.dirsep +--- are we running Windows? +-- @class field +-- @name path.is_windows + +--- path separator for this platform. +-- @class field +-- @name path.sep + +--- separator for PATH for this platform +-- @class field +-- @name path.dirsep + --- given a path, return the directory part and a file part. -- if there's no directory part, the first value will be empty -- @param P A file path @@ -142,8 +154,11 @@ end function path.abspath(P) assert_string(1,P) if not currentdir then return P end + local pwd = currentdir() if not path.isabs(P) then - return path.join(currentdir(),P) + return path.join(pwd,P) + elseif path.is_windows and P:sub(2,2) ~= ':' then + return pwd:sub(1,2)..P else return P end @@ -256,7 +271,7 @@ end -- unlike os.tmpnam(), it always gives you a writeable path (uses %TMP% on Windows) function path.tmpname () local res = tmpnam() - if is_windows then res = getenv('TMP')..res end + if path.is_windows then res = getenv('TMP')..res end return res end diff --git a/lua/pl/seq.lua b/lua/pl/seq.lua index c182b7a..20731f2 100644 --- a/lua/pl/seq.lua +++ b/lua/pl/seq.lua @@ -112,8 +112,8 @@ end -- @param optional argument to be passed to predicate as second argument. function seq.count(iter,condn,arg) local i = 0 - foreach(iter,function(val) - if condn(v,arg) then i = i + 1 end + seq.foreach(iter,function(val) + if condn(val,arg) then i = i + 1 end end) return i end @@ -148,7 +148,7 @@ end -- @param iter a sequence -- @return a List -- @usage copy(list(ls)) is equal to ls --- @usage copy(list {1,2,3},List) == List{1,2,3} +-- @usage copy(list {1,2,3}) == List{1,2,3} function seq.copy(iter) local res = {} for v in default_iter(iter) do @@ -255,6 +255,7 @@ function seq.unique(iter,returns_table) local t = count_map(iter) local res = {} for k in pairs(t) do tappend(res,k) end + table.sort(res) if returns_table then return res else @@ -361,7 +362,7 @@ function seq.reduce (fun,seq,oldval) end local val = seq() if val==nil then return oldval - else return fun(oldval,reduce(fun,seq,val)) + else return fun(oldval,seq.reduce(fun,seq,val)) end end @@ -461,6 +462,7 @@ end -- can't directly look these up in seq because of the wrong argument order... +local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod local overrides = { map = function(self,fun,arg) return map(fun,self,arg) @@ -487,7 +489,7 @@ SMT = { setmetatable(seq,{ __call = function(tbl,iter) if not callable(iter) then - if type(iter) == 'table' then iter = list(iter) + if type(iter) == 'table' then iter = seq.list(iter) else return iter end end diff --git a/lua/pl/utils.lua b/lua/pl/utils.lua index 96867fc..9639455 100644 --- a/lua/pl/utils.lua +++ b/lua/pl/utils.lua @@ -273,6 +273,11 @@ local function _string_lambda(f) end end +--- an anonymous function as a string. This string is of the form +-- '|args| expression'. +-- @param lf function as a string +-- @return a function +-- @usage string_lambda '|x|x+1' (2) == 3 utils.string_lambda = utils.memoize(_string_lambda) local ops |