/*
* This file is part of NLua.
*
* Copyright (C) 2003-2005 Fabio Mascarenhas de Queiroz.
* Copyright (C) 2012 Megax
*
* 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.
*/
using System;
using System.Threading;
using NLua;
using System.Collections.Generic;
/*
* Application to run Lua scripts that can use NLua
* from the console
*
* Author: Fabio Mascarenhas
* Version: 1.0
*/
namespace NLua
{
public class LuaNetRunner
{
const string lua_script = @"-- lua.lua - Lua 5.1 interpreter (lua.c) reimplemented in Lua.
--
-- WARNING: This is not completed but was quickly done just an experiment.
-- Fix omissions/bugs and test if you want to use this in production.
-- Particularly pay attention to error handling.
--
-- (c) David Manura, 2008-08
-- Licensed under the same terms as Lua itself.
-- Based on lua.c from Lua 5.1.3.
-- Improvements by Shmuel Zeigerman.
-- Variables analogous to those in luaconf.h
local LUA_INIT = ""LUA_INIT""
local LUA_PROGNAME = ""lua""
local LUA_PROMPT = ""> ""
local LUA_PROMPT2 = "">> ""
local function LUA_QL(x) return ""'"" .. x .. ""'"" end
local lua51 = _VERSION:match '5%.1$'
-- Variables analogous to those in lua.h
local LUA_RELEASE, LUA_COPYRIGHT, eof_ender
if lua51 then
LUA_RELEASE = ""Lua 5.1.4""
LUA_COPYRIGHT = ""Copyright (C) 1994-2008 Lua.org, PUC-Rio""
eof_ender = LUA_QL("""")
else
LUA_RELEASE = ""Lua 5.2.2""
LUA_COPYRIGHT = ""Copyright (C) 1994-2013 Lua.org, PUC-Rio""
eof_ender = ''
end
local EXTRA_COPYRIGHT = ""lua.lua (c) David Manura, 2008-08""
-- Note: don't allow user scripts to change implementation.
-- Check for globals with ""cat lua.lua | luac -p -l - | grep ETGLOBAL""
local _G = _G
local assert = assert
local collectgarbage = collectgarbage
local loadfile = loadfile
local loadstring = loadstring or load
local pcall = pcall
local rawget = rawget
local select = select
local tostring = tostring
local type = type
local unpack = unpack or table.unpack
local xpcall = xpcall
local io_stderr = io.stderr
local io_stdout = io.stdout
local io_stdin = io.stdin
local string_format = string.format
local string_sub = string.sub
local os_getenv = os.getenv
local os_exit = os.exit
local progname = LUA_PROGNAME
-- Use external functions, if available
local lua_stdin_is_tty = function() return true end
local setsignal = function() end
local function print_usage()
io_stderr:write(string_format(
""usage: %s [options] [script [args]].\n"" ..
""Available options are:\n"" ..
"" -e stat execute string "" .. LUA_QL(""stat"") .. ""\n"" ..
"" -l name require library "" .. LUA_QL(""name"") .. ""\n"" ..
"" -i enter interactive mode after executing "" ..
LUA_QL(""script"") .. ""\n"" ..
"" -v show version information\n"" ..
"" -- stop handling options\n"" ..
"" - execute stdin and stop handling options\n""
,
progname))
io_stderr:flush()
end
local our_tostring = tostring
local tuple = table.pack or function(...)
return {n=select('#', ...), ...}
end
local using_lsh,lsh
local function our_print (...)
local args = tuple(...)
for i = 1,args.n do
io.write(our_tostring(args[i]),'\t')
end
_G._ = args[1]
io.write '\n'
end
local function saveline(s)
if using_lsh then
lsh.saveline(s)
end
end
local function getline(prmt)
if using_lsh then
return lsh.readline(prmt)
else
io_stdout:write(prmt)
io_stdout:flush()
return io_stdin:read'*l'
end
end
local function l_message (pname, msg)
if pname then io_stderr:write(string_format(""%s: "", pname)) end
io_stderr:write(string_format(""%s\n"", msg))
io_stderr:flush()
end
local function report(status, msg)
if not status and msg ~= nil then
msg = tostring(msg)
--~ msg = (type(msg) == 'string' or type(msg) == 'number') and tostring(msg)
--~ or ""(error object is not a string)""
l_message(progname, msg);
end
return status
end
local function traceback (message)
local tp = type(message)
if tp ~= ""string"" and tp ~= ""number"" then return message end
local debug = _G.debug
if type(debug) ~= ""table"" then return message end
local tb = debug.traceback
if type(tb) ~= ""function"" then return message end
return tb(message, 2)
end
local function docall(f, ...)
local tp = {...} -- no need in tuple (string arguments only)
local F = function() return f(unpack(tp)) end
setsignal(true)
local result = tuple(xpcall(F, traceback))
setsignal(false)
-- force a complete garbage collection in case of errors
if not result[1] then collectgarbage(""collect"") end
return unpack(result, 1, result.n)
end
function dofile(name)
local f, msg = loadfile(name)
if f then f, msg = docall(f) end
return report(f, msg)
end
local function dostring(s, name)
local f, msg = loadstring(s, name)
if f then f, msg = docall(f) end
return report(f, msg)
end
local function dolibrary (name)
return report(docall(_G.require, name))
end
local function print_version()
l_message(nil, LUA_RELEASE .. "" "" .. LUA_COPYRIGHT..""\n""..EXTRA_COPYRIGHT)
end
local function getargs (argv, n)
local arg = {}
for i=1,#argv do arg[i - n] = argv[i] end
if _G.arg then
local i = 0
while _G.arg[i] do
arg[i - n] = _G.arg[i]
i = i - 1
end
end
return arg
end
local function get_prompt (firstline)
-- use rawget to play fine with require 'strict'
local pmt = rawget(_G, firstline and ""_PROMPT"" or ""_PROMPT2"")
local tp = type(pmt)
if tp == ""string"" or tp == ""number"" then
return tostring(pmt)
end
return firstline and LUA_PROMPT or LUA_PROMPT2
end
local function fetchline(firstline)
return getline(get_prompt(firstline))
end
local function incomplete (msg)
if msg then
if string_sub(msg, -#eof_ender) == eof_ender then
return true
end
end
return false
end
local function pushline (firstline)
local fine,b = true
repeat
b = fetchline(firstline)
if not b then return end -- no input
if using_lsh then
fine = lsh.checkline(b)
end
until fine
if firstline and string_sub(b, 1, 1) == '=' then
return ""return "" .. string_sub(b, 2) -- change '=' to `return'
else
return b
end
end
local function loadline ()
local b = pushline(true)
if not b then return -1 end -- no input
local f, msg
while true do -- repeat until gets a complete line
f, msg = loadstring(b, ""=stdin"")
if not incomplete(msg) then break end -- cannot try to add lines?
local b2 = pushline(false)
if not b2 then -- no more input?
return -1
end
b = b .. ""\n"" .. b2 -- join them
end
saveline(b)
return f, msg
end
local function dotty ()
local oldprogname = progname
progname = nil
using_lsh,lsh = false -- pcall(require, 'luaish') blows with LI ??
if using_lsh then
our_tostring = lsh.tostring
else
--print('problem loading luaish:',lsh)
our_tostring = tostring
end
while true do
local result
local status, msg = loadline()
if status == -1 then break end
if status then
result = tuple(docall(status))
status, msg = result[1], result[2]
end
report(status, msg)
if status and result.n > 1 then -- any result to print?
status, msg = pcall(our_print, unpack(result, 2, result.n))
if not status then
l_message(progname, string_format(
""error calling %s (%s)"",
LUA_QL(""print""), msg))
end
end
end
io_stdout:write""\n""
io_stdout:flush()
progname = oldprogname
end
local function handle_script(argv, n)
_G.arg = getargs(argv, n) -- collect arguments
local fname = argv[n]
if fname == ""-"" and argv[n-1] ~= ""--"" then
fname = nil -- stdin
end
local status, msg = loadfile(fname)
if status then
status, msg = docall(status, unpack(_G.arg))
end
return report(status, msg)
end
local function collectargs (argv, p)
local i = 1
while i <= #argv do
if string_sub(argv[i], 1, 1) ~= '-' then -- not an option?
return i
end
local prefix = string_sub(argv[i], 1, 2)
if prefix == '--' then
if #argv[i] > 2 then return -1 end
return argv[i+1] and i+1 or 0
elseif prefix == '-' then
return i
elseif prefix == '-i' then
if #argv[i] > 2 then return -1 end
p.i = true
p.v = true
elseif prefix == '-v' then
if #argv[i] > 2 then return -1 end
p.v = true
elseif prefix == '-e' then
p.e = true
if #argv[i] == 2 then
i = i + 1
if argv[i] == nil then return -1 end
end
elseif prefix == '-l' then
if #argv[i] == 2 then
i = i + 1
if argv[i] == nil then return -1 end
end
else
return -1 -- invalid option
end
i = i + 1
end
return 0
end
local function runargs(argv, n)
local i = 1
while i <= n do if argv[i] then
assert(string_sub(argv[i], 1, 1) == '-')
local c = string_sub(argv[i], 2, 2) -- option
if c == 'e' then
local chunk = string_sub(argv[i], 3)
if chunk == '' then i = i + 1; chunk = argv[i] end
assert(chunk)
if not dostring(chunk, ""=(command line)"") then return false end
elseif c == 'l' then
local filename = string_sub(argv[i], 3)
if filename == '' then i = i + 1; filename = argv[i] end
assert(filename)
if not dolibrary(filename) then return false end
end
i = i + 1
end end
return true
end
local function handle_luainit()
local init = os_getenv(LUA_INIT)
if init == nil then
return -- status OK
elseif string_sub(init, 1, 1) == '@' then
dofile(string_sub(init, 2))
else
dostring(init, ""="" .. LUA_INIT)
end
end
if _G.arg and _G.arg[0] and #_G.arg[0] > 0 then progname = _G.arg[0] end
local argv = arg
handle_luainit()
local has = {i=false, v=false, e=false}
local script = collectargs(argv, has)
if script < 0 then -- invalid args?
print_usage()
os_exit(1)
end
if has.v then print_version() end
local status = runargs(argv, (script > 0) and script-1 or #argv)
if not status then os_exit(1) end
if script ~= 0 then
status = handle_script(argv, script)
if not status then os_exit(1) end
else
_G.arg = nil
end
if has.i then
dotty()
elseif script == 0 and not has.e and not has.v then
if lua_stdin_is_tty() then
print_version()
import 'System'
dotty()
else dofile(nil) -- executes stdin as a file
end
end
";
/*
* Runs the Lua script passed as the first command-line argument.
* It passed all the command-line arguments to the script.
*/
[STAThread]
public static void Main(string[] args)
{
try {
using (Lua lua = new Lua ()) {
//lua.OpenLibs(); // steffenj: Lua 5.1.1 API change (all libs already opened in Lua constructor!)
lua.NewTable ("arg");
LuaTable argc = (LuaTable)lua ["arg"];
argc [0] = "NLua";
for (int i = 0; i < args.Length; i++) {
argc [i + 1] = args [i];
}
argc ["n"] = args.Length;
lua.LoadCLRPackage ();
try {
lua.DoString (lua_script,"lua");
} catch (Exception e) {
// limit size of stack traceback message to roughly 1 console screen height
string trace = e.StackTrace;
if (e.StackTrace.Length > 1300)
trace = e.StackTrace.Substring (0, 1300) + " [...] (traceback cut short)";
Console.WriteLine ();
Console.WriteLine (e.Message);
Console.WriteLine (e.Source + " raised a " + e.GetType ().ToString ());
Console.WriteLine (trace);
// wait for key press if there is an error
Console.ReadKey ();
}
}
} catch (Exception e) {
Console.WriteLine ();
Console.WriteLine (e.Message);
Console.WriteLine (e.Source + " raised a " + e.GetType ().ToString ());
Console.ReadKey ();
}
}
}
}