#!/usr/bin/env bash
# Copyright 2010 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

# Using all the non-test *.go files in the named directory, write
# out a file /tmp/$USER.try.go to evaluate the expressions on the
# command line, perhaps to discover a function or method that
# gives the desired results.  See usage message.
# Compile the program and run it.

# Makes egrep,grep work better in general if we put them
# in ordinary C mode instead of what the current language is.
unset LANG
export LC_ALL=C
export LC_CTYPE=C

export GOROOT=${GOROOT:-"@@GOROOT@@"}
eval $(gomake -j1 --no-print-directory -f "$GOROOT"/src/Make.inc go-env)
if [ -z "$O" ]; then
	echo 'missing $O - maybe no Make.$GOARCH?' 1>&2
	exit 2
fi

# Allow overrides
GC="${_GC:-$GC} -I _test"
GL="${GL:-$LD} -L _test"
AS="$AS"
CC="$CC"
LD="$LD"
export GC GL O AS CC LD

# Macros for tab and quotes for easier readability.
T='	'
BQ='`'
SQ="'"
DQ='"'
SD="$SQ$DQ"
DS="$DQ$SQ"

usage="usage: gotry [packagedirectory] expression ...
Given one expression, gotry attempts to evaluate that expression.
Given multiple expressions, gotry treats them as a list of arguments
and result values and attempts to find a function in the package
that, given the first few expressions as arguments, evaluates to
the remaining expressions as results.  If the first expression has
methods, it will also search for applicable methods.

If there are multiple expressions, a package directory must be
specified. If there is a package argument, the expressions are
evaluated in an environment that includes
	import . ${DQ}packagedirectory${DQ}

Examples:
	gotry 3+4
		# evaluates to 7
	gotry strings ${SD}abc${DS} ${SD}c${DS} 7-5
		# finds strings.Index etc.
	gotry regexp ${SQ}MustCompile(${DQ}^[0-9]+${DQ})${SQ} ${SD}12345${DS} true
		# finds Regexp.MatchString

"

function fail() {
	echo 2>&1 "$@"
	exit 2
}

case $# in
	0)
		fail "$usage"
		;;
	*)
		case "$1" in
		-*help|-*'?'|'?')
			fail "$usage"
		esac
		if test -d "$GOROOT/src/pkg/$1"
		then
			pkg=$(basename $1)
			dir=$GOROOT/src/pkg/$1
			importdir=$1
			shift
			case "$pkg" in
			os|syscall)
				fail "gotry: cannot try packages os or syscall; they are too dangerous"
			esac
		fi
		;;
esac

spaces='[ 	][ 	]*'

function getFunctions() {
	if [ "$pkg" = "" ]
	then
		return
	fi
	for file in $dir/*.go
	do
		case $file in
		*_test*)
			continue
		esac
		grep "func$spaces[A-Z]" $file | # TODO: should be Unicode upper case
			sed "s/func$spaces//;s/(.*//"
	done | sort -u
}

# Generate list of public functions.
functions=$(getFunctions)

# Write file to compile
rm -f /tmp/$USER.try.go
(
cat <<'!'
package main

import (
	"os"
	"try"
!

if [ "$pkg" != "" ]
then
	echo "$T" . '"'$importdir'"'
fi

cat <<!
)
func main() {
	try.Main("$pkg", firstArg, functions, args)
}
var functions = map[string] interface{}{
!

for i in $functions
do
	echo "$T"'"'$i'": '$i','
done
echo "}"

echo 'var args = []interface{}{'

if [ $# = 1 ]
then
	echo "${T}toSlice($1)",
else
for i
	do
		echo "$T$i",
	done
fi
echo "}"

cat <<!
var firstArg = $BQ$1$BQ
var _ os.Error
func toSlice(a ...interface{}) []interface{} { return a }
!

)>/tmp/$USER.try.go

$GC -o /tmp/$USER.try.$O /tmp/$USER.try.go &&
$GL -o /tmp/$USER.try /tmp/$USER.try.$O &&
/tmp/$USER.try "_$@"
rm -f /tmp/$USER.try /tmp/$USER.try.go /tmp/$USER.try.$O
