Skip to content

Instantly share code, notes, and snippets.

@joelonsql
Last active April 3, 2019 11:33
Show Gist options
  • Save joelonsql/f43e2fe2f9453c69754566e5fc655a55 to your computer and use it in GitHub Desktop.
Save joelonsql/f43e2fe2f9453c69754566e5fc655a55 to your computer and use it in GitHub Desktop.
Script to convert Bucklescript string interpolation to normal OCaml using Printf.sprintf
#!/usr/bin/perl
#
# jpipe2sprintf
#
# Script to convert Bucklescript string interpolation [1]
# to normal OCaml using Printf.sprintf.
#
# Note: It replaces any such occurrences, even inside comments.
# If anyone would like to fix this script to leave comments
# untouched, such a patch would be most welcomed.
#
# [1] https://bucklescript.github.io/docs/en/common-data-types.html#interpolation
#
# Example:
#
# Input:
#
# let foo = "world" in
# let bar = "backslash" in
# let baz = {j|Hello $foo \$dollar 100% "quotes" \\$bar|j} in
# print_endline baz /* Hello world $dollar 100% "quotes" \backslash */
#
# Output:
#
# let foo = "world" in
# let bar = "backslash" in
# let baz = Printf.sprintf "Hello %s $dollar 100%% \"quotes\" \\%s" foo bar in
# print_endline baz /* Hello world $dollar 100% "quotes" \backslash */
#
# Author: Joel Jakobsson <joel@compiler.org>
# License: MIT
use strict;
use warnings;
use File::Slurp qw(slurp write_file);
sub usage {
print "Usage: $0 [file containing {j| ... |j} expressions]\n";
exit(0);
}
usage() unless @ARGV == 1 && -f $ARGV[0];
my $code = slurp($ARGV[0]);
exit(0) unless $code =~ m/\{j\|.*?\|j\}/s;
while ($code =~ m/\{j\|(.*?)\|j\}/s) {
my $jpipe = $&;
my $sprintf = $1;
my @variables;
# if jpipe contains % they must be written %%
# since % has special meaning in sprintf.
$sprintf =~ s/%/%%/g;
# if jpipe contains " they must be escaped
$sprintf =~ s/"/\\"/g;
while ($sprintf =~ m!
(?:^|[^\\]) # at beginning or outside escape sequence
(?:\\.)* # consume any number of escaped characters
\$([a-z_][a-zA-Z0-9_']*)
# according to section "7.1 Lexical conventions"
# https://caml.inria.fr/pub/docs/manual-ocaml/lex.html
!xs) {
my $match = $&;
my $variable = $1;
my $replacement = $match;
$replacement =~ s/\$\Q$variable\E$/%s/;
$sprintf =~ s/\Q$match\E/$replacement/;
push @variables, $variable;
}
# if jpipe contains escaped dollars they must be un-escaped
$sprintf =~ s/
(
(?:^|[^\\]) # at beginning or outside escape sequence
(?:\\[^\$])* # consume any number of escaped non-dollar characters
)
\\\$
/$1\$/xsg;
$code =~ s/\Q$jpipe\E/(Printf.sprintf "$sprintf" @variables)/;
}
write_file($ARGV[0], $code);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment