交互式 Lua

lua-users home
wiki

ilua 是一款实用工具,它提供了一个扩展的交互式提示符,比标准 Lua 提供的功能更多。它完全用 Lua 实现。另请参见 CompleteWithReadline.

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 打印特定类型的方式很有用。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 的交互式 GUI 版本(lconsole)使用 LuaInterface,可在此处获取

它有一个有用的代码窗格,你可以在其中定义多行函数并自动保存它们。

SteveDonovan


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2011 年 6 月 14 日下午 5:28 GMT (差异)