Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torch/trepl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClement Farabet <cfarabet@twitter.com>2014-12-25 20:49:39 +0300
committerClement Farabet <cfarabet@twitter.com>2014-12-25 20:49:39 +0300
commit396db0f71e96dd66983fcca8b7461f0e452dc7dd (patch)
tree1a57f8de20a9ecfa857beab94e3ad951d4751e75
parent64f19ae831cf9690a71886e78b3976b7e8844144 (diff)
Total revamp of readline support.
(now using plain C bindings to readline)
-rw-r--r--.gitignore2
-rw-r--r--colorize.lua4
-rw-r--r--completer.lua225
-rw-r--r--init.lua398
-rw-r--r--readline.c294
-rw-r--r--readline.lua116
-rw-r--r--th16
-rw-r--r--trepl-scm-1.rockspec8
8 files changed, 421 insertions, 642 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d22eb4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*.so
diff --git a/colorize.lua b/colorize.lua
index 78b0620..0192397 100644
--- a/colorize.lua
+++ b/colorize.lua
@@ -2,9 +2,9 @@ local colors = require 'trepl.colors'
local f = {}
-for name,color in pairs(colors) do
+for name in pairs(colors) do
f[name] = function(txt)
- return color .. txt .. colors.none
+ return colors[name] .. txt .. colors.none
end
end
diff --git a/completer.lua b/completer.lua
deleted file mode 100644
index 28d379a..0000000
--- a/completer.lua
+++ /dev/null
@@ -1,225 +0,0 @@
---- Completion engine
--- Compute possible matches for input text.
--- Based on [lua-rlcompleter](https://github.com/rrthomas/lua-rlcompleter) by
--- Patrick Rapin and Reuben Thomas.
--- @alias M
-
-local lfs = pcall(require, "lfs") and require"lfs"
-local cowrap, coyield = coroutine.wrap, coroutine.yield
-
-local M = { }
-
---- The list of Lua keywords
-M.keywords = {
- 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for',
- 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
- 'return', 'then', 'true', 'until', 'while'
-}
-
---- Callback function to set final character.
--- Called to set the final chracter if a single match occur. Typically, it is used
--- to close a string if completion occurs inside an open string. It is intended
--- to be redefined as default function does nothing.
--- @param[type=string] char Either an empty string or a string of length 1.
-M.final_char_setter = function(char) end
-
---- References all completion generators (completers).
--- Completion is context sensitive and there is 2 *contexts* : values
--- (@{completers.value}) and strings (@{completers.string}). Some arguments are
--- given to the completer the first time it's called.
---
--- Each context has its corresponding table (sequence) with *completers* inside.
--- These tables works a bit like `package.loaders` : each loader is called as a
--- coroutine and can generate possible matches (by yielding strings). But, unlike
--- `package.loaders`, all completers are always called; even if matches has been
--- generated by previous ones.
---
--- It it not the completer job to filter matches with current text (this is done
--- by the complettion engine), but just generate all possible matches.
-M.completers = { }
-
---- Completers for Lua values.
--- Completers are called with two arguments :
---
--- * `value` to complete (not necessarily a table)
--- * `separator` used to index value (`.`, `:` or `[`)
---
--- Default completers for values are :
---
--- 1. Table field completer: searches for fields inside `value` if it's a table.
--- 2. Metatable completer: if `value` has a metatable, calls first completer
--- with that table.
---
--- @table completers.value
-M.completers.value = { }
-
---- Completers for strings.
--- Completers are called with the string to complete.
--- If `lfs` is can be loaded, a completer for files and folders is provided, it
--- search for items starting with given string in current directory.
--- @table completers.string
-M.completers.string = { }
-
--- Table fields completer
-table.insert(M.completers.value, function(t, sep)
- if type(t) ~= "table" then return end
- for k, v in pairs(t) do
- if type(k) == "number" and sep == "[" then
- coyield(k.."]")
- elseif type(k) == "string" and (sep ~= ":" or type(v) == "function") then
- coyield(k)
- end
- end
-end)
-
--- Metamethod completer
-table.insert(M.completers.value, function(t, sep)
- local mt = getmetatable(t)
- if mt and type(mt.__index) == "table" then
- return M.completers.value[1](mt.__index, sep) -- use regular table completer on metatable
- end
-end)
-
--- tensor/storage/torch-classes completer
-table.insert(M.completers.value, function(t, sep)
- local function enumerate_metatable(typename)
- if typename == nil then return end
- local metatable = torch.getmetatable(typename)
- for k, v in pairs(metatable) do
- if type(k) == "number" and sep == "[" then
- coyield(k.."]")
- elseif type(k) == "string" and (sep ~= ":" or type(v) == "function") then
- coyield(k)
- end
- end
- if torch.typename(metatable) ~= typename then
- enumerate_metatable(torch.typename(metatable))
- end
- end
- enumerate_metatable(torch.typename(t))
-end)
-
-
--- This function does the same job as the default completion of readline,
--- completing paths and filenames. Rewritten because
--- rl_basic_word_break_characters is different.
--- Uses LuaFileSystem (lfs) module for this task (if present).
-if lfs then
- table.insert(M.completers.string, function(str)
- local path, name = str:match("(.*)[\\/]+(.*)")
- path = (path or ".") .. "/"
- name = name or str
- -- avoid to trigger an error if folder does not exists
- if not lfs.attributes(path) then return end
- for f in lfs.dir(path) do
- if (lfs.attributes(path .. f) or {}).mode == 'directory' then
- coyield(f .. "/")
- else
- coyield(f)
- end
- end
- end)
-end
-
--- This function is called back by C function do_completion, itself called
--- back by readline library, in order to complete the current input line.
-function M.complete(word, line, startpos, endpos)
- -- Helper function registering possible completion words, verifying matches.
- local matches = {}
- local function add(value)
- value = tostring(value)
- if value:match("^" .. word) then
- matches[#matches + 1] = value
- end
- end
-
- local function call_completors(completers, ...)
- for _, completer in ipairs(completers) do
- local coro = cowrap(completer)
- local match = coro(...) -- first call => give parameters
- if match then
- add(match)
- -- continue calling to get next matches
- for match in coro do add(match) end
- end
- end
- end
-
- -- This function is called in a context where a keyword or a global
- -- variable can be inserted. Local variables cannot be listed!
- local function add_globals()
- for _, k in ipairs(M.keywords) do
- add(k)
- end
- call_completors(M.completers.value, _G)
- end
-
- -- Main completion function. It evaluates the current sub-expression
- -- to determine its type. Currently supports tables fields, global
- -- variables and function prototype completion.
- local function contextual_list(expr, sep, str)
- if str then
- M.final_char_setter('"')
- return call_completors(M.completers.string, str)
- end
- M.final_char_setter("")
- if expr and expr ~= "" then
- local v = loadstring("return " .. expr)
- if v then
- call_completors(M.completers.value, v(), sep)
- end
- end
- if #matches == 0 then
- add_globals()
- end
- end
-
- -- This complex function tries to simplify the input line, by removing
- -- literal strings, full table constructors and balanced groups of
- -- parentheses. Returns the sub-expression preceding the word, the
- -- separator item ( '.', ':', '[', '(' ) and the current string in case
- -- of an unfinished string literal.
- local function simplify_expression(expr)
- -- Replace annoying sequences \' and \" inside literal strings
- expr = expr:gsub("\\(['\"])", function (c)
- return string.format("\\%03d", string.byte(c))
- end)
- local curstring
- -- Remove (finished and unfinished) literal strings
- while true do
- local idx1, _, equals = expr:find("%[(=*)%[")
- local idx2, _, sign = expr:find("(['\"])")
- if idx1 == nil and idx2 == nil then
- break
- end
- local idx, startpat, endpat
- if (idx1 or math.huge) < (idx2 or math.huge) then
- idx, startpat, endpat = idx1, "%[" .. equals .. "%[", "%]" .. equals .. "%]"
- else
- idx, startpat, endpat = idx2, sign, sign
- end
- if expr:sub(idx):find("^" .. startpat .. ".-" .. endpat) then
- expr = expr:gsub(startpat .. "(.-)" .. endpat, " STRING ")
- else
- expr = expr:gsub(startpat .. "(.*)", function (str)
- curstring = str
- return "(CURSTRING "
- end)
- end
- end
- expr = expr:gsub("%b()"," PAREN ") -- Remove groups of parentheses
- expr = expr:gsub("%b{}"," TABLE ") -- Remove table constructors
- -- Avoid two consecutive words without operator
- expr = expr:gsub("(%w)%s+(%w)","%1|%2")
- expr = expr:gsub("%s+", "") -- Remove now useless spaces
- -- This main regular expression looks for table indexes and function calls.
- return curstring, expr:match("([%.%w%[%]_]-)([:%.%[%(])" .. word .. "$")
- end
-
- -- Now call the processing functions and return the list of results.
- local str, expr, sep = simplify_expression(line:sub(1, endpos))
- contextual_list(expr, sep, str)
- return matches
-end
-
-return M
diff --git a/init.lua b/init.lua
index b3630b2..985a69e 100644
--- a/init.lua
+++ b/init.lua
@@ -20,21 +20,30 @@
-- Require Torch
pcall(require,'torch')
pcall(require,'paths')
+pcall(require,'sys')
+pcall(require,'xlua')
-- Colors:
local colors = require 'trepl.colors'
local col = require 'trepl.colorize'
+-- Kill colors:
+function noColors()
+ for k,v in pairs(colors) do
+ colors[k] = ''
+ end
+end
+
-- Help string:
local selfhelp = [[
- ______ __
- /_ __/__ ________/ /
- / / / _ \/ __/ __/ _ \
- /_/ \___/_/ \__/_//_/
-
-]]..col.red('th')..[[ is an enhanced interpreter (repl) for Torch7/LuaJIT.
+ ______ __
+ /_ __/__ ________/ /
+ / / / _ \/ __/ __/ _ \
+ /_/ \___/_/ \__/_//_/
+
+]]..col.red('th')..[[ is an enhanced interpreter (repl) for Torch7/Lua.
-]]..col.blue('Features:')..[[
+]]..col.blue('Features:')..[[
Tab-completion on nested namespaces
Tab-completion on disk files (when opening a string)
@@ -43,20 +52,20 @@ local selfhelp = [[
Auto-print after eval (can be stopped with ;)
Each command is profiled, timing is reported
No need for '=' to print
- Easy help on functions/packages:
- ]]..col.magenta("? torch.randn")..[[
- Shell commands with:
- ]]..col.magenta("$ ls -l")..[[
+ Easy help on functions/packages:
+ ]]..col.magenta("? torch.randn")..[[
+ Shell commands with:
+ ]]..col.magenta("$ ls -l")..[[
Print all user globals with:
- ]]..col.magenta("who()")..[[
+ ]]..col.magenta("who()")..[[
Import a package's symbols globally with:
- ]]..col.magenta("import 'torch' ")..[[
+ ]]..col.magenta("import 'torch' ")..[[
Require is overloaded to provide relative (form within a file) search paths:
- ]]..col.magenta("require './local/lib' ")..[[
+ ]]..col.magenta("require './local/lib' ")..[[
Optional strict global namespace monitoring:
- ]]..col.magenta('th -g')..[[
+ ]]..col.magenta('th -g')..[[
Optional async repl (based on https://github.com/clementfarabet/async):
- ]]..col.magenta('th -a')..[[
+ ]]..col.magenta('th -a')..[[
]]
-- If no Torch:
@@ -193,7 +202,7 @@ function print_new(...)
for k,v in pairs(obj) do
if type(v) == 'table' then
if depth >= (ndepth-1) or next(v) == nil then
- line(tostring(k) .. ' : {}')
+ line(tostring(k) .. ' : {...}')
else
line(tostring(k) .. ' : ') printrecursive(v,depth+1)
end
@@ -267,24 +276,50 @@ function require(name)
if name:find('^%.') then
local file = debug.getinfo(2).source:gsub('^@','')
local dir = '.'
- if path.exists(file) then
- dir = path.dirname(file)
+ if paths.filep(file) then
+ dir = paths.dirname(file)
end
- local pkgpath = path.join(dir,name)
- if path.isfile(pkgpath..'.lua') then
+ local pkgpath = paths.concat(dir,name)
+ if paths.filep(pkgpath..'.lua') then
return dofile(pkgpath..'.lua')
- elseif path.isfile(pkgpath) then
+ elseif pkgpath:find('%.th$') and paths.filep(pkgpath) then
+ return torch.load(pkgpath)
+ elseif pkgpath:find('%.net$') and paths.filep(pkgpath) then
+ require 'nn'
+ return torch.load(pkgpath)
+ elseif pkgpath:find('%.json$') and paths.filep(pkgpath) then
+ return require('cjson').decode(io.open(pkgpath):read('*all'))
+ elseif pkgpath:find('%.csv$') and paths.filep(pkgpath) then
+ return require('csv').load(pkgpath)
+ elseif paths.filep(pkgpath) then
return dofile(pkgpath)
- elseif path.isfile(pkgpath..'.so') then
+ elseif paths.filep(pkgpath..'.th') then
+ return torch.load(pkgpath..'.th')
+ elseif paths.filep(pkgpath..'.net') then
+ require 'nn'
+ return torch.load(pkgpath..'.net')
+ elseif paths.filep(pkgpath..'.json') then
+ return require('cjson').decode(io.open(pkgpath..'.json'):read('*all'))
+ elseif paths.filep(pkgpath..'.csv') then
+ return require('csv').load(pkgpath..'.csv')
+ elseif paths.filep(pkgpath..'.so') then
return package.loadlib(pkgpath..'.so', 'luaopen_'..path.basename(name))()
- elseif path.isfile(pkgpath..'.dylib') then
+ elseif paths.filep(pkgpath..'.dylib') then
return package.loadlib(pkgpath..'.dylib', 'luaopen_'..path.basename(name))()
else
- local initpath = path.join(pkgpath,'init.lua')
+ local initpath = paths.concat(pkgpath,'init.lua')
return dofile(initpath)
end
else
- return drequire(name)
+ local ok,res = pcall(drequire,name)
+ if not ok then
+ local ok2,res2 = pcall(require,'./'..name)
+ if not ok2 then
+ error(res)
+ end
+ return res2
+ end
+ return res
end
end
@@ -422,274 +457,41 @@ local aliases = [[
-- Penlight
pcall(require,'pl')
+-- Useful globally, from penlight
+if text then
+ text.format_operator()
+end
+
-- Reults:
_RESULTS = {}
_LAST = ''
-- Readline:
-local readline_ok,readline = pcall(require,"trepl.readline")
-if not readline_ok then
- print(col.red('WARNING: ') .. 'could not find/load readline, defaulting to linenoise')
-end
-
--- REPL:
-function repl_readline()
- -- Completer:
- local completer = require 'trepl.completer'
- completer.final_char_setter = readline.completion_append_character
-
- local inputrc = paths.concat(os.getenv('HOME'),'.inputrc')
- if not paths.filep(inputrc) then
- local finputrc = io.open(inputrc,'w')
- local trepl =
-[[
-$if lua
- # filter up and down arrows using characters typed so far
- "\e[A":history-search-backward
- "\e[B":history-search-forward
-$endif
-]]
- finputrc:write(trepl)
- finputrc:close()
+local readline_ok,readline = pcall(require,'readline')
+local nextline,saveline
+if readline_ok then
+ -- Readline found:
+ local history = os.getenv('HOME') .. '/.luahistory'
+ readline.setup()
+ readline.read_history(history)
+ nextline = function(aux)
+ return readline.readline(prompt(aux))
end
-
- -- Timer
- local timer_start, timer_stop
- if torch and torch.Timer then
- local t = torch.Timer()
- local start = 0
- timer_start = function()
- start = t:time().real
- end
- timer_stop = function()
- local step = t:time().real - start
- for i = 1,70 do io.write(' ') end
- print(col.Black(string.format('[%0.04fs]', step)))
- end
- else
- timer_start = function() end
- timer_stop = function() end
+ saveline = function(line)
+ readline.add_history(line)
+ readline.write_history(history)
end
-
- -- History:
- local history = os.getenv('HOME') .. '/.luahistory'
-
- -- Readline callback:
- readline.shell{
- -- History:
- history = history,
-
- -- Completer:
- complete = completer.complete,
-
- -- Chars:
- word_break_characters = " \t\n\"\\'><=;:+-*/%^~#{}()[].,",
-
- -- Get command:
- getcommand = function()
- -- get the first line
- local line = coroutine.yield(prompt())
- local cmd = line .. '\n'
-
- -- = (lua supports that)
- if cmd:sub(1,1) == "=" then
- cmd = "return "..cmd:sub(2)
- end
-
- -- Interupt?
- if line == 'exit' then
- io.stdout:write('Do you really want to exit ([y]/n)? ') io.flush()
- local line = io.read('*l')
- if line == '' or line:lower() == 'y' then
- os.exit()
- end
- end
-
- -- OS Commands:
- if line and line:find('^%s-%$') then
- local cline = line:gsub('^%s-%$','')
- if io.popen then
- local f = io.popen(aliases .. ' ' .. cline)
- local res = f:read('*a')
- f:close()
- io.write(col.none(res)) io.flush()
- table.insert(_RESULTS, res)
- _LAST = _RESULTS[#_RESULTS]
- else
- os.execute(aliases .. ' ' .. cline)
- end
- timer_stop()
- return line
- end
-
- -- Shortcut to get help:
- if line and line:find('^%s-?') then
- local ok = pcall(require,'dok')
- if ok then
- local pkg = line:gsub('^%s-?','')
- if pkg:gsub('%s*','') == '' then
- print(selfhelp)
- return
- else
- line = 'help(' .. pkg .. ')'
- end
- else
- print('error: could not load help backend')
- return line
- end
- end
-
- -- try to return first:
- timer_start()
- local pok,ok,err
- if line:find(';%s-$') or line:find('^%s-print') then
- ok = false
- elseif line:match('^%s*$') then
- return nil
- else
- local func, perr = loadstring('local f = function() return '..line..' end local res = {f()} print(unpack(res)) table.insert(_RESULTS,res[1])')
- if func then
- pok = true
- ok,err = xpcall(func, traceback)
- end
- end
-
- -- run ok:
- if ok then
- _LAST = _RESULTS[#_RESULTS]
- timer_stop()
- return line
- end
-
- -- parsed ok, but failed to run (code error):
- if pok then
- print(err)
- return cmd:sub(1, -2)
- end
-
- -- continue to get lines until get a complete chunk
- local func, err
- while true do
- -- if not go ahead:
- func, err = loadstring(cmd)
- if func or err:sub(-7) ~= "'<eof>'" then break end
-
- -- concat:
- cmd = cmd .. coroutine.yield(prompt(true)) .. '\n'
- end
-
- -- exec chunk:
- if not cmd:match("^%s*$") then
- local ff,err=loadstring(cmd)
- if not ff then
- print(err)
- return cmd:sub(1, -2)
- end
- local res = {xpcall(ff, traceback)}
- local ok,err = res[1], res[2]
- if not ok then
- print(err)
- else
- if err ~= nil then
- table.remove(res,1)
- print(unpack(res))
- end
- end
- timer_stop()
- return cmd:sub(1, -2) -- remove last \n for history
- end
- end,
- }
- io.stderr:write"\n"
-end
-
--- No readline -> LineNoise?
-local nextline
-if not readline_ok then
- -- Load linenoise:
- local ok,L = pcall(require,'linenoise')
- ok = false
- if not ok then
- -- No readline, no linenoise... default to plain io:
- nextline = function()
- io.write(prompt()) io.flush()
- return io.read('*line')
- end
-
- -- Really poor:
- print(col.red('WARNING: ') .. 'could not find/load linenoise, defaulting to raw repl')
- else
- -- History:
- local history = os.getenv('HOME') .. '/.luahistory'
- L.historyload(history)
-
- -- Completion:
- L.setcompletion(function(c,s)
- -- Check if we're in a string
- local ignore,str = s:gfind('(.-)"([a-zA-Z%._]*)$')()
- local quote = '"'
- if not str then
- ignore,str = s:gfind('(.-)\'([a-zA-Z%._]*)$')()
- quote = "'"
- end
-
- -- String?
- if str then
- -- Complete from disk:
- local f = io.popen('ls ' .. str..'* 2> /dev/null')
- local res = f:read('*all')
- f:close()
- res = res:gsub('(%s*)$','')
- local elts = stringx.split(res,'\n')
- for _,elt in ipairs(elts) do
- L.addcompletion(c,ignore .. quote .. elt)
- end
- return
- end
-
- -- Get symbol of interest
- local ignore,str = s:gfind('(.-)([a-zA-Z%._]*)$')()
-
- -- Lookup globals:
- if not str:find('%.') then
- for k,v in pairs(_G) do
- if k:find('^'..str) then
- L.addcompletion(c,ignore .. k)
- end
- end
- end
-
- -- Lookup packages:
- local base,sub = str:gfind('(.*)%.(.*)')()
- if base then
- local ok,res = pcall(loadstring('return ' .. base))
- for k,v in pairs(res) do
- if k:find('^'..sub) then
- L.addcompletion(c,ignore .. base .. '.' .. k)
- end
- end
- end
- end)
-
- -- read line:
- nextline = function()
- -- Get line:
- local line = L.linenoise(prompt())
-
- -- Save:
- if line and not line:find('^%s-$') then
- L.historyadd(line)
- L.historysave(history)
- end
-
- -- Return line:
- return line
- end
+else
+ -- No readline... default to plain io:
+ nextline = function(aux)
+ io.write(prompt(aux)) io.flush()
+ return io.read('*line')
end
+ saveline = function() end
end
--- The default repl
-function repl_linenoise()
+-- The repl
+function repl()
-- Timer
local timer_start, timer_stop
if torch and torch.Timer then
@@ -758,6 +560,18 @@ function repl_linenoise()
-- EVAL:
if line then
+ -- Try to load line first, for multiline support:
+ local valid = loadstring('return ' .. line) or loadstring(line)
+ while not valid do
+ local nline = nextline(true)
+ if nline == '' or not nline then
+ break
+ end
+ line = line .. '\n' .. nline
+ valid = loadstring(line)
+ end
+
+ -- Execute:
timer_start()
local ok,err
if line:find(';%s-$') or line:find('^%s-print') then
@@ -766,9 +580,14 @@ function repl_linenoise()
ok,err = xpcall(loadstring('local f = function() return '..line..' end local res = {f()} print(unpack(res)) table.insert(_RESULTS,res[1])'), traceback)
end
if not ok then
- local ok,err = xpcall(loadstring(line), traceback)
- if not ok then
- print(err)
+ local parsed,perr = loadstring(line)
+ if not parsed then
+ print('syntax error: ' .. perr)
+ else
+ local ok,err = xpcall(parsed, traceback)
+ if not ok then
+ print(err)
+ end
end
end
timer_stop()
@@ -776,6 +595,9 @@ function repl_linenoise()
-- Last result:
_LAST = _RESULTS[#_RESULTS]
+
+ -- Save:
+ saveline(line)
end
end
@@ -786,4 +608,4 @@ for k,v in pairs(_G) do
end
-- return repl, just call it to start it!
-return (readline_ok and repl_readline) or repl_linenoise
+return repl
diff --git a/readline.c b/readline.c
new file mode 100644
index 0000000..9edbef2
--- /dev/null
+++ b/readline.c
@@ -0,0 +1,294 @@
+// Bindings to readline
+#include <stdlib.h>
+#include <string.h>
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+/*
+** Lua 5.1.4 advanced readline support for the GNU readline and history
+** libraries or compatible replacements.
+**
+** Author: Mike Pall.
+** Maintainer: Sean Bolton (sean at smbolton dot com).
+**
+** Copyright (C) 2004-2006, 2011 Mike Pall. Same license as Lua. See lua.h.
+**
+** Advanced features:
+** - Completion of keywords and global variable names.
+** - Recursive and metatable-aware completion of variable names.
+** - Context sensitive delimiter completion.
+** - Save/restore of the history to/from a file (LUA_HISTORY env variable).
+** - Setting a limit for the size of the history (LUA_HISTSIZE env variable).
+** - Setting the app name to allow for $if lua ... $endif in ~/.inputrc.
+**
+** Start lua and try these (replace ~ with the TAB key):
+**
+** ~~
+** fu~foo() ret~fa~end<CR>
+** io~~~s~~~o~~~w~"foo\n")<CR>
+**
+** The ~~ are just for demonstration purposes (io~s~o~w~ suffices, of course).
+**
+** If you are used to zsh/tcsh-style completion support, try adding
+** 'TAB: menu-complete' and 'C-d: possible-completions' to your ~/.inputrc.
+**
+** The patch has been successfully tested with:
+**
+** GNU readline 2.2.1 (1998-07-17)
+** GNU readline 4.0 (1999-02-18) [harmless compiler warning]
+** GNU readline 4.3 (2002-07-16)
+** GNU readline 5.0 (2004-07-27)
+** GNU readline 5.1 (2005-12-07)
+** GNU readline 5.2 (2006-10-11)
+** GNU readline 6.0 (2009-02-20)
+** GNU readline 6.2 (2011-02-13)
+** MacOSX libedit 2.11 (2008-07-12)
+** NETBSD libedit 2.6.5 (2002-03-25)
+** NETBSD libedit 2.6.9 (2004-05-01)
+**
+** Change Log:
+** 2004-2006 Mike Pall - original patch
+** 2009/08/24 Sean Bolton - updated for GNU readline version 6
+** 2011/12/14 Sean Bolton - fixed segfault when using Mac OS X libedit 2.11
+*/
+
+static char *lua_rl_hist;
+static int lua_rl_histsize;
+
+static lua_State *lua_rl_L; /* User data is not passed to rl callbacks. */
+
+/* Reserved keywords. */
+static const char *const lua_rl_keywords[] = {
+ "and", "break", "do", "else", "elseif", "end", "false",
+ "for", "function", "if", "in", "local", "nil", "not", "or",
+ "repeat", "return", "then", "true", "until", "while", NULL
+};
+
+static int valididentifier(const char *s)
+{
+ if (!(isalpha(*s) || *s == '_')) return 0;
+ for (s++; *s; s++) if (!(isalpha(*s) || isdigit(*s) || *s == '_')) return 0;
+ return 1;
+}
+
+/* Dynamically resizable match list. */
+typedef struct {
+ char **list;
+ size_t idx, allocated, matchlen;
+} dmlist;
+
+/* Add prefix + string + suffix to list and compute common prefix. */
+static int lua_rl_dmadd(dmlist *ml, const char *p, size_t pn, const char *s,
+ int suf)
+{
+ char *t = NULL;
+
+ if (ml->idx+1 >= ml->allocated &&
+ !(ml->list = realloc(ml->list, sizeof(char *)*(ml->allocated += 32))))
+ return -1;
+
+ if (s) {
+ size_t n = strlen(s);
+ if (!(t = (char *)malloc(sizeof(char)*(pn+n+(suf?2:1))))) return 1;
+ memcpy(t, p, pn);
+ memcpy(t+pn, s, n);
+ n += pn;
+ t[n] = suf;
+ if (suf) t[++n] = '\0';
+
+ if (ml->idx == 0) {
+ ml->matchlen = n;
+ } else {
+ size_t i;
+ for (i = 0; i < ml->matchlen && i < n && ml->list[1][i] == t[i]; i++) ;
+ ml->matchlen = i; /* Set matchlen to common prefix. */
+ }
+ }
+
+ ml->list[++ml->idx] = t;
+ return 0;
+}
+
+/* Get __index field of metatable of object on top of stack. */
+static int lua_rl_getmetaindex(lua_State *L)
+{
+ if (!lua_getmetatable(L, -1)) { lua_pop(L, 1); return 0; }
+
+ /* prefer __metatable if it exists */
+ lua_pushstring(L, "__metatable");
+ lua_rawget(L, -2);
+ if(lua_istable(L, -1))
+ {
+ lua_remove(L, -2);
+ return 1;
+ }
+ else
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "__index");
+ lua_rawget(L, -2);
+ lua_replace(L, -2);
+ if (lua_isnil(L, -1) || lua_rawequal(L, -1, -2)) { lua_pop(L, 2); return 0; }
+ lua_replace(L, -2);
+ return 1;
+} /* 1: obj -- val, 0: obj -- */
+
+/* Get field from object on top of stack. Avoid calling metamethods. */
+static int lua_rl_getfield(lua_State *L, const char *s, size_t n)
+{
+ int i = 20; /* Avoid infinite metatable loops. */
+ do {
+ if (lua_istable(L, -1)) {
+ lua_pushlstring(L, s, n);
+ lua_rawget(L, -2);
+ if (!lua_isnil(L, -1)) { lua_replace(L, -2); return 1; }
+ lua_pop(L, 1);
+ }
+ } while (--i > 0 && lua_rl_getmetaindex(L));
+ lua_pop(L, 1);
+ return 0;
+} /* 1: obj -- val, 0: obj -- */
+
+/* Completion callback. */
+static char **lua_rl_complete(const char *text, int start, int end)
+{
+ lua_State *L = lua_rl_L;
+ dmlist ml;
+ const char *s;
+ size_t i, n, dot, loop;
+ int savetop;
+
+ if (!(text[0] == '\0' || isalpha(text[0]) || text[0] == '_')) return NULL;
+
+ ml.list = NULL;
+ ml.idx = ml.allocated = ml.matchlen = 0;
+
+ savetop = lua_gettop(L);
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ for (n = (size_t)(end-start), i = dot = 0; i < n; i++)
+ if (text[i] == '.' || text[i] == ':') {
+ if (!lua_rl_getfield(L, text+dot, i-dot))
+ goto error; /* Invalid prefix. */
+ dot = i+1; /* Points to first char after dot/colon. */
+ }
+
+ /* Add all matches against keywords if there is no dot/colon. */
+ if (dot == 0)
+ for (i = 0; (s = lua_rl_keywords[i]) != NULL; i++)
+ if (!strncmp(s, text, n) && lua_rl_dmadd(&ml, NULL, 0, s, ' '))
+ goto error;
+
+ /* Add all valid matches from all tables/metatables. */
+ loop = 0; /* Avoid infinite metatable loops. */
+ do {
+ if (lua_istable(L, -1) &&
+ (loop == 0 || !lua_rawequal(L, -1, LUA_GLOBALSINDEX)))
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ s = lua_tostring(L, -2);
+ /* Only match names starting with '_' if explicitly requested. */
+ if (!strncmp(s, text+dot, n-dot) && valididentifier(s) &&
+ (*s != '_' || text[dot] == '_')) {
+ int suf = ' '; /* Default suffix is a space. */
+ switch (lua_type(L, -1)) {
+ case LUA_TTABLE: suf = '.'; break; /* No way to guess ':'. */
+ case LUA_TFUNCTION: suf = '('; break;
+ case LUA_TUSERDATA:
+ if (lua_getmetatable(L, -1)) { lua_pop(L, 1); suf = ':'; }
+ break;
+ }
+ if (lua_rl_dmadd(&ml, text, dot, s, suf)) goto error;
+ }
+ }
+ } while (++loop < 20 && lua_rl_getmetaindex(L));
+
+ if (ml.idx == 0) {
+error:
+ lua_settop(L, savetop);
+ return NULL;
+ } else {
+ /* list[0] holds the common prefix of all matches (may be ""). */
+ /* If there is only one match, list[0] and list[1] will be the same. */
+ if (!(ml.list[0] = (char *)malloc(sizeof(char)*(ml.matchlen+1))))
+ goto error;
+ memcpy(ml.list[0], ml.list[1], ml.matchlen);
+ ml.list[0][ml.matchlen] = '\0';
+ /* Add the NULL list terminator. */
+ if (lua_rl_dmadd(&ml, NULL, 0, NULL, 0)) goto error;
+ }
+
+ lua_settop(L, savetop);
+#if RL_READLINE_VERSION >= 0x0600
+ rl_completion_suppress_append = 1;
+#endif
+ return ml.list;
+}
+
+/* Initialize readline library. */
+static int f_setup(lua_State *L)
+{
+ char *s;
+
+ lua_rl_L = L;
+
+ /* This allows for $if lua ... $endif in ~/.inputrc. */
+ rl_readline_name = "lua";
+ /* Break words at every non-identifier character except '.' and ':'. */
+ rl_completer_word_break_characters =
+ "\t\r\n !\"#$%&'()*+,-/;<=>?@[\\]^`{|}~";
+ rl_completer_quote_characters = "\"'";
+/* #if RL_READLINE_VERSION < 0x0600 */
+ rl_completion_append_character = '\0';
+/* #endif */
+ rl_attempted_completion_function = lua_rl_complete;
+ rl_initialize();
+
+ return 0;
+}
+
+static int f_readline(lua_State* L)
+{
+ const char* prompt = lua_tostring(L,1);
+ const char* line = readline(prompt);
+ lua_pushstring(L,line);
+ free((void *)line); // Lua makes a copy...
+ return 1;
+}
+
+static int f_add_history(lua_State* L)
+{
+ if (lua_strlen(L,1) > 0)
+ add_history(lua_tostring(L, 1));
+ return 0;
+}
+
+static int f_write_history(lua_State* L)
+{
+ if (lua_strlen(L,1) > 0)
+ write_history(lua_tostring(L, 1));
+ return 0;
+}
+
+static int f_read_history(lua_State* L)
+{
+ if (lua_strlen(L,1) > 0)
+ read_history(lua_tostring(L, 1));
+ return 0;
+}
+
+static const struct luaL_reg lib[] = {
+ {"readline", f_readline},
+ {"add_history",f_add_history},
+ {"write_history",f_write_history},
+ {"read_history",f_read_history},
+ {"setup",f_setup},
+ {NULL, NULL},
+};
+
+int luaopen_readline (lua_State *L) {
+ luaL_openlib (L, "readline", lib, 0);
+ return 1;
+}
diff --git a/readline.lua b/readline.lua
deleted file mode 100644
index 2c343c8..0000000
--- a/readline.lua
+++ /dev/null
@@ -1,116 +0,0 @@
--- Very basic FFI interface to readline,
--- with history saving/restoring
---
-local ffi = require 'ffi'
-local assert = assert
-local cocreate, coresume, costatus = coroutine.create, coroutine.resume, coroutine.status
-local readline = {}
-
-ffi.cdef[[
-/* libc definitions */
-void* malloc(size_t bytes);
-void free(void *);
-
-/* basic history handling */
-char *readline (const char *prompt);
-void add_history(const char *line);
-int read_history(const char *filename);
-int write_history(const char *filename);
-
-void rl_set_signals(void);
-
-/* completion */
-typedef char **rl_completion_func_t (const char *, int, int);
-typedef char *rl_compentry_func_t (const char *, int);
-
-char **rl_completion_matches (const char *, rl_compentry_func_t *);
-
-const char *rl_basic_word_break_characters;
-const char *rl_completer_quote_characters;
-rl_completion_func_t *rl_attempted_completion_function;
-rl_completion_func_t *rl_completion_entry_function;
-char *rl_line_buffer;
-int rl_completion_append_character;
-int rl_completion_suppress_append;
-int rl_attempted_completion_over;
-const char *rl_readline_name;
-
-int rl_initialize();
-]]
-
-local libreadline = ffi.load("readline")
-
--- enable application specific parsing with inputrc
-libreadline.rl_readline_name = 'lua'
-
-function readline.completion_append_character(char)
- libreadline.rl_completion_append_character = #char > 0 and char:byte(1,1) or 0
-end
-
-if jit.os ~= 'OSX' then
- --libreadline.rl_set_signals()
-end
-
-function readline.shell(config)
- -- restore history
- libreadline.read_history(config.history)
-
- -- configure completion, if any
- if config.complete then
- if config.word_break_characters then
- libreadline.rl_basic_word_break_characters = config.word_break_characters
- end
- libreadline.rl_completer_quote_characters = '\'"'
-
- local matches
- libreadline.rl_completion_entry_function = function(word, i)
- libreadline.rl_attempted_completion_over = 1
- local strword = ffi.string(word)
- local buffer = ffi.string(libreadline.rl_line_buffer)
- if i == 0 then
- matches = config.complete(strword, buffer, startpos, endpos)
- end
- local match = matches[i+1]
- if match then
- -- readline will free the C string by itself, so create copies of them
- local buf = ffi.C.malloc(#match + 1)
- ffi.copy(buf, match, #match+1)
- return buf
- end
- end
- end
-
- -- main loop
- local running = true
- while running do
- local userfunc = cocreate(config.getcommand)
- local _, prompt = assert(coresume(userfunc))
- while costatus(userfunc) ~= "dead" do
- -- get next line
- local s = libreadline.readline(prompt)
- if s == nil then -- end of file
- running = false
- break
- end
-
- local line = ffi.string(s)
- ffi.C.free(s)
- _, prompt = assert(coresume(userfunc, line))
- end
-
- if not running then
- io.stdout:write('\nDo you really want to exit ([y]/n)? ') io.flush()
- local line = io.read('*l')
- if line == '' or line:lower() == 'y' then
- os.exit()
- else
- readline.shell(config)
- end
- elseif prompt then -- final return value is the value to add to history
- libreadline.add_history(prompt)
- libreadline.write_history(config.history)
- end
- end
-end
-
-return readline
diff --git a/th b/th
index 57b1f71..c890a03 100644
--- a/th
+++ b/th
@@ -95,7 +95,9 @@ if lgfx then
local ok = pcall(require, 'gfx.js')
if ok then
gfx.startserver()
+ gfx.clear()
gfx.show()
+ sys.sleep(1)
else
print('could not load gfx.js, please install with: luarocks install gfx.js')
end
@@ -136,20 +138,20 @@ if asyncrepl then
-- verbose
print(
[[
-
+
______ __ ]]..col.Black[[| Torch7 ]]..[[
- /_ __/__ ________/ / ]]..col.Black[[| ]]..col.magenta[[Scientific computing for LuaJIT. ]]..[[
+ /_ __/__ ________/ / ]]..col.Black[[| ]]..col.magenta[[Scientific computing for Lua. ]]..[[
/ / / _ \/ __/ __/ _ \ ]]..col.Black[[| ]]..[[
/_/ \___/_/ \__/_//_/ ]]..col.Black[[| ]]..col.blue[[https://github.com/torch ]]..[[
]]..col.Black[[| ]]..col.blue[[http://torch.ch ]]..[[
-
+
]] .. col.red('WARNING: ') .. col.Black('you are running an experimental asynchronous interpreter for Torch.') .. [[
]] .. col.Black('Note 1: It has no support for readline/completion yet.') .. [[
]] .. col.Black('Note 2: The event-loop has been started in the background: ') .. col.none('async.go()') .. [[
]] .. col.Black(' Statements like ') .. col.none('async.setInterval(1000, function() print("test") end)') .. [[
-]] .. col.Black(' are run asynchronously to the interpreter. ') .. [[
-]] .. col.Black('Note 3: See ') .. col.blue('http://github.com/clementfarabet/async') .. col.Black(' for help' ) .. [[
-
+]] .. col.Black(' are run asynchronously to the interpreter. ') .. [[
+]] .. col.Black('Note 3: See ') .. col.blue('http://github.com/clementfarabet/async') .. col.Black(' for help' ) .. [[
+
]]
)
@@ -164,7 +166,7 @@ else
[[
______ __ ]]..col.Black[[| Torch7 ]]..[[
- /_ __/__ ________/ / ]]..col.Black[[| ]]..col.magenta[[Scientific computing for LuaJIT. ]]..[[
+ /_ __/__ ________/ / ]]..col.Black[[| ]]..col.magenta[[Scientific computing for Lua. ]]..[[
/ / / _ \/ __/ __/ _ \ ]]..col.Black[[| ]]..[[
/_/ \___/_/ \__/_//_/ ]]..col.Black[[| ]]..col.blue[[https://github.com/torch ]]..[[
]]..col.Black[[| ]]..col.blue[[http://torch.ch ]]..[[
diff --git a/trepl-scm-1.rockspec b/trepl-scm-1.rockspec
index 8f3ae8c..49d29f8 100644
--- a/trepl-scm-1.rockspec
+++ b/trepl-scm-1.rockspec
@@ -17,9 +17,7 @@ An embedabble, Lua-only REPL for Torch.
dependencies = {
"torch >= 7.0",
- "linenoise >= 0.4",
"penlight >= 1.1.0",
- "luafilesystem >= 1.6.2"
}
build = {
@@ -28,8 +26,10 @@ build = {
['trepl.init'] = 'init.lua',
['trepl.colors'] = 'colors.lua',
['trepl.colorize'] = 'colorize.lua',
- ['trepl.readline'] = 'readline.lua',
- ['trepl.completer'] = 'completer.lua',
+ ['readline'] = {
+ sources = {'readline.c'},
+ libraries = {'readline'}
+ }
},
install = {
bin = {