源代码预处理 |
|
源代码过滤可以用来扩展语言的语法。在某些情况下,它也可以用来提高效率。
Lua 提供了一个loadstring
函数(类似于其他动态语言中的eval
),它可以用来动态编译 Lua 代码。这允许 Lua 程序本身以某种方式过滤自身或其他代码,而不是通过单独的预处理器(例如,像 C 预处理器一样)。
在下面的示例中,我们对使用典型方法进行向量乘法和使用源代码过滤宏展开实现的方法进行了基准测试比较。这主要用于说明,并不一定推荐。
local lua = [[ -- typical implementation (without filtering) local function cross_vector2(u, v, result) local u1, u2, u3 = u[1], u[2], u[3] local v1, v2, v3 = v[1], v[2], v[3] result[1], result[2], result[3] = u2*v3-u3*v2, u3*v1-u1*v3, u1*v2-u2*v1 return result end -- test and benchmark local function benchmark(func) local t1 = os.clock() func() local t2 = os.clock() print("time:", t2-t1) end benchmark(function() local vector u = 1, 0, 0 local vector v = 0, 1, 0 for n = 1,5000000 do cross_vector(u,v,u) --print("DEBUG: u=" .. stringify_vector(u)) end end) benchmark(function() local u = {1, 0, 0} local v = {0, 1, 0} for n = 1,5000000 do cross_vector2(u,v,u) -- print("DEBUG: u=" .. table.concat(u, ', ')) end end) ]] -- source filtering implementation lua = string.gsub(lua, "local%s+vector%s+(%w+)%s*=%s*(%w+)%s*,%s*(%w+)%s*,%s*(%w+)", "local %1_1, %1_2, %1_3 = %2, %3, %4") lua = string.gsub(lua, "cross_vector%s*%(%s*(%w+)%s*,%s*(%w+)%s*,%s*(%w+)%s*%)", "%3_1, %3_2, %3_3 = %1_2*%2_3-%1_3*%2_2, %1_3*%2_1-%1_1*%2_3, %1_1*%2_2-%1_2*%2_1") lua = string.gsub(lua, "stringify_vector%((%w+)%)", "(%1_1 .. ',' .. %1_2 .. ',' .. %1_3)") -- source filter print("DEBUG[\n" .. lua .. "]") assert(loadstring(lua))()
在一个系统上的结果
以上可以做得更健壮。特别是,应该格外小心以确保源代码过滤不会发生在不希望的地方。例如,在极端情况下,如果你的程序有
print("cross_vector(u,v,u) = ", cross_vector(u,v,u))
你可能不希望源代码过滤发生在字符串内部。为了处理这种情况,你的源代码过滤器需要跳过字符串。这可能需要一个完整的 Lua 解析器(例如,[LuaParse]),或者你甚至可以使用一个部分词法分析器(例如,LuaBalanced)。幸运的是,与其他语言相比,Lua 相对容易解析。
如果你尝试同时使用两个独立的源代码过滤器,可能会出现交互问题。
源代码过滤与代码生成有关。例如,请参阅 LuaList:2006-09/msg00798.html 中的用法。