Skip to content

Commit

Permalink
Macro callees can now have simple lookups.
Browse files Browse the repository at this point in the history
Link to new website.
  • Loading branch information
ReFreezed committed Jul 12, 2021
1 parent 1d5d2fe commit 4670621
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 33 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright © 2018 Marcus 'ReFreezed' Thunström
Copyright © 2018-2021 Marcus 'ReFreezed' Thunström

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ A separate [command line program](preprocess-cl.lua) is available too.
- [Usage](#usage)
- [Library](#preprocess-files-using-the-library)
- [Command Line](#preprocess-files-from-the-command-line)
- [Documentation](https://github.com/ReFreezed/LuaPreprocess/wiki)
- [Documentation](http://luapreprocess.refreezed.com/docs/)
- [Help](#help)



## Example Program
The exclamation mark (!) is used to indicate what code is part of the metaprogram.

The exclamation mark (`!`) is used to indicate what code is part of the metaprogram.
([See screenshot of processing steps with highlighting](https://raw.githubusercontent.com/ReFreezed/LuaPreprocess/master/misc/processingSteps.png))

```lua
Expand Down Expand Up @@ -75,6 +76,7 @@ local text = !("Precalculated hash: "..getHash())
```

#### Output

```lua
-- Normal Lua.
local n = 0
Expand Down Expand Up @@ -111,13 +113,15 @@ See the [examples folder](examples) for more.


## Usage
First you need [Lua](https://www.lua.org/versions.html) installed on your system. (Binaries can be

First you need [Lua](https://www.lua.org/) installed on your system. (Binaries can be
downloaded from [LuaBinaries via SourceForge](https://sourceforge.net/projects/luabinaries/files/5.1.5/Tools%20Executables/)
if you don't want to, or can't, compile Lua from source. For Windows I can recommend installing
[LuaForWindows](https://github.com/rjpcomputing/luaforwindows) which is a "batteries included" Lua package.)


### Preprocess Files Using the Library

```lua
local pp = require("preprocess")

Expand All @@ -134,20 +138,22 @@ end
print("Lines of code processed: "..info.lineCount)
```

See the [wiki](https://github.com/ReFreezed/LuaPreprocess/wiki)
See the [website](http://luapreprocess.refreezed.com/docs/)
or the top of [preprocess.lua](preprocess.lua) for documentation.


### Preprocess Files from the Command Line

#### Windows

```batch
Preprocess.cmd [options] filepath1 [filepath2 ...]
OR
Preprocess.cmd --outputpaths [options] inputpath1 outputpath1 [inputpath2 outputpath2 ...]
```

#### Any System

```batch
lua preprocess-cl.lua [options] filepath1 [filepath2 ...]
OR
Expand All @@ -156,20 +162,22 @@ lua preprocess-cl.lua --outputpaths [options] inputpath1 outputpath1 [inputpath2

If a filepath is, for example, `C:/MyApp/app.lua2p` then LuaPreprocess will write the processed file to `C:/MyApp/app.lua`.

See the [wiki](https://github.com/ReFreezed/LuaPreprocess/wiki/Command-Line),
See the [website](http://luapreprocess.refreezed.com/docs/command-line/),
or the top of [preprocess-cl.lua](preprocess-cl.lua)
and [preprocess.lua](preprocess.lua), for the options and more documentation.



## Documentation
- [Wiki](https://github.com/ReFreezed/LuaPreprocess/wiki)

- [Website](http://luapreprocess.refreezed.com/docs/)
- Library: See the top of [preprocess.lua](preprocess.lua)
- Command line: See the top of [preprocess-cl.lua](preprocess-cl.lua)



## Help

Got a question?
Look if someone has asked the question in the [issue tracker](https://github.com/ReFreezed/LuaPreprocess/issues?q=is%3Aissue),
or [create a new issue](https://github.com/ReFreezed/LuaPreprocess/issues/new).
Expand Down
2 changes: 1 addition & 1 deletion preprocess-cl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exec lua "$0" "$@"
--= Requires preprocess.lua to be in the same folder!
--=
--= License: MIT (see the bottom of this file)
--= Website: https://github.com/ReFreezed/LuaPreprocess
--= Website: http://luapreprocess.refreezed.com/
--=
--= Tested with Lua 5.1, 5.2, 5.3 and 5.4.
--=
Expand Down
73 changes: 48 additions & 25 deletions preprocess.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
--= by Marcus 'ReFreezed' Thunström
--=
--= License: MIT (see the bottom of this file)
--= Website: https://github.com/ReFreezed/LuaPreprocess
--= Documentation: https://github.com/ReFreezed/LuaPreprocess/wiki
--= Website: http://luapreprocess.refreezed.com/
--= Documentation: http://luapreprocess.refreezed.com/docs/
--=
--= Tested with Lua 5.1, 5.2, 5.3 and 5.4.
--=
Expand Down Expand Up @@ -112,7 +112,7 @@
-- Though in this specific case a preprocessor line (without the parenthesis) would be nicer:
!func()
-- For the full documentation, see: https://github.com/ReFreezed/LuaPreprocess/wiki
-- For the full documentation, see: http://luapreprocess.refreezed.com/docs/
--============================================================]]

Expand Down Expand Up @@ -1942,7 +1942,11 @@ local function doLateExpansionsResources(tokensToExpand, fileBuffers, params, st
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 (isTokenAndNotNil(tokNext, "string") or isTokenAndNotNil(tokNext, "punctuation", "{")) then

elseif not (tokNext and (
tokNext.type == "string"
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 @@ -2032,16 +2036,6 @@ end

local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNested)
-- @Robustness: Make sure key tokens came from the same source file.
local tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack-1, nil, -1)

if not isTokenAndNotNil(tokNext, "identifier") then
printErrorTraceback("Internal error.")
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Internal error. (%s)", (tokNext and tokNext.type or "?"))
end

local identTok = tokNext
popTokens(tokenStack, iNext) -- the identifier

-- Add '!!(' for start of preprocessor block.
if isNested then
tableInsert(tokens, newTokenAt({type="identifier", value="__ASSERTLUA", representation="__ASSERTLUA"}, macroStartTok))
Expand All @@ -2050,16 +2044,45 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
end
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="("}, macroStartTok))

-- Add 'ident' for start of (or whole) callee.
local tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack-1, nil, -1)
if not isTokenAndNotNil(tokNext, "identifier") then
printErrorTraceback("Internal error.")
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Internal error. (%s)", (tokNext and tokNext.type or "?"))
end
popTokens(tokenStack, iNext) -- the identifier
tableInsert(tokens, tokNext)
local lastCalleeTok = tokNext

-- Maybe add '.field:method' for end of callee.
-- @Incomplete: @@name[expr]()
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 == ":")

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)
lastCalleeTok = tokNext

tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)
if isMethodCall then break end
end

-- @insert identifier " ... "
if isTokenAndNotNil(tokNext, "string") then
local stringTok = tokNext
popTokens(tokenStack, iNext) -- the string

-- Add 'ident' for start of call. We don't need parenthesis for this macro variant.
tableInsert(tokens, identTok)

-- Note: We don't need parenthesis for this macro variant.
stringTok.value = stringTok.representation
stringTok.representation = F("%q", stringTok.value):gsub("\n", "n")
tableInsert(tokens, stringTok)
Expand All @@ -2072,9 +2095,8 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
-- (Similar code as `@insert identifier()` below.)
--

-- Add 'ident(' for start of call.
tableInsert(tokens, identTok)
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="("}, identTok))
-- Add '(' for start of call.
tableInsert(tokens, newTokenAt({type="punctuation", value="(", representation="("}, tokNext))

-- Collect tokens for the table arg.
-- We're looking for the closing '}'.
Expand Down Expand Up @@ -2153,14 +2175,15 @@ local function expandMacro(tokens, fileBuffers, tokenStack, macroStartTok, isNes
else
if not isTokenAndNotNil(tokNext, "punctuation", "(") then
printErrorTraceback("Internal error.")
errorAtToken(fileBuffers, tokNext, nil, "Macro", "Internal error. (%s)", (tokNext and tokNext.type or "?"))
errorAfterToken(fileBuffers, lastCalleeTok, "Macro", "Syntax error: Expected '(' after macro name.")
elseif 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

local parensStartTok = tokNext
popTokens(tokenStack, iNext) -- '('

-- Add 'ident(' for start of call.
tableInsert(tokens, identTok)
-- Add '(' for start of call.
tableInsert(tokens, parensStartTok)

tokNext, iNext = getNextUsableToken(tokenStack, #tokenStack, nil, -1)
Expand Down Expand Up @@ -2943,7 +2966,7 @@ local pp = {
-- fastStrings = boolean -- [Optional] Force fast serialization of string values. (Non-ASCII characters will look ugly.) (Default: false)
-- validate = boolean -- [Optional] Validate output. (Default: true)
--
-- onInsert = function( name ) -- [Optional] Called for each @insert"name" statement. It's expected to return a Lua code string. By default 'name' is a path to a file to be inserted.
-- onInsert = function( name ) -- [Optional] Called for each @insert"name" instruction. It's expected to return a Lua code string. By default 'name' is a path to a file to be inserted.
-- onBeforeMeta = function( ) -- [Optional] Called before the metaprogram runs.
-- onAfterMeta = function( luaString ) -- [Optional] Here you can modify and return the Lua code before it's written to 'pathOut'.
-- onError = function( error ) -- [Optional] You can use this to get traceback information. 'error' is the same value as what is returned from processFile().
Expand All @@ -2969,7 +2992,7 @@ local pp = {
-- fastStrings = boolean -- [Optional] Force fast serialization of string values. (Non-ASCII characters will look ugly.) (Default: false)
-- validate = boolean -- [Optional] Validate output. (Default: true)
--
-- onInsert = function( name ) -- [Optional] Called for each @insert"name" statement. It's expected to return a Lua code string. By default 'name' is a path to a file to be inserted.
-- onInsert = function( name ) -- [Optional] Called for each @insert"name" instruction. It's expected to return a Lua code string. By default 'name' is a path to a file to be inserted.
-- onBeforeMeta = function( ) -- [Optional] Called before the metaprogram runs.
-- onError = function( error ) -- [Optional] You can use this to get traceback information. 'error' is the same value as the second returned value from processString().
--
Expand Down
22 changes: 22 additions & 0 deletions tests/suite.lua
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,22 @@ doTest("Macros", function()
]]})
assertCodeOutput(luaOut, [[t = {}]])

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

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

-- Function as an argument.
local luaOut = assert(pp.processString{ code=[[
!function ECHO(v) return v end
Expand Down Expand Up @@ -310,6 +326,12 @@ doTest("Macros", function()
() 1
]]})

assert(not pp.processString{ code=[[
!t = {o={m=function(o,v) return v end}}
v = @@t.o:m
(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 4670621

Please sign in to comment.