Commit a425dae4 authored by Michael Hanus's avatar Michael Hanus
Browse files

runcurry added

parent b5d035e8
......@@ -22,5 +22,6 @@ genint/GenInt
importcalls/ImportCalls
optimize/bindingopt
optimize/binding_optimization/BindingOpt
runcurry/RunCurry
spicey/spiceup
xmldata/Data2Xml
......@@ -127,7 +127,7 @@ To generate the documentation, execute the command\pindex{currydoc}
currydoc Example
\end{curry}
(\code{currydoc} is a command usually stored in \code{\cyshome/bin}
where \cyshome is the installation directory of \CYS;
(where \cyshome is the installation directory of \CYS;
see Section~\ref{sec-general}).
This command creates the directory \code{DOC_Example} (if it does not exist)
and puts all HTML documentation files for the main program module
......
This directory contains some documention for the runcurry command:
manual.tex:
A short description to be included in the main manual of the Curry system.
\section{runcurry: Running Curry Programs}
\code{runcurry}\pindex{runcurry}
is a command usually stored in \code{\cyshome/bin}
(where \cyshome is the installation directory of \CYS;
see Section~\ref{sec-general}).
This command supports the execution of Curry programs
without explicitly invoking the interactive environment.
Hence, it can be useful to write short scripts in Curry
intended for direct execution.
The Curry program must always contain the definition of an operation
\code{main} of type \code{IO ()}.
The execution of the program consists of the evaluation of this operation.
Basically, the command \code{runcurry} supports three modes of operation:
\begin{itemize}
\item
One can execute a Curry program whose file name
is provided as an argument when \code{runcurry} is called.
In this case, the suffix (\ccode{.curry} or \ccode{.lcurry})
must be present and cannot be dropped.
One can write additional commands for the interactive environment,
typically settings of some options, before the Curry program name.
All arguments after the Curry program name are passed as run-time
arguments. For instance, consider the following program stored
in the file \code{ShowArgs.curry}:
\begin{curry}
import System(getArgs)
main = getArgs >>= print
\end{curry}
This program can be executed by the shell command
\begin{curry}
> runcurry ShowArgs.curry Hello World!
\end{curry}
which produces the output
\begin{curry}
["Hello","World!"]
\end{curry}
\item
One can also execute a Curry program whose whose program text
comes from the standard input. Thus, one can either ``pipe''
the program text into this command or type the program text on
the keyboard. For instance, if we type
\begin{currynomath}
> runcurry
main = putStrLn . unlines . map show . take 8 $ [1..]
\end{currynomath} % $
(followed by the end-of-file marker Ctrl-D), the output
\begin{curry}
1
2
3
4
5
6
7
8
\end{curry}
is produced.
\item
One can also write the program text in a script file to be executed
like a shell script. In this case, the script must start with the line
\begin{curry}
#!/usr/bin/env runcurry
\end{curry}
followed by the source text of the Curry program.
For instance, we can write a simple Curry script to count the
number of code lines in a Curry program by removing all blank
and comment lines and counting the remaining lines:
\begin{currynomath}
#!/usr/bin/env runcurry
import Char(isSpace)
import System(getArgs)
-- count number of program lines in a file:
countCLines :: String -> IO Int
countCLines f =
readFile f >>=
return . length . filter (not . isEmptyLine) . map stripSpaces . lines
where
stripSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
isEmptyLine [] = True
isEmptyLine [_] = False
isEmptyLine (c1:c2:_) = c1=='-' && c2=='-'
-- The main program reads Curry file names from arguments:
main = do
args <- getArgs
mapIO_ (\f -> do ls <- countCLines f
putStrLn $ "Stripped lines of file "++f++": " ++ show ls)
args
\end{currynomath} % $
%
If this script is stored in the (executable) file \ccode{codelines.sh},
we can count the code lines of the file \code{Prog.curry} by
the shell command
\begin{curry}
> ./codelines.sh Prog.curry
\end{curry}
%
When this command is executed, the command \code{runcurry}
compiles the program and evaluates the expression \code{main}.
Since the compilation might take some time in more complex scripts,
one can also save the result of the compilation in a binary file.
To obtain this behavior, one has to insert the line
\begin{curry}
#jit
\end{curry}
in the script file, e.g., in the second line.
With this option, a binary of the compiled program is saved
(in the same directory as the script).
Now, when the same script is executed the next time,
the stored binary file is executed (provided that it is still newer
than the script file itself, otherwise it will be recompiled).
This feature combines easy scripting with Curry together with
fast execution.
\end{itemize}
%%% Local Variables:
%%% mode: pdflatex
%%% TeX-master: "manual"
%%% End:
["rtarg1","rtarg2"]
Type in your program with an operation 'main':
[]
Type in your program with an operation 'main':
42
Run-time arguments: ["Hello","World"]
Run-time arguments: ["Hi","World"]
["rtarg1","rtarg2"]
Type in your program with an operation 'main':
[]
Type in your program with an operation 'main':
42
Run-time arguments: ["Hello","World"]
Run-time arguments: ["Hi","World"]
import System(getArgs)
main = getArgs >>= print
#!/usr/bin/env runcurry
#jit
import System(getArgs)
main = do
putStr "Run-time arguments: "
getArgs >>= print
#!/bin/sh
# Shell script to test the current set of examples
CURRYBIN="../../../bin"
if [ -x "$CURRYBIN/pakcs" ] ; then
CURRYEXEC=pakcs
CURRYOPTIONS="-q :set v0 :set -free :set +verbose"
elif [ -x "$CURRYBIN/kics2" ] ; then
CURRYEXEC=kics2
CURRYOPTIONS=":set v0 :set -ghci"
else
echo "ERROR: Unknown Curry system!"
exit 1
fi
LOGFILE=xxx$$
PATH=$CURRYBIN:$PATH
export PATH
$CURRYBIN/cleancurry
rm -f $LOGFILE
cat << EOM | /bin/sh | tee $LOGFILE
runcurry Test.curry rtarg1 rtarg2
cat Test.curry | runcurry
echo "main = print 42" | runcurry
./curryscript.sh Hello World
./curryscript.sh Hi World
EOM
################ end of tests ####################
# Clean:
/bin/rm -f curryscript.sh.bin
# Check differences:
DIFF=diff$$
diff TESTRESULT.$CURRYEXEC $LOGFILE > $DIFF
if [ "`cat $DIFF`" = "" ] ; then
echo
echo "Regression test successfully executed!"
/bin/rm -f $LOGFILE $DIFF
$CURRYBIN/cleancurry
else
echo
echo "Differences in regression test occurred:"
cat $DIFF
/bin/rm -f $DIFF
/bin/mv -f $LOGFILE LOGFILE
echo "Test output saved in file 'LOGFILE'."
$CURRYBIN/cleancurry
exit 1
fi
# Makefile for generating GenInt tool
# binary
TOOL = $(BINDIR)/runcurry
.PHONY: all compile install clean uninstall
all: install
compile: RunCurry
install: compile # no further installation required
rm -f $(TOOL)
ln -s $(CURDIR)/RunCurry $(TOOL)
clean:
$(CLEANCURRY)
rm -f RunCurry
uninstall: clean
rm -f $(TOOL)
# generate executable for currydoc program:
RunCurry: RunCurry.curry
$(REPL) $(REPL_OPTS) :load RunCurry :save :quit
---------------------------------------------------------------------------
--- This program implements the `runcurry` command that allows
--- to run a Curry program without explicitly invoking the REPL.
---
--- Basically, it has three modes of operation:
--- * execute the main operation of a Curry program whose file name
--- is provided as an argument
--- * execute the main operation of a Curry program whose program text
--- comes from the standard input
--- * execute the main operation of a Curry program whose program text
--- is in a script file (starting with `#!/usr/bin/env runcurry`).
--- If the script file contains the line `#jit`, it is compiled
--- and saved as an executable so that it is faster executed
--- when called the next time.
---
--- @author Michael Hanus
--- @version November 2015
---------------------------------------------------------------------------
import Char(isSpace)
import Directory
import Distribution(installDir,stripCurrySuffix)
import FileGoodies(fileSuffix)
import FilePath((<.>),(</>),isRelative)
import IO (getContents)
import List(partition)
import System(exitWith,getArgs,getPID,system)
main :: IO ()
main = do
args <- getArgs
case args of
("-h":_) -> putStrLn usageMsg
("--help":_) -> putStrLn usageMsg
("-?":_) -> putStrLn usageMsg
_ -> checkFirstArg [] args
-- Usage message:
usageMsg :: String
usageMsg = unlines $
["Usage:"
,""
,"As a shell command:"
,"> runcurry [Curry system options] <Curry program name> <run-time arguments>"
,""
,"As a shell script: start script with"
,"#!/usr/bin/env runcurry"
,"...your Curry program defining operation 'main'..."
,""
,"In interactive mode:"
,"> runcurry"
,"...type your Curry program until end-of-file..."
]
-- check whether runcurry is called in script mode, i.e., the argument
-- is not a Curry program but an existing file:
checkFirstArg :: [String] -> [String] -> IO ()
checkFirstArg curryargs [] = do
-- no program argument provided, use remaining input as program:
putStrLn "Type in your program with an operation 'main':"
progname <- getNewProgramName
getContents >>= writeFile progname
execAndDeleteCurryProgram progname curryargs [] >>= exitWith
checkFirstArg curryargs (arg1:args) =
if fileSuffix arg1 `elem` ["curry","lcurry"]
then execCurryProgram arg1 curryargs args >>= exitWith
else do
isexec <- isExecutable arg1
if isexec
then do
-- argument is not a Curry file but it is an executable, hence, a script:
-- store it in a Curry program, where lines starting with '#' are removed
progname <- getNewProgramName
proginput <- readFile arg1
let (proglines, hashlines) = partition noHashLine (lines proginput)
progtext = unlines proglines
if any isHashJITOption hashlines
then execOrJIT arg1 progname progtext curryargs args >>= exitWith
else do
writeFile progname progtext
execAndDeleteCurryProgram progname curryargs args >>= exitWith
else checkFirstArg (curryargs++[arg1]) args
-- Execute an already compiled binary (if it is newer than the first file arg)
-- or compile the program and execute the binary:
execOrJIT :: String -> String -> String -> [String] -> [String] -> IO Int
execOrJIT scriptfile progname progtext curryargs rtargs = do
let binname = if isRelative scriptfile
then "." </> scriptfile <.> "bin"
else scriptfile <.> "bin"
binexists <- doesFileExist binname
binok <- if binexists then do
stime <- getModificationTime scriptfile
btime <- getModificationTime binname
return (btime>stime)
else return False
if binok
then do
ec <- system (unwords (binname : rtargs))
if ec==0
then return 0
else -- An error occurred with the old binary, hence we try to re-compile:
compileAndExec binname
else compileAndExec binname
where
compileAndExec binname = do
writeFile progname progtext
ec <- saveCurryProgram progname curryargs binname
if ec==0 then system (unwords (binname : rtargs))
else return ec
-- Is a hash line a JIT option, i.e., of the form "#jit"?
isHashJITOption :: String -> Bool
isHashJITOption s = stripSpaces (tail s) == "jit"
noHashLine :: String -> Bool
noHashLine [] = True
noHashLine (c:_) = c /= '#'
-- Generates a new program name for temporary program:
getNewProgramName :: IO String
getNewProgramName = do
pid <- getPID
genNewProgName ("RUNCURRY_" ++ show pid)
where
genNewProgName name = do
let progname = name++".curry"
exname <- doesFileExist progname
if exname then genNewProgName (name++"_0")
else return progname
-- Is the argument the name of an executable file?
isExecutable :: String -> IO Bool
isExecutable fname = do
ec <- system $ "test -x " ++ fname
return (ec==0)
-- Our default options for the REPL:
replOpts :: String
replOpts = ":set v0 :set parser -Wnone :set -time"
-- Saves a Curry program with given Curry system arguments into a binary
-- (last argument) and delete the program after the compilation:
saveCurryProgram :: String -> [String] -> String -> IO Int
saveCurryProgram progname curryargs binname = do
ec <- system $ installDir ++ "/bin/curry " ++ replOpts ++ " " ++
unwords curryargs ++ " :load " ++ progname ++
" :save :quit"
unless (ec/=0) $ renameFile (stripCurrySuffix progname) binname
system (installDir++"/bin/cleancurry "++progname)
removeFile progname
return ec
-- Executes a Curry program with given Curry system arguments and
-- run-time arguments:
execCurryProgram :: String -> [String] -> [String] -> IO Int
execCurryProgram progname curryargs rtargs = system $
installDir ++ "/bin/curry " ++ replOpts ++ " " ++
unwords curryargs ++ " :load " ++ progname ++
" :set args " ++ unwords rtargs ++ " :eval main :quit"
-- Executes a Curry program with given Curry system arguments and
-- run-time arguments and delete the program after the execution:
execAndDeleteCurryProgram :: String -> [String] -> [String] -> IO Int
execAndDeleteCurryProgram progname curryargs rtargs = do
ec <- execCurryProgram progname curryargs rtargs
system (installDir++"/bin/cleancurry "++progname)
removeFile progname
return ec
-- Strips leading and tailing spaces:
stripSpaces :: String -> String
stripSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment