rebar3 服务器
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = 'apps/game_server/src/behavior_tree'
|
||||
$changed = 0
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
if ($text -notmatch '-include\("bt_game\.hrl"\)\.' -and
|
||||
($text -match '\btree_node\(\)' -or $text -match '#tree_node\{' -or $text -match '\bblackboard\(\)' -or $text -match '\bbt_status\(\)')) {
|
||||
$rx = [regex]::new('(^-module\([^)]+\)\.\s*)', [Text.RegularExpressions.RegexOptions]::Multiline)
|
||||
$new = $rx.Replace($text, "`$1`r`n-include(`"bt_game.hrl`").`r`n", 1)
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,56 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$hrls = Get-ChildItem apps -Recurse -Filter '*_records.hrl' |
|
||||
Where-Object { $_.FullName -match '\\include\\auto_records\\' }
|
||||
|
||||
$changed = 0
|
||||
|
||||
function Insert-Include($lines, $includeLine) {
|
||||
$lastInclude = -1
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^\s*-include(_lib)?\(') {
|
||||
$lastInclude = $i
|
||||
}
|
||||
}
|
||||
if ($lastInclude -ge 0) {
|
||||
$before = $lines[0..$lastInclude]
|
||||
$after = @()
|
||||
if ($lastInclude + 1 -lt $lines.Count) { $after = $lines[($lastInclude + 1)..($lines.Count - 1)] }
|
||||
return @($before + $includeLine + $after)
|
||||
}
|
||||
|
||||
$moduleLine = -1
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^\s*-module\(') {
|
||||
$moduleLine = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($moduleLine -ge 0) {
|
||||
$before = $lines[0..$moduleLine]
|
||||
$after = @()
|
||||
if ($moduleLine + 1 -lt $lines.Count) { $after = $lines[($moduleLine + 1)..($lines.Count - 1)] }
|
||||
return @($before + '' + $includeLine + $after)
|
||||
}
|
||||
return @($includeLine + $lines)
|
||||
}
|
||||
|
||||
foreach ($hrl in $hrls) {
|
||||
$module = $hrl.BaseName -replace '_records$', ''
|
||||
$appRoot = Split-Path (Split-Path (Split-Path $hrl.FullName -Parent) -Parent) -Parent
|
||||
$srcs = Get-ChildItem $appRoot -Recurse -Filter "$module.erl" |
|
||||
Where-Object { $_.FullName -match '\\(src|db_patch|db_merge|db_clear)\\' }
|
||||
foreach ($src in $srcs) {
|
||||
$include = "-include(""auto_records/$($hrl.Name)"")."
|
||||
$text = [IO.File]::ReadAllText($src.FullName)
|
||||
if ($text.Contains($include)) { continue }
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
$text -split "`r?`n", -1 | ForEach-Object { [void]$lines.Add($_) }
|
||||
$newLines = Insert-Include $lines $include
|
||||
if ($src.IsReadOnly) { $src.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($src.FullName, ($newLines -join "`r`n"))
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,100 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$cacheCommon = 'apps/game_server/include/auto_gen/cache/cache_common.hrl'
|
||||
$cacheErlDir = 'apps/game_server/cache'
|
||||
|
||||
if (-not (Test-Path $cacheCommon)) {
|
||||
throw "Missing cache_common.hrl: $cacheCommon"
|
||||
}
|
||||
if (-not (Test-Path $cacheErlDir)) {
|
||||
throw "Missing cache erl dir: $cacheErlDir"
|
||||
}
|
||||
|
||||
$macroToServerType = @{
|
||||
'ZONE_SERVER_CACHE_LIST' = 'zone_server'
|
||||
'CENTER_SERVER_CACHE_LIST' = 'center_server'
|
||||
'CROSS_SERVER_CACHE_LIST' = 'cross_server'
|
||||
'GAME_SERVER_CACHE_LIST' = 'game_server'
|
||||
}
|
||||
|
||||
$text = [IO.File]::ReadAllText((Resolve-Path $cacheCommon).Path)
|
||||
$tableServers = @{}
|
||||
|
||||
foreach ($macro in $macroToServerType.Keys) {
|
||||
$serverType = $macroToServerType[$macro]
|
||||
$m = [regex]::Match($text, "(?ms)-define\($macro,\s*\[(?<body>.*?)\]\)\.")
|
||||
if (-not $m.Success) { continue }
|
||||
|
||||
foreach ($entry in [regex]::Matches($m.Groups['body'].Value, '\{\s*(?<table>[a-zA-Z][a-zA-Z0-9_]*)\s*,\s*[a-zA-Z][a-zA-Z0-9_]*\s*\}')) {
|
||||
$table = $entry.Groups['table'].Value
|
||||
if (-not $tableServers.ContainsKey($table)) {
|
||||
$tableServers[$table] = [System.Collections.Generic.List[string]]::new()
|
||||
}
|
||||
if (-not $tableServers[$table].Contains($serverType)) {
|
||||
[void]$tableServers[$table].Add($serverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Format-ServerTypes($items) {
|
||||
$order = @('game_server', 'center_server', 'cross_server', 'zone_server')
|
||||
$sorted = @()
|
||||
foreach ($x in $order) {
|
||||
if ($items.Contains($x)) { $sorted += $x }
|
||||
}
|
||||
foreach ($x in $items) {
|
||||
if ($sorted -notcontains $x) { $sorted += $x }
|
||||
}
|
||||
return '[' + ($sorted -join ',') + ']'
|
||||
}
|
||||
|
||||
$changed = 0
|
||||
$updatedTables = 0
|
||||
$missing = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
Get-ChildItem $cacheErlDir -Filter 'table_*.erl' | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$content = [IO.File]::ReadAllText($path)
|
||||
$newContent = $content
|
||||
|
||||
$tableNames = [regex]::Matches($content, 'tab_name\s*=\s*(?<table>[a-zA-Z][a-zA-Z0-9_]*)') |
|
||||
ForEach-Object { $_.Groups['table'].Value } |
|
||||
Select-Object -Unique
|
||||
|
||||
foreach ($table in $tableNames) {
|
||||
if (-not $tableServers.ContainsKey($table)) {
|
||||
[void]$missing.Add($table)
|
||||
$serverTypes = '[]'
|
||||
} else {
|
||||
$serverTypes = Format-ServerTypes $tableServers[$table]
|
||||
}
|
||||
|
||||
$pattern = "(?ms)(#table\{\s*.*?tab_name\s*=\s*$table\b.*?prim_key_list\s*=\s*\[[^\]]*\])(?<rest>.*?)(?=\r?\n\s*\}\s*(?:,|\r?\n\s*\]))"
|
||||
$newContent = [regex]::Replace($newContent, $pattern, {
|
||||
param($match)
|
||||
$head = $match.Groups[1].Value
|
||||
$rest = $match.Groups['rest'].Value
|
||||
if ($rest -match '(?m)^\s*,\s*server_types\s*=') {
|
||||
$rest = [regex]::Replace($rest, '(?m)^(\s*,\s*server_types\s*=\s*)\[[^\]]*\]', "`${1}$serverTypes")
|
||||
} else {
|
||||
$rest = $rest + "`r`n , server_types = $serverTypes"
|
||||
}
|
||||
$script:updatedTables++
|
||||
return $head + $rest
|
||||
}, 1)
|
||||
}
|
||||
|
||||
if ($newContent -ne $content) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $newContent)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
|
||||
"server_type_tables=$($tableServers.Count)"
|
||||
"changed_files=$changed"
|
||||
"updated_tables=$updatedTables"
|
||||
"missing_tables=$($missing.Count)"
|
||||
if ($missing.Count -gt 0) {
|
||||
$missing | Sort-Object -Unique | ForEach-Object { "MISSING $_" }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env escript
|
||||
%%! -noshell
|
||||
|
||||
main(_) ->
|
||||
Files = filelib:wildcard("apps/game_server/cache_recovered_grouped/*.table"),
|
||||
Bad = [{F, E} || F <- Files, {error, E} <- [file:consult(F)]],
|
||||
io:format("files=~p bad_count=~p~n", [length(Files), length(Bad)]),
|
||||
case Bad of
|
||||
[] ->
|
||||
halt(0);
|
||||
_ ->
|
||||
io:format("~p~n", [Bad]),
|
||||
halt(1)
|
||||
end.
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env escript
|
||||
%%! -noshell
|
||||
|
||||
main(_) ->
|
||||
Files = filelib:wildcard("apps/game_server/cache_recovered/*.table"),
|
||||
Bad = [{F, E} || F <- Files, {error, E} <- [file:consult(F)]],
|
||||
io:format("files=~p bad_count=~p~n", [length(Files), length(Bad)]),
|
||||
case Bad of
|
||||
[] ->
|
||||
halt(0);
|
||||
_ ->
|
||||
io:format("~p~n", [Bad]),
|
||||
halt(1)
|
||||
end.
|
||||
@@ -0,0 +1,147 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$sourceDir = 'apps/game_server/cache_recovered_grouped'
|
||||
$hrlDir = 'apps/game_server/include/auto_gen/cache'
|
||||
$outDir = 'apps/game_server/cache'
|
||||
|
||||
if (-not (Test-Path $sourceDir)) {
|
||||
throw "Missing source dir: $sourceDir"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||
|
||||
function Escape-BinaryString([string]$Text) {
|
||||
if ($null -eq $Text -or $Text -eq '') { return '<<>>' }
|
||||
$escaped = $Text.Replace('\', '\\').Replace('"', '\"')
|
||||
return "<<`"$escaped`"/utf8>>"
|
||||
}
|
||||
|
||||
function Read-HrlComments([string]$Table) {
|
||||
$comments = @{}
|
||||
$path = Join-Path $hrlDir "$Table`_c.hrl"
|
||||
if (-not (Test-Path $path)) { return $comments }
|
||||
|
||||
$text = [IO.File]::ReadAllText((Resolve-Path $path).Path)
|
||||
$pattern = '(?m)^\s*(?<field>''_id''|[a-zA-Z][a-zA-Z0-9_]*)\s*=\s*.*?\s*%%\s*(?<comment>.*?)(?=\r?\n\s*(?:''_id''|[a-zA-Z][a-zA-Z0-9_]*)\s*=|\r?\n\s*\}\)\.|\z)'
|
||||
foreach ($m in [regex]::Matches($text, $pattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)) {
|
||||
$field = $m.Groups['field'].Value.Trim("'")
|
||||
if ($field -eq '_id') { continue }
|
||||
$comment = $m.Groups['comment'].Value
|
||||
$comment = [regex]::Replace($comment, '\s+', ' ').Trim()
|
||||
if ($comment -ne '') {
|
||||
$comments[$field] = $comment
|
||||
}
|
||||
}
|
||||
return $comments
|
||||
}
|
||||
|
||||
function Read-TableTerms([string]$Path) {
|
||||
$text = [IO.File]::ReadAllText($Path)
|
||||
$terms = [System.Collections.Generic.List[object]]::new()
|
||||
|
||||
$pattern = '(?ms)^\{(?<table>[a-zA-Z][a-zA-Z0-9_]*),\s*\[\s*(?<fields>.*?)\s*\],\s*(?<keys>\[[^\]]*\]),\s*(?<opts>#\{.*?\})\s*\}\.'
|
||||
foreach ($m in [regex]::Matches($text, $pattern)) {
|
||||
$table = $m.Groups['table'].Value
|
||||
$fieldsText = $m.Groups['fields'].Value
|
||||
$keys = ($m.Groups['keys'].Value -replace '\s+', '')
|
||||
$opts = ($m.Groups['opts'].Value -replace '\s+', '')
|
||||
|
||||
$fields = [System.Collections.Generic.List[object]]::new()
|
||||
$fieldPattern = '\{\s*(?<name>[a-zA-Z][a-zA-Z0-9_]*)\s*,\s*(?<type>[a-zA-Z][a-zA-Z0-9_]*)\s*,\s*(?<len>\d+)\s*,\s*"(?<comment>(?:\\.|[^"\\])*)"\s*,\s*(?<sync>true|false)\s*(?:,\s*(?<default>.*?))?\s*\}\s*,?'
|
||||
foreach ($fm in [regex]::Matches($fieldsText, $fieldPattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)) {
|
||||
[void]$fields.Add([pscustomobject]@{
|
||||
Name = $fm.Groups['name'].Value
|
||||
Type = $fm.Groups['type'].Value
|
||||
Len = $fm.Groups['len'].Value
|
||||
Comment = $fm.Groups['comment'].Value.Replace('\"', '"')
|
||||
Sync = $fm.Groups['sync'].Value
|
||||
})
|
||||
}
|
||||
|
||||
[void]$terms.Add([pscustomobject]@{
|
||||
Table = $table
|
||||
Fields = $fields
|
||||
Keys = $keys
|
||||
Opts = $opts
|
||||
})
|
||||
}
|
||||
|
||||
return $terms
|
||||
}
|
||||
|
||||
function Opt-Value([string]$Opts, [string]$Key) {
|
||||
$m = [regex]::Match($Opts, "$Key=>(?<value>(\[[^\]]*\]|[a-zA-Z0-9_]+))")
|
||||
if ($m.Success) { return $m.Groups['value'].Value }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-ServerTypes([string]$Table) {
|
||||
if ($Table.StartsWith('center_')) {
|
||||
return '[center_server]'
|
||||
}
|
||||
if ($Table.StartsWith('cross_') -or $Table -match '_cross($|_)') {
|
||||
return '[cross_server]'
|
||||
}
|
||||
return '[game_server]'
|
||||
}
|
||||
|
||||
function Write-Module([string]$GroupName, $Terms) {
|
||||
$module = "table_$GroupName"
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
[void]$lines.Add("-module($module).")
|
||||
[void]$lines.Add("")
|
||||
[void]$lines.Add("-export([list/0]).")
|
||||
[void]$lines.Add("")
|
||||
[void]$lines.Add('-include("table.hrl").')
|
||||
[void]$lines.Add("")
|
||||
[void]$lines.Add("list() ->")
|
||||
[void]$lines.Add(" [")
|
||||
|
||||
for ($ti = 0; $ti -lt $Terms.Count; $ti++) {
|
||||
$term = $Terms[$ti]
|
||||
$comments = Read-HrlComments $term.Table
|
||||
[void]$lines.Add(" #table{")
|
||||
[void]$lines.Add(" tab_name = $($term.Table),")
|
||||
[void]$lines.Add(" field_list = [")
|
||||
|
||||
for ($fi = 0; $fi -lt $term.Fields.Count; $fi++) {
|
||||
$field = $term.Fields[$fi]
|
||||
$comment = if ($comments.ContainsKey($field.Name)) { $comments[$field.Name] } else { $field.Comment }
|
||||
$common = Escape-BinaryString $comment
|
||||
$comma = if ($fi -lt $term.Fields.Count - 1) { "," } else { "" }
|
||||
[void]$lines.Add(" #field{name = $($field.Name), type = $($field.Type), len = $($field.Len), common = $common, sync = $($field.Sync)}$comma")
|
||||
}
|
||||
|
||||
[void]$lines.Add(" ],")
|
||||
[void]$lines.Add(" prim_key_list = $($term.Keys)")
|
||||
|
||||
$indexList = Opt-Value $term.Opts 'indexes'
|
||||
$uidField = Opt-Value $term.Opts 'uid_field'
|
||||
$preLoad = Opt-Value $term.Opts 'pre_load'
|
||||
$clearAll = Opt-Value $term.Opts 'clear_all'
|
||||
$mergeType = Opt-Value $term.Opts 'merge_type'
|
||||
if ($indexList) { [void]$lines.Add(" , index_list = $indexList") }
|
||||
if ($uidField) { [void]$lines.Add(" , uniq_id_field = $uidField") }
|
||||
if ($preLoad) { [void]$lines.Add(" , pre_load = $preLoad") }
|
||||
if ($clearAll) { [void]$lines.Add(" , all_clear = $clearAll") }
|
||||
if ($mergeType) { [void]$lines.Add(" , merge_type = $mergeType") }
|
||||
[void]$lines.Add(" , server_types = $(Get-ServerTypes $term.Table)")
|
||||
|
||||
$suffix = if ($ti -lt $Terms.Count - 1) { "," } else { "" }
|
||||
[void]$lines.Add(" }$suffix")
|
||||
}
|
||||
|
||||
[void]$lines.Add(" ].")
|
||||
$outPath = Join-Path $outDir "$module.erl"
|
||||
[IO.File]::WriteAllText($outPath, ($lines -join "`r`n"))
|
||||
}
|
||||
|
||||
$count = 0
|
||||
Get-ChildItem $sourceDir -Filter '*.table' | Sort-Object Name | ForEach-Object {
|
||||
$terms = Read-TableTerms $_.FullName
|
||||
if ($terms.Count -gt 0) {
|
||||
Write-Module $_.BaseName $terms
|
||||
$count++
|
||||
}
|
||||
}
|
||||
|
||||
"generated=$count"
|
||||
@@ -0,0 +1,86 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$includeRoots = @(
|
||||
'apps/game_server/include',
|
||||
'apps/game_server/include/auto_gen/cache',
|
||||
'apps/game_server/include/auto_gen/tbllog',
|
||||
'apps/game_server/include/auto_gen/proto',
|
||||
'apps/base/include',
|
||||
'apps/game_config/include',
|
||||
'apps/game_proto/include'
|
||||
)
|
||||
|
||||
function Get-TopRecordForms([string]$text) {
|
||||
$forms = New-Object System.Collections.Generic.List[object]
|
||||
$len = $text.Length
|
||||
$i = 0
|
||||
while ($i -lt $len) {
|
||||
if (($i -eq 0 -or $text[$i - 1] -eq "`n") -and $text[$i] -eq '-' -and $text.Substring($i) -match '^-record\(\s*(''[^'']+''|[A-Za-z0-9_]+)\s*,') {
|
||||
$name = $Matches[1].Trim("'")
|
||||
$depth = 0; $inString = $false; $inAtom = $false; $escape = $false; $j = $i
|
||||
while ($j -lt $len) {
|
||||
$ch = $text[$j]
|
||||
if ($inString) {
|
||||
if ($escape) { $escape = $false } elseif ($ch -eq '\') { $escape = $true } elseif ($ch -eq '"') { $inString = $false }
|
||||
} elseif ($inAtom) {
|
||||
if ($escape) { $escape = $false } elseif ($ch -eq '\') { $escape = $true } elseif ($ch -eq "'") { $inAtom = $false }
|
||||
} else {
|
||||
if ($ch -eq '"') { $inString = $true }
|
||||
elseif ($ch -eq "'") { $inAtom = $true }
|
||||
elseif ($ch -eq '(' -or $ch -eq '[' -or $ch -eq '{') { $depth++ }
|
||||
elseif ($ch -eq ')' -or $ch -eq ']' -or $ch -eq '}') { if ($depth -gt 0) { $depth-- } }
|
||||
elseif ($ch -eq '.' -and $depth -eq 0) {
|
||||
$end = $j + 1
|
||||
$forms.Add([pscustomobject]@{ Name = $name; Start = $i; End = $end; Text = $text.Substring($i, $end - $i) })
|
||||
$i = $end - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
$j++
|
||||
}
|
||||
}
|
||||
$i++
|
||||
}
|
||||
return $forms
|
||||
}
|
||||
|
||||
function Remove-DuplicateFieldLines([string]$form) {
|
||||
$seen = New-Object System.Collections.Generic.HashSet[string]
|
||||
$out = New-Object System.Collections.Generic.List[string]
|
||||
$removed = 0
|
||||
$lines = $form -split "\r?\n"
|
||||
foreach ($line in $lines) {
|
||||
$match = [regex]::Match($line, '^\s*,?\s*([a-z][A-Za-z0-9_]*)\b')
|
||||
if ($match.Success) {
|
||||
$field = $match.Groups[1].Value
|
||||
if ($seen.Contains($field)) {
|
||||
$removed++
|
||||
continue
|
||||
}
|
||||
[void]$seen.Add($field)
|
||||
}
|
||||
$out.Add($line)
|
||||
}
|
||||
return [pscustomobject]@{ Text = ($out -join "`r`n"); Removed = $removed }
|
||||
}
|
||||
|
||||
$changed = 0
|
||||
$removedTotal = 0
|
||||
Get-ChildItem $includeRoots -Recurse -Filter *.hrl | ForEach-Object {
|
||||
$full = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($full)
|
||||
$new = $text
|
||||
foreach ($record in @(Get-TopRecordForms $text | Sort-Object Start -Descending)) {
|
||||
$deduped = Remove-DuplicateFieldLines $record.Text
|
||||
if ($deduped.Removed -gt 0) {
|
||||
$new = $new.Remove($record.Start, $record.End - $record.Start).Insert($record.Start, $deduped.Text)
|
||||
$removedTotal += $deduped.Removed
|
||||
}
|
||||
}
|
||||
if ($new -ne $text) {
|
||||
[IO.File]::WriteAllText($full, $new, [Text.UTF8Encoding]::new($false))
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
|
||||
"changed_headers=$changed removed_duplicate_fields=$removedTotal"
|
||||
@@ -0,0 +1,148 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$includeRoots = @(
|
||||
'apps/game_server/include',
|
||||
'apps/game_server/include/auto_gen/cache',
|
||||
'apps/game_server/include/auto_gen/tbllog',
|
||||
'apps/game_server/include/auto_gen/proto',
|
||||
'apps/base/include',
|
||||
'apps/game_config/include',
|
||||
'apps/game_proto/include'
|
||||
)
|
||||
|
||||
$sourceRoots = @(
|
||||
'apps/game_server/src',
|
||||
'apps/game_server/db_patch',
|
||||
'apps/game_server/db_merge',
|
||||
'apps/game_server/db_clear'
|
||||
) | Where-Object { Test-Path $_ }
|
||||
|
||||
function Get-TopRecordForms([string]$text) {
|
||||
$forms = New-Object System.Collections.Generic.List[object]
|
||||
$len = $text.Length
|
||||
$i = 0
|
||||
while ($i -lt $len) {
|
||||
if (($i -eq 0 -or $text[$i - 1] -eq "`n") -and $text[$i] -eq '-' -and $text.Substring($i) -match '^-record\(\s*(''[^'']+''|[A-Za-z0-9_]+)\s*,') {
|
||||
$name = $Matches[1].Trim("'")
|
||||
$depth = 0; $inString = $false; $inAtom = $false; $escape = $false; $j = $i
|
||||
while ($j -lt $len) {
|
||||
$ch = $text[$j]
|
||||
if ($inString) {
|
||||
if ($escape) { $escape = $false } elseif ($ch -eq '\') { $escape = $true } elseif ($ch -eq '"') { $inString = $false }
|
||||
} elseif ($inAtom) {
|
||||
if ($escape) { $escape = $false } elseif ($ch -eq '\') { $escape = $true } elseif ($ch -eq "'") { $inAtom = $false }
|
||||
} else {
|
||||
if ($ch -eq '"') { $inString = $true }
|
||||
elseif ($ch -eq "'") { $inAtom = $true }
|
||||
elseif ($ch -eq '(' -or $ch -eq '[' -or $ch -eq '{') { $depth++ }
|
||||
elseif ($ch -eq ')' -or $ch -eq ']' -or $ch -eq '}') { if ($depth -gt 0) { $depth-- } }
|
||||
elseif ($ch -eq '.' -and $depth -eq 0) {
|
||||
$end = $j + 1
|
||||
$form = $text.Substring($i, $end - $i)
|
||||
$forms.Add([pscustomobject]@{ Name = $name; Start = $i; End = $end; Text = $form })
|
||||
$i = $end - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
$j++
|
||||
}
|
||||
}
|
||||
$i++
|
||||
}
|
||||
return $forms
|
||||
}
|
||||
|
||||
function Header-Score([string]$name, [string]$path) {
|
||||
$file = [IO.Path]::GetFileNameWithoutExtension($path)
|
||||
$p = $path -replace '\\', '/'
|
||||
$score = 1000
|
||||
if ($file -eq $name) { $score -= 700 }
|
||||
if ($file -eq ($name + '_c')) { $score -= 650 }
|
||||
if ($name -like 'cfg_misc*' -and $file -eq 'cfg_misc') { $score -= 800 }
|
||||
if ($name -like 'cfg_*' -and $file -eq $name) { $score -= 800 }
|
||||
if ($p -like 'apps/game_config/include/*') { $score -= 80 }
|
||||
if ($p -like 'apps/game_server/include/auto_gen/cache/*') { $score -= 70 }
|
||||
if ($p -like 'apps/game_server/include/*') { $score -= 50 }
|
||||
return $score
|
||||
}
|
||||
|
||||
function Get-RecordFields([string]$form) {
|
||||
$fields = New-Object System.Collections.Generic.HashSet[string]
|
||||
$bodyStart = $form.IndexOf('{')
|
||||
$bodyEnd = $form.LastIndexOf('}')
|
||||
if ($bodyStart -lt 0 -or $bodyEnd -le $bodyStart) { return $fields }
|
||||
$body = $form.Substring($bodyStart + 1, $bodyEnd - $bodyStart - 1)
|
||||
foreach ($m in [regex]::Matches($body, '(?m)(?:^|,)\s*([a-z][A-Za-z0-9_]*)\s*(?:=|::|,|$)')) {
|
||||
[void]$fields.Add($m.Groups[1].Value)
|
||||
}
|
||||
return $fields
|
||||
}
|
||||
|
||||
$usage = @{}
|
||||
Get-ChildItem $sourceRoots -Recurse -Filter *.erl | ForEach-Object {
|
||||
$text = [IO.File]::ReadAllText($_.FullName)
|
||||
foreach ($m in [regex]::Matches($text, '#([a-zA-Z0-9_]+)\s*\{([^}]*)\}', 'Singleline')) {
|
||||
$name = $m.Groups[1].Value
|
||||
if (-not $usage.ContainsKey($name)) {
|
||||
$usage[$name] = New-Object System.Collections.Generic.HashSet[string]
|
||||
}
|
||||
foreach ($fm in [regex]::Matches($m.Groups[2].Value, '\b([a-z][A-Za-z0-9_]*)\s*=')) {
|
||||
[void]$usage[$name].Add($fm.Groups[1].Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$records = @{}
|
||||
Get-ChildItem $includeRoots -Recurse -Filter *.hrl | ForEach-Object {
|
||||
$rel = $_.FullName.Substring((Get-Location).Path.Length + 1)
|
||||
$text = [IO.File]::ReadAllText($_.FullName)
|
||||
foreach ($record in Get-TopRecordForms $text) {
|
||||
if (-not $records.ContainsKey($record.Name)) {
|
||||
$records[$record.Name] = New-Object System.Collections.Generic.List[object]
|
||||
}
|
||||
$fieldSet = Get-RecordFields $record.Text
|
||||
if ($null -eq $fieldSet) {
|
||||
$fieldSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
}
|
||||
$records[$record.Name].Add([pscustomobject]@{
|
||||
Path = $rel
|
||||
Score = Header-Score $record.Name $rel
|
||||
Fields = $fieldSet
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$updatesByPath = @{}
|
||||
foreach ($name in $usage.Keys) {
|
||||
if (-not $records.ContainsKey($name)) { continue }
|
||||
$best = @($records[$name] | Sort-Object Score, Path | Select-Object -First 1)[0]
|
||||
$missing = @($usage[$name] | Where-Object { -not $best.Fields.Contains($_) } | Sort-Object)
|
||||
if ($missing.Count -eq 0) { continue }
|
||||
if (-not $updatesByPath.ContainsKey($best.Path)) {
|
||||
$updatesByPath[$best.Path] = @{}
|
||||
}
|
||||
$updatesByPath[$best.Path][$name] = $missing
|
||||
}
|
||||
|
||||
$changedHeaders = 0
|
||||
$addedFields = 0
|
||||
foreach ($path in $updatesByPath.Keys) {
|
||||
$full = Join-Path (Get-Location) $path
|
||||
$text = [IO.File]::ReadAllText($full)
|
||||
$forms = @(Get-TopRecordForms $text | Sort-Object Start -Descending)
|
||||
foreach ($form in $forms) {
|
||||
if (-not $updatesByPath[$path].ContainsKey($form.Name)) { continue }
|
||||
$missing = $updatesByPath[$path][$form.Name]
|
||||
$insertAt = $form.Start + $form.Text.LastIndexOf('}')
|
||||
$addition = ''
|
||||
foreach ($field in $missing) {
|
||||
$addition += ",`r`n $field = undefined"
|
||||
$addedFields++
|
||||
}
|
||||
$text = $text.Insert($insertAt, $addition)
|
||||
}
|
||||
[IO.File]::WriteAllText($full, $text, [Text.UTF8Encoding]::new($false))
|
||||
$changedHeaders++
|
||||
}
|
||||
|
||||
"changed_headers=$changedHeaders added_fields=$addedFields"
|
||||
@@ -0,0 +1,72 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$sourceDir = 'apps/game_server/cache_recovered'
|
||||
$prefixDir = 'apps/game_server/cache'
|
||||
$outDir = 'apps/game_server/cache_recovered_grouped'
|
||||
|
||||
if (-not (Test-Path $sourceDir)) {
|
||||
throw "Missing source dir: $sourceDir"
|
||||
}
|
||||
|
||||
if (Test-Path $outDir) {
|
||||
$resolved = (Resolve-Path $outDir).Path
|
||||
$root = (Resolve-Path '.').Path
|
||||
if (-not $resolved.StartsWith($root, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Refusing to clean outside workspace: $resolved"
|
||||
}
|
||||
Remove-Item -LiteralPath $resolved -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||
|
||||
$knownPrefixes = @()
|
||||
if (Test-Path $prefixDir) {
|
||||
$knownPrefixes = Get-ChildItem $prefixDir -Filter '*.table' |
|
||||
ForEach-Object { $_.BaseName } |
|
||||
Sort-Object Length -Descending
|
||||
}
|
||||
|
||||
function Get-GroupName([string]$TableName, $KnownPrefixes) {
|
||||
foreach ($prefix in $KnownPrefixes) {
|
||||
if ($TableName -eq $prefix -or $TableName.StartsWith("$prefix`_", [StringComparison]::Ordinal)) {
|
||||
return $prefix
|
||||
}
|
||||
}
|
||||
|
||||
$idx = $TableName.IndexOf('_')
|
||||
if ($idx -gt 0) {
|
||||
return $TableName.Substring(0, $idx)
|
||||
}
|
||||
return $TableName
|
||||
}
|
||||
|
||||
$groups = @{}
|
||||
Get-ChildItem $sourceDir -Filter '*.table' | Sort-Object Name | ForEach-Object {
|
||||
$tableName = $_.BaseName
|
||||
$groupName = Get-GroupName $tableName $knownPrefixes
|
||||
if (-not $groups.ContainsKey($groupName)) {
|
||||
$groups[$groupName] = [System.Collections.Generic.List[object]]::new()
|
||||
}
|
||||
[void]$groups[$groupName].Add($_)
|
||||
}
|
||||
|
||||
foreach ($groupName in ($groups.Keys | Sort-Object)) {
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
[void]$lines.Add("%% recovered grouped table file: $groupName.table")
|
||||
[void]$lines.Add("%% source: apps/game_server/cache_recovered/*.table")
|
||||
[void]$lines.Add('')
|
||||
|
||||
foreach ($file in ($groups[$groupName] | Sort-Object Name)) {
|
||||
$content = [IO.File]::ReadAllText($file.FullName).Trim()
|
||||
$content = [regex]::Replace($content, '(?m)^%% recovered from .*\r?\n', '')
|
||||
[void]$lines.Add("%% -----------------------------------------------------------------------------")
|
||||
[void]$lines.Add("%% $($file.BaseName)")
|
||||
[void]$lines.Add($content)
|
||||
[void]$lines.Add('')
|
||||
}
|
||||
|
||||
$outFile = Join-Path $outDir "$groupName.table"
|
||||
[IO.File]::WriteAllText($outFile, ($lines -join "`r`n"))
|
||||
}
|
||||
|
||||
"source_files=$((Get-ChildItem $sourceDir -Filter '*.table').Count)"
|
||||
"grouped_files=$((Get-ChildItem $outDir -Filter '*.table').Count)"
|
||||
@@ -0,0 +1,138 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @(
|
||||
'apps/base/src',
|
||||
'apps/game_config/src',
|
||||
'apps/game_proto/src',
|
||||
'apps/game_server/src',
|
||||
'apps/game_server/db_patch',
|
||||
'apps/game_server/db_merge',
|
||||
'apps/game_server/db_clear',
|
||||
'db_patch',
|
||||
'db_merge',
|
||||
'db_clear'
|
||||
) | Where-Object { Test-Path $_ }
|
||||
|
||||
$changed = 0
|
||||
$recordFiles = 0
|
||||
|
||||
function Get-AppRoot($path) {
|
||||
$full = (Resolve-Path $path).Path
|
||||
$apps = (Resolve-Path 'apps').Path
|
||||
if ($full.StartsWith($apps, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
$rest = $full.Substring($apps.Length).TrimStart('\', '/')
|
||||
$app = $rest -split '[\\/]'
|
||||
$app = $app[0]
|
||||
return Join-Path $apps $app
|
||||
}
|
||||
return (Resolve-Path '.').Path
|
||||
}
|
||||
|
||||
function Get-ModuleName($lines) {
|
||||
foreach ($line in $lines) {
|
||||
if ($line -match '^\s*-module\(([^)]+)\)\.') {
|
||||
return $Matches[1].Trim()
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Insert-Include($lines, $includeLine) {
|
||||
$lastInclude = -1
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^\s*-include(_lib)?\(') {
|
||||
$lastInclude = $i
|
||||
}
|
||||
}
|
||||
if ($lastInclude -ge 0) {
|
||||
$before = @()
|
||||
if ($lastInclude -ge 0) { $before = $lines[0..$lastInclude] }
|
||||
$after = @()
|
||||
if ($lastInclude + 1 -lt $lines.Count) { $after = $lines[($lastInclude + 1)..($lines.Count - 1)] }
|
||||
return @($before + $includeLine + $after)
|
||||
}
|
||||
|
||||
$moduleLine = -1
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
if ($lines[$i] -match '^\s*-module\(') {
|
||||
$moduleLine = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($moduleLine -ge 0) {
|
||||
$before = $lines[0..$moduleLine]
|
||||
$after = @()
|
||||
if ($moduleLine + 1 -lt $lines.Count) { $after = $lines[($moduleLine + 1)..($lines.Count - 1)] }
|
||||
return @($before + '' + $includeLine + $after)
|
||||
}
|
||||
|
||||
return @($includeLine + $lines)
|
||||
}
|
||||
|
||||
Get-ChildItem $roots -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
$text -split "`r?`n", -1 | ForEach-Object { [void]$lines.Add($_) }
|
||||
|
||||
$module = Get-ModuleName $lines
|
||||
if (-not $module) { return }
|
||||
|
||||
$newLines = [System.Collections.Generic.List[string]]::new()
|
||||
$records = [System.Collections.Generic.List[string]]::new()
|
||||
$i = 0
|
||||
while ($i -lt $lines.Count) {
|
||||
$line = $lines[$i]
|
||||
if ($line -match '^\s*-record\(') {
|
||||
$block = [System.Collections.Generic.List[string]]::new()
|
||||
while ($i -lt $lines.Count) {
|
||||
[void]$block.Add($lines[$i])
|
||||
if ($lines[$i] -match '\}\)\.') {
|
||||
break
|
||||
}
|
||||
$i++
|
||||
}
|
||||
[void]$records.Add(($block -join "`r`n"))
|
||||
$i++
|
||||
continue
|
||||
}
|
||||
[void]$newLines.Add($line)
|
||||
$i++
|
||||
}
|
||||
|
||||
if ($records.Count -eq 0) { return }
|
||||
|
||||
$appRoot = Get-AppRoot $path
|
||||
$includeDir = Join-Path $appRoot 'include\auto_records'
|
||||
New-Item -ItemType Directory -Force -Path $includeDir | Out-Null
|
||||
|
||||
$safeModule = $module -replace '[^A-Za-z0-9_]', '_'
|
||||
$hrlName = "$safeModule`_records.hrl"
|
||||
$hrlPath = Join-Path $includeDir $hrlName
|
||||
$guard = "__AUTO_RECORDS_$($safeModule.ToUpperInvariant())_HRL__"
|
||||
$hrl = @(
|
||||
"-ifndef($guard).",
|
||||
"-define($guard, true).",
|
||||
''
|
||||
) + $records + @(
|
||||
'',
|
||||
'-endif.'
|
||||
)
|
||||
if (Test-Path $hrlPath) {
|
||||
$hrlItem = Get-Item $hrlPath
|
||||
if ($hrlItem.IsReadOnly) { $hrlItem.IsReadOnly = $false }
|
||||
}
|
||||
[IO.File]::WriteAllText($hrlPath, ($hrl -join "`r`n"))
|
||||
|
||||
$include = "-include(""auto_records/$hrlName"")."
|
||||
if (($newLines -join "`n") -notmatch [regex]::Escape($include)) {
|
||||
$newLines = [System.Collections.Generic.List[string]](Insert-Include $newLines $include)
|
||||
}
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, ($newLines -join "`r`n"))
|
||||
$changed++
|
||||
$recordFiles += $records.Count
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
"moved_records=$recordFiles"
|
||||
@@ -0,0 +1,261 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$srcDir = 'apps/game_server/src/auto_gen/cache'
|
||||
$outDir = 'apps/game_server/cache_recovered'
|
||||
|
||||
if (Test-Path $outDir) {
|
||||
$resolved = (Resolve-Path $outDir).Path
|
||||
$root = (Resolve-Path '.').Path
|
||||
if (-not $resolved.StartsWith($root, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Refusing to clean outside workspace: $resolved"
|
||||
}
|
||||
Remove-Item -LiteralPath $resolved -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
|
||||
|
||||
function Read-FunctionExpr([string]$Text, [string]$Name) {
|
||||
$m = [regex]::Match($Text, "(?ms)^$([regex]::Escape($Name))\(\)\s*->\s*(?<expr>.*?)(?<!\\)\.")
|
||||
if ($m.Success) { return $m.Groups['expr'].Value.Trim() }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Parse-AtomList([string]$Expr) {
|
||||
if ([string]::IsNullOrWhiteSpace($Expr)) { return @() }
|
||||
$m = [regex]::Match($Expr, '(?s)\[(?<body>.*)\]')
|
||||
if (-not $m.Success) { return @() }
|
||||
$body = $m.Groups['body'].Value -replace '\s+', ''
|
||||
if ($body.Length -eq 0) { return @() }
|
||||
return @($body.Split(',') | ForEach-Object { $_.Trim("' ") } | Where-Object { $_ -ne '' })
|
||||
}
|
||||
|
||||
function Parse-Atom([string]$Expr) {
|
||||
if ($null -eq $Expr) { return $null }
|
||||
return (($Expr -replace '\s+', '')).Trim("' ")
|
||||
}
|
||||
|
||||
function Parse-Bool([string]$Expr, [bool]$Default) {
|
||||
if ($null -eq $Expr) { return $Default }
|
||||
$v = ($Expr -replace '\s+', '').Trim()
|
||||
if ($v -eq 'true') { return $true }
|
||||
if ($v -eq 'false') { return $false }
|
||||
return $Default
|
||||
}
|
||||
|
||||
function Decode-ErlString([string]$S) {
|
||||
$S = [regex]::Replace($S, '\\x\{([0-9A-Fa-f]+)\}', {
|
||||
param($m)
|
||||
[string][char]([Convert]::ToInt32($m.Groups[1].Value, 16))
|
||||
})
|
||||
$S = $S -replace '\\n', "`n"
|
||||
$S = $S -replace '\\t', "`t"
|
||||
$S = $S -replace '\\r', "`r"
|
||||
$S = $S -replace '\\"', '"'
|
||||
$S = $S -replace '\\\\', '\'
|
||||
return $S
|
||||
}
|
||||
|
||||
function Extract-ErlStrings([string]$Block) {
|
||||
$parts = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($m in [regex]::Matches($Block, '"((?:\\.|[^"\\])*)"')) {
|
||||
[void]$parts.Add((Decode-ErlString $m.Groups[1].Value))
|
||||
}
|
||||
return ($parts -join '')
|
||||
}
|
||||
|
||||
function Get-CreateSql([string]$Text) {
|
||||
$m = [regex]::Match($Text, '(?ms)create\([^)]*\)\s*->.*?Sql\s*=\s*<<(?<bin>.*?)\/utf8>>')
|
||||
if (-not $m.Success) { return '' }
|
||||
return Extract-ErlStrings $m.Groups['bin'].Value
|
||||
}
|
||||
|
||||
function Get-DbColumns([string]$Sql) {
|
||||
$cols = @{}
|
||||
if ([string]::IsNullOrWhiteSpace($Sql)) { return $cols }
|
||||
foreach ($m in [regex]::Matches($Sql, '`(?<name>[^`]+)`\s+(?<dbtype>bigint|int|tinyint|varchar|text|blob|mediumblob)(?:\((?<len>\d+)\))?', 'IgnoreCase')) {
|
||||
$name = $m.Groups['name'].Value
|
||||
if ($name -in @('PRIMARY', 'KEY')) { continue }
|
||||
$dbType = $m.Groups['dbtype'].Value.ToLowerInvariant()
|
||||
$len = if ($m.Groups['len'].Success) { [int64]$m.Groups['len'].Value } else { 0 }
|
||||
$cols[$name] = @{ db_type = $dbType; len = $len }
|
||||
}
|
||||
return $cols
|
||||
}
|
||||
|
||||
function Get-FieldDefault([string]$Text, [string]$Field) {
|
||||
$m = [regex]::Match($Text, "maps:get\($([regex]::Escape($Field)),\s*RecOrMap,\s*(?<def>[^)]+)\)")
|
||||
if ($m.Success) { return $m.Groups['def'].Value.Trim() }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Infer-FieldMeta([string]$Text, [string]$Table, [string]$Field, $DbCols) {
|
||||
$type = $null
|
||||
$len = $null
|
||||
|
||||
$typeMatch = [regex]::Match($Text, "\[$([regex]::Escape($Table))\s+$([regex]::Escape($Field))\]\s+type_not_(?<type>[a-z_]+)")
|
||||
if ($typeMatch.Success) {
|
||||
switch ($typeMatch.Groups['type'].Value) {
|
||||
'integer' { $type = 'int' }
|
||||
'binary' { $type = 'binary' }
|
||||
'list' { $type = 'list' }
|
||||
'map' { $type = 'map' }
|
||||
'tuple' { $type = 'tuple' }
|
||||
'atom' { $type = 'atom' }
|
||||
'boolean' { $type = 'boolean' }
|
||||
default { $type = 'any' }
|
||||
}
|
||||
}
|
||||
|
||||
$fieldPos = if ($typeMatch.Success) { $typeMatch.Index } else { $Text.IndexOf("[$Table $Field]") }
|
||||
if ($fieldPos -ge 0) {
|
||||
$nextType = [regex]::Match($Text.Substring($fieldPos + 1), "\[$([regex]::Escape($Table))\s+[^]]+\]\s+type_not_[a-z_]+")
|
||||
$sliceLen =
|
||||
if ($nextType.Success) {
|
||||
$nextType.Index + 1
|
||||
} else {
|
||||
[Math]::Min(900, $Text.Length - $fieldPos)
|
||||
}
|
||||
$slice = $Text.Substring($fieldPos, $sliceLen) -replace '"\s*"', ''
|
||||
$lenMatch = [regex]::Match($slice, 'value_len_invalid\((?<len>\d+)')
|
||||
if ($lenMatch.Success) {
|
||||
$len = [int64]$lenMatch.Groups['len'].Value
|
||||
} else {
|
||||
$maxMatch = [regex]::Match($slice, 'value_invalid\((?<max>[0-9]+|true\|false)')
|
||||
if ($maxMatch.Success) {
|
||||
switch ($maxMatch.Groups['max'].Value) {
|
||||
'9223372036854775807' { $len = 20 }
|
||||
'2147483647' { $len = 11 }
|
||||
'true|false' { $len = 8 }
|
||||
default { $len = 20 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($DbCols.ContainsKey($Field)) {
|
||||
$db = $DbCols[$Field]
|
||||
if ($null -eq $type) {
|
||||
switch ($db.db_type) {
|
||||
'bigint' { $type = 'int' }
|
||||
'int' { $type = 'int' }
|
||||
'tinyint' { $type = 'boolean' }
|
||||
'varchar' { $type = 'binary' }
|
||||
'blob' { $type = 'any' }
|
||||
'mediumblob' { $type = 'any' }
|
||||
default { $type = 'list' }
|
||||
}
|
||||
}
|
||||
if ($null -eq $len) { $len = $db.len }
|
||||
}
|
||||
|
||||
$def = Get-FieldDefault $Text $Field
|
||||
if ($null -eq $type) {
|
||||
if ($def -eq '<<>>') { $type = 'binary' }
|
||||
elseif ($def -eq '[]') { $type = 'list' }
|
||||
elseif ($def -eq '#{}') { $type = 'map' }
|
||||
elseif ($def -match '^\{') { $type = 'tuple' }
|
||||
elseif ($def -eq 'true' -or $def -eq 'false') { $type = 'boolean' }
|
||||
elseif ($def -match '^-?\d+$') { $type = 'int' }
|
||||
else { $type = 'any' }
|
||||
}
|
||||
if ($null -eq $len) {
|
||||
switch ($type) {
|
||||
'int' { $len = 20 }
|
||||
'boolean' { $len = 8 }
|
||||
'binary' { $len = 0 }
|
||||
default { $len = 0 }
|
||||
}
|
||||
}
|
||||
if ($null -eq $def -or $def -eq '') {
|
||||
switch ($type) {
|
||||
'int' { $def = '0' }
|
||||
'boolean' { $def = 'false' }
|
||||
'binary' { $def = '<<>>' }
|
||||
'list' { $def = '[]' }
|
||||
'map' { $def = '#{}' }
|
||||
'tuple' { $def = '{}' }
|
||||
default { $def = 'undefined' }
|
||||
}
|
||||
}
|
||||
|
||||
return @{ type = $type; len = $len; default = $def; db_save = $DbCols.ContainsKey($Field) }
|
||||
}
|
||||
|
||||
function Format-AtomList($List) {
|
||||
if ($List.Count -eq 0) { return '[]' }
|
||||
return '[' + (($List | ForEach-Object { $_ }) -join ', ') + ']'
|
||||
}
|
||||
|
||||
function Add-Option([System.Collections.Generic.List[string]]$Parts, [string]$Key, [string]$Value) {
|
||||
[void]$Parts.Add("$Key=>$Value")
|
||||
}
|
||||
|
||||
$files = Get-ChildItem $srcDir -Filter '*_c.erl' | Sort-Object Name
|
||||
$ok = 0
|
||||
$failed = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
foreach ($file in $files) {
|
||||
try {
|
||||
$text = [IO.File]::ReadAllText($file.FullName)
|
||||
$table = Parse-Atom (Read-FunctionExpr $text 'record_name')
|
||||
if ([string]::IsNullOrWhiteSpace($table)) { throw 'record_name/0 not found' }
|
||||
|
||||
$fields = @(Parse-AtomList (Read-FunctionExpr $text 'field_list') | Where-Object { $_ -ne '_id' })
|
||||
$keys = @(Parse-AtomList (Read-FunctionExpr $text 'key_list'))
|
||||
$indexes = @(Parse-AtomList (Read-FunctionExpr $text 'index_list'))
|
||||
$preLoad = Parse-Bool (Read-FunctionExpr $text 'is_pre_load') $false
|
||||
$clearAll = Parse-Bool (Read-FunctionExpr $text 'can_clear_all') $false
|
||||
$tableType = Parse-Atom (Read-FunctionExpr $text 'table_type')
|
||||
$dbSaveTime = Parse-Atom (Read-FunctionExpr $text 'db_save_time')
|
||||
$dbSaveCount = Parse-Atom (Read-FunctionExpr $text 'db_save_count')
|
||||
|
||||
$uidField = $null
|
||||
$uidMatch = [regex]::Match($text, 'SELECT IFNULL\(MAX\(`(?<field>[^`]+)`\), 0\)')
|
||||
if ($uidMatch.Success) { $uidField = $uidMatch.Groups['field'].Value }
|
||||
|
||||
$sql = Get-CreateSql $text
|
||||
$dbCols = Get-DbColumns $sql
|
||||
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
[void]$lines.Add('%% recovered from src/auto_gen/cache/' + $file.Name)
|
||||
[void]$lines.Add('{' + $table + ',')
|
||||
[void]$lines.Add(' [')
|
||||
|
||||
for ($i = 0; $i -lt $fields.Count; $i++) {
|
||||
$field = $fields[$i]
|
||||
$meta = Infer-FieldMeta $text $table $field $dbCols
|
||||
$comma = if ($i -lt $fields.Count - 1) { ',' } else { '' }
|
||||
$comment = $field
|
||||
$line = " {$field, $($meta.type), $($meta.len), `"$comment`", $($meta.db_save.ToString().ToLowerInvariant()), $($meta.default)}$comma"
|
||||
[void]$lines.Add($line)
|
||||
}
|
||||
|
||||
[void]$lines.Add(' ],')
|
||||
|
||||
$optParts = [System.Collections.Generic.List[string]]::new()
|
||||
if ($tableType -and $tableType -ne 'set') { Add-Option $optParts 'type' $tableType }
|
||||
if ($indexes.Count -gt 0) { Add-Option $optParts 'indexes' (Format-AtomList $indexes) }
|
||||
if ($uidField) { Add-Option $optParts 'uid_field' $uidField }
|
||||
if ($preLoad) { Add-Option $optParts 'pre_load' 'true' }
|
||||
if ($clearAll) { Add-Option $optParts 'clear_all' 'true' }
|
||||
if ($dbSaveTime -and $dbSaveTime -ne 'undefined') { Add-Option $optParts 'db_save_time' $dbSaveTime }
|
||||
if ($dbSaveCount -and $dbSaveCount -ne 'undefined') { Add-Option $optParts 'db_save_count' $dbSaveCount }
|
||||
$opt = if ($optParts.Count -eq 0) { '#{}' } else { '#{' + ($optParts -join ',') + '}' }
|
||||
|
||||
[void]$lines.Add(' ' + (Format-AtomList $keys) + ', ' + $opt)
|
||||
[void]$lines.Add('}.')
|
||||
[void]$lines.Add('')
|
||||
|
||||
$outBase = $file.BaseName -replace '_c$', ''
|
||||
$outFile = Join-Path $outDir "$outBase.table"
|
||||
[IO.File]::WriteAllText($outFile, ($lines -join "`r`n"))
|
||||
$ok++
|
||||
} catch {
|
||||
[void]$failed.Add("$($file.Name): $($_.Exception.Message)")
|
||||
}
|
||||
}
|
||||
|
||||
"recovered=$ok"
|
||||
"failed=$($failed.Count)"
|
||||
if ($failed.Count -gt 0) {
|
||||
$failed | ForEach-Object { "FAILED $_" }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = 'apps/game_server/src/behavior_tree'
|
||||
$changed = 0
|
||||
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$pattern = '(?ms)^\s*-type node_id\(\) :: integer\(\)\.\s*' +
|
||||
'-record\(tree_node,\{.*?' +
|
||||
'-type bt_operator\(\) :: integer\(\)\.\s*'
|
||||
$new = [regex]::Replace($text, $pattern, '')
|
||||
$pattern2 = '(?ms)^\s*-type node_id\(\) :: integer\(\)\.\s*' +
|
||||
'-record\(tree_node,\{.*?' +
|
||||
'-type bt_status\(\) :: 1 \| 2 \| 3 \| 4\.\s*'
|
||||
$new = [regex]::Replace($new, $pattern2, '')
|
||||
$battlePattern = '(?ms)^\s*(?:-type object_id\(\) :: integer\(\)\.\s*)?' +
|
||||
'-record\(battle_object,\{.*?' +
|
||||
'-record\(battle_command_camp_info,\{.*?\}\)\.\s*'
|
||||
$new = [regex]::Replace($new, $battlePattern, '')
|
||||
$battleObjectPattern = '(?ms)^\s*(?:-type object_id\(\) :: integer\(\)\.\s*)?' +
|
||||
'-record\(battle_object,\{.*?' +
|
||||
'-record\(battle_action_object,\{.*?\}\)\.\s*'
|
||||
$new = [regex]::Replace($new, $battleObjectPattern, '')
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) {
|
||||
$_.IsReadOnly = $false
|
||||
}
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,37 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @(
|
||||
'apps/game_server/src',
|
||||
'apps/game_server/db_patch',
|
||||
'apps/game_server/db_merge',
|
||||
'apps/game_server/db_clear'
|
||||
)
|
||||
$changed = 0
|
||||
|
||||
$patterns = @(
|
||||
'(?ms)^\s*(?:-type object_id\(\) :: integer\(\)\.\s*)?-record\(battle_object,\{.*?-record\(battle_command_camp_info,\{.*?\}\)\.\s*',
|
||||
'(?ms)^\s*(?:-type object_id\(\) :: integer\(\)\.\s*)?-record\(battle_object,\{.*?-record\(battle_action_object,\{.*?\}\)\.\s*',
|
||||
'(?ms)^\s*-record\(battle_command_camp_info,\{.*?-record\(battle_processing,\{.*?\}\)\.\s*',
|
||||
'(?ms)^\s*-record\(battle_command_camp_info,\{.*?\}\)\.\s*'
|
||||
)
|
||||
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$new = $text
|
||||
foreach ($pattern in $patterns) {
|
||||
$new = [regex]::Replace($new, $pattern, '')
|
||||
}
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) {
|
||||
$_.IsReadOnly = $false
|
||||
}
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,20 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @('apps/game_server/src', 'apps/game_server/db_patch', 'apps/game_server/db_merge', 'apps/game_server/db_clear')
|
||||
$changed = 0
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$new = [regex]::Replace($text, '^\s*-type big_battle_record_type\(\) :: .*?\.\s*$', '', 'Multiline')
|
||||
$new = [regex]::Replace($new, '^\s*-type big_battle_grab_type\(\) :: .*?\.\s*$', '', 'Multiline')
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,19 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @('apps/game_server/src', 'apps/game_server/db_patch', 'apps/game_server/db_merge', 'apps/game_server/db_clear')
|
||||
$changed = 0
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$new = [regex]::Replace($text, '^\s*-type farm_log_arg_item\(\) :: .*?\.\s*$', '', 'Multiline')
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,20 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @('apps/game_server/src', 'apps/game_server/db_patch', 'apps/game_server/db_merge', 'apps/game_server/db_clear')
|
||||
$changed = 0
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$new = [regex]::Replace($text, '^\s*-type privilege_id\(\) :: .*?\.\s*$', '', 'Multiline')
|
||||
$new = [regex]::Replace($new, '^\s*-type privilege_source\(\) :: .*?\.\s*$', '', 'Multiline')
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,31 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$roots = @('apps/game_server/src', 'apps/game_server/db_patch', 'apps/game_server/db_merge', 'apps/game_server/db_clear')
|
||||
$patterns = @(
|
||||
'^\s*-type tutor_type\(\) :: .*?\.\s*$',
|
||||
'^\s*-type tutor_op\(\) :: .*?\.\s*$',
|
||||
'^\s*-type tutor_sub_type\(\) :: .*?\.\s*$',
|
||||
'^\s*-type tutor_dismiss_type\(\) :: .*?\.\s*$',
|
||||
'^\s*-type tutor_relation_net_privilege_state\(\) :: .*?\.\s*$',
|
||||
'^\s*-type tutor_guide_status\(\) :: .*?\.\s*$'
|
||||
)
|
||||
$changed = 0
|
||||
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -Recurse -Filter *.erl | ForEach-Object {
|
||||
$path = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$new = $text
|
||||
foreach ($pattern in $patterns) {
|
||||
$new = [regex]::Replace($new, $pattern, '', 'Multiline')
|
||||
}
|
||||
if ($new -ne $text) {
|
||||
if ($_.IsReadOnly) { $_.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($path, $new)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,58 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$files = Get-ChildItem apps -Recurse -Filter *.erl
|
||||
$changed = 0
|
||||
|
||||
foreach ($file in $files) {
|
||||
$text = [IO.File]::ReadAllText($file.FullName)
|
||||
if ($text -notmatch '-include\("auto_records/') { continue }
|
||||
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
$text -split "`r?`n", -1 | ForEach-Object { [void]$lines.Add($_) }
|
||||
|
||||
$includes = [System.Collections.Generic.List[string]]::new()
|
||||
$kept = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($line in $lines) {
|
||||
if ($line -match '^\s*-include\("auto_records/[^"]+"\)\.') {
|
||||
if (-not $includes.Contains($line.Trim())) {
|
||||
[void]$includes.Add($line.Trim())
|
||||
}
|
||||
} else {
|
||||
[void]$kept.Add($line)
|
||||
}
|
||||
}
|
||||
if ($includes.Count -eq 0) { continue }
|
||||
|
||||
$insertAt = -1
|
||||
for ($i = 0; $i -lt $kept.Count; $i++) {
|
||||
$line = $kept[$i]
|
||||
if ($line -match '^\s*-(type|spec)\b' -or
|
||||
$line -match '^\s*[a-z][A-Za-z0-9_@]*\s*\(') {
|
||||
$insertAt = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($insertAt -lt 0) {
|
||||
for ($i = 0; $i -lt $kept.Count; $i++) {
|
||||
if ($kept[$i] -match '^\s*-module\(') {
|
||||
$insertAt = $i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($insertAt -lt 0) { $insertAt = 0 }
|
||||
|
||||
$newLines = @()
|
||||
if ($insertAt -gt 0) { $newLines += $kept[0..($insertAt - 1)] }
|
||||
$newLines += $includes
|
||||
if ($insertAt -lt $kept.Count) { $newLines += $kept[$insertAt..($kept.Count - 1)] }
|
||||
|
||||
$newText = $newLines -join "`r`n"
|
||||
if ($newText -ne $text) {
|
||||
if ($file.IsReadOnly) { $file.IsReadOnly = $false }
|
||||
[IO.File]::WriteAllText($file.FullName, $newText)
|
||||
$changed++
|
||||
}
|
||||
}
|
||||
|
||||
"changed_files=$changed"
|
||||
@@ -0,0 +1,234 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$includeRoots = @(
|
||||
'apps/game_server/include',
|
||||
'apps/game_server/include/auto_gen/cache',
|
||||
'apps/game_server/include/auto_gen/tbllog',
|
||||
'apps/game_server/include/auto_gen/proto',
|
||||
'apps/base/include',
|
||||
'apps/game_config/include',
|
||||
'apps/game_proto/include'
|
||||
)
|
||||
|
||||
$sourceRoots = @(
|
||||
'apps/game_server/src',
|
||||
'apps/game_server/db_patch',
|
||||
'apps/game_server/db_merge',
|
||||
'apps/game_server/db_clear'
|
||||
) | Where-Object { Test-Path $_ }
|
||||
|
||||
function Get-TopRecordForms([string]$text) {
|
||||
$forms = New-Object System.Collections.Generic.List[object]
|
||||
$len = $text.Length
|
||||
$i = 0
|
||||
while ($i -lt $len) {
|
||||
if (($i -eq 0 -or $text[$i - 1] -eq "`n") -and $text[$i] -eq '-' -and $text.Substring($i) -match '^-record\(\s*(''[^'']+''|[A-Za-z0-9_]+)\s*,') {
|
||||
$name = $Matches[1].Trim("'")
|
||||
$depth = 0
|
||||
$inString = $false
|
||||
$inAtom = $false
|
||||
$escape = $false
|
||||
$j = $i
|
||||
while ($j -lt $len) {
|
||||
$ch = $text[$j]
|
||||
if ($inString) {
|
||||
if ($escape) { $escape = $false }
|
||||
elseif ($ch -eq '\') { $escape = $true }
|
||||
elseif ($ch -eq '"') { $inString = $false }
|
||||
} elseif ($inAtom) {
|
||||
if ($escape) { $escape = $false }
|
||||
elseif ($ch -eq '\') { $escape = $true }
|
||||
elseif ($ch -eq "'") { $inAtom = $false }
|
||||
} else {
|
||||
if ($ch -eq '"') { $inString = $true }
|
||||
elseif ($ch -eq "'") { $inAtom = $true }
|
||||
elseif ($ch -eq '(' -or $ch -eq '[' -or $ch -eq '{') { $depth++ }
|
||||
elseif ($ch -eq ')' -or $ch -eq ']' -or $ch -eq '}') { if ($depth -gt 0) { $depth-- } }
|
||||
elseif ($ch -eq '.' -and $depth -eq 0) {
|
||||
$end = $j + 1
|
||||
while ($end -lt $len -and ($text[$end] -eq "`r" -or $text[$end] -eq "`n")) { $end++ }
|
||||
$form = $text.Substring($i, $end - $i)
|
||||
$forms.Add([pscustomobject]@{ Name = $name; Start = $i; End = $end; Text = $form })
|
||||
$i = $end - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
$j++
|
||||
}
|
||||
}
|
||||
$i++
|
||||
}
|
||||
return $forms
|
||||
}
|
||||
|
||||
function Normalize-Record([string]$text) {
|
||||
return ([regex]::Replace($text, '\s+', '')).Trim()
|
||||
}
|
||||
|
||||
function Header-Score([string]$name, [string]$path) {
|
||||
$file = [IO.Path]::GetFileNameWithoutExtension($path)
|
||||
$p = $path -replace '\\', '/'
|
||||
$score = 1000
|
||||
if ($file -eq $name) { $score -= 700 }
|
||||
if ($file -eq ($name + '_c')) { $score -= 650 }
|
||||
if ($name -like 'cfg_misc*' -and $file -eq 'cfg_misc') { $score -= 800 }
|
||||
if ($name -like 'cfg_*' -and $file -eq $name) { $score -= 800 }
|
||||
if ($name -like 'p_*' -and $file -eq 'proto_type_pb') { $score -= 500 }
|
||||
if ($p -like 'apps/game_config/include/*') { $score -= 80 }
|
||||
if ($p -like 'apps/game_server/include/auto_gen/cache/*') { $score -= 70 }
|
||||
if ($p -like 'apps/game_server/include/auto_gen/proto/*') { $score -= 60 }
|
||||
if ($p -like 'apps/game_server/include/*') { $score -= 50 }
|
||||
return $score
|
||||
}
|
||||
|
||||
function New-HeaderPath([string]$name) {
|
||||
if ($name -like 'cfg_misc*') { return 'apps/game_config/include/cfg_misc.hrl' }
|
||||
if ($name -like 'cfg_*') { return "apps/game_config/include/$name.hrl" }
|
||||
if ($name -like 'p_*') { return 'apps/game_server/include/auto_gen/proto/proto_type_pb.hrl' }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Ensure-HeaderRecord([string]$path, [string]$name, [string]$form) {
|
||||
$full = Join-Path (Get-Location) $path
|
||||
if (-not (Test-Path $full)) {
|
||||
$guard = '__' + (($name.ToUpperInvariant()) -replace '[^A-Z0-9_]', '_') + '_HRL__'
|
||||
$content = @(
|
||||
"-ifndef($guard).",
|
||||
"-define($guard, true).",
|
||||
"",
|
||||
$form.Trim(),
|
||||
"",
|
||||
"-endif.",
|
||||
""
|
||||
) -join "`r`n"
|
||||
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($full)) | Out-Null
|
||||
[IO.File]::WriteAllText($full, $content, [Text.UTF8Encoding]::new($false))
|
||||
return $true
|
||||
}
|
||||
|
||||
$text = [IO.File]::ReadAllText($full)
|
||||
$records = @(Get-TopRecordForms $text | Where-Object { $_.Name -eq $name })
|
||||
if ($records.Count -gt 0) {
|
||||
$record = $records[0]
|
||||
if ((Normalize-Record $record.Text) -eq (Normalize-Record $form)) {
|
||||
return $false
|
||||
}
|
||||
$new = $text.Remove($record.Start, $record.End - $record.Start).Insert($record.Start, $form.Trim() + "`r`n`r`n")
|
||||
[IO.File]::WriteAllText($full, $new, [Text.UTF8Encoding]::new($false))
|
||||
return $true
|
||||
}
|
||||
|
||||
$insert = $text.Length
|
||||
$endifs = [regex]::Matches($text, '(?m)^-endif\.\s*$')
|
||||
if ($endifs.Count -gt 0 -and $text -match '(?m)^-ifndef\(') {
|
||||
$insert = $endifs[$endifs.Count - 1].Index
|
||||
}
|
||||
$addition = $form.Trim() + "`r`n`r`n"
|
||||
$newText = $text.Insert($insert, $addition)
|
||||
[IO.File]::WriteAllText($full, $newText, [Text.UTF8Encoding]::new($false))
|
||||
return $true
|
||||
}
|
||||
|
||||
$headerIndex = @{}
|
||||
Get-ChildItem $includeRoots -Recurse -Filter *.hrl | ForEach-Object {
|
||||
$rel = $_.FullName.Substring((Get-Location).Path.Length + 1)
|
||||
$text = [IO.File]::ReadAllText($_.FullName)
|
||||
foreach ($record in Get-TopRecordForms $text) {
|
||||
if (-not $headerIndex.ContainsKey($record.Name)) {
|
||||
$headerIndex[$record.Name] = New-Object System.Collections.Generic.List[object]
|
||||
}
|
||||
$headerIndex[$record.Name].Add([pscustomobject]@{
|
||||
Path = $rel
|
||||
Include = [IO.Path]::GetFileName($rel)
|
||||
Score = Header-Score $record.Name $rel
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$sourceRecords = @{}
|
||||
Get-ChildItem $sourceRoots -Recurse -Filter *.erl | ForEach-Object {
|
||||
$text = [IO.File]::ReadAllText($_.FullName)
|
||||
foreach ($record in Get-TopRecordForms $text) {
|
||||
if (-not $sourceRecords.ContainsKey($record.Name)) {
|
||||
$sourceRecords[$record.Name] = New-Object System.Collections.Generic.List[string]
|
||||
}
|
||||
$sourceRecords[$record.Name].Add($record.Text)
|
||||
}
|
||||
}
|
||||
|
||||
$recordTarget = @{}
|
||||
$headerChanged = 0
|
||||
$skippedNames = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($name in $sourceRecords.Keys) {
|
||||
$forms = @($sourceRecords[$name] | ForEach-Object { [pscustomobject]@{ Raw = $_; Norm = Normalize-Record $_ } })
|
||||
$uniqueNorms = @($forms | Select-Object -ExpandProperty Norm -Unique)
|
||||
if ($uniqueNorms.Count -ne 1) {
|
||||
if ($skippedNames.Count -lt 80) { $skippedNames.Add("$name(multiple)") }
|
||||
continue
|
||||
}
|
||||
$form = ($forms | Select-Object -First 1).Raw
|
||||
$target = $null
|
||||
if ($headerIndex.ContainsKey($name)) {
|
||||
$target = @($headerIndex[$name] | Sort-Object Score, Include | Select-Object -First 1)[0].Path
|
||||
} else {
|
||||
$target = New-HeaderPath $name
|
||||
}
|
||||
if (-not $target) {
|
||||
if ($skippedNames.Count -lt 80) { $skippedNames.Add($name) }
|
||||
continue
|
||||
}
|
||||
if (Ensure-HeaderRecord $target $name $form) { $headerChanged++ }
|
||||
$recordTarget[$name] = [pscustomobject]@{ Path = $target; Include = [IO.Path]::GetFileName($target) }
|
||||
}
|
||||
|
||||
$changedFiles = 0
|
||||
$removedRecords = 0
|
||||
Get-ChildItem $sourceRoots -Recurse -Filter *.erl | ForEach-Object {
|
||||
$full = $_.FullName
|
||||
$text = [IO.File]::ReadAllText($full)
|
||||
$records = @(Get-TopRecordForms $text | Where-Object { $recordTarget.ContainsKey($_.Name) })
|
||||
if ($records.Count -eq 0) { return }
|
||||
|
||||
$includes = New-Object System.Collections.Generic.HashSet[string]
|
||||
foreach ($record in $records) {
|
||||
[void]$includes.Add($recordTarget[$record.Name].Include)
|
||||
}
|
||||
|
||||
$new = $text
|
||||
foreach ($record in @($records | Sort-Object Start -Descending)) {
|
||||
$new = $new.Remove($record.Start, $record.End - $record.Start)
|
||||
}
|
||||
|
||||
$existing = [regex]::Matches($new, '^-include(?:_lib)?\("([^"]+)"\)\.', 'Multiline') |
|
||||
ForEach-Object { [IO.Path]::GetFileName($_.Groups[1].Value) }
|
||||
foreach ($include in $existing) {
|
||||
[void]$includes.Remove($include)
|
||||
}
|
||||
|
||||
if ($includes.Count -gt 0) {
|
||||
$includeLines = (($includes | Sort-Object | ForEach-Object { '-include("' + $_ + '").' }) -join "`r`n") + "`r`n"
|
||||
$lastInclude = $null
|
||||
$includeMatches = [regex]::Matches($new, '^-include(?:_lib)?\("[^"]+"\)\.[\r\n]+', 'Multiline')
|
||||
if ($includeMatches.Count -gt 0) {
|
||||
$lastInclude = $includeMatches[$includeMatches.Count - 1]
|
||||
$new = $new.Insert($lastInclude.Index + $lastInclude.Length, $includeLines)
|
||||
} else {
|
||||
$module = [regex]::Match($new, '^-module\([^\r\n]+\)\.[\r\n]+', 'Multiline')
|
||||
if ($module.Success) {
|
||||
$new = $new.Insert($module.Index + $module.Length, $includeLines)
|
||||
} else {
|
||||
$new = $includeLines + $new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[IO.File]::WriteAllText($full, $new, [Text.UTF8Encoding]::new($false))
|
||||
$changedFiles++
|
||||
$removedRecords += $records.Count
|
||||
}
|
||||
|
||||
"header_changed=$headerChanged changed_files=$changedFiles removed_records=$removedRecords target_records=$($recordTarget.Count) skipped=$($skippedNames.Count)"
|
||||
if ($skippedNames.Count -gt 0) {
|
||||
"skipped_samples=$([string]::Join(',', $skippedNames))"
|
||||
}
|
||||
Reference in New Issue
Block a user