@@ -185,9 +185,11 @@ local escapePattern
185185local F
186186local getFileContents , fileExists
187187local getNextUsableToken
188+ local insertTokenRepresentations
188189local isAny
190+ local isToken
189191local loadLuaString , loadLuaFile
190- local maybeOutputLineNumber
192+ local outputLineNumber , maybeOutputLineNumber
191193local pack , unpack
192194local parseStringlike
193195local printf , printTokens
@@ -515,6 +517,12 @@ function concatTokens(tokens, lastLn, addLineNumbers)
515517 return table.concat (parts )
516518end
517519
520+ function insertTokenRepresentations (parts , tokens , i1 , i2 )
521+ for i = i1 , i2 do
522+ table.insert (parts , tokens [i ].representation )
523+ end
524+ end
525+
518526function getFileContents (path , isTextFile )
519527 assertarg (1 , path , " string" )
520528 assertarg (2 , isTextFile , " boolean" ," nil" )
@@ -667,16 +675,29 @@ function escapePattern(s)
667675 return (s :gsub (" [-+*^?$.%%()[%]]" , " %%%0" ))
668676end
669677
678+ function outputLineNumber (parts , ln )
679+ table.insert (parts , " --[[@" )
680+ table.insert (parts , ln )
681+ table.insert (parts , " ]]" )
682+ end
683+ function maybeOutputLineNumber (parts , tok , lastLn )
684+ if tok .line == lastLn or USELESS_TOKENS [tok .type ] then return lastLn end
685+
686+ outputLineNumber (parts , tok .line )
687+ return tok .line
688+ end
689+ --[=[
670690function maybeOutputLineNumber(parts, tok, lastLn, fromMetaToOutput)
671691 if tok.line == lastLn or USELESS_TOKENS[tok.type] then return lastLn end
672692
673- -- if fromMetaToOutput then
674- -- table.insert(parts, '__LUA"--[[@'..tok.line..']]"\n')
675- -- else
693+ if fromMetaToOutput then
694+ table.insert(parts, '__LUA"--[[@'..tok.line..']]"\n')
695+ else
676696 table.insert(parts, "--[[@"..tok.line.."]]")
677- -- end
697+ end
678698 return tok.line
679699end
700+ ]=]
680701
681702function isAny (v , ...)
682703 for i = 1 , select (" #" , ... ) do
@@ -767,16 +788,29 @@ else
767788 end
768789end
769790
770- -- token, index = getNextUsableToken( tokens, startIndex [, maxIndex=#tokens ] )
771- function getNextUsableToken (tokens , i , iEnd )
772- for i = i , math.min ((iEnd or math.huge ), # tokens ) do
791+ -- token, index = getNextUsableToken( tokens, startIndex [, indexLimit, direction=1 ] )
792+ function getNextUsableToken (tokens , i , iLimit , dir )
793+ dir = dir or 1
794+
795+ iLimit
796+ = dir < 0
797+ and math.max ((iLimit or 1 ), 1 )
798+ or math.min ((iLimit or math.huge ), # tokens )
799+
800+ for i = i , iLimit , dir do
773801 if not USELESS_TOKENS [tokens [i ].type ] then
774802 return tokens [i ], i
775803 end
776804 end
805+
777806 return nil
778807end
779808
809+ -- bool = isToken( token, tokenType [, tokenValue=any ] )
810+ function isToken (tok , tokType , v )
811+ return tok .type == tokType and (v == nil or tok .value == v )
812+ end
813+
780814-- ==============================================================
781815-- = Preprocessor Functions =====================================
782816-- ==============================================================
@@ -820,6 +854,11 @@ metaFuncs.serialize = serialize
820854-- escapedString = escapePattern( string )
821855metaFuncs .escapePattern = escapePattern
822856
857+ -- isToken()
858+ -- Check if a token is of a specific type, optionally also check it's value.
859+ -- bool = isToken( token, tokenType [, tokenValue=any ] )
860+ metaFuncs .isToken = isToken
861+
823862-- run()
824863-- Execute a Lua file. Similar to dofile().
825864-- returnValue1, ... = run( path )
@@ -926,13 +965,6 @@ function metaFuncs.eachToken(tokens, ignoreUselessTokens)
926965 end
927966end
928967
929- -- isToken()
930- -- Check if a token is of a specific type, optionally also check it's value.
931- -- bool = isToken( token, tokenType [, tokenValue=any ] )
932- function metaFuncs .isToken (tok , tokType , v )
933- return tok .type == tokType and (v == nil or tok .value == v )
934- end
935-
936968-- newToken()
937969-- Create a new token. Different token types take different arguments.
938970-- token = newToken( tokenType, ... )
@@ -1151,7 +1183,7 @@ local function _processFileOrString(params, isFile)
11511183 -- ==============================================================
11521184
11531185 for _ , tok in ipairs (tokens ) do
1154- if tok . type == " pp_entry" then
1186+ if isToken ( tok , " pp_entry" ) then
11551187 hasPreprocessorCode = true
11561188 break
11571189 end
@@ -1181,36 +1213,35 @@ local function _processFileOrString(params, isFile)
11811213 tokensToProcess = {}
11821214 end
11831215
1184- local function outputFinalDualValueStatement (metaLineStartIndex )
1216+ local function outputFinalDualValueStatement (metaLineIndexStart , metaLineIndexEnd )
11851217 -- We expect the statement to look like any of these:
11861218 -- !!local x = ...
11871219 -- !!x = ...
11881220
1189- -- Note: Something like the following produces a valid program, but won't work as expected:
1190- -- !!local x = 1; local y = 2;
1191- -- Only x will be outputted. @Robustness: Don't allow this.
1192-
1193- local tok , i = getNextUsableToken (tokens , metaLineStartIndex , tokenIndex - 1 )
1221+ -- Check whether local or not.
1222+ local tok , i = getNextUsableToken (tokens , metaLineIndexStart , metaLineIndexEnd )
11941223 if not tok then
11951224 errorInFile (
1196- luaUnprocessed , pathIn , tokens [metaLineStartIndex ].position , " Parser" ,
1225+ luaUnprocessed , pathIn , tokens [metaLineIndexStart ].position , " Parser" ,
11971226 " Unexpected end of preprocessor line."
11981227 )
11991228 end
12001229
1201- local isLocal = (tok . type == " keyword" and tok . value == " local" )
1230+ local isLocal = isToken (tok , " keyword" , " local" )
12021231
12031232 if isLocal then
1204- tok , i = getNextUsableToken (tokens , i + 1 , tokenIndex - 1 )
1233+ tok , i = getNextUsableToken (tokens , i + 1 , metaLineIndexEnd )
12051234 if not tok then
12061235 errorInFile (
1207- luaUnprocessed , pathIn , tokens [metaLineStartIndex ].position , " Parser" ,
1236+ luaUnprocessed , pathIn , tokens [metaLineIndexStart ].position , " Parser" ,
12081237 " Unexpected end of preprocessor line."
12091238 )
12101239 end
12111240 end
12121241
1213- if tok .type ~= " identifier" then
1242+ -- Check for identifier.
1243+ -- @Incomplete: Support multiple assignments. :MultipleAssignments
1244+ if not isToken (tok , " identifier" ) then
12141245 errorInFile (
12151246 luaUnprocessed , pathIn , tok .position , " Parser" ,
12161247 " Expected an identifier."
@@ -1219,28 +1250,65 @@ local function _processFileOrString(params, isFile)
12191250
12201251 local ident = tok .value
12211252
1222- tok , i = getNextUsableToken (tokens , i + 1 , tokenIndex - 1 )
1253+ -- Check for "=".
1254+ tok , i = getNextUsableToken (tokens , i + 1 , metaLineIndexEnd )
12231255 if not tok then
12241256 errorInFile (
1225- luaUnprocessed , pathIn , tokens [metaLineStartIndex ].position , " Parser" ,
1257+ luaUnprocessed , pathIn , tokens [metaLineIndexStart ].position , " Parser" ,
12261258 " Unexpected end of preprocessor line."
12271259 )
1228- elseif not (tok .type == " punctuation" and tok .value == " =" ) then
1260+ elseif isToken (tok , " punctuation" , " ," ) then
1261+ -- :MultipleAssignments
1262+ errorInFile (
1263+ luaUnprocessed , pathIn , tok .position , " Parser" ,
1264+ " Preprocessor line must be a single assignment. (Multiple assignments are not supported.)"
1265+ )
1266+ elseif not isToken (tok , " punctuation" , " =" ) then
12291267 errorInFile (
12301268 luaUnprocessed , pathIn , tok .position , " Parser" ,
12311269 " Preprocessor line must be an assignment."
12321270 )
12331271 end
12341272
1235- if not getNextUsableToken (tokens , i + 1 , tokenIndex - 1 ) then
1273+ local indexAfterEqualSign = i + 1
1274+
1275+ if not getNextUsableToken (tokens , indexAfterEqualSign , metaLineIndexEnd ) then
12361276 errorInFile (
12371277 luaUnprocessed , pathIn , tok .position , " Parser" ,
12381278 " Unexpected end of preprocessor line."
12391279 )
12401280 end
12411281
1282+ -- Check if the rest of the line is an expression.
1283+ if true then
1284+ local lastUsableToken , lastUsableIndex = getNextUsableToken (tokens , metaLineIndexEnd + 1 , 1 , - 1 )
1285+ local parts = {}
1286+
1287+ table.insert (parts , " return (" )
1288+ if isToken (lastUsableToken , " punctuation" , " ;" ) then
1289+ insertTokenRepresentations (parts , tokens , indexAfterEqualSign , lastUsableIndex - 1 )
1290+ else
1291+ insertTokenRepresentations (parts , tokens , indexAfterEqualSign , metaLineIndexEnd )
1292+ end
1293+ table.insert (parts , " \n )" )
1294+
1295+ if not loadstring (table.concat (parts )) then
1296+ errorInFile (
1297+ luaUnprocessed , pathIn , tokens [metaLineIndexStart ].position , " Parser" ,
1298+ " Dual code line must be a single assignment statement."
1299+ )
1300+ end
1301+ end
1302+
1303+ -- Output.
12421304 table.insert (metaParts , ' __LUA"' )
1305+
1306+ if params .addLineNumbers then
1307+ outputLineNumber (metaParts , tokens [metaLineIndexStart ].line )
1308+ end
1309+
12431310 if isLocal then table.insert (metaParts , ' local ' ) end
1311+
12441312 table.insert (metaParts , ident )
12451313 table.insert (metaParts , ' = "__VAL(' )
12461314 table.insert (metaParts , ident )
@@ -1251,7 +1319,7 @@ local function _processFileOrString(params, isFile)
12511319
12521320 -- Note: Can be multiple lines if extended.
12531321 local function processMetaLine (isDual , metaStartLine )
1254- local metaLineStartIndex = tokenIndex
1322+ local metaLineIndexStart = tokenIndex
12551323 local bracketBalance = 0
12561324
12571325 while true do
@@ -1273,7 +1341,7 @@ local function _processFileOrString(params, isFile)
12731341 end
12741342
12751343 if isDual then
1276- outputFinalDualValueStatement (metaLineStartIndex )
1344+ outputFinalDualValueStatement (metaLineIndexStart , tokenIndex - 1 )
12771345 end
12781346
12791347 break
@@ -1323,12 +1391,7 @@ local function _processFileOrString(params, isFile)
13231391 -- !( function sum(a, b) return a+b; end )
13241392 -- local text = !("Hello, mr. "..getName())
13251393 -- _G.!!("myRandomGlobal"..math.random(5)) = 99
1326- if
1327- tokType == " pp_entry"
1328- and tokens [tokenIndex + 1 ]
1329- and tokens [tokenIndex + 1 ].type == " punctuation"
1330- and tokens [tokenIndex + 1 ].value == " ("
1331- then
1394+ if tokType == " pp_entry" and tokens [tokenIndex + 1 ] and isToken (tokens [tokenIndex + 1 ], " punctuation" , " (" ) then
13321395 local startToken = tok
13331396 local startPos = tok .position
13341397 local startLine = tok .line
0 commit comments