Chapter 1. Getting Started
建立Lua 5.3的chunk加载执行模型,掌握词法规则、local/global解析、基础值类型、nil/boolean真假语义与独立解释器工作流。
从一个Chunk开始,而不是从一行立即执行开始
Lua把一段可执行source称为chunk。文件、交互式输入、load接收的字符串以及module loader最终都要形成chunk;实现会先解析/编译成可调用函数,再执行函数体。要求把syntax error与runtime error分开,也要求说明environment、arguments、results和error owner。
先预测:load("return missing + 1")是否立刻报错?答案是不一定。Source语法合法时load返回function;直到调用时才读取global missing并在算术处失败。相反,load("return )")在load阶段就返回失败。若不分阶段,错误日志只会写“脚本失败”,无法判断是source、binding还是execution。
local factory, syntax_error = load(
"local name = ...; return 'hello, ' .. name",
"welcome-chunk",
"t",
{}
)
assert(factory, syntax_error)
local ok, result = pcall(factory, "Lua")
assert(ok and result == "hello, Lua")第四个参数给chunk一个显式空environment,避免source意外读写host globals;pcall把runtime error变成status与value。真正的sandbox还需要resource limits、allowed capabilities和host isolation,本章只建立边界词汇。
Chunks:File、String、Module与Call是四个阶段
File chunk由stand-alone interpreter或host读取;string chunk由load编译;require通过package.searchers找到loader、执行并缓存module value;function call才产生side effects和results。dofile近似“load file并执行”,不缓存;loadfile只准备function。选错API会导致重复side effect、环境泄漏或module状态不一致。
Chunk可以包含definitions、statements和return values。Lua不要求每个文件声明main;file顶层本身就是function body。Host可多次调用同一compiled chunk,每次有新的local activation,但其environment/upvalues是否共享取决于创建方式。不要把source file等同于singleton object。
Some Lexical Conventions
Identifier由字母、数字、下划线组成且不能以数字开头,区分大小写;keywords不能作普通identifier。惯例中以下划线加大写字母开头的名字常留给Lua内部用途。Semicolon通常可省略;换行多数时候只是空白,但把多条statement压在一行会降低错误定位质量。
短comment从--到行末;long comment使用long brackets,并可用等号level处理内容中已有]]的情况。Long string也使用同一delimiter机制,不处理普通escape,适合嵌入配置、模板与测试fixture。Source encoding与identifier字符策略应由项目固定,不能依赖编辑器猜测。
提醒我们,拼写相似不是同一binding,comment/string delimiter也影响parser看到的token。静态检查与小chunk测试应覆盖shadowing、case与long-bracket level。
Global Variables是Environment Table访问
在Lua 5.3中,未解析为local/upvalue的free name经当前_ENV处理;概念上score = 10接近_ENV.score = 10。说明global不是VM中独立的神秘槽,而是environment capability。
local function new_strict_environment(allowed)
return setmetatable({}, {
__index = function(_, key)
if allowed[key] ~= nil then return allowed[key] end
error("unknown global read: " .. tostring(key), 2)
end,
__newindex = function(_, key)
error("unknown global write: " .. tostring(key), 2)
end,
})
end
local env = new_strict_environment({ print = print })
local chunk = assert(load("print(message)", "strict", "t", env))
assert(not pcall(chunk)) -- message拼错/未提供,在边界失败默认global方便REPL和小脚本,却让typo变成远处故障:total = toal + price可能读nil后失败,也可能污染已有global。Production module默认local,明确导出table;动态source给capability-limited environment。_G通常指初始global table,但替换局部_ENV不要求修改_G。
Types and Values:变量没有固定Type
Lua是动态类型语言:type属于value,variable是保存value的binding。一个local先保存number再保存string是合法的,但每个时刻value仍有确定runtime type。不等于没有类型约束;operator、library和host API仍检查实际value。
第四版开篇逐步引入nil、boolean,后续章节再展开number、string、table、function、userdata与thread。type(value)返回type name string,可用于诊断,但大量手工type switch常表示缺少data contract。Boundary应验证shape并给出上下文,内部代码依赖已建立的不变量。
Table、function、thread和full userdata具有identity/reference-like行为;number、boolean、nil和string按value语义理解,string不可变且实现可共享storage。Assignment复制value:对table复制的是table reference,不是deep clone。真正结构与copy policy将在Tables/Data Structures章节展开。
Nil:缺失、未初始化与删除Key
未初始化global/local(读取前)通常得到nil;function缺失result也用nil补齐;给table key赋nil会删除entry。Nil可表达absence,却不能自动区分missing、deleted、not loaded和invalid。Sequence中间的nil还会破坏长度与遍历假设,不能把“稀疏table”当普通list。
检查是否存在不能只写if t[key] then,因为stored false也会走false branch。若nil是唯一absence sentinel,应写t[key] ~= nil;若需要存nil概念或区分missing,使用显式sentinel/object或额外presence flag。Host API也要说明Lua nil怎样映射C null/optional value。
Booleans与Lua Truth Contract
只有false与nil为false;number 0、empty string、empty table和所有其它values都为true。是从C、JavaScript、Python迁移时最早的高风险差异。
and与or返回operand value而非强制boolean,因此可写value = supplied or default,但supplied为false时也会被default替换。若false是合法业务值,必须用nil检查。not才总返回boolean。逻辑operator短路,可用于guard,但不要把side effect藏在难读的链里。
local cases = { false, 0, "", {}, true }
for index, value in ipairs(cases) do
print(index, type(value), value and "truthy" or "falsey")
end
local configured = false
local wrong = configured or true
local correct = configured == nil and true or configured
assert(wrong == true and correct == false)Table literal无法用ipairs保留开头nil,因此实验把nil单独测试;这本身就是nil影响sequence shape的证据。
The Stand-Alone Interpreter
独立解释器可执行file、-e expression、-l preload module或进入interactive mode。命令行参数通过arg提供,具体负索引/零/正索引含义要用目标版本验证。REPL会显示expression results,file mode不会自动打印return;不要把REPL convenience当程序语义。
启动环境、默认path/cpath、startup script与OS locale会影响可重复性。教学实验记录_VERSION、可执行文件路径、arguments和package paths;CI用明确Lua 5.3 runtime。生产host若嵌入Lua,还要记录allocator、opened libraries、panic/protected call策略与environment,不假设stand-alone defaults。
安全方面,io、os.execute、package.loadlib和debug facilities都是capabilities。运行不可信source时,不要只删除几个global就称为sandbox;CPU loop、memory exhaustion、native module和已有reference都需隔离。第25章会系统讨论reflection/sandbox,第27章以后处理C API。
本章回顾:先固定Source、Name、Value与Owner
- Chunk先load/compile成function,再call;syntax与runtime error属于不同阶段。
- Lexical convention决定parser看到的identifier、comment和long bracket,name区分大小写。
- Local按lexical scope解析;global是当前
_ENVtable access,默认共享应受控。 - Type属于value,variable只保存value;assignment不会给binding永久固定type。
- Nil表达absence并删除table key,只有nil与false为假;0和空string都为真。
- Stand-alone interpreter适合实验,但version、path、libraries与capabilities必须记录。