Skip to content

Commit be7cf95

Browse files
committed
!() and !!() work in parenthesis macros.
1 parent 16174dd commit be7cf95

File tree

2 files changed

+152
-69
lines changed

2 files changed

+152
-69
lines changed

preprocess.lua

Lines changed: 128 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -748,18 +748,20 @@ end
748748

749749

750750

751-
function _concatTokens(tokens, lastLn, addLineNumbers)
751+
-- luaString = _concatTokens( tokens, lastLn=nil, addLineNumbers, fromIndex=1, toIndex=#tokens )
752+
function _concatTokens(tokens, lastLn, addLineNumbers, i1, i2)
752753
local parts = {}
753754

754755
if addLineNumbers then
755-
for _, tok in ipairs(tokens) do
756-
lastLn = maybeOutputLineNumber(parts, tok, lastLn)
756+
for i = (i1 or 1), (i2 or #tokens) do
757+
local tok = tokens[i]
758+
lastLn = maybeOutputLineNumber(parts, tok, lastLn)
757759
tableInsert(parts, tok.representation)
758760
end
759761

760762
else
761-
for i, tok in ipairs(tokens) do
762-
parts[i] = tok.representation
763+
for i = (i1 or 1), (i2 or #tokens) do
764+
tableInsert(parts, tokens[i].representation)
763765
end
764766
end
765767

@@ -1726,7 +1728,7 @@ end
17261728
-- Concatinate tokens by their representations.
17271729
-- luaString = concatTokens( tokens )
17281730
function metaFuncs.concatTokens(tokens)
1729-
return _concatTokens(tokens)
1731+
return _concatTokens(tokens, nil, false, nil, nil)
17301732
end
17311733

17321734
-- Extra stuff used by the command line program:
@@ -1736,9 +1738,18 @@ metaFuncs.tryToFormatError = tryToFormatError
17361738

17371739
for k, v in pairs(metaFuncs) do metaEnv[k] = v end
17381740

1739-
metaEnv.__LUA = metaEnv.outputLua
1740-
metaEnv.__VAL = metaEnv.outputValue
1741-
metaEnv.__TOLUA = function(v) return (assert(toLua(v))) end
1741+
metaEnv.__LUA = metaEnv.outputLua
1742+
metaEnv.__VAL = metaEnv.outputValue
1743+
1744+
function metaEnv.__TOLUA(v)
1745+
return (assert(toLua(v)))
1746+
end
1747+
function metaEnv.__ASSERTLUA(lua)
1748+
if type(lua) ~= "string" then
1749+
error("Value is not Lua code.", 2)
1750+
end
1751+
return lua
1752+
end
17421753

17431754

17441755

@@ -1962,6 +1973,20 @@ local function doLateExpansionsResources(tokensToExpand, fileBuffers, params, st
19621973
return tokens
19631974
end
19641975

1976+
-- luaString = insertTokensAsStringLiteral( tokens, tokensToConcat, locationToken )
1977+
local function insertTokensAsStringLiteral(tokens, tokensToConcat, locationTok)
1978+
local lua = _concatTokens(tokensToConcat, nil, false, nil, nil)
1979+
1980+
tableInsert(tokens, newTokenAt({
1981+
type = "string",
1982+
value = lua,
1983+
representation = F("%q", lua):gsub("\n", "n"),
1984+
long = false,
1985+
}, locationTok))
1986+
1987+
return lua
1988+
end
1989+
19651990
local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats)
19661991
--
19671992
-- Expand expressions:
@@ -2008,7 +2033,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20082033
local stringTok = tokNext
20092034
popTokens(tokenStack, iNext) -- the string
20102035

2011-
-- Add "!!(ident".
2036+
-- Add '!!(ident'.
20122037
tableInsert(tokens, newTokenAt({type="pp_entry", value="!!", representation="!!", double=true}, ppKeywordTok))
20132038
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, ppKeywordTok))
20142039
tableInsert(tokens, identTok)
@@ -2017,7 +2042,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20172042
stringTok.representation = F("%q", stringTok.value):gsub("\n", "n")
20182043
tableInsert(tokens, stringTok)
20192044

2020-
-- Add ")".
2045+
-- Add ')'.
20212046
tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, ppKeywordTok))
20222047

