moonscript, busted, penlight packages + debugger speed improvements

This commit is contained in:
kepler155c@gmail.com
2020-06-09 18:17:21 -06:00
parent de3d73de70
commit 0f7534d12c
19 changed files with 1065 additions and 1561 deletions

9
busted/.package Normal file
View File

@@ -0,0 +1,9 @@
{
title = 'busted',
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/moonscript',
description = [[]],
license = 'MIT',
required = {
'penlight',
},
}

16
busted/depend/system.lua Normal file
View File

@@ -0,0 +1,16 @@
return {
-- Returns the monotonic time the system has been up, in secconds.
monotime = function()
return os.clock()
end,
-- Sleep for n seconds.
sleep = function(n)
os.sleep(n)
end,
-- Returns the current system time, 1970 (UTC), in secconds.
gettime = function()
return os.epoch('utc') / 1000
end,
}

5
busted/depend/term.lua Normal file
View File

@@ -0,0 +1,5 @@
return {
isatty = function()
return false
end,
}

8
busted/etc/fstab Normal file
View File

@@ -0,0 +1,8 @@
packages/busted/busted urlfs https://raw.githubusercontent.com/Olivine-Labs/busted/master/bin/busted
rom/modules/main/busted gitfs Olivine-Labs/busted/master/busted
rom/modules/main/mediator.lua urlfs https://raw.githubusercontent.com/Olivine-Labs/mediator_lua/master/src/mediator.lua
rom/modules/main/cliargs gitfs amireh/lua_cliargs/master/src/cliargs
rom/modules/main/luassert gitfs Olivine-Labs/luassert/master/src
rom/modules/main/say.lua urlfs https://raw.githubusercontent.com/Olivine-Labs/say/master/src/init.lua
rom/modules/main/term.lua linkfs packages/busted/depend/term.lua
rom/modules/main/system.lua linkfs packages/busted/depend/system.lua

View File

@@ -9,21 +9,25 @@ local dbg = {
breakpoints = nil,
}
local function breakpointHook(info)
local function breakpointHook(depth, lineNo)
if dbg.breakpoints then
local src = info.short_src
for _,v in pairs(dbg.breakpoints) do
if v.line == info.currentline
and (v.file == src or v.bfile == src) then
local info
for _,v in ipairs(dbg.breakpoints) do
if v.line == lineNo then
if not info then
info = debug.getinfo(depth)
end
if (v.file == info.short_src or v.bfile == info.short_src) then
return not v.disabled
end
end
end
end
end
local function functionHook(fn)
return function(info)
return info.func == fn
return function()
return debug.getinfo(3).func == fn
end
end
@@ -42,9 +46,9 @@ local function stackSizeHook(n)
end
i = i + 1
end
return function(info)
return function(depth, lineNo)
return not debug.getinfo(i - n)
or breakpointHook(info)
or breakpointHook(depth + 1, lineNo)
end
end
@@ -133,12 +137,10 @@ local function get_trace(offset, stack_inspect_offset)
return t
end
local function hook()
local info = debug.getinfo(2)
local function hook(_, lineNo)
local h = dbg.hooks[coroutine.running()]
if h and h.eval(info) then
if h and h.eval(3, lineNo) then
local inspectOffset = 0
repeat

View File

