aboutsummaryrefslogtreecommitdiff
path: root/lib/tools/replace.nom
blob: aea1b035a389b1b892afa48fd2ee579b392df3bb (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
146
147
148
#!/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)