diff --git a/repl/main.lua b/repl/main.lua deleted file mode 100644 index 1a4c1cf..0000000 --- a/repl/main.lua +++ /dev/null @@ -1,19 +0,0 @@ -local repl = require 'repl.console' -local rcfile_loaded = repl:loadplugin 'rcfile' - -if not rcfile_loaded then - local has_linenoise = pcall(require, 'linenoise') - - if has_linenoise then - repl:loadplugin 'linenoise' - else - pcall(repl.loadplugin, repl, 'rlwrap') - end - - repl:loadplugin 'history' - repl:loadplugin 'completion' - repl:loadplugin 'autoreturn' -end - -print('Lua REPL ' .. tostring(repl.VERSION)) -repl:run() diff --git a/repl/repl/compat.lua b/repl/repl/compat.lua deleted file mode 100644 index fd47748..0000000 --- a/repl/repl/compat.lua +++ /dev/null @@ -1,30 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -return { - -- unpack was moved to table.unpack on Lua version 5.2 - -- See https://www.lua.org/manual/5.2/manual.html#8 - unpack = unpack or table.unpack, - -- loadstring was deprecated in favor of load, which was updated - -- to handle string arguments - loadstring = loadstring or load, - -- package.loaders was renamed package.searchers in Lua 5.2 - package = { - searchers = package.loaders or package.searchers - } -} diff --git a/repl/repl/console.lua b/repl/repl/console.lua deleted file mode 100644 index 154d037..0000000 --- a/repl/repl/console.lua +++ /dev/null @@ -1,57 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- @class repl.console ---- This module implements a command line-based REPL, ---- similar to the standalone Lua interpreter. - -local sync_repl = require 'repl.sync' -local compat = require 'repl.compat' -local console_repl = sync_repl:clone() -local stdout = io.stdout -local stdin = io.stdin -local print = print -local unpack = unpack - --- @see repl:showprompt(prompt) -function console_repl:showprompt(prompt) - stdout:write(prompt .. ' ') -end - --- @see repl.sync:lines() -function console_repl:lines() - return stdin:lines() -end - --- @see repl:displayresults(results) -function console_repl:displayresults(results) - if results.n == 0 then - return - end - - print(compat.unpack(results, 1, results.n)) -end - --- @see repl:displayerror(err) -function console_repl:displayerror(err) - print(err) -end - -console_repl._features.console = true - -return console_repl diff --git a/repl/repl/init.lua b/repl/repl/init.lua deleted file mode 100644 index 5e5a95e..0000000 --- a/repl/repl/init.lua +++ /dev/null @@ -1,415 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- @class repl ---- This module implements the core functionality of a REPL. - -local plugins_lookup_meta = { __mode = 'k' } - -local repl = { _buffer = '', _plugins = setmetatable({}, plugins_lookup_meta), _features = {}, _ifplugin = {}, _iffeature = {}, VERSION = 0.9 } -local compat = require 'repl.compat' -local select = select -local dtraceback = debug.traceback -local setmetatable = setmetatable -local sformat = string.format -local smatch = string.match -local error = error -local setfenv = require('repl.utils').setfenv - -local function gather_results(success, ...) - local n = select('#', ...) - return success, { n = n, ... } -end - -local function tcopy(t, copy) - copy = copy or {} - - for k, v in pairs(t) do - copy[k] = v - end - - return copy -end - ---- Returns the prompt for a given level. --- @param level The prompt level. Either 1 or 2. -function repl:getprompt(level) - return level == 1 and '>' or '>>' -end - ---- Displays a prompt for the given prompt level. --- @param level The prompt level. Either 1 or 2. -function repl:prompt(level) - self:showprompt(self:getprompt(level)) -end - ---- Returns the name of the REPL. For usage in chunk compilation. --- @return The REPL's name. --- @see load -function repl:name() - return 'REPL' -end - ---- Gets a traceback for an error. --- @param ... All of the stuff that xpcall passes to error functions. --- @see xpcall --- @return A stack trace. The default implementation returns a simple string-based trace. -function repl:traceback(...) - return dtraceback(...) -end - ---- Uses the compilation error to determine whether or not further input ---- is pending after the last line. You shouldn't have to override this ---- unless you use an implementation of Lua that varies in its error ---- messages. --- @param err The compilation error from Lua. --- @return Whether or not the input should continue after this line. -function repl:detectcontinue(err) - return smatch(err, "''$") or smatch(err, "$") -end - -function repl:compilechunk(chunk) - return compat.loadstring(chunk, self:name()) -end - ---- Evaluates a line of input, and displays return value(s). --- @param line The line to evaluate --- @return The prompt level (1 or 2) -function repl:handleline(line) - local chunk = self._buffer .. line - local f, err = self:compilechunk(chunk) - - if f then - self._buffer = '' - - setfenv(f, self:getcontext()) - local success, results = gather_results(xpcall(f, function(...) return self:traceback(...) end)) - if success then - self:displayresults(results) - else - self:displayerror(results[1]) - end - else - if self:detectcontinue(err) then - self._buffer = chunk .. '\n' - return 2 - else - self:displayerror(err) - end - end - - return 1 -end - ---- Creates a new REPL object, so you can override methods without fear. --- @return A REPL clone. -function repl:clone() - local plugins_copy = tcopy(self._plugins, setmetatable({}, plugins_lookup_meta)) - local features_copy = tcopy(self._features) - local ifplugin_copy = {} - local iffeature_copy = {} - - for k, v in pairs(self._ifplugin) do - ifplugin_copy[k] = tcopy(v) - end - - for k, v in pairs(self._iffeature) do - iffeature_copy[k] = tcopy(v) - end - - return setmetatable({ - _buffer = '', - _plugins = plugins_copy, - _features = features_copy, - _ifplugin = ifplugin_copy, - _iffeature = iffeature_copy, - }, { __index = self }) -end - ---- Displays the given prompt to the user. Must be overriden. --- @param prompt The prompt to display. -function repl:showprompt(prompt) - error 'You must implement the showprompt method' -end - ---- Displays the results from evaluate(). Must be overriden. --- @param results The results to display. The results are a table, with the integer keys containing the results, and the 'n' key containing the highest integer key. - -function repl:displayresults(results) - error 'You must implement the displayresults method' -end - ---- Displays errors from evaluate(). Must be overriden. --- @param err The error value returned from repl:traceback(). --- @see repl:traceback -function repl:displayerror(err) - error 'You must implement the displayerror method' -end - ---- Checks whether this REPL object has loaded the given plugin. --- @param plugin The plugin that the REPL may have loaded. -function repl:hasplugin(plugin) - return self._plugins[plugin] -end - -function repl:hasfeature(feature) - return self._features[feature] -end - -function repl:requirefeature(feature) - if not self:hasfeature(feature) then - error(sformat('required feature %q not present', feature), 2) - end -end - -function repl:ifplugin(plugin, action) - if self:hasplugin(plugin) then - action() - else - local pending_actions = self._ifplugin[plugin] - - if not pending_actions then - pending_actions = {} - self._ifplugin[plugin] = pending_actions - end - - pending_actions[#pending_actions + 1] = action - end -end - ---- If the given feature has been loaded, call `action`. Otherwise, if the --- feature is ever loaded in the future, call `action` after that loading occurs. -function repl:iffeature(feature, action) - if self:hasfeature(feature) then - action() - else - local pending_features = self._iffeature[feature] - - if not pending_features then - pending_features = {} - self._iffeature[feature] = pending_features - end - - pending_features[#pending_features + 1] = action - end -end - -local function setup_before(repl) - local mt = {} - - function mt:__newindex(key, value) - if type(value) ~= 'function' then - error(tostring(value) .. " is not a function", 2) - end - - local old_value = repl[key] - - if old_value == nil then - error(sformat("The '%s' method does not exist", key), 2) - end - - repl[key] = function(...) - value(...) - return old_value(...) - end - end - - return setmetatable({}, mt) -end - -local function setup_after(repl) - local mt = {} - - function mt:__newindex(key, value) - if type(value) ~= 'function' then - error(tostring(value) .. " is not a function", 2) - end - - local old_value = repl[key] - - if old_value == nil then - error(sformat("The '%s' method does not exist", key), 2) - end - - repl[key] = function(...) - local _, results = gather_results(true, old_value(...)) - value(...) - return compat.unpack(results, 1, results.n) - end - end - - return setmetatable({}, mt) -end - -local function setup_around(repl) - local mt = {} - - function mt:__newindex(key, value) - if type(value) ~= 'function' then - error(tostring(value) .. " is not a function", 2) - end - - local old_value = repl[key] - - if old_value == nil then - error(sformat("The '%s' method does not exist", key), 2) - end - - repl[key] = function(self, ...) - return value(self, old_value, ...) - end - end - - return setmetatable({}, mt) -end - -local function setup_override(repl) - local mt = {} - - function mt:__newindex(key, value) - if type(value) ~= 'function' then - error(tostring(value) .. " is not a function", 2) - end - - local old_value = repl[key] - - if old_value == nil then - error(sformat("The '%s' method does not exist", key), 2) - end - - repl[key] = value - end - - return setmetatable({}, mt) -end - -local function setup_repl(repl) - local mt = {} - - function mt:__newindex(key, value) - local old_value = repl[key] - - if old_value ~= nil then - error(sformat("The '%s' method already exists", key), 2) - end - - repl[key] = value - end - - function mt:__index(key) - local value = repl[key] - - if type(value) == 'function' then - -- XXX cache this? - return function(_, ...) - return value(repl, ...) - end - end - - return value - end - - return setmetatable({}, mt) -end - --- TODO use lua-procure for this (eventually) -local function findchunk(name) - for _, loader in pairs(compat.package.searchers) do - local chunk = loader(name) - - if type(chunk) == 'function' then - return chunk - end - end - - error('unable to locate plugin', 3) -end - -function repl:loadplugin(chunk) - if self:hasplugin(chunk) then - error(sformat('plugin %q has already been loaded', tostring(chunk)), 2) - end - self._plugins[chunk] = true - - local plugin_actions = self._ifplugin[chunk] - self._ifplugin[chunk] = nil - - if type(chunk) == 'string' then - chunk = findchunk('repl.plugins.' .. chunk) - end - - local plugin_env = { - repl = setup_repl(self), - before = setup_before(self), - after = setup_after(self), - around = setup_around(self), - override = setup_override(self), - init = function() end, - } - - local function ro_globals(_, key, _) - error(sformat('global environment is read-only (key = %q)', key), 2) - end - - plugin_env._G = plugin_env - plugin_env.features = {} - setmetatable(plugin_env, { __index = _G, __newindex = ro_globals }) - - setfenv(chunk, plugin_env) - local _, results = gather_results(nil, chunk()) - - local features = plugin_env.features or {} - - if type(features) == 'string' then - features = { features } - end - - for _, feature in ipairs(features) do - if self._features[feature] then - error(sformat('feature %q already present', feature), 2) - end - - self._features[feature] = true - - local feature_actions = self._iffeature[feature] - self._iffeature[feature] = nil - if feature_actions then - for _, action in ipairs(feature_actions) do - action() - end - end - end - - if plugin_actions then - for _, action in ipairs(plugin_actions) do - action() - end - end - - return compat.unpack(results, 1, results.n) -end - --- XXX how to guarantee this gets called? -function repl:shutdown() -end - -function repl:getcontext() - return _G -end - -return repl diff --git a/repl/repl/plugins/autoreturn.lua b/repl/repl/plugins/autoreturn.lua deleted file mode 100644 index a0e15ea..0000000 --- a/repl/repl/plugins/autoreturn.lua +++ /dev/null @@ -1,29 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- A plugin that causes the REPL to automatically return evaluation results - -function around:compilechunk(orig, chunk) - local f, err = orig(self, 'return ' .. chunk) - - if not f then - f, err = orig(self, chunk) - end - - return f, err -end diff --git a/repl/repl/plugins/completion.lua b/repl/repl/plugins/completion.lua deleted file mode 100644 index 938c743..0000000 --- a/repl/repl/plugins/completion.lua +++ /dev/null @@ -1,192 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local utils = require 'repl.utils' -local getmetatable = getmetatable -local pairs = pairs -local sfind = string.find -local sgmatch = string.gmatch -local smatch = string.match -local ssub = string.sub -local tconcat = table.concat -local tsort = table.sort -local type = type - -local function isindexable(value) - if type(value) == 'table' then - return true - end - - local mt = getmetatable(value) - - return mt and mt.__index -end - -local function getcompletions(t) - local union = {} - - while isindexable(t) do - if type(t) == 'table' then - -- XXX what about the pairs metamethod in 5.2? - -- either we don't care, we implement a __pairs-friendly - -- pairs for 5.1, or implement a 'rawpairs' for 5.2 - for k, v in pairs(t) do - if union[k] == nil then - union[k] = v - end - end - end - - local mt = getmetatable(t) - t = mt and mt.__index or nil - end - - return pairs(union) -end - -local function split_ns(expr) - if expr == '' then - return { '' } - end - - local pieces = {} - - -- XXX method calls too (option?) - for m in sgmatch(expr, '[^.]+') do - pieces[#pieces + 1] = m - end - - -- logic for determining whether to pad the matches with the empty - -- string (ugly) - if ssub(expr, -1) == '.' then - pieces[#pieces + 1] = '' - end - - return pieces -end - -local function determine_ns(expr) - local ns = _G -- XXX what if the REPL lives in a special context? (option?) - local pieces = split_ns(expr) - - for index = 1, #pieces - 1 do - local key = pieces[index] - -- XXX rawget? or regular access? (option?) - ns = ns[key] - - if not isindexable(ns) then - return {}, '', '' - end - end - - expr = pieces[#pieces] - - local prefix = '' - - if #pieces > 1 then - prefix = tconcat(pieces, '.', 1, #pieces - 1) .. '.' - end - - local last_piece = pieces[#pieces] - - local before, after = smatch(last_piece, '(.*):(.*)') - - if before then - ns = ns[before] -- XXX rawget - prefix = prefix .. before .. ':' - expr = after - end - - return ns, prefix, expr -end - -local isidentifierchar - -do - local ident_chars_set = {} - -- XXX I think this can be done with isalpha in C... - local ident_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:_0123456789' - - for i = 1, #ident_chars do - local char = ssub(ident_chars, i, i) - ident_chars_set[char] = true - end - - function isidentifierchar(char) - return ident_chars_set[char] - end -end - -local function extract_innermost_expr(expr) - local index = #expr - - while index > 0 do - local char = ssub(expr, index, index) - if isidentifierchar(char) then - index = index - 1 - else - break - end - end - - index = index + 1 - - return ssub(expr, 1, index - 1), ssub(expr, index) -end - --- XXX is this logic (namely, returning the entire line) too specific to --- linenoise? -function repl:complete(expr, callback) - if utils.ends_in_unfinished_string(expr) then - return - end - - local ns, prefix, path - - prefix, expr = extract_innermost_expr(expr) - - ns, path, expr = determine_ns(expr) - - prefix = prefix .. path - - local completions = {} - - for k, v in getcompletions(ns) do - if sfind(k, expr, 1, true) == 1 then - local suffix = '' - local type = type(v) - - -- XXX this should be optional - if type == 'function' then - suffix = '(' - elseif type == 'table' then - suffix = '.' - end - - completions[#completions + 1] = prefix .. k .. suffix - end - end - - tsort(completions) - - for _, completion in ipairs(completions) do - callback(completion) - end -end - -features = 'completion' diff --git a/repl/repl/plugins/example.lua b/repl/repl/plugins/example.lua deleted file mode 100644 index d55fd07..0000000 --- a/repl/repl/plugins/example.lua +++ /dev/null @@ -1,41 +0,0 @@ --- Example plugin that demonstrates the objects available to a --- plugin, as well as the methods that a plugin should make use --- of - --- Adding methods and properties to the repl object adds them to --- the REPL object loading the plugin. If such a method or property --- already exists, the current plugin will fail to load. -function repl:newmethod(...) -end - --- Adding methods to the before object causes them to be called --- before the actual method itself. If the method being added --- (in this case displayresults) does not exist on the REPL object --- loading this plugin, the current plugin will fail to load. -function before:displayresults(results) -end - --- Adding methods to the after object causes them to be called --- after the actual method itself. If the method being added --- (in this case displayresults) does not exist on the REPL object --- loading this plugin, the current plugin will fail to load. -function after:displayresults(results) -end - --- Adding methods to the around object causes them to be called --- instead of the original method of the same name. The new --- method receives all of the arguments that the original would, --- except it also receives the original method as the first argument. --- This way, the new method may invoke the original as it pleases. --- If the method being added (in this case displayresults) does not exist on --- the REPL object loading this plugin, the current plugin will fail to load. -function around:evalute(orig, chunk) -end - --- Adding methods to the override object causes them to be called --- instead of the original method of the same name. If the method being added --- (in this case displayresults) does not exist on the REPL object loading this --- plugin, the current plugin will fail to load. -function override:name() - return 'Plugin!' -end diff --git a/repl/repl/plugins/filename_completion.lua b/repl/repl/plugins/filename_completion.lua deleted file mode 100644 index c16729f..0000000 --- a/repl/repl/plugins/filename_completion.lua +++ /dev/null @@ -1,63 +0,0 @@ -local utils = require 'repl.utils' -local lfs = require 'lfs' - -repl:requirefeature 'completion' - -local function guess_directory_separator(file_name) - return file_name:match('/') or - file_name:match('\\') or - '/' -end - -local function split_parent_directory(file_name) - local parent_directory, directory_entry = - file_name:match('^(.+)[\\/](.+)$') - if not parent_directory then - parent_directory = '.' - directory_entry = file_name - end - return parent_directory, directory_entry -end - -local function is_ignored_directory_entry(entry) - return entry == '.' or - entry == '..' -end - -local function replace_end_of_string(str, suffix, replacement) - assert(str:sub(-#suffix) == suffix) - return str:sub(1, -(#suffix+1)) .. replacement -end - -local function complete_file_name(file_name, expr, callback) - local directory, partial_entry = split_parent_directory(file_name) - for entry in lfs.dir(directory) do - if not is_ignored_directory_entry(entry) and - entry:find(partial_entry, 1, true) == 1 then - callback(replace_end_of_string(expr, partial_entry, entry)) - end - end -end - -local function complete_directory(directory, expr, callback) - for entry in lfs.dir(directory) do - if not is_ignored_directory_entry(entry) then - callback(expr..entry) - end - end -end - -function after:complete(expr, callback) - if utils.ends_in_unfinished_string(expr) then - local file_name = expr:match('[%w@/\\.-_+#$%%{}[%]!~ ]+$') - if file_name then - if file_name:find('[/\\]$') then - complete_directory(file_name, expr, callback) - else - complete_file_name(file_name, expr, callback) - end - else - complete_directory('.', expr, callback) - end - end -end diff --git a/repl/repl/plugins/history.lua b/repl/repl/plugins/history.lua deleted file mode 100644 index 6330dbd..0000000 --- a/repl/repl/plugins/history.lua +++ /dev/null @@ -1,60 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local history_file - -local function invokecallback(self, name, ...) - if not self._history_callbacks then - return - end - - local impl = self._history_callbacks[name] - return impl(...) -end - -local function init() - if os.getenv 'HOME' then - history_file = os.getenv('HOME') .. '/.rep.lua.history' - end -end - --- XXX I don't know if this callback setup way --- is the best way to go about this (in fact --- I'm pretty sure it isn't), but I just need --- something that works right now. -function repl:setuphistorycallbacks(callbacks) - self._history_callbacks = callbacks - - if history_file then - invokecallback(self, 'load', history_file) - end -end - -function after:handleline(line) - invokecallback(self, 'addline', line) -end - -function before:shutdown() - if history_file then - invokecallback(self, 'save', history_file) - end -end - -features = 'history' - -init() diff --git a/repl/repl/plugins/keep_last_eval.lua b/repl/repl/plugins/keep_last_eval.lua deleted file mode 100644 index 01a7794..0000000 --- a/repl/repl/plugins/keep_last_eval.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- A plugin that stores the results of the last evaluation in _G._ - -local tostring = tostring - -function before:displayresults(results) - local context = self:getcontext() - - if self._keep_eval_lastn then - context._ = nil - - for i = 1, self._keep_eval_lastn do - context['_' .. tostring(i)] = nil - end - end - - if results.n > 0 then - context._ = results[1] - - for i = 1, results.n do - context['_' .. tostring(i)] = results[i] - end - - self._keep_eval_lastn = results.n - end -end diff --git a/repl/repl/plugins/linenoise.lua b/repl/repl/plugins/linenoise.lua deleted file mode 100644 index 4407c53..0000000 --- a/repl/repl/plugins/linenoise.lua +++ /dev/null @@ -1,59 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- A plugin that uses linenoise (https://github.com/hoelzro/lua-linenoise) for prompting - -local ln = require 'linenoise' - -repl:requirefeature 'console' - -function override:showprompt(prompt) - self._prompt = prompt -- XXX how do we make sure other plugins don't step on this? -end - -function override:lines() - return function() - return ln.linenoise(self._prompt .. ' ') - end -end - -repl:iffeature('completion', function() - ln.setcompletion(function(completions, line) - repl:complete(line, function(completion) - ln.addcompletion(completions, completion) - end) - end) -end) - -repl:ifplugin('history', function() - repl:setuphistorycallbacks { - load = function(filename) - ln.historyload(filename) - end, - - addline = function(line) - ln.historyadd(line) - end, - - save = function(filename) - ln.historysave(filename) - end, - } -end) - -features = 'input' diff --git a/repl/repl/plugins/pretty_print.lua b/repl/repl/plugins/pretty_print.lua deleted file mode 100644 index 2318f44..0000000 --- a/repl/repl/plugins/pretty_print.lua +++ /dev/null @@ -1,262 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- Pretty prints expression results (console only) - -local format = string.format -local tconcat = table.concat -local tsort = table.sort -local tostring = tostring -local type = type -local floor = math.floor -local pairs = pairs -local ipairs = ipairs -local error = error -local stderr = io.stderr - -pcall(require, 'luarocks.require') -local ok, term = pcall(require, 'term') -if not ok then - term = nil -end - -local keywords = { - ['and'] = true, - ['break'] = true, - ['do'] = true, - ['else'] = true, - ['elseif'] = true, - ['end'] = true, - ['false'] = true, - ['for'] = true, - ['function'] = true, - ['if'] = true, - ['in'] = true, - ['local'] = true, - ['nil'] = true, - ['not'] = true, - ['or'] = true, - ['repeat'] = true, - ['return'] = true, - ['then'] = true, - ['true'] = true, - ['until'] = true, - ['while'] = true, -} - -local function compose(f, g) - return function(...) - return f(g(...)) - end -end - -local emptycolormap = setmetatable({}, { __index = function() - return function(s) - return s - end -end}) - -local colormap = emptycolormap - -if term then - colormap = { - ['nil'] = term.colors.blue, - string = term.colors.yellow, - punctuation = compose(term.colors.green, term.colors.bright), - ident = term.colors.red, - boolean = term.colors.green, - number = term.colors.cyan, - path = term.colors.white, - misc = term.colors.magenta, - } -end - -local function isinteger(n) - return type(n) == 'number' and floor(n) == n -end - -local function isident(s) - return type(s) == 'string' and not keywords[s] and s:match('^[a-zA-Z_][a-zA-Z0-9_]*$') -end - --- most of these are arbitrary, I *do* want numbers first, though -local type_order = { - number = 0, - string = 1, - userdata = 2, - table = 3, - thread = 4, - boolean = 5, - ['function'] = 6, - cdata = 7, -} - -local function cross_type_order(a, b) - local pos_a = type_order[ type(a) ] - local pos_b = type_order[ type(b) ] - - if pos_a == pos_b then - return a < b - else - return pos_a < pos_b - end -end - -local function sortedpairs(t) - local keys = {} - - local seen_non_string - - for k in pairs(t) do - keys[#keys + 1] = k - - if not seen_non_string and type(k) ~= 'string' then - seen_non_string = true - end - end - - local sort_func = seen_non_string and cross_type_order or nil - tsort(keys, sort_func) - - local index = 1 - return function() - if keys[index] == nil then - return nil - else - local key = keys[index] - local value = t[key] - index = index + 1 - - return key, value - end - end, keys -end - -local function find_longstring_nest_level(s) - local level = 0 - - while s:find(']' .. string.rep('=', level) .. ']', 1, true) do - level = level + 1 - end - - return level -end - -local function dump(params) - local pieces = params.pieces - local seen = params.seen - local path = params.path - local v = params.value - local indent = params.indent - - local t = type(v) - - if t == 'nil' or t == 'boolean' or t == 'number' then - pieces[#pieces + 1] = colormap[t](tostring(v)) - elseif t == 'string' then - if v:match '\n' then - local level = find_longstring_nest_level(v) - pieces[#pieces + 1] = colormap.string('[' .. string.rep('=', level) .. '[' .. v .. ']' .. string.rep('=', level) .. ']') - else - pieces[#pieces + 1] = colormap.string(format('%q', v)) - end - elseif t == 'table' then - if seen[v] then - pieces[#pieces + 1] = colormap.path(seen[v]) - return - end - - seen[v] = path - - local lastintkey = 0 - - pieces[#pieces + 1] = colormap.punctuation '{\n' - for i, v in ipairs(v) do - for j = 1, indent do - pieces[#pieces + 1] = ' ' - end - dump { - pieces = pieces, - seen = seen, - path = path .. '[' .. tostring(i) .. ']', - value = v, - indent = indent + 1, - } - pieces[#pieces + 1] = colormap.punctuation ',\n' - lastintkey = i - end - - for k, v in sortedpairs(v) do - if not (isinteger(k) and k <= lastintkey and k > 0) then - for j = 1, indent do - pieces[#pieces + 1] = ' ' - end - - if isident(k) then - pieces[#pieces + 1] = colormap.ident(k) - else - pieces[#pieces + 1] = colormap.punctuation '[' - dump { - pieces = pieces, - seen = seen, - path = path .. '.' .. tostring(k), - value = k, - indent = indent + 1, - } - pieces[#pieces + 1] = colormap.punctuation ']' - end - pieces[#pieces + 1] = colormap.punctuation ' = ' - dump { - pieces = pieces, - seen = seen, - path = path .. '.' .. tostring(k), - value = v, - indent = indent + 1, - } - pieces[#pieces + 1] = colormap.punctuation ',\n' - end - end - - for j = 1, indent - 1 do - pieces[#pieces + 1] = ' ' - end - - pieces[#pieces + 1] = colormap.punctuation '}' - else - pieces[#pieces + 1] = colormap.misc(tostring(v)) - end -end - -repl:requirefeature 'console' - -function override:displayresults(results) - local pieces = {} - - for i = 1, results.n do - dump { - pieces = pieces, - seen = {}, - path = '', - value = results[i], - indent = 1, - } - pieces[#pieces + 1] = '\n' - end - - stderr:write(tconcat(pieces, '')) -end diff --git a/repl/repl/plugins/rcfile.lua b/repl/repl/plugins/rcfile.lua deleted file mode 100644 index a74e81a..0000000 --- a/repl/repl/plugins/rcfile.lua +++ /dev/null @@ -1,54 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --- A plugin that runs code in $HOME/.rep.lua before the REPL starts - -local setfenv = require('repl.utils').setfenv - -local function readable(filename) - local f = io.open(filename, 'r') - if not f then - return false - end - f:close() - return true -end - -local function init() - local home = os.getenv 'HOME' - - if not home then - return - end - - local rcfile = home .. '/.rep.lua' - - if not readable(rcfile) then - return - end - - local chunk = assert(loadfile(rcfile)) - local env = setmetatable({ repl = repl }, { __index = _G, __newindex = _G }) - - setfenv(chunk, env) - - chunk() - return true -end - -return init() diff --git a/repl/repl/plugins/rlwrap.lua b/repl/repl/plugins/rlwrap.lua deleted file mode 100644 index 9f195a3..0000000 --- a/repl/repl/plugins/rlwrap.lua +++ /dev/null @@ -1,41 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if os.getenv 'LUA_REPL_RLWRAP' then - features = 'input' -else - -- XXX check that we're not receiving input from a non-tty - local has_rlwrap = os.execute('which rlwrap >/dev/null 2>/dev/null') - - if type(has_rlwrap) ~= 'boolean' then - has_rlwrap = has_rlwrap == 0 - end - - if not has_rlwrap then - error 'Please install rlwrap in order to use the rlwrap plugin' - end - - local lowest_index = -1 - - while arg[lowest_index] ~= nil do - lowest_index = lowest_index - 1 - end - lowest_index = lowest_index + 1 - os.execute(string.format('LUA_REPL_RLWRAP=1 rlwrap %q %q', arg[lowest_index], arg[0])) - os.exit(0) -end diff --git a/repl/repl/plugins/semicolon_suppress_output.lua b/repl/repl/plugins/semicolon_suppress_output.lua deleted file mode 100644 index 4adaecb..0000000 --- a/repl/repl/plugins/semicolon_suppress_output.lua +++ /dev/null @@ -1,36 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local smatch = string.match - --- XXX will this affect any other plugins? -function around:compilechunk(orig, chunk) - local f, err = orig(self, chunk) - - if not f then - return f, err - end - - if smatch(chunk, ';%s*$') then - return function() - f() - end - end - - return f -end diff --git a/repl/repl/sync.lua b/repl/repl/sync.lua deleted file mode 100644 index a422ed4..0000000 --- a/repl/repl/sync.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local repl = require 'repl' -local sync_repl = repl:clone() -local error = error - --- @class repl.sync ---- This module implements a synchronous REPL. It provides ---- a run() method for actually running the REPL, and requires ---- that implementors implement the lines() method. - ---- Run a REPL loop in a synchronous fashion. --- @name repl.sync:run -function sync_repl:run() - self:prompt(1) - for line in self:lines() do - local level = self:handleline(line) - self:prompt(level) - end - self:shutdown() -end - ---- Returns an iterator that yields lines to be evaluated. --- @name repl.sync:lines --- @return An iterator. -function sync_repl:lines() - error 'You must implement the lines method' -end - -return sync_repl diff --git a/repl/repl/utils.lua b/repl/repl/utils.lua deleted file mode 100644 index 3e2ca18..0000000 --- a/repl/repl/utils.lua +++ /dev/null @@ -1,70 +0,0 @@ --- Copyright (c) 2011-2015 Rob Hoelz --- --- Permission is hereby granted, free of charge, to any person obtaining a copy of --- this software and associated documentation files (the "Software"), to deal in --- the Software without restriction, including without limitation the rights to --- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of --- the Software, and to permit persons to whom the Software is furnished to do so, --- subject to the following conditions: --- --- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS --- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR --- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER --- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN --- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -local setfenv = setfenv or function(f, t) - local upvalue_index = 1 - - -- XXX we may need a utility library if debug isn't available - while true do - local name = debug.getupvalue(f, upvalue_index) - -- some functions don't have an _ENV upvalue, because - -- they never refer to globals - if not name then - return - end - if name == '_ENV' then - debug.setupvalue(f, upvalue_index, t) - return - end - upvalue_index = upvalue_index + 1 - end -end - ---- Tests wheter an expression ends in an unfinished string literal. --- @return First position in the unfinished string literal or `nil`. -local function ends_in_unfinished_string(expr) - local position = 0 - local quote - local current_delimiter - local last_unmatched_start - while true do - -- find all quotes: - position, quote = expr:match('()([\'"])', position+1) - if not position then break end - -- if we're currently in a string: - if current_delimiter then - -- would end current string? - if current_delimiter == quote then - -- not escaped? - if expr:sub(position-1, position-1) ~= '\\' then - current_delimiter = nil - last_unmatched_start = nil - end - end - else - current_delimiter = quote - last_unmatched_start = position+1 - end - end - return last_unmatched_start -end - -return { - setfenv = setfenv, - ends_in_unfinished_string = ends_in_unfinished_string -}