20232048
-- @insert identifier { ... }
@@ -2028,7 +2053,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20282053
-- (Similar code as `@insert identifier()` below.)
20292054
--
20302055

2031-
-- Add "!!(ident".
2056+
-- Add '!!(ident'.
20322057
tableInsert(tokens, newTokenAt({type="pp_entry", value="!!", representation="!!", double=true}, ppKeywordTok))
20332058
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, ppKeywordTok))
20342059
tableInsert(tokens, identTok)
@@ -2046,7 +2071,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20462071
errorAtToken(fileBuffers, tableStartTok, nil, "Macro", "Syntax error: Could not find end of table constructor before EOF.")
20472072

20482073
elseif tok.type:find"^pp_" then
2049-
errorAtToken(fileBuffers, tok, nil, "Macro", "Non-simple preprocessor code not supported in macros. (Macro starts %s)", getRelativeLocationText(ppKeywordTok, tok))
2074+
errorAtToken(fileBuffers, tok, nil, "Macro", "Unsupported preprocessor code in macro. (Macro starts %s)", getRelativeLocationText(ppKeywordTok, tok))
20502075

20512076
elseif bracketDepth == 1 and isToken(tok, "punctuation", "}") then
20522077
tableInsert(argTokens, tableRemove(tokenStack)) -- '}'
@@ -2062,11 +2087,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20622087
end
20632088
end
20642089

2065-
local parts = {}
2066-
for i, argTok in ipairs(argTokens) do
2067-
parts[i] = argTok.representation
2068-
end
2069-
local argStr = table.concat(parts)
2090+
local argStr = _concatTokens(argTokens, nil, false, nil, nil)
20702091

20712092
local chunk, err = loadLuaString("return "..argStr, "@")
20722093
if not chunk then
@@ -2083,7 +2104,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20832104
long = false,
20842105
}, tableStartTok))
20852106

2086-
-- Add ")".
2107+
-- Add ')'.
20872108
tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, ppKeywordTok))
20882109

20892110
-- @insert identifier ( argument1, ... )
@@ -2096,7 +2117,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
20962117
local parensStartTok = tokNext
20972118
popTokens(tokenStack, iNext) -- '('
20982119

2099-
-- Add "!!(ident(".
2120+
-- Add '!!(ident('.
21002121
tableInsert(tokens, newTokenAt({type="pp_entry", value="!!", representation="!!", double=true}, ppKeywordTok))
21012122
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="(" }, ppKeywordTok))
21022123
tableInsert(tokens, identTok)
@@ -2110,25 +2131,83 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
21102131
local lastArgSeparatorTok = nil
21112132

