#!/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 <.mli \ --par-tokens=.mly .mly Generates in place .msg, the form containing the exhaustive list of errors for the LR automaton generated by Menhir from .mly, .mly and .mli. The file .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=.mli the lexical tokens --par-tokens=.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