Skip to content

Instantly share code, notes, and snippets.

@lukacat10
Last active September 16, 2021 13:11
Show Gist options
  • Save lukacat10/16e1a06d89b5829f743e398ce99cf81f to your computer and use it in GitHub Desktop.
Save lukacat10/16e1a06d89b5829f743e398ce99cf81f to your computer and use it in GitHub Desktop.
package.preload["argparse"] = function(...)
local function errorf(msg, ...)
error(msg:format(...), 0)
end
local function setter(arg, result, value)
result[arg.name] = value or true
end
local parser = { __name = "ArgParser" }
parser.__index = parser
function parser:add(names, arg)
if type(names) == "string" then names = { names } end
arg.names = names
for i = 1, #names do
local name = names[i]
if name:sub(1, 2) == "--" then self.options[name:sub(3)] = arg
elseif name:sub(1, 1) == "-" then self.flags[name:sub(2)] = arg
else self.arguments[#self.arguments + 1] = arg; arg.argument = true end
end
table.insert(self.list, #self.list, arg)
if arg.action == nil then arg.action = setter end
if arg.required == nil then arg.required = names[1]:sub(1, 1) ~= "-" end
if arg.name == nil then arg.name = names[1]:gsub("^-+", "") end
if arg.mvar == nil then arg.mvar = arg.name:upper() end
end
function parser:parse(...)
local args = table.pack(...)
local i, n = 1, #args
local arg_idx = 1
local result = {}
while i <= n do
local arg = args[i]
i = i + 1
if arg:find("^%-%-([^=]+)=(.+)$") then
local name, value = arg:match("^%-%-([^=]+)=(.+)$")
local arg = self.options[name]
if not arg then errorf("Unknown argument %q", name) end
if not arg.many and result[arg.name] ~= nil then errorf("%s has already been set", name) end
if not arg.value then errorf("%s does not accept a value", name) end
arg:action(result, value)
elseif arg:find("^%-%-(.*)$") then
local name = arg:match("^%-%-(.*)$")
local arg = self.options[name]
if not arg then errorf("Unknown argument %q", name) end
if not arg.many and result[arg.name] ~= nil then errorf("%s has already been set", name) end
if arg.value then
local value = args[i]
i = i + 1
if not value then errorf("%s needs a value", name) end
arg:action(result, value)
else
arg:action(result)
end
elseif arg:find("^%-(.+)$") then
local flags = arg:match("^%-(.+)$")
for j = 1, #flags do
local name = flags:sub(j, j)
local arg = self.flags[name]
if not arg then errorf("Unknown argument %q", name) end
if not arg.many and result[arg.name] ~= nil then errorf("%s has already been set", name) end
if arg.value then
local value
if j == #flags then
value = args[i]
i = i + 1
else
value = flags:sub(j + 1)
end
if not value then errorf("%s expects a value", name) end
arg:action(result, value)
break
else
arg:action(result)
end
end
else
local argument = self.arguments[arg_idx]
if argument then
argument:action(result, arg)
arg_idx = arg_idx + 1
else
errorf("Unexpected argument %q", arg)
end
end
end
for i = 1, #self.list do
local arg = self.list[i]
if arg and arg.required and result[arg.name] == nil then
errorf("%s is required (use -h to see usage)", arg.name)
end
end
return result
end
local function get_usage(arg)
local name
if arg.argument then name = arg.mvar
elseif arg.value then name = arg.names[1] .. "=" .. arg.mvar
else name = arg.names[1]
end
if #arg.names > 1 then name = name .. "," .. table.concat(arg.names, ",", 2) end
return name
end
local function create(prefix)
local parser = setmetatable({
options = {},
flags = {},
arguments = {},
list = {},
}, parser)
parser:add({ "-h", "--help", "-?" }, {
value = false, required = false,
doc = "Show this help message",
action = function()
if prefix then print(prefix) print() end
print("USAGE")
local max = 0
for i = 1, #parser.list do max = math.max(max, #get_usage(parser.list[i])) end
local format = " %-" .. max .. "s %s"
for i = 1, #parser.list do
local arg = parser.list[i]
print(format:format(get_usage(arg), arg.doc or ""))
end
error("", 0)
end,
})
return parser
end
local function is_help(cmd)
return cmd == "help" or cmd == "--help" or cmd == "-h" or cmd == "-?"
end
return { create = create, is_help = is_help }
end
package.preload["json"] = function(...)
local tonumber = tonumber
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then error('Expected ' .. delim) end
return pos, false
end
return pos + 1, true
end
local esc_map = { b = '\b', f = '\f', n = '\n', r = '\r', t = '\t' }
local function parse_str_val(str, pos)
local out, n = {}, 0
if pos > #str then error("Malformed JSON (in string)") end
while true do
local c = str:sub(pos, pos)
if c == '"' then return table.concat(out, "", 1, n), pos + 1 end
n = n + 1
if c == '\\' then
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error("Malformed JSON (in string)") end
if nextc == "u" then
local num = tonumber(str:sub(pos + 2, pos + 5), 16)
if not num then error("Malformed JSON (in unicode string) ") end
if num <= 255 then
pos, out[n] = pos + 6, string.char(num)
else
pos, out[n] = pos + 6, "?"
end
else
pos, out[n] = pos + 2, esc_map[nextc] or nextc
end
else
pos, out[n] = pos + 1, c
end
end
end
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end
local null = {}
local literals = {['true'] = true, ['false'] = false, ['null'] = null }
local escapes = {}
for i = 0, 255 do
local c = string.char(i)
if i >= 32 and i <= 126
then escapes[c] = c
else escapes[c] = ("\\u00%02x"):format(i)
end
end
escapes["\t"], escapes["\n"], escapes["\r"], escapes["\""], escapes["\\"] = "\\t", "\\n", "\\r", "\\\"", "\\\\"
local function parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos)
local first = str:sub(pos, pos)
if first == '{' then
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true)
obj[key], pos = parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then
return parse_num_val(str, pos)
elseif first == end_delim then
return nil, pos + 1
else
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end
local format, gsub, tostring, pairs, next, type, concat
= string.format, string.gsub, tostring, pairs, next, type, table.concat
local function stringify_impl(t, out, n)
local ty = type(t)
if ty == "table" then
local first_ty = type(next(t))
if first_ty == "nil" then
out[n], n = "{}", n + 1
return n
elseif first_ty == "string" then
out[n], n = "{", n + 1
local first = true
for k, v in pairs(t) do
if first then first = false else out[n], n = ",", n + 1 end
out[n] = format("\"%s\":", k)
n = stringify_impl(v, out, n + 1)
end
out[n], n = "}", n + 1
return n
elseif first_ty == "number" then
out[n], n = "[", n + 1
for i = 1, #t do
if i > 1 then out[n], n = ",", n + 1 end
n = stringify_impl(t[i], out, n)
end
out[n], n = "]", n + 1
return n
else
error("Cannot serialize key " .. first_ty)
end
elseif ty == "string" then
if t:match("^[ -~]*$") then
out[n], n = gsub(format("%q", t), "\n", "n"), n + 1
else
out[n], n = "\"" .. gsub(t, ".", escapes) .. "\"", n + 1
end
return n
elseif ty == "number" or ty == "boolean" then
out[n],n = tostring(t), n + 1
return n
elseif ty == "function" then
out[n], n = tostring(t), n+ 1
else error("Cannot serialize type " .. ty)
end
end
local function stringify(object)
local buffer = {}
local n = stringify_impl(object, buffer, 1)
return concat(buffer, "", 1, n - 1)
end
local function try_parse(msg)
local ok, res = pcall(parse, msg)
if ok then return res else return nil, res end
end
return {
stringify = stringify,
try_parse = try_parse,
parse = parse,
null = null
}
end
local tonumber, type, keys = tonumber, type, keys
local argparse = require "argparse"
local hson = require "json"
local current_path = shell.getRunningProgram()
local current_name = fs.getName(current_path)
local arguments = argparse.create(current_name .. ": Run Lua on this computer from the World Wide Web")
arguments:add({ "url" }, { doc = "The url of the websocket" })
arguments:add({ "turtleid" }, { doc = "The id to give to the turtle" })
arguments:add({ "password" }, { doc = "The password for this connect" })
local args = arguments:parse(...)
local turtleid = args.turtleid
local url = args.url
local password = args.password
local remote, err = http.websocket(url)
if not remote then error("Cannot connect to cloud-catcher server: " .. err, 0) end
function codesnip(ws, received)
loadstring("returned={"..received.."}")()
ws.send(textutils.serializeJSON(returned))
end
function connect()
local ws, err = http.websocket(url)
if ws then
ws.send(turtleid)
ws.send(password)
while true do
received = ws.receive()
if received ~= nil then
print(received)
ok, err = pcall(codesnip, ws, received)
if ok == false then
print("errored!")
ws.send(err)
end
end
end
ws.close()
end
while true do
pcall(connect)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment