/* * 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 (); } } } }