diff --git a/preprocess.lua b/preprocess.lua index 9ddffc0..f83a54f 100644 --- a/preprocess.lua +++ b/preprocess.lua @@ -26,6 +26,7 @@ - getCurrentPathIn, getCurrentPathOut - getOutputSoFar, getOutputSizeSoFar, getCurrentLineNumberInOutput - outputValue, outputLua, outputLuaTemplate + - startInterceptingOutput, stopInterceptingOutput Search this file for 'EnvironmentTable' for more info. Exported stuff from the library: @@ -188,7 +189,8 @@ local isRunningMeta = false local currentPathIn = "" local currentPathOut = "" local metaPathForErrorMessages = "" -local outputFromMeta = nil +local outputFromMetaStack = nil +local outputFromMeta = nil -- Top item in outputFromMetaStack. local canOutputNil = true local fastStrings = false @@ -1321,6 +1323,8 @@ end -- :EnvironmentTable +---------------------------------------------------------------- + metaEnv = copyTable(_G, true) -- Include all standard Lua stuff. metaEnv._G = metaEnv @@ -1485,7 +1489,7 @@ end -- Raises an error if no file or string is being processed. function metaFuncs.getOutputSoFar(asTable) errorIfNotRunningMeta(2) - return asTable and copyArray(outputFromMeta) or table.concat(outputFromMeta) + return asTable and copyArray(outputFromMetaStack[1]) or table.concat(outputFromMetaStack[1]) -- Should there be a way to get the contents of outputFromMeta etc.? :GetMoreOutputFromStack end -- getOutputSizeSoFar() @@ -1497,7 +1501,7 @@ function metaFuncs.getOutputSizeSoFar() local size = 0 - for _, lua in ipairs(outputFromMeta) do + for _, lua in ipairs(outputFromMetaStack[1]) do -- :GetMoreOutputFromStack size = size + #lua end @@ -1512,7 +1516,7 @@ function metaFuncs.getCurrentLineNumberInOutput() local ln = 1 - for _, lua in ipairs(outputFromMeta) do + for _, lua in ipairs(outputFromMetaStack[1]) do -- :GetMoreOutputFromStack ln = ln + countString(lua, "\n", true) end @@ -1825,9 +1829,34 @@ function metaFuncs.concatTokens(tokens) return _concatTokens(tokens, nil, false, nil, nil) end +-- startInterceptingOutput() +-- startInterceptingOutput( ) +-- Start intercepting output until stopInterceptingOutput() is called. +-- The function can be called multiple times to intercept interceptions. +function metaFuncs.startInterceptingOutput() + errorIfNotRunningMeta(2) + + outputFromMeta = {} + tableInsert(outputFromMetaStack, outputFromMeta) +end + +-- stopInterceptingOutput() +-- luaString = stopInterceptingOutput( ) +-- Stop intercepting output. +function metaFuncs.stopInterceptingOutput() + errorIfNotRunningMeta(2) + + local interceptedLua = tableRemove(outputFromMetaStack) + outputFromMeta = outputFromMetaStack[#outputFromMetaStack] or error("Called stopInterceptingOutput() before calling startInterceptingOutput()", 2) + + return table.concat(interceptedLua) +end + -- Extra stuff used by the command line program: metaFuncs.tryToFormatError = tryToFormatError +---------------------------------------------------------------- + for k, v in pairs(metaFuncs) do metaEnv[k] = v end @@ -1845,6 +1874,23 @@ function metaEnv.__ASSERTLUA(lua) return lua end +local function finalizeMacro(lua) + if lua == nil then + return (metaFuncs.stopInterceptingOutput()) + elseif type(lua) ~= "string" then + error("[Macro] Value is not Lua code.", 2) + elseif outputFromMeta[1] then + error("[Macro] Got Lua code from both value expression and outputLua(). Only one method may be used.", 2) -- It's also possible interception calls are unbalanced. + else + metaFuncs.stopInterceptingOutput() -- Returns "" because nothing was outputted. + return lua + end +end +function metaEnv.__MACRO() + metaFuncs.startInterceptingOutput() + return finalizeMacro +end + function metaEnv.__EVALSYMBOL(v) if type(v) == "function" then v = v() @@ -2152,13 +2198,18 @@ end local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNested) -- @Robustness: Make sure key tokens came from the same source file. + -- Add '!!(' for start of preprocessor block. - if isNested then - tableInsert(tokens, newTokenAt({type="identifier", value="__ASSERTLUA", representation="__ASSERTLUA"}, macroStartTok)) - else - tableInsert(tokens, newTokenAt({type="pp_entry", value="!!", representation="!!", double=true}, macroStartTok)) + if not isNested then + tableInsert(tokens, newTokenAt({type="pp_entry", value="!!", representation="!!", double=true}, macroStartTok)) + tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, macroStartTok)) end - tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="("}, macroStartTok)) + + -- Start macro wrapper. + tableInsert(tokens, newTokenAt({type="identifier", value="__MACRO", representation="__MACRO" }, macroStartTok)) + tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, macroStartTok)) + tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")" }, macroStartTok)) + tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, macroStartTok)) -- -- Callee. @@ -2496,8 +2547,13 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes -- End. -- - -- Add ')' for end of preprocessor block. + -- End macro wrapper. tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, tokens[#tokens])) + + -- Add ')' for end of preprocessor block. + if not isNested then + tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, tokens[#tokens])) + end end local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats) @@ -2969,6 +3025,7 @@ local function _processFileOrString(params, isFile) metaPathForErrorMessages = params.pathMeta or "" outputFromMeta = {} + outputFromMetaStack = {outputFromMeta} canOutputNil = params.canOutputNil ~= false fastStrings = params.fastStrings @@ -2994,6 +3051,10 @@ local function _processFileOrString(params, isFile) os.remove(params.pathMeta) end + if outputFromMetaStack[2] then + error("Called startInterceptingOutput() more times than stopInterceptingOutput().") + end + local lua = table.concat(outputFromMeta) --[[ :PrintCode print("=OUTPUT=============================") @@ -3002,6 +3063,7 @@ local function _processFileOrString(params, isFile) --]] metaPathForErrorMessages = "" + outputFromMetaStack = nil outputFromMeta = nil canOutputNil = true @@ -3103,6 +3165,7 @@ local function processFileOrString(params, isFile) currentPathIn = "" currentPathOut = "" metaPathForErrorMessages = "" + outputFromMetaStack = nil outputFromMeta = nil canOutputNil = true fastStrings = false diff --git a/tests/quickTest.lua2p b/tests/quickTest.lua2p index 2358100..a2bddfc 100644 --- a/tests/quickTest.lua2p +++ b/tests/quickTest.lua2p @@ -142,6 +142,18 @@ local n = @@t.field[keys[1]]:method(58) local n1 = @@ADD1!(43-2) local n2 = @@ADD1!!("43-2") +!local function FOO1(x) return x end +!local function FOO2(x) outputLua(x) end +local x = 7 +local y = @@FOO1(x) +local y = @@FOO2(x) + +!startInterceptingOutput() +a = some +other = 500921 +!local lua = stopInterceptingOutput():gsub("%a+", "%0Derp") +!!(lua) + -- Symbols. diff --git a/tests/quickTest.output.lua b/tests/quickTest.output.lua index acb5b40..785a5de 100644 --- a/tests/quickTest.output.lua +++ b/tests/quickTest.output.lua @@ -102,6 +102,13 @@ local n = 58 local n1 = 41+1 local n2 = 43-2+1 +local x = 7 +local y = x +local y = x + +aDerp = someDerp +otherDerp = 500921 + -- Symbols diff --git a/tests/suite.lua b/tests/suite.lua index e3291ea..ab7b455 100644 --- a/tests/suite.lua +++ b/tests/suite.lua @@ -369,6 +369,13 @@ doTest("Macros", function() -- Invalid: Nested code block in macro. assert(not pp.processString{ code=[[ !(function ECHO(v) return v end) v = @@ECHO( !!( !(1) ) ) ]]}) + + -- Using outputLua(). + assertCodeOutput(assert(pp.processString{ code=[[ !(function Y() return ("y") end) x = @@Y() ]]}), [[x = y]]) + assertCodeOutput(assert(pp.processString{ code=[[ !(function Y() outputLua("y") end) x = @@Y() ]]}), [[x = y]]) + + -- Invalid: Both using outputLua() and returning code. + assert(not pp.processString{ code=[[ !(function Y() outputLua("y") ; return "z" end) x = @@Y() ]]}) end) doTest("Preprocessor symbols", function() @@ -514,6 +521,22 @@ doTest("Serialize", function() assertCodeOutput(luaOut, [[{a=2,f=176,z=99}]]) -- Note: Table keys should be sorted. end) +doTest("Output interception", function() + local pp = ppChunk() + + local luaOut = assert(pp.processString{ code=[[ + !startInterceptingOutput() + local foo = bar + !local lua = stopInterceptingOutput():gsub("(%a+) *= *(%a+)", "%2 = %1") + $lua + ]] }) + assertCodeOutput(luaOut, [[local bar = foo]]) + + -- Invalid: Unbalanced interception start/stop calls. + assert(not pp.processString{ code=[[ !startInterceptingOutput() ]]}) + assert(not pp.processString{ code=[[ !stopInterceptingOutput() ]]}) +end) + addLabel("Command line")