21122133
for argNum = 1, 1/0 do
2113-
local argStartTok = tokenStack[#tokenStack]
2114-
local argTokens = {}
2115-
local depthStack = {}
2116-
popUseless(tokenStack)
2134+
-- Add argument separator.
2135+
if lastArgSeparatorTok then
2136+
tableInsert(tokens, lastArgSeparatorTok)
2137+
end
21172138

21182139
-- Collect tokens for this arg.
21192140
-- We're looking for the next comma at depth 0 or closing ')'.
2141+
local argNonPpStartTok = tokenStack[#tokenStack]
2142+
local argNonPpTokens = {}
2143+
local depthStack = {}
2144+
local insertedPpEntry = false
2145+
popUseless(tokenStack) -- Trim leading useless tokens.
2146+
21202147
while true do
2121-
tok = tokenStack[#tokenStack]
2148+
local len = #tokenStack
2149+
tok = tokenStack[len]
21222150

21232151
if not tok then
21242152
errorAtToken(fileBuffers, parensStartTok, nil, "Macro", "Syntax error: Could not find end of argument list before EOF.")
21252153

2154+
-- Preprocessor block in macro.
2155+
elseif tok.type == "pp_entry" and isTokenAndNotNil(tokenStack[len-1], "punctuation", "(") then
2156+
local ppEntryTok = tok
2157+
tableRemove(tokenStack) -- '!' or '!!'
2158+
2159+
if insertedPpEntry then
2160+
tableInsert(tokens, newTokenAt({type="punctuation", value="..", representation=".."}, argNonPpStartTok))
2161+
end
2162+
if argNonPpTokens[1] then
2163+
insertTokensAsStringLiteral(tokens, argNonPpTokens, argNonPpStartTok)
2164+
argNonPpTokens = {}
2165+
tableInsert(tokens, newTokenAt({type="punctuation", value="..", representation=".."}, ppEntryTok))
2166+
end
2167+
2168+
local ident = (ppEntryTok.value == "!") and "__TOLUA" or "__ASSERTLUA"
2169+
tableInsert(tokens, newTokenAt({type="identifier", value=ident, representation=ident}, ppEntryTok))
2170+
tableInsert(tokens, tableRemove(tokenStack)) -- '('
2171+
local exprStartTokIndex = #tokens
2172+
2173+
local parensDepth = 1
2174+
2175+
while true do
2176+
tok = tableRemove(tokenStack) -- anything
2177+
if not tok then
2178+
errorAtToken(fileBuffers, ppEntryTok, nil, "Parser", "Missing end of preprocessor block.")
2179+
end
2180+
tableInsert(tokens, tok)
2181+
2182+
if isToken(tok, "punctuation", "(") then
2183+
parensDepth = parensDepth + 1
2184+
elseif isToken(tok, "punctuation", ")") then
2185+
parensDepth = parensDepth - 1
2186+
if parensDepth == 0 then break end
2187+
elseif tok.type:find"^pp_" then
2188+
errorAtToken(fileBuffers, tok, nil, "Parser", "Preprocessor token inside metaprogram (starting %s).", getRelativeLocationText(ppEntryTok, tok))
2189+
end
2190+
end
2191+
2192+
local chunk, err = loadLuaString("return ".._concatTokens(tokens, nil, false, exprStartTokIndex, nil), "@")
2193+
if not chunk then
2194+
errorAtToken(fileBuffers, tokens[exprStartTokIndex+1], nil, "Macro", "Syntax error: Invalid expression in preprocessor block.")
2195+
-- err = err:gsub("^:%d+: ", "")
2196+
-- errorAtToken(fileBuffers, tokens[exprStartTokIndex+1], nil, "Macro", "Syntax error: Invalid expression in preprocessor block. (%s)", err)
2197+
end
2198+
2199+
insertedPpEntry = true
2200+
argNonPpStartTok = tokenStack[#tokenStack] -- Could be nil, but that should be fine I think.
2201+
2202+
-- Other preprocessor code in macro.
21262203
elseif tok.type:find"^pp_" then
2127-
errorAtToken(fileBuffers, tok, nil, "Macro", "Non-simple preprocessor code not supported in macros. (Macro starts %s)", getRelativeLocationText(ppKeywordTok, tok))
2204+
errorAtToken(fileBuffers, tok, nil, "Macro", "Unsupported preprocessor code in macro. (Macro starts %s)", getRelativeLocationText(ppKeywordTok, tok))
21282205

2206+
-- End of argument.
21292207
elseif not depthStack[1] and (isToken(tok, "punctuation", ",") or isToken(tok, "punctuation", ")")) then
21302208
break
21312209

2210+
-- Normal token.
21322211
else
21332212
if isToken(tok, "punctuation", "(") then
21342213
tableInsert(depthStack, {startToken=tok, [1]="punctuation", [2]=")"})
@@ -2161,41 +2240,34 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
21612240
tableRemove(depthStack)
21622241
end
21632242

2164-
tableInsert(argTokens, tableRemove(tokenStack)) -- anything
2243+
tableInsert(argNonPpTokens, tableRemove(tokenStack)) -- anything
21652244
end
21662245
end
21672246

2168-
popUseless(argTokens)
2169-
if not argTokens[1] then
2170-
errorAtToken(fileBuffers, argStartTok, nil, "Macro", "Syntax error: Expected argument #%d.", argNum)
2171-
end
2247+
-- Add last part of argument value.
2248+
popUseless(argNonPpTokens) -- Trim trailing useless tokens.
21722249

2173-
local parts = {}
2174-
for i, argTok in ipairs(argTokens) do
2175-
parts[i] = argTok.representation
2176-
end
2177-
local argStr = table.concat(parts)
2250+
if argNonPpTokens[1] then
2251+
if insertedPpEntry then
2252+
tableInsert(tokens, newTokenAt({type="punctuation", value="..", representation=".."}, argNonPpStartTok))
2253+
end
2254+
local argStr = insertTokensAsStringLiteral(tokens, argNonPpTokens, argNonPpStartTok)
21782255

2179-
local chunk, err = loadLuaString("return ("..argStr..")", "@")
2180-
if not chunk then
2181-
errorAtToken(fileBuffers, argTokens[1], nil, "Macro", "Syntax error: Invalid expression for argument #%d.", argNum)
2182-
-- err = err:gsub("^:%d+: ", "")
2183-
-- errorAtToken(fileBuffers, argTokens[1], nil, "Macro", "Syntax error: Invalid expression for argument #%d. (%s)", argNum, err)
2184-
end
2256+
if not insertedPpEntry then
2257+
local chunk, err = loadLuaString("return ("..argStr..")", "@")
21852258

2186-
-- Add argument separator.
2187-
if lastArgSeparatorTok then
2188-
tableInsert(tokens, lastArgSeparatorTok)
2189-
end
2259+
if not chunk then
2260+
errorAtToken(fileBuffers, argNonPpStartTok, nil, "Macro", "Syntax error: Invalid expression for argument #%d.", argNum)
2261+
-- err = err:gsub("^:%d+: ", "")
2262+
-- errorAtToken(fileBuffers, argNonPpStartTok, nil, "Macro", "Syntax error: Invalid expression for argument #%d. (%s)", argNum, err)
2263+
end
2264+
end
21902265

2191-
-- Add argument value.
2192-
tableInsert(tokens, newTokenAt({
2193-
type = "string",
2194-
value = argStr,
2195-
representation = F("%q", argStr):gsub("\n", "n"),
2196-
long = false,
2197-
}, argStartTok))
2266+
elseif not insertedPpEntry then
2267+
errorAtToken(fileBuffers, argNonPpStartTok, nil, "Macro", "Syntax error: Expected argument #%d.", argNum)
2268+
end
21982269

2270+
-- Do next argument or finish arguments.
21992271
if isLastToken(tokenStack, "punctuation", ")") then
22002272
break
22012273
end
@@ -2205,7 +2277,7 @@ local function doLateExpansionsMacros(tokensToExpand, fileBuffers, params, stats
22052277
end--for argNum
22062278
end
22072279

2208-
-- Add "))".
2280+
-- Add '))'.
22092281
tableInsert(tokens, tableRemove(tokenStack)) -- ')'
22102282
tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, ppKeywordTok))
22112283
end
@@ -2306,7 +2378,7 @@ local function _processFileOrString(params, isFile)
23062378
local function flushTokensToProcess()
23072379
if not tokensToProcess[1] then return end
23082380

2309-
local lua = _concatTokens(tokensToProcess, ln, params.addLineNumbers)
2381+
local lua = _concatTokens(tokensToProcess, ln, params.addLineNumbers, nil, nil)
23102382
local luaMeta
23112383

23122384
if isDebug then
@@ -2580,7 +2652,7 @@ local function _processFileOrString(params, isFile)
25802652
tokenIndex = tokenIndex+1
25812653
end
25822654

2583-
local metaBlock = _concatTokens(tokensInBlock, nil, params.addLineNumbers)
2655+
local metaBlock = _concatTokens(tokensInBlock, nil, params.addLineNumbers, nil, nil)
25842656

25852657
if loadLuaString("return("..metaBlock..")") then
25862658
tableInsert(metaParts, (doOutputLua and "__LUA((" or "__VAL(("))

tests/quickTest.lua2p

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,17 @@ print(s:match(funcCall)) -- "hello5( foo )"
7979
@insert "tests/quickTest.luapart"
8080
print("Metaprogram - someString: "..someString)
8181
)
82-
local uhh = !(@insert"tests/quickTest.txt")
83-
print("Final program - uhh: "..uhh)
82+
local uhh1 = !(@insert"tests/quickTest.txt")
83+
local uhh2 = !(@@"tests/quickTest.txt") -- @@ means the same as @insert.
84+
print("Final program - uhh: "..uhh1..", "..uhh2)
8485

8586

8687

8788
-- Macros.
8889
!local function BLARGH() return 'print("Blargh!")' end
8990
@insert BLARGH()
90-
-- !@insert BLARGH() -- Error: Preprocessor token inside metaprogram.
91-
-- @insert BLARGH( @insert BLARGH() ) -- Error: Preprocessor code not supported in macros.
92-
-- @insert BLARGH(function() return 1,2 end) -- Syntax error! Caused by the comma in the return statement. (This would work if the preprocessor was smarter.)
91+
-- !@insert BLARGH() -- Error: Macro inside metaprogram.
92+
-- @insert BLARGH( @insert BLARGH() ) -- Error: Nested macros. (This could be supported e.g. if we parsed macros from right to left.)
9393

9494
!local function WHERE(filename, ln) return "print(string.format('We are at %s:%d!', "..filename..", "..ln.."))" end
9595
@insert WHERE(@file, @line)
@@ -116,11 +116,11 @@ local ok = 1==1
116116

117117
!local function PASS_THROUGH(lua) return lua end
118118
local s = @insert PASS_THROUGH "foo"
119-
local t = @@ PASS_THROUGH { 496, b=true } -- @@func() means the same as @insert func().
119+
local t = @@ PASS_THROUGH { 496, b=true } -- @@ means the same as @insert.
120120

121-
-- local s = @insert PASS_THROUGH `foo`
121+
-- local s = @@PASS_THROUGH `foo`
122122

123-
local f = @insert PASS_THROUGH(function(a, b)
123+
local f = @@PASS_THROUGH(function(a, b)
124124
while true do
125125
repeat until arePlanetsAligned("mars", "jupiter")
126126
-- repeat until arePlanetsAligned(`mars`, `jupiter`)
@@ -130,11 +130,22 @@ local f = @insert PASS_THROUGH(function(a, b)
130130
end)
131131

132132
local x = @@PASS_THROUGH( @@"tests/quickTest.txt" )
133-
-- local y = @@PASS_THROUGH( 1 + !!( "3" ) * 8 )
134-
-- local z = @@PASS_THROUGH( 1 + !( 3 ) * 8 )
135-
136-
-- local no = @@PASS_THROUGH( @insert 654 ) -- Error!
137-
-- local no = @@PASS_THROUGH( @insert aaaaa ) -- Error!
133+
local y = @@PASS_THROUGH( 1 + !!( "3" ) * 8 )
134+
local z = @@PASS_THROUGH( 1 + !( 3 ) * 8 )
135+
136+
local a = @@PASS_THROUGH( !(2) )
137+
local b = @@PASS_THROUGH( !(2) + 3 )
138+
local c = @@PASS_THROUGH( 1 + !(2) )
139+
local d = @@PASS_THROUGH( 1 + !(2) + 3 )
140+
141+
local m = @@PASS_THROUGH( !!("1")!!("+")!!("2") )
142+
143+
-- local no = @@PASS_THROUGH( @@654 ) -- Error!
144+
-- local no = @@PASS_THROUGH( @@aaaaa ) -- Error!
145+
-- local no = @@PASS_THROUGH( !() ) -- Error!
146+
-- local no = @@PASS_THROUGH( !!() ) -- Error!
147+
-- local no = @@PASS_THROUGH( !!( 1 ) ) -- Error!
148+
-- local no = @@PASS_THROUGH( !!( !(3) ) ) -- Error!
138149

139150

140151

0 commit comments

Comments
 (0)