#!/bin/sh

# This script uses Menhir to generate the exhaustive list of errors
# for a given parser specification. The generated file has to be
# filled with the error messages. The script must be called in the
# same directory where the parser specification and external token
# specifications are located, in accordance with the convention of the
# LIGO compiler source code.

# set -x

# ====================================================================
# General Settings and wrappers

script=$(basename $0)

print_nl () { test "$quiet" != "yes" && echo "$1"; }

print () { test "$quiet" != "yes" && printf "$1";  }

fatal_error () {
  echo "$script: fatal error:"
  echo "$1" 1>&2
  exit 1
}

warn () {
 print_nl "$script: warning:"
 print_nl "$1"
}

failed () {
  printf "\033[31mFAILED$1\033[0m\n"
}

emphasise () {
  printf "\033[31m$1\033[0m\n"
}

# ====================================================================
# Parsing loop
#
while : ; do
  case "$1" in
    "") break;;
    --par-tokens=*)
      if test -n "$par_tokens"; then
        fatal_error "Repeated option --par-tokens."; fi
      par_tokens=$(expr "$1" : "[^=]*=\(.*\)")
      ;;
    --par-tokens)
      no_eq=$1
      break
      ;;
    --lex-tokens=*)
      if test -n "$lex_tokens"; then
        fatal_error "Repeated option --lex-tokens."; fi
      lex_tokens=$(expr "$1" : "[^=]*=\(.*\)")
      ;;
    --lex-tokens)
      no_eq=$1
      break
      ;;
    -h | --help | -help)
      help=yes
      ;;
      # Invalid option
      #
    -*)
      fatal_error "Invalid option \"$1\"."
      ;;
      # Invalid argument
      #
     *)
      if test -n "$parser"; then
        fatal_error "Only one Menhir specification allowed."; fi
      parser=$1
  esac
  shift
done

# ====================================================================
# Help
#
usage () {
  cat <<EOF
Usage: $(basename $0) [-h|--help] --lex-tokens=<lex_tokens>.mli \
--par-tokens=<par_tokens>.mly <parser>.mly

Generates in place <parser>.msg, the form containing the exhaustive
list of errors for the LR automaton generated by Menhir from
<parser>.mly, <par_tokens>.mly and <lex_tokens>.mli. The file
<parser>.msg is meant to be edited and filled with the error messages.

The following options, if given, must be given only once.

Display control:
  -h, --help                  display this help and exit
Mandatory options:
      --lex-tokens=<name>.mli the lexical tokens
      --par-tokens=<name>.mly the syntactical tokens
EOF
  exit 1
}

if test "$help" = "yes"; then usage; fi

# ====================================================================
# Checking the command-line options and arguments and applying some of
# them.

# It is a common mistake to forget the "=" in GNU long-option style.

if test -n "$no_eq"; then
  fatal_error "Long option style $no_eq must be followed by \"=\"."
fi

# Checking the parser and tokens

if test -z "$parser"; then
  fatal_error "No parser specification."; fi

if test -z "$par_tokens"; then
  fatal_error "No syntactical tokens specification (use --par-tokens)."; fi

if test -z "$lex_tokens"; then
  fatal_error "No lexical tokens specification (use --lex-tokens)."; fi

if test ! -e "$parser"; then
  fatal_error "Parser specification \"$parser\" not found."; fi

if test ! -e "$lex_tokens"; then
  fatal_error "Lexical tokens specification \"$lex_tokens\" not found."; fi

if test ! -e "$par_tokens"; then
  fatal_error "Syntactical tokens specification \"$par_tokens\" not found."; fi

parser_ext=$(expr "$parser" : ".*\.mly$")
if test "$parser_ext" = "0"; then
  fatal_error "Parser specification must have extension \".mly\"."; fi

par_tokens_ext=$(expr "$par_tokens" : ".*\.mly$")
if test "$par_tokens_ext" = "0"; then
  fatal_error "Syntactical tokens specification must have extension \".mly\"."
fi

lex_tokens_ext=$(expr "$lex_tokens" : ".*\.mli$")
if test "$lex_tokens_ext" = "0"; then
  fatal_error "Lexical tokens specification must have extension \".mli\"."
fi

mly=$parser
parser_base=$(basename $mly .mly)
par_tokens_base=$(basename $par_tokens .mly)
lex_tokens_base=$(basename $lex_tokens .mli)

# ====================================================================
# Menhir's flags

flags="--table --strict --external-tokens $lex_tokens_base \
       --base $parser_base $par_tokens"

# ====================================================================
# Generating error messages with Menhir

msg=$parser_base.msg
err=.$msg.err
out=.$mly.out

if test -e $msg; then mv -f $msg $msg.old; echo "Saved $msg."; fi

printf "Making new $msg from $mly... "
menhir --list-errors $flags $mly > $msg 2>$out

if test "$?" = "0"; then
  sentences=$(grep "YOUR SYNTAX ERROR MESSAGE HERE" $msg | wc -l)
  if test -z "$sentences"; then printf "done.\n"
  else
    spurious=$(grep WARNING $msg | wc -l)
    printf "done:\n"
    printf "There are %s error sentences, %s with spurious reductions.\n" \
           $sentences $spurious; fi
  if test -s $out; then cat $out; fi
  if test -f $msg.old; then
    printf "Checking inclusion of mappings (new in old)... "
    menhir --compare-errors $msg \
           --compare-errors $msg.old \
           $flags $mly 2> $out
    if test "$?" = "0"; then
      if test -s $out; then
        printf "done:\n"
        cat $out
      else printf "done.\n"; fi
      rm -f $out
      printf "Updating $msg... "
      menhir --update-errors $msg.old \
             $flags $mly > $msg 2> $err
      if test "$?" = "0"; then
        if $(diff $msg $msg.old 2>&1 > /dev/null); then
          echo "done."
        else
          printf "done:\n"
          emphasise "Warning: The LR items may have changed."
          emphasise "> Check your error messages again."
        fi
        rm -f $err
      else failed "."
           touch $err
           mv -f $msg.old $msg
           echo "Restored $msg."; fi
    else failed ":"
         mv -f $out $err
         sed -i -e "s/\.msg/.msg.new/g" \
                -e "s/\.new\.old//g" $err
         mv -f $msg $msg.new
         emphasise "See $err and update $msg."
         echo "The default messages are in $msg.new."
         mv -f $msg.old $msg
         echo "Restored $msg."; fi; fi
else
  failed ":"
  mv -f $out $err
  emphasise "> See $err."
  mv -f $msg.old $msg
  echo "Restored $msg."
fi
