互動式 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」的行,它會將該行的其餘部分當成作業系統命令!如果你傳回字串,ilua 會繼續處理,所以你也有機會以某種方式濾掉該字串。

限制

與一般的 lua 不同,你無法在 ilua 中輸入多行陳述式

> 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) 匯入程式碼。如果需要的話,要解決這個問題並不困難。

後續工作

iluaPlutoLibrary 整合起來,以實現真正的「工作空間」持續性,會很有趣。這是部份語言系統提供的一種非常有生產力的模式,你可以將工作階段狀態連同函式和資料儲存起來,稍後再重新載入。

來源程式碼

使用 LuaInterface 的 ilua (lconsole) 的互動式 GUI 版本可在這裡取得

它有一個有用的簡短程式碼窗格,您可以在其中定義多行功能,並自動儲存它們。

SteveDonovan


最近變更 · 喜好設定
編輯 · 歷史記錄
最後編輯於 2011 年 6 月 14 日 下午 5:28 GMT (差異)