Skip to content

Commit

Permalink
Macro callees can now have lookups using square brackets.
Browse files Browse the repository at this point in the history
  • Loading branch information
ReFreezed committed Jul 12, 2021
1 parent 4670621 commit 7999e0a
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 41 deletions.
99 changes: 70 additions & 29 deletions preprocess.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1937,15 +1937,9 @@ local function doLateExpansionsResources(tokensToExpand, fileBuffers, params, st
local identTok = tokNext
tokNext, iNext = getNextUsableToken(tokenStack, iNext-1, nil, -1)

if isTokenAndNotNil(tokNext, "punctuation", "(") then
-- Apply the same 'ambiguous syntax' rule as Lua.
if isTokenAndNotNil(tokenStack[iNext+1], "whitespace") and tokenStack[iNext+1].value:find"\n" then
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Ambiguous syntax near '(' - part of macro, or new statement?")
end

elseif not (tokNext and (
if not (tokNext and (
tokNext.type == "string"
or (tokNext.type == "punctuation" and isAny(tokNext.value, "{",".",":"))
or (tokNext.type == "punctuation" and isAny(tokNext.value, "(","{",".",":","["))
)) then
errorAtToken(fileBuffers, identTok, identTok.position+#identTok.representation, "Macro", "Syntax error: Expected '(' after macro name '%s'.", identTok.value)
end
Expand Down Expand Up @@ -2044,6 +2038,10 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
end
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="("}, macroStartTok))

--
-- Callee.
--

-- Add 'ident' for start of (or whole) callee.
local tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack-1, nil, -1)
if not isTokenAndNotNil(tokNext, "identifier") then
Expand All @@ -2054,29 +2052,67 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
tableInsert(tokens, tokNext)
local lastCalleeTok = tokNext

-- Maybe add '.field:method' for end of callee.
-- @Incomplete: @@name[expr]()
-- Maybe add '.field[expr]:method' for rest of callee.
tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)

while isTokenAndNotNil(tokNext, "punctuation") and (tokNext.value == "." or tokNext.value == ":") do
local punctTok = tokNext
local isMethodCall = (punctTok.value == ":")
while tokNext do
if isToken(tokNext, "punctuation", ".") or isToken(tokNext, "punctuation", ":") then
local punctTok = tokNext
popTokens(tokenStack, iNext) -- '.' or ':'
tableInsert(tokens, tokNext)

popTokens(tokenStack, iNext) -- '.' or ':'
tableInsert(tokens, tokNext)
tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)
if not tokNext then
errorAfterToken(fileBuffers, punctTok, "Macro", "Syntax error: Expected an identifier after '%s'.", punctTok.value)
end
popTokens(tokenStack, iNext) -- the identifier
tableInsert(tokens, tokNext)

tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)
if not tokNext then
errorAfterToken(fileBuffers, punctTok, "Macro", "Syntax error: Expected an identifier after '%s'.", punctTok.value)
end
popTokens(tokenStack, iNext) -- the identifier
tableInsert(tokens, tokNext)
lastCalleeTok = tokNext
lastCalleeTok = tokNext
tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)

tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)
if isMethodCall then break end
if punctTok.value == ":" then break end

elseif isToken(tokNext, "punctuation", "[") then
local punctTok = tokNext
popTokens(tokenStack, iNext) -- '['
tableInsert(tokens, tokNext)

local bracketBalance = 1

while true do
tokNext = tableRemove(tokenStack) -- anything
if not tokNext then
errorAtToken(
fileBuffers, punctTok, nil, "Macro",
"Syntax error: Could not find matching bracket before EOF. (Macro starts %s)",
getRelativeLocationText(macroStartTok, punctTok)
)
end
tableInsert(tokens, tokNext)

if isToken(tokNext, "punctuation", "[") then
bracketBalance = bracketBalance + 1
elseif isToken(tokNext, "punctuation", "]") then
bracketBalance = bracketBalance - 1
if bracketBalance == 0 then break end
elseif tokNext.type:find"^pp_" then
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Preprocessor token inside metaprogram/macro name expression (starting %s).", getRelativeLocationText(macroStartTok, tokNext))
end
end

lastCalleeTok = tokNext
tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)

else
break
end
end

--
-- Arguments.
--

