1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-07 13:11:28 +00:00

Formatter for Lua/NSE code.

This commit is contained in:
batrick
2012-08-11 02:45:14 +00:00
parent 1c328a01a4
commit b58d4c92d1
3 changed files with 420 additions and 0 deletions

8
docs/style/README Normal file
View File

@@ -0,0 +1,8 @@
lua-format:
This bash script uses lua-format.lua to indent and style Lua code
(including NSE scripts and libraries). You can use this to make
your script conform to the style guidelines for NSE.
In order to run the script, you need Lua 5.1 or Lua 5.2 installed as well
as LPeg. LPeg is a parsing expression grammar library which is basically a
far more powerful pattern matching utility than regular expressions.

90
docs/style/lua-format Executable file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
# Copyright (c) 2011 Patrick Joseph Donnelly
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
DIRECTORY=$(dirname "$0") # directory of executable
ARGUMENTS='--options i,h --long in-place,help --name lua-format'
IN_PLACE=0
NEW_ARGUMENTS=$(getopt $ARGUMENTS -- "$@")
function usage {
echo "$0 [--in-place] file1 [file2 [...]]"
}
if [ $? -ne 0 ]; then
usage
exit 1
fi
eval set -- "$NEW_ARGUMENTS"
while [ $# -ge 0 ]; do
case "$1" in
-i|--in-place)
IN_PLACE=1
shift
;;
-h|--help)
usage
exit
;;
--)
shift
break
;;
esac
done
set -e
# batrick@batbytes:~$ luac -l -p -
# foo = function () end
#
# main <stdin:0,0> (3 instructions, 12 bytes at 0x172c230)
# 0+ params, 2 slots, 0 upvalues, 0 locals, 1 constant, 1 function
# 1 [1] CLOSURE 0 0 ; 0x172c410
# 2 [1] SETGLOBAL 0 -1 ; foo
# 3 [1] RETURN 0 1
#
# function <stdin:1,1> (1 instruction, 4 bytes at 0x172c410)
# 0 params, 2 slots, 0 upvalues, 0 locals, 0 constants, 0 functions
# 1 [1] RETURN 0 1
#
# We filter out everything but the opcodes and the lines specifying the
# function statistics (# of parameters, upvalues, etc.). We also remove CLOSURE
# opcodes because they include a runtime pointer address which changes across
# luac invocations.
function filter {
grep --invert-match -E "^function|main" | grep --invert-match "^[[:space:]]*$" | grep --invert-match CLOSURE | cut -f 2,4-
}
for file do
out="${file}.out"
echo Formatting "$file" >&2
lua "$DIRECTORY/lua-format.lua" < "$file" > "$out"
diff -Naur <(luac -l -p "$file" | filter) <(luac -l -p "$out" | filter) >&2
if [ $IN_PLACE -eq 1 ]; then
mv -f "$out" "$file"
fi
done

322
docs/style/lua-format.lua Executable file
View File

