aboutsummaryrefslogtreecommitdiff
path: root/tools/replace.nom
blob: aa5d08c74b5528bd6cbeca79020214c9c66d644e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env nomsu -V6.14
#
    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):
    fail ("
        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))):
    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 = ($(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:
        fail "File does not exist: \$filename"
    $code = (NomsuCode from ($Source $filename 1 (size of $file)) $file)
    try:
        $tree = ($code parsed)
    ..if it fails $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)