Add 32x32 example.

This commit is contained in:
TSnake41 2020-04-17 19:40:45 +02:00
parent 109c23517b
commit 841bd316f7
23 changed files with 1687 additions and 0 deletions

View File

@ -0,0 +1,39 @@
local lynx = require "lynx"
local raylua_funcs = require "lynx.raylua_lynx"
local menu = lynx.menu ({
lynx.text("Test Lynx menu in Raylib", { selectable = false }),
lynx.text("", { selectable = false }),
lynx.text("Long life to Lynx !"),
lynx.text("the best UI library ever !"),
lynx.text("which works everywhere !"),
lynx.text("", { selectable = false }),
lynx.text("@TSnake41"),
}, {
x = 0,
y = 0,
w = 500,
h = 500,
default_height = 24,
current = 3,
funcs = raylua_funcs
})
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
rl.InitWindow(800, 450, "raylib [lua] example - lynx menu")
while not rl.WindowShouldClose() do
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
local pos = rl.GetMousePosition()
menu:input_mouse(pos.x, pos.y, 0)
menu:update(rl.GetFrameTime())
menu:draw()
rl.EndDrawing()
end
rl.CloseWindow()

View File

@ -0,0 +1,40 @@
local lynx = require "lynx"
local raylua_funcs = require "lynx.raylua_lynx"
local menu = lynx.menu ({
lynx.text("Test Lynx menu in Raylib", { selectable = false }),
lynx.text("", { selectable = false }),
lynx.text("Long life to Lynx !"),
lynx.text("the best UI library ever !"),
lynx.text("which works everywhere !"),
lynx.text("", { selectable = false }),
lynx.text("@TSnake41"),
}, {
x = 0,
y = 0,
w = 500,
h = 500,
default_height = 24,
current = 3,
funcs = raylua_funcs
})
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
--rl.SetTargetFPS(60)
rl.InitWindow(800, 450, "raylib [lua] example - lynx menu")
while not rl.WindowShouldClose() do
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
local pos = rl.GetMousePosition()
menu:input_mouse(pos.x, pos.y, 0)
menu:update(rl.GetFrameTime())
menu:draw()
rl.EndDrawing()
end
rl.CloseWindow()

1
examples/lynx Submodule

@ -0,0 +1 @@
Subproject commit 088321f96efc444eb99f1b35ed1718003bad3b78

90
examples/misc_32x32.lua Normal file
View File

@ -0,0 +1,90 @@
--[[
raylib 32x32 game/demo competition
Competition consist in developing a videogame in a 32x32 pixels screen size.
RULES:
1) Use only raylib (and included libraries), no external libraries allowed
2) The submission should consist of just one source file
3) Render your game/demo to a 32x32 pixels render texture,
show what you could do with a 32x32 RGB LED matrix!
4) No external resources, you CAN only create them programmatically,
5) Game/demo can be 2D or 3D, choose wisely
5) Shaders (if used) should be included in the source as string (char *)
and loaded with LoadShaderCode()
6) Code must compile and execute in PLATFORM_DESKTOP (Windows, Linux, macOS)
LICENSE: zlib/libpng
Copyright (c) 2020 <TODO: participant_name> (<TODO: participant_github_name>)
This software is provided "as-is", without any express or implied warranty. In no event
will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you
wrote the original software. If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented
as being the original software.
3. This notice may not be removed or altered from any source distribution.
]]
local ffi = require "ffi"
local windowWidth = 512
local windowHeight = 512
local gameScreenWidth = 32
local gameScreenHeight = 32
rl.SetTraceLogLevel(rl.LOG_WARNING)
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
rl.InitWindow(windowWidth, windowHeight, "my 32x32 game/demo")
local target = rl.LoadRenderTexture(gameScreenWidth, gameScreenHeight)
rl.SetTextureFilter(target.texture, rl.FILTER_POINT)
rl.SetTargetFPS(60)
while not rl.WindowShouldClose() do
local scale = math.min(rl.GetScreenWidth() / gameScreenWidth, rl.GetScreenHeight() / gameScreenHeight)
rl.BeginDrawing()
rl.BeginTextureMode(target)
rl.ClearBackground(rl.WHITE)
rl.DrawTextEx(rl.GetFontDefault(), "Hello", ffi.new("Vector2", 4, 0), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
rl.DrawTextEx(rl.GetFontDefault(), "it's", ffi.new("Vector2", 8, 10), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
rl.DrawTextEx(rl.GetFontDefault(), "raylua", ffi.new("Vector2", 0, 20), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
rl.EndTextureMode()
rl.DrawTexturePro(
target.texture,
ffi.new("Rectangle", 0, 0, target.texture.width, -target.texture.height),
ffi.new("Rectangle",
(rl.GetScreenWidth() - gameScreenWidth * scale) * 0.5,
(rl.GetScreenHeight() - gameScreenHeight * scale) * 0.5,
gameScreenWidth * scale, gameScreenHeight * scale
), ffi.new("Vector2", 0, 0), 0, rl.WHITE
)
for x=0,rl.GetScreenWidth(),16 do
rl.DrawRectangle(x, 0, 4, rl.GetScreenHeight(), rl.BLACK)
end
for y=0,rl.GetScreenHeight(),16 do
rl.DrawRectangle(0, y, rl.GetScreenWidth(), 4, rl.BLACK)
end
rl.EndDrawing()
end
rl.UnloadRenderTexture(target)
rl.CloseWindow()

BIN
raylua_e Normal file

Binary file not shown.

BIN
raylua_s Normal file

Binary file not shown.

19
repl/main.lua Normal file
View File

@ -0,0 +1,19 @@
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()

30
repl/repl/compat.lua Normal file
View File

@ -0,0 +1,30 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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
}
}

57
repl/repl/console.lua Normal file
View File

@ -0,0 +1,57 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

415
repl/repl/init.lua Normal file
View File

@ -0,0 +1,415 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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, "'<eof>'$") or smatch(err, "<eof>$")
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

View File

@ -0,0 +1,29 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

View File

@ -0,0 +1,192 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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'

View File

@ -0,0 +1,41 @@
-- 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

View File

@ -0,0 +1,63 @@
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

View File

@ -0,0 +1,60 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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()

View File

@ -0,0 +1,43 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

View File

@ -0,0 +1,59 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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'

View File

@ -0,0 +1,262 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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 = '<topvalue>',
value = results[i],
indent = 1,
}
pieces[#pieces + 1] = '\n'
end
stderr:write(tconcat(pieces, ''))
end

View File

@ -0,0 +1,54 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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()

View File

@ -0,0 +1,41 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

View File

@ -0,0 +1,36 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

46
repl/repl/sync.lua Normal file
View File

@ -0,0 +1,46 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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

70
repl/repl/utils.lua Normal file
View File

@ -0,0 +1,70 @@
-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
--
-- 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
}