#!/usr/bin/env nomsu -V6.13.12.8
#
    This is a tool to replace syntax trees with something new.
    
    Usage:
        nomsu -t replace [-i] [-f] [-q] [--literal="$v1 $v2..."] <pattern> <replacement> file1 file2...
    
    Example:
        nomsu -t replace "($1 and $2) and $3" "all of [$1, $2, $3]" my_file.nom
    
    If the "-i" flag is used, the file(s) will be edited in-place.
    When editing in-place, if the "-f" flag is not used, each change will be
        run past the user first.
    If the "-q" flag is used and a file fails to parse, the original file
        contents will be output.
    If no files are passed in, this will read from stdin.
    
use "lib/os.nom"
use "lib/consolecolor.nom"

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if ((#$(COMMAND LINE ARGS).extras) < 2):
    barf ("
        Usage: nomsu -t replace [--literal="$v1 $v2..."] <pattern> <replacement> file1 file2...
    ")
$pattern = $(COMMAND LINE ARGS).extras.1
$replacement = $(COMMAND LINE ARGS).extras.2
$pattern_tree = ($pattern parsed)
$replacement_tree = ($replacement parsed)
$literal_vars = {}
if $(COMMAND LINE ARGS).literal:
    for $var in ($(COMMAND LINE ARGS).literal, all matches of "$([^ ]*)"):
        $literal_vars.$var = (yes)

if (($pattern_tree.type == "Var") and (not $literal_vars.($pattern_tree.1))):
    barf "Pattern matches every part of the file."

$pattern_vars = {
    : for $ in recursive $pattern_tree:
        if (($.type == "Var") and (not $literal_vars.($.1))): add $.1
        for $child in $:
            if ($child is a "Syntax Tree"):
                recurse $ on $child
}

# TODO: support wildcards and unpacking
    e.g. nomsu -t replace "test(: $test; *$more_tests)" "*$more_tests; *$test"
($tree matches $patt with $substitution_values) means:
    # TODO: optimize
    $substitution_values = {: for $k = $v in $substitution_values: add $k = $v}
    when:
        (not ($tree is syntax tree)): return (no)
        (($patt.type == "Var") and $pattern_vars.($patt.1)):
            if $substitution_values.($patt.1):
                if ($tree == $substitution_values.($patt.1)):
                    return $substitution_values
                ..else:
                    return (nil)
            ..else:
                $substitution_values.($patt.1) = $tree
                return $substitution_values
        ($tree.type != $patt.type): return (nil)
        ($tree.type == "Action"):
            if (($tree, get stub) != ($patt, get stub)): return (nil)
    
    for $ in 1 to (#$patt):
        if ($patt.$ is syntax tree):
            $new_values = ($tree.$ matches $patt.$ with $substitution_values)
            unless $new_values:
                return (nil)
            
            for $k = $v in $new_values:
                $substitution_values.$k = $v
        ..else:
            unless ($tree.$ == $patt.$): return (nil)
    
    if ((#$tree) != (#$patt)): return (nil)
    return $substitution_values

$filenames = ($(COMMAND LINE ARGS).extras, from 3 to -1)
if ((#$filenames) == 0):
    say ("
        Warning: searching stdin (ctrl-d to abort). To avoid this message, use nomsu -t find -
    ")
    $filenames = ["stdin"]

for $filename in $filenames:
    $file = (read file $filename)
    unless $file:
        barf "File does not exist: \$filename"
    $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file)
    try:
        $tree = ($code parsed)
    ..and if it barfs $msg:
        if $(COMMAND LINE ARGS).q:
            unless $(COMMAND LINE ARGS).i:
                say $code
        ..else:
            say $msg
    
    unless $tree:
        do next $filename
    
    $replaced = {}
    $matched = {}
    $user_answers = {}
    ($tree with replacements) means
        $tree, map
            for $t:
                $values = ($t matches $pattern_tree with {})
                if $values:
                    $matched.$t = (yes)
                    for $k = $v in $values:
                        $values.$k = ($v with replacements)
                    $ret = ($replacement_tree with vars $values)
                    if ($(COMMAND LINE ARGS).i and (not $(COMMAND LINE ARGS).f)):
                        if ($user_answers.$t == (nil)):
                            if ((#$user_answers) > 0): say ""
                            $user_answers.$t = "n"
                            say "\(bright)Should this:"
                            say ("
                                    \(bright)\(yellow)\("\(($t with replacements) as nomsu)", with "\n" -> "\n    ")\
                                ..\(reset color)
                            ")
                            say "\(bright)..be replaced with:"
                            
                            say ("
                                    \(bright)\(blue)\("\($ret as nomsu)", with "\n" -> "\n    ")\(reset color)
                            ")
                            
                            $user_answers.$t = (ask "\(bright)..? [Y/n]\(reset color) ")
                        
                        if ($user_answers.$t == "n"): return (nil)
                    $replaced.$t = (yes)
                    return $ret
    $tree2 = ($tree with replacements)
    if $(COMMAND LINE ARGS).i:
        if ((#$user_answers) > 0): say ""
        say ("
            \(#$replaced)/\(#$matched) replacement\("" if ((#$replaced) == 1) else "s") in \$filename
        ")
        
        if ((#$replaced) > 0):
            write "\($tree2 as nomsu)" to file $filename
    ..else:
        say ($tree2 as nomsu)