Skip to content

Instantly share code, notes, and snippets.

@jemc
Last active August 10, 2017 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jemc/95969e3e2b58ddb0dede138c737907f5 to your computer and use it in GitHub Desktop.
Save jemc/95969e3e2b58ddb0dede138c737907f5 to your computer and use it in GitHub Desktop.
#!/usr/bin/bash
##
# In Pony 0.16.0, we introduced a breaking syntax change that affects all calls
# to partial functions (functions that can raise an error).
#
# All partial function calls are now required to be followed by a question mark.
#
# For example, `iterator.next()` is now `iterator.next()?`, providing a visual
# indication at the call site of any places where an error may be raised.
#
# Call sites to partial functions that fail to include the question mark
# will result in a compiler error that looks like this:
#
# /tmp/test.pony:5:19: call is not partial but the method is - a question mark
# is required after this call
# iterator.next()
# ^
# Info:
# /tmp/ponyc/packages/builtin/array.pony:578:24: method is here
# fun ref next(): B->A ? =>
# ^
#
# See this RFC for more details:
# https://github.com/ponylang/rfcs/pull/82
#
# As you might imagine, this change will affect nearly every Pony codebase,
# so we want to provide support for developers who are making this transition.
#
# This script is that support. It is an executable code migration tool that
# can ingest Pony compiler errors in the format emitted by ponyc to STDERR
# and make the necessary modifications to the Pony source code files that caused
# the errors, adding the question mark wherever it is needed for compliance
# with the new syntax requirement.
#
# This script will ignore any other errors in the ingested compiler output.
#
# An example invocation of this script might look like:
#
# ponyc |& bash ./pony-explicit-partial-calls.bash
#
# This script requires Bash version 4 for associative arrays.
# MacOS users who are missing Bash 4 can install it using homebrew:
# https://github.com/Toberumono/Miscellaneous/wiki/Installing-Bash-4.3-on-Mac-OSX
#
# A Windows-compatible implementation of this script is available at:
# https://gist.github.com/kulibali/cd5caf3a32d510bb86412f3fd4d52d0f
#
# If you need any other assistance with this transition, please reach out to
# us on IRC or on the mailing list. We're here to help!
#
set -e
error_pattern='^(.+):([0-9]+):([0-9]+): call is not partial but'
error_key_pattern='^(.+):([0-9]+)$'
tmpfile=`mktemp -t pony.XXXXXXXX`
declare -A errors
# Iterate over each line from stdin, which is expected to be the stderr stream
# of a failed Pony compilation that includes the error message we're targetting.
# Collect the errors into the associative array named `errors`, with each key
# being a filename and row number (separated by colon), and each value being
# a list of matching error columns on that row of that file, separated by colon.
while IFS=$'\n' read -r line; do
if [[ $line =~ $error_pattern ]]; then
error_filename="${BASH_REMATCH[1]}"
error_row="${BASH_REMATCH[2]}"
error_col="${BASH_REMATCH[3]}"
error_cols_this_row=${errors["$error_filename:$error_row"]}
error_cols_this_row="$error_cols_this_row:$error_col"
errors["$error_filename:$error_row"]="$error_cols_this_row"
fi
done
# Iterate over each gathered error, so we can fix it.
for error_key in "${!errors[@]}"; do
if [[ $error_key =~ $error_key_pattern ]]; then
error_filename="${BASH_REMATCH[1]}"
error_row="${BASH_REMATCH[2]}"
error_cols="${errors[$error_key]}"
# Split error_cols into an array, using ':' as the separator.
IFS=':' read -r -a error_cols <<< "$error_cols"
# Remove the (empty) first element of error_cols.
unset error_cols[0]
# Sort error_cols into reverse numeric order, keeping unique columns only.
# This keeps earlier edits on a line from affecting the index of later ones.
IFS=$'\n' error_cols=($(sort -r -n -u <<<"${error_cols[*]}"))
unset IFS
# For each error, open the file to edit it and fix the error.
for error_col in "${error_cols[@]}"; do
# Iterate over each line in the file, emitting the (possibly altered)
# line to stdout, which gets redirected to be written to a tmpfile.
row=0
while IFS=$'\n' read -r line; do
row=$(( $row + 1 ))
# Compare the row number we're at with the row number of the error,
# emitting the altered line if a match, otherwise emitting it as normal.
if [[ $row == $error_row ]]; then
# Insert a '?' character at the appropriate column in the line.
printf '%s?%s\n' "${line:0:$error_col}" "${line:$error_col}"
else
# Print the line, unaltered.
echo "$line"
fi
done < "$error_filename" > "$tmpfile"
# Finish the edit of the file by moving the tmpfile into its place.
mv "$tmpfile" "$error_filename"
done
fi
done
echo DONE
@jmiven
Copy link

jmiven commented Aug 10, 2017

Thank you for the script!

I have some suggestions:

  • if for some reason the last line of the source file doesn't end with a newline, the script will drop it. I saw this while opening a PR to port Acolyte to the new syntax: src/datast/matrix.pony didn't have a final newline and as a result line 49 got dropped.
    A way to fix this would be to replace line 103 by
while IFS=$'\n' read -r line || [[ -n "$line" ]]; do
  • less importantly, /usr/bin/bash isn't present on all systems (for example, it is /bin/bash on Ubuntu 17.04). I believe that having #!/usr/bin/env bash as shebang would make the script easier to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment