implementation module EstherScript

import EstherPostParser, EstherTransform
import StdBool, StdList, StdMisc, StdFunc, StdTuple, StdParsComb, DynamicFileSystem

compose :: !String !*env -> (!MaybeException Dynamic, !*env) | resolveFilename, ExceptionEnv, bimap{|*|} env
compose input env 
	# (maybe, env) = getExceptionIO (compile input) env
	= case maybe of
		Exception d -> (Exception (handler d), env)
		NoException d 
			# (maybe, env) = getException d env
			-> case maybe of
				Exception d -> (Exception (handler d), env)
				else -> (else, env)
where
	compile :: !String !*env -> (!Dynamic, !*env) | resolveFilename, bimap{|*|} env
	compile input env
		# syntax = parseStatements input
		  (syntax, fv, env`) = resolveNames{|*|} syntax [] env
		  syntax = fixInfix{|*|} syntax
		  core = transform{|*|} syntax
		= generateCode core env`

	handler :: !Dynamic -> Dynamic
	handler ((ApplyTypeError df dx) :: ComposeException) = dynamic EstherError ("cannot apply `" +++ tf +++ "' to `" +++ tx +++ "'")
	where
		(vf, tf) = toStringDynamic df
		(vx, tx) = toStringDynamic dx
	handler (UnboundVariable v :: ComposeException) = dynamic EstherError ("unbound variable (internal error) `" +++ v +++ "'")
	handler (InstanceNotFound c dt :: ComposeException) = dynamic EstherError ("`instance " +++ c +++ " " +++ snd (toStringDynamic dt) +++ "' not found")
	handler (InvalidInstance c t dt :: ComposeException) = dynamic EstherError ("`instance " +++ c +++ " " +++ snd (toStringDynamic t) +++ "' has invalid type `" +++ snd (toStringDynamic dt) +++ "'")
	handler (UnsolvableOverloading :: ComposeException) = dynamic EstherError ("unsolvable overloading")
	handler (InfixRightArgumentMissing :: PostParseException) = dynamic EstherError ("right argument of infix operator is missing")
	handler (InfixLeftArgumentMissing :: PostParseException) = dynamic EstherError ("left argument of infix operator is missing")
	handler (UnsolvableInfixOrder :: PostParseException) = dynamic EstherError ("conflicting priorities of infix operators")
	handler (NameNotFound n :: PostParseException) = dynamic EstherError ("file `" +++ n +++ "' not found")
	handler (CaseBadConstructorArity :: TransformException) = dynamic EstherError ("constructor in pattern has too many or too little arguments")
	handler (NotSupported s :: TransformException) = dynamic EstherError ("feature not (yet) supported: `" +++ s +++ "'")
	handler (NotSupported` s :: ComposeException) = dynamic EstherError ("feature not (yet) supported: `" +++ s +++ "'")
	handler (_ :: ParseException) = dynamic EstherError ("parser error")
	handler d = d

instance resolveFilename World
where
	resolveFilename name env
		# (ok, dyn, env) = dynamicRead ENV_CWD env
		  cwd = if ok (case dyn of (p :: DynamicPath) -> p; _ -> []) []
		  (ok, dyn, env) = dynamicRead ENV_PATH env
		  path = [cwd:if ok (case dyn of (p :: [DynamicPath]) -> p; _ -> [[]]) [[]]]
		  (cache, env) = cacheSearchPath path env
		  (ok, file, prio) = findFile name cache
		| not ok = (Nothing, env)
		# (ok, dyn, env) = dynamicRead file env
		| not ok = (Nothing, env)
		= (Just (dyn, prio), env)
	where
		cacheSearchPath :: ![DynamicPath] !*env -> (![(String, DynamicPath)], !*env) | DynamicFileSystem env
		cacheSearchPath [p:ps] env 
			# (ok, d, env) = dynamicRead p env
			| not ok = cacheSearchPath ps env
			= case d of
				(dir :: DynamicDirectory)
					# (searchCache, env) = cacheSearchPath ps env
					-> ([(f, p ++ [f]) \\ DynamicFile f <- dir] ++ searchCache, env)
				_ -> cacheSearchPath ps env
		cacheSearchPath _ env = ([], env)
		
findFile n [] = (False, undef, undef)
findFile n [(x, d):xs] = case begin (sp (parse (fromString n)) <& sp eof) (fromString x) of
	[([], p)] -> (True, d, p)
	_ -> findFile n xs
where
	parse name = symbol '(' &> sptoken name &> spsymbol ')' &> sp prio
			<!> token name &> yield GenConsNoPrio
	where
		prio = token ['infixl'] &> digitOr9 <@ GenConsPrio GenConsAssocLeft
			<!> token ['infixr'] &> digitOr9 <@ GenConsPrio GenConsAssocRight
			<!> token ['infix'] &> digitOr9 <@ GenConsPrio GenConsAssocNone
			<!> yield (GenConsPrio GenConsAssocLeft 9)
		digitOr9 = sp digit <!> yield 9

instance resolveFilename (EstherBuiltin *env) | resolveFilename env
where
	resolveFilename name eb=:{builtin, env}
		# (maybe, env) = resolveFilename name env
		  eb = {eb & env = env}
		= case maybe of
			Nothing
				# (ok, dyn, prio) = findFile name builtin
				| not ok -> (maybe, eb)
				-> (Just (dyn, prio), eb)
			_ -> (maybe, eb)
	
instance ExceptionEnv (EstherBuiltin *env) | ExceptionEnv env
where
	catchAllIO f g = catch
	where
		catch eb=:{builtin, env}
			# (x, env) = (f` catchAllIO g`) env
			= (x, {eb & env = env})
		where
			f` env
				# (x, {env}) = f {builtin = builtin, env = env}
				= (x, env)
	
			g` d env
				# (x, {env}) = g d {builtin = builtin, env = env}
				= (x, env)