@@ -1,3 +1,4 @@
local class = require('opus.class')
local Config = require('opus.config')
local Event = require('opus.event')
local UI = require('opus.ui')
@@ -128,7 +129,7 @@ local function message(...)
client:resume('debugger', ...)
end
UI.InverseButton = require('opus.class')(UI.Button)
UI.InverseButton = class(UI.Button)
UI.InverseButton.defaults = {
UIElement = 'InverseButton',
backgroundColor = 'primary',

View File

@@ -1,5 +1,7 @@
running the compiler works fine...
moonc T.moon <-- OK
moonscript must be run in compatibility mode:
> compat moon T.moon
> compat moonc T.moon
working on getting the moon command to work properly
moon T.moon <-- NOPE
moon and moonc were modified to allow relative paths:
> cd /packages/moonscript
> compat moonc T.moon

View File

@@ -1,11 +1,13 @@
local Event = require('opus.event')
local UI = require('opus.ui')
local kernel = _G.kernel
local multishell = _ENV.multishell
local kernel
kernel = _G.kernel
local multishell
multishell = _ENV.multishell
local tasks = multishell and multishell.getTabs and multishell.getTabs() or kernel.routines
UI:configure('Tasks', ...)
local page = UI.Page({
menuBar = UI.MenuBar({
UI.MenuBar({
buttons = {
{
text = 'Activate',

View File

@@ -1,14 +1,15 @@
Event = require('opus.event')
UI = require('opus.ui')
kernel = _G.kernel
multishell = _ENV.multishell
tasks = multishell and multishell.getTabs and multishell.getTabs() or kernel.routines
import kernel from _G
import multishell from _ENV
tasks = multishell and multishell.getTabs and multishell.getTabs! or kernel.routines
UI\configure 'Tasks', ...
page = UI.Page {
menuBar: UI.MenuBar {
UI.MenuBar {
buttons: {
{ text: 'Activate', event: 'activate' },
{ text: 'Terminate', event: 'terminate' },
@@ -43,7 +44,7 @@ page = UI.Page {
t: 'terminate',
},
eventHandler: (event) =>
t = self.grid\getSelected!
t = @grid\getSelected!
switch event.type
when 'activate', 'grid_select'
multishell.setFocus t.uid if t

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
packages/moonscript gitfs leafo/moonscript/master/bin
rom/modules/main/moonscript gitfs leafo/moonscript/master/moonscript
rom/modules/main/moon gitfs leafo/moonscript/master/moon
rom/modules/main/argparse.lua linkfs packages/moonscript/argparse.lua
packages/moonscript/repo gitfs leafo/moonscript/master
rom/modules/main/moonscript linkfs packages/moonscript/repo/moonscript
rom/modules/main/moon linkfs packages/moonscript/repo/moon
rom/modules/main/moonutil gitfs natnat-mc/moonutil/master/moonutil
rom/modules/main/argparse urlfs https://raw.githubusercontent.com/mpeterv/argparse/master/src/argparse.lua

116
moonscript/moon Normal file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env lua
local argparse = require("argparse")
local moonscript = require("moonscript.base")
local util = require("moonscript.util")
local errors = require("moonscript.errors")
local unpack = util.unpack
local argparser = argparse()({
name = "moon"
})
argparser:argument("script")
argparser:argument("args"):args("*")
argparser:option("-c --coverage", "Collect and print code coverage")
argparser:option("-d", "Disable stack trace rewriting")
argparser:option("-v --version", "Print version information")
local base = 0
local _list_0 = arg
for _index_0 = 1, #_list_0 do
local flag = _list_0[_index_0]
base = base + 1
if flag:sub(1, 1) ~= "-" then
break
end
end
local args = {
unpack(arg, 1, base)
}
local opts = argparser:parse(args)
local print_err
print_err = function(...)
local msg = table.concat((function(...)
local _accum_0 = { }
local _len_0 = 1
local _list_1 = {
...
}
for _index_0 = 1, #_list_1 do
local v = _list_1[_index_0]
_accum_0[_len_0] = tostring(v)
_len_0 = _len_0 + 1
end
return _accum_0
end)(...), "\t")
return io.stderr:write(msg .. "\n")
end
local run
run = function()
if opts.version then
require("moonscript.version").print_version()
os.exit()
end
local script_fname = shell.resolve(opts.script)
args = {
unpack(arg, base + 1)
}
args[-1] = arg[0]
args[0] = opts.script
local moonscript_chunk, lua_parse_error
local passed, err = pcall(function()
moonscript_chunk, lua_parse_error = moonscript.loadfile(script_fname, {
implicitly_return_root = false
})
end)
if not (passed) then
print_err(err)
os.exit(1)
end
if not (moonscript_chunk) then
if lua_parse_error then
print_err(lua_parse_error)
else
print_err("Can't file file: " .. tostring(script_fname))
end
os.exit(1)
end
util.getfenv(moonscript_chunk).arg = args
local run_chunk
run_chunk = function()
moonscript.insert_loader()
moonscript_chunk(unpack(args))
return moonscript.remove_loader()
end
if opts.d then
return run_chunk()
end
local err, trace, cov
if opts.coverage then
print("starting coverage")
local coverage = require("moonscript.cmd.coverage")
cov = coverage.CodeCoverage()
cov:start()
end
xpcall(run_chunk, function(_err)
err = _err
trace = debug.traceback("", 2)
end)
if err then
local truncated = errors.truncate_traceback(util.trim(trace))
local rewritten = errors.rewrite_traceback(truncated, err)
if rewritten then
print_err(rewritten)
else
print_err(table.concat({
err,
util.trim(trace)
}, "\n"))
end
return os.exit(1)
else
if cov then
cov:stop()
return cov:print_results()
end
end
end
return run()
-- vim: set filetype=lua:

235
moonscript/moonc Normal file
View File

@@ -0,0 +1,235 @@
#!/usr/bin/env lua
local argparse = require "argparse"
local lfs = require "lfs"
local parser = argparse()
parser:flag("-l --lint", "Perform a lint on the file instead of compiling")
parser:flag("-v --version", "Print version")
parser:flag("-w --watch", "Watch file/directory for updates")
parser:option("--transform", "Transform syntax tree with module")
parser:mutex(
parser:option("-t --output-to", "Specify where to place compiled files"),
parser:option("-o", "Write output to file"),
parser:flag("-p", "Write output to standard output"),
parser:flag("-T", "Write parse tree instead of code (to stdout)"),
parser:flag("-b", "Write parse and compile time instead of code(to stdout)"),
parser:flag("-X", "Write line rewrite map instead of code (to stdout)")
)
parser:flag("-",
"Read from standard in, print to standard out (Must be only argument)")
local read_stdin = arg[1] == "--" -- luacheck: ignore 113
if not read_stdin then
parser:argument("file/directory"):args("+")
end
local opts = parser:parse()
if opts.version then
local v = require "moonscript.version"
v.print_version()
os.exit()
end
function log_msg(...)
if not opts.p then
io.stderr:write(table.concat({...}, " ") .. "\n")
end
end
local moonc = require("moonscript.cmd.moonc")
local util = require "moonscript.util"
local normalize_dir = moonc.normalize_dir
local compile_and_write = moonc.compile_and_write
local path_to_target = moonc.path_to_target
local function scan_directory(root, collected)
root = normalize_dir(root)
collected = collected or {}
for fname in lfs.dir(root) do
if not fname:match("^%.") then
local full_path = root..fname
if lfs.attributes(full_path, "mode") == "directory" then
scan_directory(full_path, collected)
elseif fname:match("%.moon$") then
table.insert(collected, full_path)
end
end
end
return collected
end
local function remove_dups(tbl, key_fn)
local hash = {}
local final = {}
for _, v in ipairs(tbl) do
local dup_key = key_fn and key_fn(v) or v
if not hash[dup_key] then
table.insert(final, v)
hash[dup_key] = true
end
end
return final
end
-- creates tuples of input and target
local function get_files(fname, files)
files = files or {}
if lfs.attributes(fname, "mode") == "directory" then
for _, sub_fname in ipairs(scan_directory(fname)) do
table.insert(files, {
sub_fname,
path_to_target(sub_fname, opts.output_to, fname)
})
end
else
if fname:sub(1, 1) ~= '/' then
fname = lfs.currentdir() .. '/' .. fname
end
table.insert(files, {
fname,
path_to_target(fname, opts.output_to)
})
end
return files
end
if read_stdin then
local parse = require "moonscript.parse"
local compile = require "moonscript.compile"
local text = io.stdin:read("*a")
local tree, err = parse.string(text)
if not tree then error(err) end
local code, err, pos = compile.tree(tree)
if not code then
error(compile.format_error(err, pos, text))
end
print(code)
os.exit()
end
local inputs = opts["file/directory"]
local files = {}
for _, input in ipairs(inputs) do
get_files(input, files)
end
files = remove_dups(files, function(f)
return f[2]
end)
-- returns an iterator that returns files that have been updated
local function create_watcher(files)
local watchers = require("moonscript.cmd.watchers")
if watchers.InotifyWacher:available() then
return watchers.InotifyWacher(files):each_update()
end
return watchers.SleepWatcher(files):each_update()
end
if opts.watch then
-- build function to check for lint or compile in watch
local handle_file
if opts.lint then
local lint = require "moonscript.cmd.lint"
handle_file = lint.lint_file
else
handle_file = compile_and_write
end
local watcher = create_watcher(files)
-- catches interrupt error for ctl-c
local protected = function()
local status, file = true, watcher()
if status then
return file
elseif file ~= "interrupted!" then
error(file)
end
end
for fname in protected do
local target = path_to_target(fname, opts.t)
if opts.o then
target = opts.o
end
local success, err = handle_file(fname, target)
if opts.lint then
if success then
io.stderr:write(success .. "\n\n")
elseif err then
io.stderr:write(fname .. "\n" .. err .. "\n\n")
end
elseif not success then
io.stderr:write(table.concat({
"",
"Error: " .. fname,
err,
"\n",
}, "\n"))
elseif success == "build" then
log_msg("Built", fname, "->", target)
end
end
io.stderr:write("\nQuitting...\n")
elseif opts.lint then
local has_linted_with_error;
local lint = require "moonscript.cmd.lint"
for _, tuple in pairs(files) do
local fname = tuple[1]
local res, err = lint.lint_file(fname)
if res then
has_linted_with_error = true
io.stderr:write(res .. "\n\n")
elseif err then
has_linted_with_error = true
io.stderr:write(fname .. "\n" .. err.. "\n\n")
end
end
if has_linted_with_error then
os.exit(1)
end
else
for _, tuple in ipairs(files) do
local fname, target = util.unpack(tuple)
if opts.o then
target = opts.o
end
local success, err = compile_and_write(fname, target, {
print = opts.p,
fname = fname,
benchmark = opts.b,
show_posmap = opts.X,
show_parse_tree = opts.T,
transform_module = opts.transform
})
if not success then
io.stderr:write(fname .. "\t" .. err .. "\n")
os.exit(1)
end
end
end

View File

@@ -1 +0,0 @@
#rom/modules/main/pl gitfs Tieske/Penlight/master/lua/pl

View File

@@ -1,6 +1,6 @@
{
title = 'Penlight apis',
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/penlight',
title = 'Penlight',
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/pl',
description = [[See: https://github.com/Tieske/Penlight
Penlight brings together a set of generally useful pure Lua modules, focusing on input data handling (such as reading configuration files), functional programming (such as map, reduce, placeholder expressions, etc), and OS path management. Much of the functionality is inspired by the Python standard libraries.]],

188
pl/apis/compat.lua Normal file
View File

@@ -0,0 +1,188 @@
----------------
--- Lua 5.1/5.2/5.3 compatibility.
-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global
-- environment, to make sure they are available for Lua 5.1 and LuaJIT.
--
-- All other functions are exported as usual in the returned module table.
--
-- NOTE: everything in this module is also available in `pl.utils`.
-- @module pl.compat
local compat = {}
--- boolean flag this is Lua 5.1 (or LuaJIT).
-- @field lua51
compat.lua51 = _VERSION == 'Lua 5.1'
--- boolean flag this is LuaJIT.
-- @field jit
compat.jit = (tostring(assert):match('builtin') ~= nil)
--- boolean flag this is LuaJIT with 5.2 compatibility compiled in.
-- @field jit52
if compat.jit then
-- 'goto' is a keyword when 52 compatibility is enabled in LuaJit
compat.jit52 = not loadstring("local goto = 1")
end
--- the directory separator character for the current platform.
-- @field dir_separator
compat.dir_separator = _ENV.package.config:sub(1,1)
--- boolean flag this is a Windows platform.
-- @field is_windows
compat.is_windows = compat.dir_separator == '\\'
--- execute a shell command, in a compatible and platform independent way.
-- This is a compatibility function that returns the same for Lua 5.1 and
-- Lua 5.2+.
--
-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems
-- only use exitcodes 0-255, anything else is undefined.
-- @param cmd a shell command
-- @return true if successful
-- @return actual return code
function compat.execute(cmd)
local res1,res2,res3 = os.execute(cmd)
if res2 == "No error" and res3 == 0 and compat.is_windows then
-- os.execute bug in Lua 5.2+ not reporting -1 properly on Windows
res3 = -1
end
if compat.lua51 and not compat.jit52 then
if compat.is_windows then
return res1==0,res1
else
res1 = res1 > 255 and res1 / 256 or res1
return res1==0,res1
end
else
if compat.is_windows then
return res3==0,res3
else
return not not res1,res3
end
end
end
----------------
-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).
-- @param ld code string or loader
-- @param[opt] source name of chunk for errors
-- @param[opt] mode 'b', 't' or 'bt'
-- @param[opt] env environment to load the chunk in
-- @function compat.load
---------------
-- Get environment of a function (in a Lua 5.1 compatible way).
-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no
-- global references!
-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
-- @param f a function or a call stack reference
-- @function compat.getfenv
---------------
-- Set environment of a function (in a Lua 5.1 compatible way).
-- @param f a function or a call stack reference
-- @param env a table that becomes the new environment of `f`
-- @function compat.setfenv
if compat.lua51 then -- define Lua 5.2 style load()
if not compat.jit then -- but LuaJIT's load _is_ compatible
local lua51_load = load
function compat.load(str,src,mode,env)
local chunk,err
if type(str) == 'string' then
if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then
return nil,"attempt to load a binary chunk"
end
chunk,err = loadstring(str,src)
else
chunk,err = lua51_load(str,src)
end
if chunk and env then setfenv(chunk,env) end
return chunk,err
end
else
compat.load = load
end
compat.setfenv, compat.getfenv = setfenv, getfenv
else
compat.load = load
-- setfenv/getfenv replacements for Lua 5.2
-- by Sergey Rozhenko
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
-- Roberto Ierusalimschy notes that it is possible for getfenv to return nil
-- in the case of a function with no globals:
-- http://lua-users.org/lists/lua-l/2010-06/msg00315.html
function compat.setfenv(f, t)
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name
local up = 0
repeat
up = up + 1
name = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
if name then
debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
debug.setupvalue(f, up, t)
end
if f ~= 0 then return f end
end
function compat.getfenv(f)
local f = f or 0
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name, val
local up = 0
repeat
up = up + 1
name, val = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
return val
end
end
--- Global exported functions (for Lua 5.1 & LuaJIT)
-- @section lua52
--- pack an argument list into a table.
-- @param ... any arguments
-- @return a table with field n set to the length
-- @function table.pack
if not table.pack then
function table.pack (...) -- luacheck: ignore
return {n=select('#',...); ...}
end
end
--- unpack a table and return the elements.
--
-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe.
-- See `utils.unpack` for a version that is nil-safe.
-- @param t table to unpack
-- @param[opt] i index from which to start unpacking, defaults to 1
-- @param[opt] t index of the last element to unpack, defaults to #t
-- @return multiple return values from the table
-- @function table.unpack
-- @see utils.unpack
if not table.unpack then
table.unpack = unpack -- luacheck: ignore
end
--- return the full path where a Lua module name would be matched.
-- @param mod module name, possibly dotted
-- @param path a path in the same form as package.path or package.cpath
-- @see path.package_path
-- @function package.searchpath
if not package.searchpath then
local sep = package.config:sub(1,1)
function package.searchpath (mod,path) -- luacheck: ignore
mod = mod:gsub('%.',sep)
for m in path:gmatch('[^;]+') do
local nm = m:gsub('?',mod)
local f = io.open(nm,'r')
if f then f:close(); return nm end
end
end
end
return compat

445
pl/apis/path.lua Normal file
View File

@@ -0,0 +1,445 @@
--- Path manipulation and file queries.
--
-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}.
--
-- Dependencies: `pl.utils`, `lfs`
-- @module pl.path
-- imports and locals
local _G = _G
local sub = string.sub
local getenv = os.getenv
local tmpnam = os.tmpname
local attributes, currentdir, link_attrib
local package = package
local append, concat, remove = table.insert, table.concat, table.remove
local utils = require 'pl.utils'
local assert_string,raise = utils.assert_string,utils.raise
local attrib
local path = {}
local lfs = require('lfs')
attributes = lfs.attributes
currentdir = lfs.currentdir
link_attrib = lfs.symlinkattributes
attrib = attributes
path.attrib = attrib
path.link_attrib = link_attrib
--- Lua iterator over the entries of a given directory.
-- Behaves like `lfs.dir`
path.dir = lfs.dir
--- Creates a directory.
path.mkdir = lfs.mkdir
--- Removes a directory.
path.rmdir = lfs.rmdir
---- Get the working directory.
path.currentdir = currentdir
--- Changes the working directory.
path.chdir = lfs.chdir
--- is this a directory?
-- @string P A file path
function path.isdir(P)
assert_string(1,P)
if P:match("\\$") then
P = P:sub(1,-2)
end
return attrib(P,'mode') == 'directory'
end
--- is this a file?.
-- @string P A file path
function path.isfile(P)
assert_string(1,P)
return attrib(P,'mode') == 'file'
end
-- is this a symbolic link?
-- @string P A file path
function path.islink(P)
assert_string(1,P)
if link_attrib then
return link_attrib(P,'mode')=='link'
else
return false
end
end
--- return size of a file.
-- @string P A file path
function path.getsize(P)
assert_string(1,P)
return attrib(P,'size')
end
--- does a path exist?.
-- @string P A file path
-- @return the file path if it exists, nil otherwise
function path.exists(P)
assert_string(1,P)
return attrib(P,'mode') ~= nil and P
end
--- Return the time of last access as the number of seconds since the epoch.
-- @string P A file path
function path.getatime(P)
assert_string(1,P)
return attrib(P,'access')
end
--- Return the time of last modification
-- @string P A file path
function path.getmtime(P)
assert_string(1,P)
return attrib(P,'modification')
end
---Return the system's ctime.
-- @string P A file path
function path.getctime(P)
assert_string(1,P)
return path.attrib(P,'change')
end
local function at(s,i)
return sub(s,i,i)
end
path.is_windows = utils.is_windows
local other_sep
-- !constant sep is the directory separator for this platform.
if path.is_windows then
path.sep = '\\'; other_sep = '/'
path.dirsep = ';'
else
path.sep = '/'
path.dirsep = ':'
end
local sep = path.sep
--- 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
-- @string P A file path
function path.splitpath(P)
assert_string(1,P)
local i = #P
local ch = at(P,i)
while i > 0 and ch ~= sep and ch ~= other_sep do
i = i - 1
ch = at(P,i)
end
if i == 0 then
return '',P
else
return sub(P,1,i-1), sub(P,i+1)
end
end
--- return an absolute path.
-- @string P A file path
-- @string[opt] pwd optional start path to use (default is current dir)
function path.abspath(P,pwd)
assert_string(1,P)
if pwd then assert_string(2,pwd) end
local use_pwd = pwd ~= nil
if not use_pwd and not currentdir then return P end
P = P:gsub('[\\/]$','')
pwd = pwd or currentdir()
if not path.isabs(P) then
P = path.join(pwd,P)
elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then
P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt'
end
return path.normpath(P)
end
--- given a path, return the root part and the extension part.
-- if there's no extension part, the second value will be empty
-- @string P A file path
-- @treturn string root part
-- @treturn string extension part (maybe empty)
function path.splitext(P)
assert_string(1,P)
local i = #P
local ch = at(P,i)
while i > 0 and ch ~= '.' do
if ch == sep or ch == other_sep then
return P,''
end
i = i - 1
ch = at(P,i)
end
if i == 0 then
return P,''
else
return sub(P,1,i-1),sub(P,i)
end
end
--- return the directory part of a path
-- @string P A file path
function path.dirname(P)
assert_string(1,P)
local p1 = path.splitpath(P)
return p1
end
--- return the file part of a path
-- @string P A file path
function path.basename(P)
assert_string(1,P)
local _,p2 = path.splitpath(P)
return p2
end
--- get the extension part of a path.
-- @string P A file path
function path.extension(P)
assert_string(1,P)
local _,p2 = path.splitext(P)
return p2
end
--- is this an absolute path?.
-- @string P A file path
function path.isabs(P)
assert_string(1,P)
if path.is_windows then
return at(P,1) == '/' or at(P,1)=='\\' or at(P,2)==':'
else
return at(P,1) == '/'
end
end
--- return the path resulting from combining the individual paths.
-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following).
-- empty elements (except the last) will be ignored.
-- @string p1 A file path
-- @string p2 A file path
-- @string ... more file paths
function path.join(p1,p2,...)
assert_string(1,p1)
assert_string(2,p2)
if select('#',...) > 0 then
local p = path.join(p1,p2)
local args = {...}
for i = 1,#args do
assert_string(i,args[i])
p = path.join(p,args[i])
end
return p
end
if path.isabs(p2) then return p2 end
local endc = at(p1,#p1)
if endc ~= path.sep and endc ~= other_sep and endc ~= "" then
p1 = p1..path.sep
end
return p1..p2
end
--- normalize the case of a pathname. On Unix, this returns the path unchanged;
-- for Windows, it converts the path to lowercase, and it also converts forward slashes
-- to backward slashes.
-- @string P A file path
function path.normcase(P)
assert_string(1,P)
if path.is_windows then
return (P:lower():gsub('/','\\'))
else
return P
end
end
--- normalize a path name.
-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`.
-- @string P a file path
function path.normpath(P)
assert_string(1,P)
-- Split path into anchor and relative path.
local anchor = ''
if path.is_windows then
if P:match '^\\\\' then -- UNC
anchor = '\\\\'
P = P:sub(3)
elseif at(P, 1) == '/' or at(P, 1) == '\\' then
anchor = '\\'
P = P:sub(2)
elseif at(P, 2) == ':' then
anchor = P:sub(1, 2)
P = P:sub(3)
if at(P, 1) == '/' or at(P, 1) == '\\' then
anchor = anchor..'\\'
P = P:sub(2)
end
end
P = P:gsub('/','\\')
else
-- According to POSIX, in path start '//' and '/' are distinct,
-- but '///+' is equivalent to '/'.
if P:match '^//' and at(P, 3) ~= '/' then
anchor = '//'
P = P:sub(3)
elseif at(P, 1) == '/' then
anchor = '/'
P = P:match '^/*(.*)$'
end
end
local parts = {}
for part in P:gmatch('[^'..sep..']+') do
if part == '..' then
if #parts ~= 0 and parts[#parts] ~= '..' then
remove(parts)
else
append(parts, part)
end
elseif part ~= '.' then
append(parts, part)
end
end
P = anchor..concat(parts, sep)
if P == '' then P = '.' end
return P
end
--- relative path from current directory or optional start point
-- @string P a path
-- @string[opt] start optional start point (default current directory)
function path.relpath (P,start)
assert_string(1,P)
if start then assert_string(2,start) end
local split,min,append = utils.split, math.min, table.insert
P = path.abspath(P,start)
start = start or currentdir()
local compare
if path.is_windows then
P = P:gsub("/","\\")
start = start:gsub("/","\\")
compare = function(v) return v:lower() end
else
compare = function(v) return v end
end
local startl, Pl = split(start,sep), split(P,sep)
local n = min(#startl,#Pl)
if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then
return P
end
local k = n+1 -- default value if this loop doesn't bail out!
for i = 1,n do
if compare(startl[i]) ~= compare(Pl[i]) then
k = i
break
end
end
local rell = {}
for i = 1, #startl-k+1 do rell[i] = '..' end
if k <= #Pl then
for i = k,#Pl do append(rell,Pl[i]) end
end
return table.concat(rell,sep)
end
--- Replace a starting '~' with the user's home directory.
-- In windows, if HOME isn't set, then USERPROFILE is used in preference to
-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows.
-- @string P A file path
function path.expanduser(P)
assert_string(1,P)
if at(P,1) == '~' then
local home = getenv('HOME')
if not home then -- has to be Windows
home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH')
end
return home..sub(P,2)
else
return P
end
end
---Return a suitable full path to a new temporary file name.
-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows)
function path.tmpname ()
local res = tmpnam()
-- On Windows if Lua is compiled using MSVC14 os.tmpname
-- already returns an absolute path within TEMP env variable directory,
-- no need to prepend it.
if path.is_windows and not res:find(':') then
res = getenv('TEMP')..res
end
return res
end
--- return the largest common prefix path of two paths.
-- @string path1 a file path
-- @string path2 a file path
function path.common_prefix (path1,path2)
assert_string(1,path1)
assert_string(2,path2)
-- get them in order!
if #path1 > #path2 then path2,path1 = path1,path2 end
local compare
if path.is_windows then
path1 = path1:gsub("/", "\\")
path2 = path2:gsub("/", "\\")
compare = function(v) return v:lower() end
else
compare = function(v) return v end
end
for i = 1,#path1 do
if compare(at(path1,i)) ~= compare(at(path2,i)) then
local cp = path1:sub(1,i-1)
if at(path1,i-1) ~= sep then
cp = path.dirname(cp)
end
return cp
end
end
if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end
return path1
--return ''
end
--- return the full path where a particular Lua module would be found.
-- Both package.path and package.cpath is searched, so the result may
-- either be a Lua file or a shared library.
-- @string mod name of the module
-- @return on success: path of module, lua or binary
-- @return on error: nil,error string
function path.package_path(mod)
assert_string(1,mod)
local res
mod = mod:gsub('%.',sep)
res = package.searchpath(mod,package.path)
if res then return res,true end
res = package.searchpath(mod,package.cpath)
if res then return res,false end
return raise 'cannot find module on path'
end
---- finis -----
return path

1
pl/etc/fstab Normal file
View File

@@ -0,0 +1 @@
packages/pl/apis gitfs Tieske/Penlight/master/lua/pl