#!/bin/sh
#
# A poor (wo)man's interactive top-level for Curry
#
# $Id: cyi.in,v 1.5 2004/05/29 09:41:10 berrueta Exp $
#
# Copyright (c) 2002-2003, Wolfgang Lux
# Copyright (c) 2003, Diego Berrueta
# See LICENSE for the full license.
#

# Configuration parameters
cyi=`basename $0`
version=\1.0.2
prefix=/usr
exec_prefix=${prefix}
bindir=${exec_prefix}/bin
libdir=${exec_prefix}/lib
: ${cymake=${exec_prefix}/bin/cymake}
: ${cyc=${exec_prefix}/bin/cyc}

echo
echo "ZZZZZ  IIIII  N   N   CCCC    Zinc interactive version $version"
echo "   Z     I    NN  N  C          (Copyright (c) 2003-2004, Diego Berrueta)"
echo "  Z      I    N N N  C        "
echo " Z       I    N  NN  C        Derived from Muenster Curry Compiler"
echo "ZZZZZ  IIIII  N   N   CCCC      (Copyright (c) 2002-2003, Wolfgang Lux)"
echo
echo "Type :h for help, :q to exit"
echo 

editor=${VISUAL-${EDITOR-/usr/bin/vi}}
srcdir=
source=
target=
module=
cyopts=
prompt='> '

case `echo "echo\c"` in
  echo ) echo_c='\c';;		# SYSV style
  *    ) echo_c= ;;
esac
case `echo -n "echo"` in
  echo ) echo_n=-n;;		# BSD style
  *    ) echo_n= ;;
esac

backslash_warning ( ) {
cat <<\EOF 2>&1
Warning: Backslashes on the command line must be escaped
  For instance, use \\x -> x for a lambda abstraction
  and '\\n' for the linefeed character.
EOF
}

read_test ( ) {
  input='foo\nbar'
  read $read_r line <<EOF
$input
EOF
  test "$input" = "$line"
}

# Some shells (e.g. Solaris /bin/sh, AIX /bin/bsh) terminate if read -r
# is used with a here document, therefore check that read -r is accepted
# before checking that it works. Note that some versions of GNU bash
# (at least 2.05 on Mac OS X) fail to read from an input pipe.

rawread=no
read_r=
if read_test; then
  rawread=yes
else
  if echo "" | read -r line 2>/dev/null; then
    read_r=-r
    if read_test; then rawread=yes; else read_r=; fi
  fi
  test $rawread = yes || backslash_warning
fi

help ( ) {
  echo "Commands"
  echo "  GOAL                  evaluate GOAL"
  echo "  :d[ebug]              recompile current module for debugging"
  echo "  :d[ebug] GOAL         debug GOAL"
  echo "  :t[ype] GOAL          print type of GOAL"
  echo "  :l[oad] MODULE        load (and compile) MODULE"
  echo "  :l[oad]               unload current module"
  echo "  :r[eload]             repeat last :load command"
  echo "  :f[reshen]            recompile current module"
  echo "  :clean                remove compile files for current module"
  echo "  :e[dit] FILE          edit FILE"
  echo "  :e[dit]               edit current module"
  echo "  :s[et] OPTIONS        set compiler options"
  echo "  :s[et]                show current compiler options"
  echo "  :u[nset]              unset compiler options"
  echo "  :cd DIR           	change current directory"
  echo "  :cd           	show current directory"
  echo "  :!COMMAND          	execute shell COMMAND"
  echo "  :v[ersion]            print the compiler version"
  echo "  :q[uit]               quit"
  echo
  echo "Valid GOALs (all variables must be declared)"
  echo "  EXPR                  expression"
  echo "  EXPR where DECLS      expression with additional declarations"
  echo
  test $rawread = yes || backslash_warning
}

compile_source ( ) {
  test -n "$source" && (cd "$srcdir" && $cymake $* $cyopts $source)
}

msg_comp ( ) {
  echo $echo_n "[Compiling ...$echo_c"
}

msg_done ( ) {
  case "$TERM" in
    "" | dumb | emacs ) echo "]";;
    * ) echo $echo_n "              $echo_c";;
  esac
}

