#!/usr/bin/env nomsu -V7.0.0
###
    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 "filesystem"
use "consolecolor"
use "commandline"

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

command line program with $args:
    if (#$args.extras < 2):
        fail ("
            Usage: nomsu -t replace [--literal="$v1 $v2..."] <pattern> <replacement> file1 file2...
        ")
    $pattern = $args.extras.1
    $replacement = $args.extras.2
    $pattern_tree = ($pattern parsed)
    $replacement_tree = ($replacement parsed)
    $literal_vars = {}
    if $args.literal:
        for $var in ($args.literal, all matches of "$([^ ]*)"):
            $literal_vars.$var = (yes)
    
    if (($pattern_tree.type == "Var") and (not $literal_vars.($pattern_tree.1))):
        fail "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 = ($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:
            fail "File does not exist: \$filename"
        $code = (NomsuCode from ($Source $filename 1 #$file) $file)
        try:
            $tree = ($code parsed)
        ..if it fails with $msg:
            if $args.q:
                unless $args.i:
                    say $code
            ..else:
                say $msg
        
        unless $tree:
            do next $filename
        
        $replaced = {}
        $matched = {}
        $user_answers = {}
        ($tree with replacements) means
            $tree, with
                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 $values)
                        if ($args.i and (not $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 $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)