nomsu/lib/tools/replace.nom
Bruce Hill 72d699fe86 Bunch of changes:
- Added shebangs to generated code output
- SyntaxTree:map() -> SyntaxTree:with(), and corresponding changes to
metaprogramming API
- Added (return Lua 1) shorthand for (return (Lua 1))
- (1 and 2 and 3) compile rule mapping to -> (1 and (*extra arguments*))
- Don't scan for errors, just report them when compiling
- Syntax changes:
    - Added prefix actions (e.g. #$foo)
    - Operator chars now include utf8 chars
    - Ditch "escaped nomsu" type (use (\ 1) compile action instead)
2019-02-05 15:47:01 -08:00

146 lines
5.7 KiB
Plaintext
Executable File

#!/usr/bin/env nomsu -V6.15.13.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 "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 (size of $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 vars $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)