-- @insert identifier " ... "
if isTokenAndNotNil(tokNext, "string") then
local stringTok = tokNext
Expand Down Expand Up @@ -2172,11 +2208,9 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, tokens[#tokens]))

-- @insert identifier ( argument1, ... )
else
if not isTokenAndNotNil(tokNext, "punctuation", "(") then
printErrorTraceback("Internal error.")
errorAfterToken(fileBuffers, lastCalleeTok, "Macro", "Syntax error: Expected '(' after macro name.")
elseif isTokenAndNotNil(tokenStack[iNext+1], "whitespace") and tokenStack[iNext+1].value:find"\n" then
elseif isTokenAndNotNil(tokNext, "punctuation", "(") then
-- Apply the same 'ambiguous syntax' rule as Lua.
if isTokenAndNotNil(tokenStack[iNext+1], "whitespace") and tokenStack[iNext+1].value:find"\n" then
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Ambiguous syntax near '(' - part of macro, or new statement?")
end

Expand Down Expand Up @@ -2313,8 +2347,15 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes

-- Add ')' for end of call.
tableInsert(tokens, tableRemove(tokenStack)) -- ')'

else
errorAfterToken(fileBuffers, lastCalleeTok, "Macro", "Syntax error: Expected '(' after macro name.")
end

--
-- End.
--

-- Add ')' for end of preprocessor block.
tableInsert(tokens, newTokenAt({type="punctuation", value=")", representation=")"}, tokens[#tokens]))
end
Expand Down
4 changes: 4 additions & 0 deletions tests/quickTest.lua2p
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ local b = @@PASS_THROUGH( !!("2") )
local c = @@PASS_THROUGH{ 1 + !(2) + 3 }
local d = @@PASS_THROUGH(@@PASS_THROUGH{@@PASS_THROUGH( !!("1")!!("+")!!("2") )})

!local t = {field={object={method=function(obj, lua) return lua end}}}
!local keys = {"object"}
local n = @@t.field[keys[1]]:method(58)



-- Misc.
Expand Down
27 changes: 22 additions & 5 deletions tests/quickTest.output.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,37 +56,54 @@ print(s:match(funcCall)) -- "hello5( foo )"

-- File inserts.

local uhh = 7
print("Final program - uhh: "..uhh)
local uhh1 = 7
local uhh2 = 7 -- @@ means the same as @insert.
print("Final program - uhh: "..uhh1..", "..uhh2)



-- Macros.

print("Blargh!")
print("Blargh!")
-- !@insert BLARGH() -- Error: Macro inside metaprogram.

print(string.format('We are at %s:%d!', "misc/quickTest.lua2p", 87))
print(string.format('We are at %s:%d!', "tests/quickTest.lua2p", 95))

local ok = 1==1

if not (ok) then error("Oh "..tonumber("7",10).." noes!") end
-- @insert ASSERT ( 1 1 ) -- Syntax error!
-- @insert ASSERT ( ok , ) -- Syntax error!
-- @insert ASSERT ( , ok ) -- Syntax error!
-- @insert ASSERT ( --[[]] , ok ) -- Syntax error!

local s = "foo"
local t = { 496, b=true } -- @@func() means the same as @insert func().
local t = { 496, b=true } -- @@ means the same as @insert.

-- local s = @@PASS_THROUGH `foo`

local f = function(a, b)
while true do
repeat until arePlanetsAligned("mars", "jupiter")
-- repeat until arePlanetsAligned(`mars`, `jupiter`)
break
end
return "", nil
end

local a = 1+2*3
local b = 2
local c = { 1 + 2 + 3 }
local d = {1+2}

local n = 58



-- Misc.
print("dataFromCommandLine: Hello, world!")
print("This file and line: tests/quickTest.lua2p:133")
print("This file and line: tests/quickTest.lua2p:145")

for i = 1, 3 do
do
Expand Down
20 changes: 13 additions & 7 deletions tests/suite.lua
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,19 @@ doTest("Macros", function()
]]})
assertCodeOutput(luaOut, [[t = {}]])

-- Macro with lookups.
-- Macro name with lookups.
local luaOut = assert(pp.processString{ code=[[
!t = {o={m=function(o,v) return v end}}
v = @@t.o:m(foo)
!t, a = {t={o={m=function(o,v) return v end}}}, {"o"}
v = @@t.t[ a[1] ]:m(foo)
]]})
assertCodeOutput(luaOut, [[v = foo]])

assert(not pp.processString{ code=[[
!t = {o={m=function(o,v) return v end}}
!t = {f=function(v) return v end}
v = @@t.(foo)
]]})
assert(not pp.processString{ code=[[
!t = {o={m=function(o,v) return v end}}
!t = {o={m=function(o,v) return v end}}
v = @@t.o:(foo)
]]})

Expand Down Expand Up @@ -325,13 +325,19 @@ doTest("Macros", function()
v = @@VOID
() 1
]]})

assert(not pp.processString{ code=[[
!t = {o={m=function(o,v) return v end}}
!t = {o={m=function(o,v) return v end}}
v = @@t.o:m
(foo)
]]})

-- Invalid: Preprocessor code inside macro name expression.
assert(not pp.processString{ code=[[
!function bad() return "f" end
!t = {f=function(v) return v end}
v = @@t[ @@bad() ](foo)
]]})

-- Invalid: Bad macro arguments format.
assert(not pp.processString{ code=[[ @insert type[] ]]})
assert(not pp.processString{ code=[[ @insert type + 1 ]]})
Expand Down

0 comments on commit 7999e0a

Please sign in to comment.