summaryrefslogtreecommitdiffstats
path: root/docs/style/lua-format
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xdocs/style/lua-format90
-rwxr-xr-xdocs/style/lua-format.lua322
2 files changed, 412 insertions, 0 deletions
diff --git a/docs/style/lua-format b/docs/style/lua-format
new file mode 100755
index 0000000..fe008fd
--- /dev/null
+++ b/docs/style/lua-format
@@ -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
diff --git a/docs/style/lua-format.lua b/docs/style/lua-format.lua
new file mode 100755
index 0000000..470d4ab
--- /dev/null
+++ b/docs/style/lua-format.lua
@@ -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"))));