You can write a parse in Lua using LPEG which matches balanced braces. Be aware that this is line-based, i.e. if you have constructs where the braces span several lines, this will fail.
test.lua
local lpeg = require("lpeg")
local P, S, V = lpeg.P, lpeg.S, lpeg.V
local function err(match, pos, cap)
print(match, pos, cap)
end
local rule = P{"text",
any = 1 - S"{}",
balanced = "{" * (V"any" + V"balanced")^0 * "}",
text = V"any" * (V"balanced" + V"any")^0 * P(-1)
}
local path = arg[1] or error("Usage: " .. arg[0] .. " <filename>")
local file = io.open(path, "r")
local n = 1
for line in file:lines() do
local match = rule:match(line)
if not match then
error("Closing } missing on line " .. n)
end
n = n + 1
end
test.tex
\documentclass{article}
\usepackage{url}
\begin{document}
\url{http://google.com
\end{document}
Running this with texlua
gives
$ texlua test.lua test.tex
test.lua:21: Closing } missing on line 4
It is also possible to automatically add a closing brace on the offending line. To this end, we parse the line and if it fails, we add braces at the end until it succeeds.
test2.lua
local lpeg = require("lpeg")
local P, S, V = lpeg.P, lpeg.S, lpeg.V
local function err(match, pos, cap)
print(match, pos, cap)
end
local rule = P{"text",
any = 1 - S"{}",
balanced = "{" * (V"any" + V"balanced")^0 * "}",
text = V"any" * (V"balanced" + V"any")^0 * P(-1)
}
local path = arg[1] or error("Usage: " .. arg[0] .. " <filename>")
local file = io.open(path, "r")
for line in file:lines() do
local match
while not match do
match = rule:match(line)
if not match then
line = line .. "}"
end
end
print(line)
end
There are some obvious limitations with this approach, namely that the machine cannot know where exactly the closing brace has to be placed.
test2.tex
\documentclass{article}
\usepackage{url}
\begin{document}
Use \url{http://google.com to reach Google search
\end{document}
Running against the example above gives syntactically correct but semantically wrong output:
$ texlua test2.lua test2.tex
\documentclass{article}
\usepackage{url}
\begin{document}
Use \url{http://google.com to reach Google search}
\end{document}
This is not fixable using an automated approach. Only the user can know where the closing brace has to be placed to be semantically correct.
\url
that you show) and a\newcommand
that is spread across multiple lines, say? – Werner Aug 23 '19 at 4:56