根据缩进深度格式化Lua代码

清华大佬耗费三个月吐血整理的几百G的资源,免费分享!....>>>

package LuaTidy;
# 对 Lua 代码进行缩进重排
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(lua_tidy);

use 5.010;
use strict;
use warnings;
use File::Slurp qw(read_file);

# ----------------------------------------------
# 初始化全局变量
# global variable to save all matched string
our $cluster_str = {};
our $INDENT = 2;
my @observe_words = qw(function end if while repeat for do until else elsif then);

# replace all the observed word to char
our $observed_words = {};
our $reverse_words  = {};

my $start_count = 128;
foreach my $word (@observe_words) {
    my $char = chr($start_count);
    $start_count++;
    $observed_words->{$word} = $char;
    $reverse_words->{$char}  = $word;
}
# ---------------------------------------------
# 命令行接口
if (scalar @ARGV > 0) {
  # Get argument from command line
  my ($file, $indent) = @ARGV;
  # Indent is space
  $INDENT = $indent || 2;

  my @lines = <DATA>;
  my $text = join '', @lines;

  if (defined $file and -e $file) {
    # read file and save to one text
    $text = read_file $file;
  }
  else {
    say "Usage:\nperl LuaTidy.pm need_tidy.lua [ indent length ]\n";
    say "Pls see sample:\n---------------------------------";
  }
  say $text;
  my $code_str = lua_tidy($text, 2);
  say "After Tidy:\n---------------------------";
  say $code_str;
}

# --------------------------------------
# 模块接口
#
sub lua_tidy {
  my ($text, $indent) = @_;

  # conceal the do after while or for to !done!
  $text =~ s/\b((?:while|for)\b.*?)do/$1!done!/g;

  # delete all the space of all line_start and line_end
  $text =~ s/^\h+|\h+$//xmsg;

  # conceal all reserved words to char
  foreach my $word (keys $observed_words) {
    my $char = $observed_words->{$word};
    $text =~ s/\b$word\b/$char/g;
  }

  my $start_class = get_class_str([qw(function if while do repeat for)]);
  my $end_class   = get_class_str([qw(end until)]);

  foreach (1 .. 100) {
    my $t = $text =~ 
    s/([$start_class][^$start_class]+?[$end_class])/replace_cluster($1)/ge;
    last if ($t eq '');
  }
  $text = recover_words(write_text($text));

  $text =~ s/!done!/do/g;

  return $text;
}

# -----------------------------------------
# 子程序
#
sub replace_cluster {
    my ($match_str) = @_;
    my $number = apply_number();
    my $recover_str = recover_words($match_str);
    $cluster_str->{$number} = $recover_str;
    return $number;
}

sub recover_words {
    my ($str) = @_;
    my %reverse_observed_words = reverse %{$observed_words};
    foreach my $char (keys %reverse_observed_words) {
        my $word = $reverse_observed_words{$char};
        $str =~ s/$char/$word/g;
    }
    return $str;
}

sub write_text {
    my ($text) = @_;
    foreach ( 0 .. 20 ) {
        # This is most import regexp use to neizhi function
        my $t = $text =~ 
        s/^(\h*)(\S.*?)?(!\d{4}!)/write_indent_str($1, $2, $3)/xmge;
        last if ($t eq '');
    }
    return $text;
}

sub write_indent_str {
    my ($indent, $content, $id) = @_;
    $content = $content || '';
    my $str = $cluster_str->{$id};
    $str = indent_str($str, $indent);
    return $indent . $content . $str;
}

sub indent_str {
    my ($str, $indent) = @_;
    my @lines = split /\n/, $str;
    my $line_number = scalar @lines;
    my @indent_lines = ();
    my $count = 0;
    my $indent_space = ' ' x $INDENT;
    foreach my $line (@lines) {
        $count++;
        if ($count > 1) {
            $line = $indent . $line;
            if ( $count < $line_number) {
                $line = $indent_space . $line;
            }
        }
        push @indent_lines, $line;
    }
    $str = join "\n", @indent_lines;
}

sub apply_number {
    state $count = 0;
    $count++;
    return sprintf("!%.4d!", $count);
}
# ----------------------------------------------------
# Transfer words list to class cluster to integrate in regexp
# -----------------------------------------------------------
sub get_class_str {
    my ($list) = @_;
    my @array = map { $observed_words->{$_} } @{$list};
    my $str = join '', @array;
    return $str;
}

# #################################################
# Debug function
# ------------------------------------------------
# Check if have any conceal char have not recover
# ------------------------------------------------
sub check_conceal {
    my ($str, $call_name) = @_;
    foreach my $char (keys $reverse_words) {
        if ($str =~ /$char/) {
            say "find it in call: $call_name" ;
            return 1;
        }
    }
}
# ----------------------------------------
# View cluster_str data
# ----------------------------------------------
sub view_cluster {
    foreach my $id (sort keys $cluster_str) {
        my $str = $cluster_str->{$id};
        $str =~ s/^\s+|\s+$//xmsg;
        $str = indent_str($str);
    }
}

__DATA__
local function test_defined(value, path)
if defined[value] then
if path:match("^getmetatable.*%)$") then
out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value])
else
out[#out+1] = path .. " = " .. defined[value] .. "\n"
end
return true
end
defined[value] = path
end