trap 'rm -f /tmp/cyi$$' 0 1 2 3 15
run_goal ( ) {
  msg_comp && 
  (cd "$srcdir" && $cymake $cyopts -q $2 -a $module -e "$1" -o /tmp/cyi$$) && 
  msg_done &&
  /tmp/cyi$$
  rm -f /tmp/cyi$$
}

load ( ) {
  eval set -- $1
  case $# in
    0 )
      srcdir=.
      source=
      target=
      module=
      prompt="prelude> "
      ;;
    1 )
      case $1 in
        *.curry | *.lcurry ) 
	    if test -f "$1"; then
              srcdir=`pwd`; source=$1
            else
	      echo "source file $1 does not exist"
	    fi
	    ;;
        * ) f=`echo $1 | tr '.' '/'`
            if test -f "$f.lcurry"; then
              srcdir=`pwd`; source="$f.lcurry"
            else if test -f "$f.curry"; then
              srcdir=`pwd`; source="$f.curry"
            else
              echo "missing source file for $1"
	      return
            fi; fi
            ;;
      esac
      reload
      ;;
    * ) echo "too many files";;
  esac
}

reload ( ) {
  compile_source || return
  target=$source
  case $target in
    *.curry ) module=`expr "$target" : '\(.*\).curry'`;;
    *.lcurry ) module=`expr "$target" : '\(.*\).lcurry'`;;
  esac
  read line < $module.icurry
  set -- $line
  prompt="$2> "
}

clean ( ) {
  test -n "$target" || return
  (cd "$srcdir" && $cymake -q --clean $cyopts $module $target)
}

edit ( ) {
  eval set -- $1
  case $# in
    0 ) test -n "$source" || { echo "no source file"; return; }
	(cd "$srcdir" && $editor "$source");;
    1 ) $editor "$1";;
    * ) echo "too many files";;
  esac
}

evaluate ( ) {
  run_goal "$1"
}

debug ( ) {
  set -- "$1" $1
  case $# in
    1 ) compile_source "-g";;
    * ) run_goal "$1" -g;;
  esac
}

type_goal ( ) {
  msg_comp && 
  { test -z "$target" || (cd "$srcdir" && $cymake $cyopts -q $target); } &&
  msg_done &&
  (cd "$srcdir"; $cyc $cyopts -m "$target" -y "$1")
}

chdir ( ) {
  set -- $line
  case $# in
    0 ) pwd;;
    1 ) cd "$1";;
    * ) echo "too many directories";;
  esac
}

system ( ) {
  set -- $line
  case $# in
    0 ) ${SHELL-/bin/sh};;
    * ) /bin/sh -c "$line";;
  esac
}

while test $# -gt 0; do
  case $1 in
    -[ilDILU] ) cyopts="$cyopts $1 $2"; shift;;
    -[ilDILOU]* ) cyopts="$option $1";;
    -* ) echo "$cyi: unknown option $1"; exit 1;;
    * ) break;;
  esac
  shift
done

load "$1"
echo $echo_n "$prompt$echo_c"
while read $read_r line; do
  set -- $line
  case $1 in
    "" ) ;;
    :h | :help ) help;;
    :l | :load ) line=`expr "$line" : $1'\([^;]*\)'`; load "$line";;
    :r | :reload ) reload;;
    :f | :freshen ) clean && reload;;
    :clean ) clean;;
    :e | :edit ) line=`expr "$line" : $1'\([^;]*\)'`; edit "$line";;
    :t ) line=`expr "$line" : ':t\(.*\)'`; type_goal "  $line";;
    :type ) line=`expr "$line" : ':type\(.*\)'`; type_goal "     $line";;
    :s | :set ) shift
                if test $# -eq 0; then echo $cyopts; else cyopts="$*"; fi;;
    :u | :un | :unset ) cyopts= ;;
    :cd ) line=`expr "$line" : $1'\([^;]*\)'`; chdir "$line";;
    :!* ) line=`expr "$line" : ':![ 	]*\(.*\)'`; system "$line";;
    :v | :version ) $cyc -v;;
    :q | :quit ) break;;
    :d ) line=`expr "$line" : ':d\(.*\)'`; debug "  $line";;
    :debug ) line=`expr "$line" : ':debug\(.*\)'`; debug "      $line";;
    :* ) echo "unknown command, use :h for help";;
    * ) evaluate "$line";;
  esac
  echo $echo_n "$prompt$echo_c"
done

echo "[Leaving $cyi]"
