#!/usr/bin/env bash
# Ask five engines the same questions, and print what they answer.
#
# The table on this page is generated by running this, not by reading
# documentation. Every cell is one engine being handed one pattern and either
# compiling it or refusing it -- "yes" means it compiled, nothing more.
set -u

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

cat > "$tmp/P.java" <<'JAVA'
public class P {
  public static void main(String[] args) {
    try { java.util.regex.Pattern.compile(args[0]); System.out.println("yes"); }
    catch (Exception e) { System.out.println("no"); }
  }
}
JAVA

# macOS still ships Ruby 2.6 at /usr/bin/ruby; prefer a modern one if present.
RUBY=$(command -v ruby)
for candidate in /usr/local/opt/ruby/bin/ruby /opt/homebrew/opt/ruby/bin/ruby; do
  [ -x "$candidate" ] && RUBY=$candidate && break
done

probe_python() { python3 -c 'import re,sys
try:
    re.compile(sys.argv[1]); print("yes")
except re.error:
    print("no")' "$1"; }
probe_perl()   { perl -e 'eval { qr/$ARGV[0]/ }; print $@ ? "no" : "yes", "\n"' "$1"; }
probe_node()   { node -e 'try { new RegExp(process.argv[1]); console.log("yes") } catch { console.log("no") }' "$1"; }
probe_ruby()   { "$RUBY" -e 'begin; Regexp.new(ARGV[0]); puts "yes"; rescue; puts "no"; end' "$1"; }
probe_java()   { java "$tmp/P.java" "$1"; }

printf '%-26s %-7s %-6s %-6s %-6s %-6s\n' "does it COMPILE?" "python" "perl" "node" "ruby" "java"
while IFS='|' read -r name pattern; do
  printf '%-26s %-7s %-6s %-6s %-6s %-6s\n' "$name" \
    "$(probe_python "$pattern")" "$(probe_perl "$pattern")" "$(probe_node "$pattern")" \
    "$(probe_ruby "$pattern")" "$(probe_java "$pattern")"
done <<'PATTERNS'
backreference             |(a)\1
named backref, \k<w>      |(?<w>a)\k<w>
named backref, (?P=w)     |(?P<w>a)(?P=w)
forward reference         |(?:\1x|(a))+
lookahead                 |a(?=b)
lookbehind, fixed width   |(?<=a)b
lookbehind, two widths    |(?<=EUR|USD )b
lookbehind, variable width|(?<=a{1,3})b
atomic group              |(?>a+)b
possessive quantifier     |a++b
recursion                 |\((?:[^()]|(?R))*\)
PATTERNS

echo
echo "and does it MATCH 'aax' with the forward reference ^(?:\\1x|(a))+\$ ?"
printf '%-7s %s\n' python "$(python3 -c 'print("compile error")')"
printf '%-7s %s\n' perl   "$(perl -e 'print "aax" =~ /^(?:\1x|(a))+$/ ? "yes" : "no"')"
printf '%-7s %s\n' node   "$(node -e 'process.stdout.write(/^(?:\1x|(a))+$/.test("aax") ? "yes" : "no")')"
printf '%-7s %s\n' ruby   "$("$RUBY" -e 'print(("aax" =~ /^(?:\1x|(a))+$/) ? "yes" : "no")')"
