Skip to content

Commit cb6bc20

Browse files
committed
Changed/improved !!.... Fixed an internal 'trying to call nil' error.
Dual code preprocessor lines must now be assignments. The value expression is now evaluated in the metaprogram before being outputted into the final program. I think this is more useful than simply outputting literally the same line in the generated program as in the metaprogram.
1 parent eba4446 commit cb6bc20

File tree

2 files changed

+128
-76
lines changed

2 files changed

+128
-76
lines changed

preprocess.lua

Lines changed: 117 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
8989
--============================================================]]
9090

91-
local VERSION = "1.4.0"
91+
local VERSION = "1.5.0"
9292

9393
local KEYWORDS = {
9494
"and","break","do","else","elseif","end","false","for","function","if","in",
@@ -158,6 +158,7 @@ local errorIfNotRunningMeta
158158
local escapePattern
159159
local F
160160
local getFileContents, fileExists
161+
local getNextUsableToken
161162
local isAny
162163
local loadLuaString, loadLuaFile
163164
local maybeOutputLineNumber
@@ -505,7 +506,7 @@ function assertarg(n, v, ...)
505506
if vType == select(i, ...) then return end
506507
end
507508

508-
local fName = debug.getInfo(2, "n").name
509+
local fName = debug.getinfo(2, "n").name
509510
local expects = table.concat({...}, " or ")
510511

511512
if fName == "" then fName = "?" end
@@ -728,6 +729,16 @@ else
728729
end
729730
end
730731

732+
-- token, index = getNextUsableToken( tokens, startIndex [, maxIndex=#tokens ] )
733+
function getNextUsableToken(tokens, i, iEnd)
734+
for i = i, math.min((iEnd or math.huge), #tokens) do
735+
if not isAny(tokens[i].type, "whitespace","comment") then
736+
return tokens[i], i
737+
end
738+
end
739+
return nil
740+
end
741+
731742
--==============================================================
732743
--= Preprocessor Functions =====================================
733744
--==============================================================
@@ -1062,22 +1073,16 @@ local function _processFileOrString(params, isFile)
10621073
end
10631074
end
10641075

1065-
-- local startOfLine = true
1066-
local isMeta = false
1067-
local isDual = false
1068-
local metaStartLine = 0
1069-
local bracketBalance = 0
1070-
10711076
local tokensToProcess = {}
10721077
local metaParts = {}
10731078

10741079
local tokenIndex = 1
10751080
local ln = 0
10761081

1077-
local function outputTokens(tokens)
1078-
if not tokens[1] then return end
1082+
local function flushTokensToProcess()
1083+
if not tokensToProcess[1] then return end
10791084

1080-
local lua = concatTokens(tokens, ln, params.addLineNumbers)
1085+
local lua = concatTokens(tokensToProcess, ln, params.addLineNumbers)
10811086
local luaMeta
10821087

10831088
if isDebug then
@@ -1087,42 +1092,107 @@ local function _processFileOrString(params, isFile)
10871092
end
10881093

10891094
table.insert(metaParts, luaMeta)
1090-
ln = tokens[#tokens].line
1095+
ln = tokensToProcess[#tokensToProcess].line
1096+
1097+
tokensToProcess = {}
10911098
end
10921099

1093-
while true do
1094-
local tok = tokens[tokenIndex]
1095-
if not tok then break end
1100+
local function outputFinalDualValueStatement(metaLineStartIndex)
1101+
-- We expect the statement to look like any of these:
1102+
-- !!local x = ...
1103+
-- !!x = ...
1104+
1105+
-- Note: Something like the following produces a valid program, but won't work as expected:
1106+
-- !!local x = 1; local y = 2;
1107+
-- Only x will be outputted. @Robustness: Don't allow this.
1108+
1109+
local tok, i = getNextUsableToken(tokens, metaLineStartIndex, tokenIndex-1)
1110+
if not tok then
1111+
errorInFile(
1112+
luaUnprocessed, pathIn, tokens[metaLineStartIndex].position, "Parser",
1113+
"Unexpected end of preprocessor line."
1114+
)
1115+
end
10961116

1097-
local tokType = tok.type
1117+
local isLocal = (tok.type == "keyword" and tok.value == "local")
1118+
1119+
if isLocal then
1120+
tok, i = getNextUsableToken(tokens, i+1, tokenIndex-1)
1121+
if not tok then
1122+
errorInFile(
1123+
luaUnprocessed, pathIn, tokens[metaLineStartIndex].position, "Parser",
1124+
"Unexpected end of preprocessor line."
1125+
)
1126+
end
1127+
end
1128+
1129+
if tok.type ~= "identifier" then
1130+
errorInFile(
1131+
luaUnprocessed, pathIn, tok.position, "Parser",
1132+
"Expected an identifier."
1133+
)
1134+
end
1135+
1136+
local ident = tok.value
1137+
1138+
tok, i = getNextUsableToken(tokens, i+1, tokenIndex-1)
1139+
if not tok then
1140+
errorInFile(
1141+
luaUnprocessed, pathIn, tokens[metaLineStartIndex].position, "Parser",
1142+
"Unexpected end of preprocessor line."
1143+
)
1144+
elseif not (tok.type == "punctuation" and tok.value == "=") then
1145+
errorInFile(
1146+
luaUnprocessed, pathIn, tok.position, "Parser",
1147+
"Preprocessor line must be an assignment."
1148+
)
1149+
end
1150+
1151+
if not getNextUsableToken(tokens, i+1, tokenIndex-1) then
1152+
errorInFile(
1153+
luaUnprocessed, pathIn, tok.position, "Parser",
1154+
"Unexpected end of preprocessor line."
1155+
)
1156+
end
1157+
1158+
table.insert(metaParts, '__LUA"')
1159+
if isLocal then table.insert(metaParts, 'local ') end
1160+
table.insert(metaParts, ident)
1161+
table.insert(metaParts, ' = "__VAL(')
1162+
table.insert(metaParts, ident)
1163+
table.insert(metaParts, ')__LUA"\\n"\n')
1164+
1165+
flushTokensToProcess()
1166+
end
1167+
1168+
-- Note: Can be multiple lines if extended.
1169+
local function processMetaLine(isDual, metaStartLine)
1170+
local metaLineStartIndex = tokenIndex
1171+
local bracketBalance = 0
1172+
1173+
while true do
1174+
local tok = tokens[tokenIndex]
1175+
if not tok then break end
1176+
1177+
local tokType = tok.type
10981178

1099-
-- Meta line (or lines if extended).
1100-
--------------------------------
1101-
if isMeta then
11021179
if
1103-
(
1104-
(tokType == "whitespace" and tok.value:find("\n", 1, true))
1105-
or (tokType == "comment" and not tok.long)
1180+
bracketBalance == 0 and (
1181+
(tokType == "whitespace" and tok.value:find("\n", 1, true)) or
1182+
(tokType == "comment" and not tok.long)
11061183
)
1107-
and bracketBalance == 0
11081184
then
11091185
if tokType == "comment" then
11101186
table.insert(metaParts, tok.representation)
1111-
if isDual then table.insert(tokensToProcess, tok) end
11121187
else
11131188
table.insert(metaParts, "\n")
1114-
if isDual then table.insert(tokensToProcess, {type="whitespace", value="\n", representation="\n"}) end
11151189
end
11161190

11171191
if isDual then
1118-
outputTokens(tokensToProcess)
1119-
tokensToProcess = {}
1192+
outputFinalDualValueStatement(metaLineStartIndex)
11201193
end
11211194

1122-
-- startOfLine = true
1123-
isMeta = false
1124-
isDual = false
1125-
bracketBalance = 0
1195+
break
11261196

11271197
elseif tokType == "pp_entry" then
11281198
errorInFile(
@@ -1134,9 +1204,6 @@ local function _processFileOrString(params, isFile)
11341204

11351205
else
11361206
table.insert(metaParts, tok.representation)
1137-
if isDual then
1138-
table.insert(tokensToProcess, tok)
1139-
end
11401207

11411208
if tokType == "punctuation" and isAny(tok.value, "(","{","[") then
11421209
bracketBalance = bracketBalance+1
@@ -1155,14 +1222,24 @@ local function _processFileOrString(params, isFile)
11551222
end
11561223
end
11571224

1225+
tokenIndex = tokenIndex+1
1226+
end
1227+
end
1228+
1229+
while true do
1230+
local tok = tokens[tokenIndex]
1231+
if not tok then break end
1232+
1233+
local tokType = tok.type
1234+
11581235
-- Meta block or start of meta line.
11591236
--------------------------------
11601237

11611238
-- Meta block. Examples:
11621239
-- !( function sum(a, b) return a+b; end )
11631240
-- local text = !("Hello, mr. "..getName())
11641241
-- _G.!!("myRandomGlobal"..math.random(5)) = 99
1165-
elseif
1242+
if
11661243
tokType == "pp_entry"
11671244
and tokens[tokenIndex+1]
11681245
and tokens[tokenIndex+1].type == "punctuation"
@@ -1174,10 +1251,7 @@ local function _processFileOrString(params, isFile)
11741251
local doOutputLua = tok.double
11751252
tokenIndex = tokenIndex+2 -- Jump past "!(" or "!!(".
11761253

1177-
if tokensToProcess[1] then
1178-
outputTokens(tokensToProcess)
1179-
tokensToProcess = {}
1180-
end
1254+
flushTokensToProcess()
11811255

11821256
local tokensInBlock = {}
11831257
local depth = 1
@@ -1244,48 +1318,22 @@ local function _processFileOrString(params, isFile)
12441318
-- local bar = foo..!(foo)
12451319
--
12461320
elseif tokType == "pp_entry" then
1247-
-- elseif startOfLine and tokType == "pp_entry" then
1248-
isMeta = true
1249-
isDual = tok.double
1250-
metaStartLine = tok.line
1251-
1252-
if tokensToProcess[1] then
1253-
outputTokens(tokensToProcess)
1254-
tokensToProcess = {}
1255-
end
1321+
flushTokensToProcess()
12561322

1257-
elseif tokType == "pp_entry" then
1258-
if tok.double then
1259-
errorInFile(luaUnprocessed, pathIn, tok.position, "Parser", "Unexpected double preprocessor token.")
1260-
else
1261-
errorInFile(luaUnprocessed, pathIn, tok.position, "Parser", "Unexpected preprocessor token.")
1262-
end
1323+
tokenIndex = tokenIndex+1
1324+
processMetaLine(tok.double, tok.line)
12631325

12641326
-- Non-meta.
12651327
--------------------------------
1266-
1267-
--[[ Potential start of meta line. (Must be at the start of the line, possibly after whitespace.) UPDATE: No longer true.
1268-
elseif tokType == "whitespace" or (tokType == "comment" and not tok.long) then
1269-
table.insert(tokensToProcess, tok)
1270-
1271-
if not (tokType == "whitespace" and not tok.value:find("\n", 1, true)) then
1272-
startOfLine = true
1273-
end
1274-
--]]
1275-
12761328
else
12771329
table.insert(tokensToProcess, tok)
1278-
-- startOfLine = false
12791330
end
12801331
--------------------------------
12811332

12821333
tokenIndex = tokenIndex+1
12831334
end
12841335

1285-
if tokensToProcess[1] then
1286-
outputTokens(tokensToProcess)
1287-
tokensToProcess = {}
1288-
end
1336+
flushTokensToProcess()
12891337

12901338
-- Run metaprogram.
12911339
--==============================================================

test.lua2p

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,19 @@ _G.!!("global"..math.random(5)) = 99
4444
]
4545
local z = !(z)
4646

47-
-- Dual code (outputs both both preprocessor code and normal Lua).
47+
!local justTheLetterD = "d"
48+
49+
-- Dual code. (Outputs both both preprocessor code and normal Lua. Can only be used for assignment statements.)
4850
!!local alpha = "[%a_]"
49-
!!local alphanum = "[%w_]"
50-
!!local num = "%d"
51-
local ident = !(alpha..alphanum)
51+
!!local alphanum = "[%a%d_]"
52+
!!local num = "%"..justTheLetterD -- The value expression is evaluated in the metaprogram before outputted to the final program.
53+
!!local ident = alpha..alphanum.."*"
54+
local funcCall = !(ident.."%(.-%)")
5255

53-
local s = [[:: 2 * hello5(){ ... }]]
54-
print(s:match(ident)) -- "hello5"
55-
print(s:match(num)) -- "2"
56+
local s = [[:: 2 * hello5( foo ){ ... }]]
57+
print(s:match(ident)) -- "hello5"
58+
print(s:match(num)) -- "2"
59+
print(s:match(funcCall)) -- "hello5( foo )"
5660

5761
print("The end.")
5862

0 commit comments

Comments
 (0)