交互式 Lua |
|
Lua 的一个优点是你可以与它进行交互式操作,就像与解释器进行对话一样。就像对话是学习人类语言的最佳方式一样,交互式尝试想法非常有帮助。(或者,你只是想在你的电脑上获得一个更好的计算器;)。如果未指定脚本或指定了 -i
选项,独立的 lua 解释器将进入交互模式。
但是,标准交互式提示符有一些限制;如果你想评估一个表达式,你必须在它前面加上 '='。解释器将其翻译为 'return ',这样你始终输入的是一段有效的 Lua 代码块。此外,除非在它们的元表中显式定义了 __tostring
,否则不会打印出表。
$ lua Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio > = 10+20 30 > t = {10,20,30} > = t table: 003DB790
这有点笨拙,而且与其他交互式语言的用户期望不符。使用 ilua,表达式不需要 '=',并且表会尽可能漂亮地打印出来。最后一个表达式的值始终在 '_' 变量中可用。
$ lua ilua.lua ILUA: Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio "quit" to end > 10+20 30 > t = {10,20,30} > t {10,20,30} > _ {10,20,30} > m = {alice=1,john=2,betty=3} > m {betty=3,john=2,alice=1} > x+1 [string "local"]:1: variable 'x' is not declared > quit
请注意,变量必须在使用之前声明;ilua
在严格模式下运行。对变量的任何赋值(包括 nil)都会声明它。
事实仍然是 10+20
不是有效的 Lua 语句。我使用的技术是实验性编译 - 首先假设用户输入了一个表达式,如果失败,则尝试将其编译为语句。这展示了基本逻辑
function eval_lua(line) -- is it an expression? local err,chunk = compile('_pretty_print('..line..')') if err then -- otherwise, a statement? err,chunk = compile(line) end -- if compiled ok, then evaluate the chunk if not err then err = evaluate(chunk) end -- if there was any error, print it out if err then print(err) end end
我们可以使用全局 _pretty_print
精确控制值的打印方式;它最重要的工作是扩展表。需要注意一些失控情况。表可能太大或嵌套太深;它可能包含循环引用。ilua
通过指定默认长度和深度来处理这种情况;函数 ilua.table_options
可用于更改最大长度和深度(默认分别为 20 和 7)。例如,ilua.table_options {limit=100}
允许你打印更长的表。它检测到任何以前打印过或当前正在打印的表。这些目前被渲染为 '{<self>}'。例如,为定义良好的库调用 require
将返回这种类型的表,它具有循环引用 _M
。
> require 'utils' {hex=function: 00487548,_PACKAGE='',printf=function: 003DD3F0,choose=function: 04878D8,import=function: 00487888,quit=function: 00486678,dump=function: 004875 0,_NAME='utils',timer=function: 00487638,_M={<self>},fprintf=function: 00485310 readf=function: 004878F8}
默认情况下,ilua
尝试在打印表方面变得聪明。我使用了一个简单的方案来决定它们是列表式还是映射式;有时它会出错,因为并非所有表都可以归类为其中之一。你可以使用 table_options
关闭这种智能行为
> s = {1,2,bonzo='dog',felix='cat'} > s {1,2} > ilua.table_options {clever = false} > s {[1]=1,[2]=2,felix='cat',bonzo='dog'}
在处理任何选项或文件名之前,ilua
会尝试使用 require
加载 'ilua-defs.lua',因此它可以在您的包路径上的任何位置。这是一个放置您希望每次加载的任何 Lua 代码的实用位置。
ilua
有几个命令行标志。-l
的作用与 Lua 中的相同选项类似;它预加载指定的库。-L
加载库并将所有函数引入全局命名空间。
$ lua ilua.lua -Lmath ILUA: Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio "quit" to end > sin(1.2) 0.93203908596723
-L
可能会造成严重问题,因此它会警告发现的任何冲突。
$ lua ilua.lua -Lutils -Ltest ILUA: Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio "quit" to end warning: test.printf overwrites utils.printf warning: test.unpack overwrites global unpack
-t
选项会将您的会话记录保存到日志文件中。如果未指定文件,它将使用 'ilua.log'。由于此选项需要参数,因此它必须是行上的最后一个选项。
$ lua ilua.lua -t saving transcript "ilua.log" ILUA: Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio "quit" to end > 2.3*math(1.2) [string "local"]:1: attempt to call global 'math' (a table value) > 2.3*math.sin(1.2) 2.1436898977246 > quit $ cat ilua.log ! ilua -t > 2.3*math(1.2) [string "local"]:1: attempt to call global 'math' (a table value) > 2.3*math.sin(1.2) 2.1436898977246
另一种获取日志的方法是 -T
,它会自动生成一个名为 ilua_yyyy_mm_dd_HH_MM.log
的日志文件。
将表导入全局命名空间通常对测试很有用,因此可以使用 ilua.import
。例如,ilua.import(require 'lfs')
将引入 lfs
库并使所有函数成为全局函数。
有时控制浮点输出的小数位数很有用。ilua.precision
允许您设置数字的宽度和精度(如果您不特别关心,请使用宽度 0)。'整数' 的处理方式略有不同。要重置为通常的表示形式,只需调用 ilua.precision
而不带任何参数即可。
> t = {sin(1),sin(1.5),sin(5.2)} > t {0.8414709848079,0.99749498660405,-0.88345465572015} > ilua.precision(0,2) > t {0.84,1.00,-0.88} > {10,20,30} {10,20,30} > ilua.precision() > t {0.8414709848079,0.99749498660405,-0.88345465572015}
有时更改 ilua
打印特定类型的方式很有用。ilua.print_handler
可用于为特定类型设置处理程序。
> ilua.print_handler('function',function(f) return 'fun' end) > math.sin fun > math {log=fun,max=fun,acos=fun,huge=1.#INF,ldexp=fun,pi=3.1415926535898,cos=fun,tanh=fun, pow=fun,deg=fun,tan=fun,cosh=fun,sinh=fun,random=fun,randomseed=fun,frexp=fun, ceil=fun,floor=fun,rad=fun,abs=fun,sqrt=fun ... }
如前所述,ilua
在严格模式下运行。但是,您可以指定一个处理程序,如果找不到变量,该处理程序将被调用。此示例使操作系统环境变量可用,就像它们是只读 Lua 变量一样。
> ilua.global_handler(function(s) return os.getenv(s) end) > COMSPEC 'C:\WINDOWS\system32\cmd.exe' > WINDIR 'C:\WINDOWS' > s [string "local"]:1: variable 's' is not declared
最终的自定义允许您的代码查看用户输入的每一行。例如,您可以在 'ilua-defs.lua' 文件中添加以下内容。
ilua.line_handler(function (line) if line:sub(1,1) == '.' then -- a shell command! os.execute(line:sub(2)) return nil else return line end end)
现在,如果您输入类似 '. ls' 或 '. dir' 的行,它将把该行的其余部分视为 OS 命令!如果您传递回字符串,ilua
将继续处理,因此您也有机会以某种方式过滤该字符串。
您不能在 ilua
中输入多行语句,这与常规 lua
不同。
> for i=1,5 do print(i) end 1 2 3 4 5 > for i=1,10 do [string "local"]:1: 'end' expected near '<eof>'
实际上,这通常不是问题,因为你可以在任何阶段使用 `dofile(filename)` 引入代码。如果需要,解决这个问题并不困难。
将 `ilua` 与 PlutoLibrary 集成以实现真正的“工作区”持久性将会很有趣。这是一种由某些语言系统提供的非常高效的模式,允许你保存你的会话状态(包括函数和数据),以便稍后重新加载。
ilua.lua
可在 Files:wiki_insecure/users/steved/ilua.lua 获取。
ilua 的交互式 GUI 版本(lconsole)使用 LuaInterface,可在此处获取
它有一个有用的代码窗格,你可以在其中定义多行函数并自动保存它们。