@@ -0,0 +1,322 @@
-- Copyright (c) 2011 Patrick Joseph Donnelly
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
local DEBUG = false;
local assert = assert;
local pairs = pairs;
local require = require;
local tostring = tostring;
local io = require "io";
local read = io.read;
local write = io.write;
local stderr = io.stderr;
local table = require "table";
local concat = table.concat;
local lpeg = require "lpeg";
lpeg.setmaxstack(8000);
local P = lpeg.P;
local S = lpeg.S;
local V = lpeg.V;
local C = lpeg.C;
local Cb = lpeg.Cb;
local Cc = lpeg.Cc;
local Cf = lpeg.Cf;
local Cg = lpeg.Cg;
local Cp = lpeg.Cp;
local Cs = lpeg.Cs;
local Cmt = lpeg.Cmt;
local Ct = lpeg.Ct;
local NEWLINE = Cb "newline" * ((V "space" - P "\n")^0 * P "\n")^-1;
local SPACE = Cb "space";
local INDENT_SPACE = Cb "indent_space";
local function INDENT_INCREASE (p, nonewline)
if nonewline then
return Cg(Cg(Cb "indent" * INDENT_SPACE, "indent") * p);
else
return Cg(Cg(Cb "indent" * INDENT_SPACE, "indent") * NEWLINE * p);
end
end
local INDENT = Cb "indent";
local shebang = P "#" * (P(1) - P "\n")^0 * P "\n";
local function K (k) -- keyword
return C(k) * -(V "alnum" + P "_");
end
-- The formatter uses captures to indent code. We necessarily use thousands and
-- thousands of them. At various strategic points, we concatenate these captures
-- so we don't overflow the Lua stack.
local function FLATTEN (pattern)
return Ct(pattern) / concat;
end
local lua = lpeg.locale {
V "_init" * FLATTEN(V "_script");
_init = Cg(Cc "\n", "newline") * Cg(Cc "", "indent") * Cg(Cc " ", "indent_space") * Cg(Cc " ", "space");
_script = C(shebang)^-1 * V "filler" * V "chunk" * V "filler" * -P(1);
-- keywords
keywords = K "and" + K "break" + K "do" + K "else" + K "elseif" +
K "end" + K "false" + K "for" + K "function" + K "if" +
K "in" + K "local" + K "nil" + K "not" + K "or" + K "repeat" +
K "return" + K "then" + K "true" + K "until" + K "while";
-- longstrings
longstring = P { -- from Roberto Ierusalimschy's lpeg examples
(V "open" * (P(1) - V "closeeq")^0 * V "close") / "%0";
open = "[" * Cg((P "=")^0, "init") * P "[" * (P "\n")^-1;
close = "]" * C((P "=")^0) * "]";
closeeq = Cmt(V "close" * Cb "init", function (s, i, a, b) return a == b end)
};
-- comments & whitespace
-- read a comment but do not capture any whitespace at the end
chomp_comment = C((P(1) - (V "space" - P "\n")^0 * (P "\n" + -P(1)))^0) * (V "space" - P "\n")^0 * (P "\n" + -P(1)) * Cc "\n";
one_line_comment = -V "multi_line_comment" * C "--" * V "chomp_comment";
multi_line_comment = C "--" * V "longstring";
comment = V "multi_line_comment" + V "one_line_comment" * INDENT;
whitespace = (V "space" + (SPACE * V "comment" * SPACE))^0;
space_after_stat = ((V "space" - P "\n")^0 * (P ";")^-1 * (V "space" - P "\n")^0 * SPACE * V "one_line_comment") +
(V "whitespace" * P ";")^-1 * NEWLINE;
-- match "filler" comments
line_of_space = (V "space" - P "\n")^0 * P "\n";
collapse_whitespace = V "line_of_space"^2 * Cc "\n\n" + V "line_of_space"^1 * Cc "\n";
filler_comment = (V "space" - P "\n")^0 * INDENT * V "one_line_comment"; -- * C "\n"^-1;
--filler_comment = (V "space" - P "\n")^0 * INDENT * (V "one_line_comment" - V "multi_line_comment"); -- * C "\n"^-1; -- FIXME highlighted after INDENT
filler = (V "collapse_whitespace" + V "filler_comment")^0 * V "whitespace" + V "whitespace";
-- Types and Comments
Name = C((V "alpha" + P "_") * (V "alnum" + P "_")^0) - V "keywords";
BinaryExponent = S "pP" * (P "-")^-1 * V "digit"^1;
DecimalExponent = S "eE" * (P "-")^-1 * V "digit"^1;
Number = C((P "-")^-1 * V "whitespace" * P "0" * S "xX" * V "xdigit"^1 * (P "." * V "xdigit"^0)^-1 * V "BinaryExponent"^-1 * -(V "alnum" + P "_")) +
C((P "-")^-1 * V "whitespace" * V "digit"^1 * (P "." * V "digit"^0)^-1 * V "DecimalExponent"^-1 * -(V "alnum" + P "_")) +
C((P "-")^-1 * V "whitespace" * P "." * V "digit"^1 * V "DecimalExponent"^-1 * -(V "alnum" + P "_"));
String = C(P "\"" * (P "\\" * P(1) + (1 - P "\""))^0 * P "\"") +
C(P "'" * (P "\\" * P(1) + (1 - P "'"))^0 * P "'") +
V "longstring";
-- Lua Complete Syntax
chunk = FLATTEN((V "filler" * INDENT * FLATTEN(V "stat") * V "space_after_stat")^0 * (V "filler" * INDENT * V "retstat" * V "space_after_stat")^-1);
block = V "chunk";
stat = P ";" +
V "label" +
K "break" +
K "goto" * SPACE * V "whitespace" * V "Name" +
K "do" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * INDENT * K "end" +
K "while" * SPACE * V "whitespace" * V "_oneline_exp" * V "whitespace" * SPACE * K "do" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * INDENT * K "end" +
K "repeat" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * INDENT * K "until" * SPACE * V "whitespace" * V "_oneline_exp" +
K "if" * SPACE * V "whitespace" * V "_oneline_exp" * V "whitespace" * SPACE * K "then" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * (INDENT * K "elseif" * SPACE * V "whitespace" * V "_oneline_exp" * V "whitespace" * SPACE * K "then" * INDENT_INCREASE(V "filler" * V "block" * V "filler"))^0 * (INDENT * K "else" * INDENT_INCREASE(V "filler" * V "block" * V "filler"))^-1 * INDENT * K "end" +
K "for" * SPACE * V "whitespace" * V "Name" * V "whitespace" * SPACE * C "=" * SPACE * V "whitespace" * V "_oneline_exp" * V "whitespace" * C "," * SPACE * V "whitespace" * V "_oneline_exp" * (V "whitespace" * C "," * SPACE * V "whitespace" * V "_oneline_exp")^-1 * V "whitespace" * SPACE * K "do" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * INDENT * K "end" +
K "for" * SPACE * V "whitespace" * V "namelist" * V "whitespace" * SPACE * K "in" * SPACE * V "whitespace" * V "explist" * V "whitespace" * SPACE * K "do" * INDENT_INCREASE(V "filler" * V "block" * V "filler") * INDENT * K "end" +
K "function" * SPACE * V "whitespace" * V "funcname" * SPACE * V "whitespace" * V "funcbody" +
K "local" * SPACE * V "whitespace" * K "function" * SPACE * V "whitespace" * V "Name" * V "whitespace" * SPACE * V "funcbody" +
K "local" * SPACE * V "whitespace" * V "namelist" * (SPACE * V "whitespace" * C "=" * SPACE * V "whitespace" * V "explist")^-1 * V "_check_ambiguous" +
V "_function_declaration" +
V "varlist" * V "whitespace" * SPACE * C "=" * SPACE * V "whitespace" * V "explist" * V "_check_ambiguous" +
V "functioncall" * V "_check_ambiguous";
-- If the script uses a semicolon to avoid an ambiguous syntax situation, we keep it.
-- Example:
-- a = f()
-- ("foo"):method()
--
-- Can be parsed as:
-- a = f()("foo"):method();
-- or
-- a = f();
-- ("foo"):method();
_check_ambiguous = #(V "whitespace" * P ";" * V "whitespace" * P "(") * Cc ";" + P(true);
_function_declaration = Cmt(V "Name" * V "space"^0 * P "=" * V "space"^0 * FLATTEN(V "function") * -(V "whitespace" * (V "binop" + P ",")), function (s, p, name, f) local new = f:gsub("^function", "function "..name) return true, new end);
label = C "::" * V "whitespace" * V "Name" * V "whitespace" * C "::";
retstat = K "return" * (SPACE * V "whitespace" * V "explist")^-1;
funcname = V "Name" * (V "whitespace" * C "." * V "whitespace" * V "Name")^0 * (V "whitespace" * C ":" * V "whitespace" * V "Name")^-1;
namelist = V "Name" * (V "whitespace" * C "," * SPACE * V "whitespace" * V "Name")^0;
varlist = V "var" * (V "whitespace" * C "," * SPACE * V "whitespace" * V "var")^0;
-- Let's come up with a syntax that does not use left recursion (only listing changes to Lua 5.1 extended BNF syntax)
-- value ::= nil | false | true | Number | String | '...' | function | tableconstructor | functioncall | var | '(' exp ')'
-- exp ::= unop exp | value [binop exp]
-- prefix ::= '(' exp ')' | Name
-- index ::= '[' exp ']' | '.' Name
-- call ::= args | ':' Name args
-- suffix ::= call | index
-- var ::= prefix {suffix} index | Name
-- functioncall ::= prefix {suffix} call
_deparenthesis_value = P "(" * V "whitespace" * (V "_deparenthesis_value" + V "_value_simple") * V "whitespace" * P ")";
_value_simple = K "nil" +
K "false" +
K "true" +
V "Number" +
V "String" +
V "function" +
V "tableconstructor" +
V "var";
-- Something that represents a value (or many values)
value = K "nil" +
K "false" +
K "true" +
V "Number" +
V "String" +
K "..." +
V "function" +
V "tableconstructor" +
V "functioncall" +
V "var" +
V "_deparenthesis_value" + -- remove redundant parenthesis
C "(" * V "whitespace" * V "exp" * V "whitespace" * C ")";
-- An expression operates on values to produce a new value or is a value
exp = V "unop" * V "whitespace" * V "exp" +
V "value" * (V "whitespace" * V "binop" * V "whitespace" * V "exp")^-1;
-- This is an expression which is always truncated to 1 result, and so we can remove
-- redundant parenthesis.
_single_exp = P "(" * V "whitespace" * V "_single_exp" * V "whitespace" * P ")" * -(V "whitespace" * (V "suffix" + V "binop")) +
V "exp";
_oneline_exp = Cg(Cg(Cc " ", "newline") * Cg(Cc "", "indent") * Cg(Cc "", "indent_space") * V "_single_exp");
-- Index and Call
index = C "[" * V "whitespace" * V "_single_exp" * V "whitespace" * C "]" +
C "." * V "whitespace" * V "Name";
call = V "args" +
C ":" * V "whitespace" * V "Name" * V "whitespace" * V "args";
-- A Prefix is a the leftmost side of a var(iable) or functioncall
prefix = C "(" * V "whitespace" * V "exp" * V "whitespace" * C ")" +
V "Name";
-- A Suffix is a Call or Index
suffix = V "call" +
V "index";
var = V "prefix" * (V "whitespace" * V "suffix" * #(V "whitespace" * V "suffix"))^0 * V "whitespace" * V "index" +
V "Name";
functioncall = V "prefix" * (V "whitespace" * V "suffix" * #(V "whitespace" * V "suffix"))^0 * V "whitespace" * V "call";
explist = V "exp" * (V "whitespace" * C "," * SPACE * V "whitespace" * V "exp")^0;
-- Change func({}) to func {}
-- Change func("...") to func "..."
args = P "(" * SPACE * V "whitespace" * V "tableconstructor" * V "whitespace" * P ")" +
P "(" * SPACE * V "whitespace" * V "String" * V "whitespace" * P ")" +
C "(" * INDENT_INCREASE(V "whitespace" * (V "explist" * V "whitespace")^-1, true) * C ")" +
SPACE * V "tableconstructor" +
SPACE * V "String";
["function"] = FLATTEN(K "function" * SPACE * V "whitespace" * V "funcbody");
funcbody = C "(" * V "whitespace" * (V "parlist" * V "whitespace")^-1 * C ")" * INDENT_INCREASE(V "block" * V "whitespace") * INDENT * K "end";
parlist = V "namelist" * (V "whitespace" * C "," * SPACE * V "whitespace" * K "...")^-1 +
K "...";
tableconstructor = FLATTEN(C "{" * (INDENT_INCREASE(V "filler" * V "fieldlist" * V "filler") * INDENT + V "filler") * C "}");
field_space_after = (V "space" - P "\n")^0 * SPACE * V "one_line_comment";
fieldlist = INDENT * FLATTEN(V "field") * (V "whitespace" * V "fieldsep" * (V "field_space_after" + NEWLINE) * V "filler" * INDENT * FLATTEN(V "field"))^0 * (V "whitespace" * V "fieldsep" + Cc ",")^-1 * (V "field_space_after" + NEWLINE);
field = C "[" * V "whitespace" * V "_oneline_exp" * V "whitespace" * C "]" * SPACE * V "whitespace" * C "=" * SPACE * V "whitespace" * V "_single_exp" +
V "Name" * SPACE * V "whitespace" * C "=" * SPACE * V "whitespace" * V "_single_exp" +
V "exp";
fieldsep = C "," +
P ";" * Cc ","; -- use only commas
binop = SPACE * K "and" * SPACE + -- match longest token sequences first
SPACE * K "or" * SPACE +
SPACE * C ".." * SPACE +
SPACE * C "<=" * SPACE +
SPACE * C ">=" * SPACE +
SPACE * C "==" * SPACE +
SPACE * C "~=" * SPACE +
SPACE * C "+" * SPACE +
SPACE * (C "-" - P "--") * SPACE +
SPACE * C "*" * SPACE +
SPACE * C "/" * SPACE +
C "^" + -- no space for power
SPACE * C "%" * SPACE +
SPACE * C "<" * SPACE +
SPACE * C ">" * SPACE;
unop = (C "-" - P "--") +
C "#" +
K "not" * SPACE;
};
if DEBUG then
local level = 0;
for k, p in pairs(lua) do
local enter = lpeg.Cmt(lpeg.P(true), function (s, p)
stderr:write((" "):rep(level*2), "ENTER ", k, ": ", s:sub(p, p), "\n");
level = level+1;
return true;
end);
local match = lpeg.Cmt(lpeg.P(true), function (s, p)
level = level-1;
if k == "space" or k == "comment" then
return true;
else
stderr:write((" "):rep(level*2), "MATCH ", k, "\n", s:sub(p - 200 < 0 and 1 or p-200, p-1), "\n");
return true;
end
end);
local leave = lpeg.Cmt(lpeg.P(true), function (s, p)
level = level-1;
stderr:write((" "):rep(level*2), "LEAVE ", k, "\n");
return false;
end);
lua[k] = enter * p * match + leave;
end
end
lua = P(lua);
write(assert(lua:match(assert(read "*a"))));