Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle EOF #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/Console.elm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Console (putChar, putStr, putStrLn, getChar, getLine, readUntil, writeFile,
exit, map, mapIO, forEach, pure, apply, (<*>), andThen, (>>=),
seq, (>>>), forever, IO, run) where
seq, (>>>), forever, IO, run, getContents) where

{-|

Expand All @@ -17,7 +17,7 @@ how to run such a computation.
@docs putChar, putStr, putStrLn

# Stdin
@docs getChar, getLine, readUntil
@docs getChar, getLine, getContents, readUntil

# File Operations
@docs writeFile
Expand Down Expand Up @@ -61,23 +61,27 @@ readUntil = Core.readUntil

{-| Write content to a file -}
writeFile : { file : String, content : String } -> Core.IO ()
writeFile = Core.writeFile
writeFile = Core.writeFile

{-| Read a line from stdin -}
getLine : Core.IO String
getLine = Core.getLine

{-| Read from stdin until EOF -}
getContents : Core.IO String
getContents = Core.getContents

{-| Apply a pure function to an IO value -}
map : (a -> b) -> Core.IO a -> Core.IO b
map = Core.map
map = Core.map

{-| Alternative interface to forEach -}
mapIO : (a -> Core.IO ()) -> List a -> Core.IO ()
mapIO = Core.mapIO

{-| Run an IO computation for each element of a list -}
forEach : List a -> (a -> Core.IO ()) -> Core.IO ()
forEach = Core.forEach
forEach = Core.forEach

{-| Use a pure value where an IO computation is expected. -}
pure : a -> Core.IO a
Expand Down
5 changes: 5 additions & 0 deletions src/Console/Core.elm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ writeFile obj = Impure (WriteF obj (\_ -> Pure ()))
getLine : IO String
getLine = readUntil '\n'


{-| Read all stdin into a single string. -}
getContents : IO String
getContents = readUntil '\0'

-- | IO Combinators

{-| Apply a pure function to an IO value -}
Expand Down
9 changes: 2 additions & 7 deletions src/Console/NativeCom.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ module Console.NativeCom where

import Task exposing (Task)

import Native.Console.NativeCom
import Console.NativeTypes exposing (IResponse, IRequest)

type IRequest = Put String
| Exit Int
| Get
| WriteFile { file : String, content : String }
| Init
import Native.Console.NativeCom

type alias IResponse = Maybe String

sendRequests : Signal (List IRequest) -> Signal (Task x ())
sendRequests requests =
Expand Down
13 changes: 13 additions & 0 deletions src/Console/NativeTypes.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Console.NativeTypes where

type IRequest
= Init
| Get
| Put String
| WriteFile { file : String, content : String }
| Exit Int

type IResponse
= Empty
| Data String
| EOF
40 changes: 22 additions & 18 deletions src/Console/Runner.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,32 @@ import Trampoline
import Console.Core as Core
import Console.Core exposing (IO(..), IOF(..))
import Console.NativeCom as NativeCom
import Console.NativeCom as NC
import Console.NativeCom exposing (IRequest, IResponse)
import Console.NativeTypes as NT
import Console.NativeTypes exposing (IRequest, IResponse)

type alias IOState = { buffer : String }
type alias IOState = { buffer : String, eof : Bool }

start : IOState
start = { buffer = "" }
start = { buffer = "", eof = False }

run : IO () -> Signal (Task x ())
run io =
let init = (\_ -> io, start, [NC.Init])
let init = (\_ -> io, start, [NT.Init])
f resp (io, st, _) = step resp io st
third (_, _, z) = z
in NativeCom.sendRequests (third <~ foldp f init NativeCom.responses)

putS : String -> IRequest
putS = NC.Put
putS = NT.Put

exit : Int -> IRequest
exit = NC.Exit
exit = NT.Exit

getS : IRequest
getS = NC.Get
getS = NT.Get

writeF : { file : String, content : String } -> IRequest
writeF = NC.WriteFile
writeF = NT.WriteFile

-- | Extract all of the requests that can be run now
extractRequests : IO a -> State IOState (List IRequest, () -> IO a)
Expand All @@ -53,11 +53,14 @@ extractRequests io =
Exit n -> pure ([exit n], \_ -> io)
GetC k ->
get >>= \st ->
case String.uncons st.buffer of
Nothing -> pure ([getS], \_ -> io)
Just (c, rest) ->
put ({ buffer = rest }) >>= \_ ->
extractRequests (k c)
case String.uncons st.buffer of
Nothing ->
if st.eof
then put st >>= \_ -> extractRequests (k '\0')
else pure ([getS], \_ -> io)
Just (c, rest) ->
put { st | buffer <- rest } >>= \_ ->
extractRequests (k c)

flattenReqs : List IRequest -> List IRequest
flattenReqs rs =
Expand All @@ -70,8 +73,8 @@ flattenReqs rs =
[r] -> loop [] (r::acc) (n+1)
r1 :: r2 :: rs' ->
case (r1, r2) of
(NC.Exit n, _) -> loop [] (r1::acc) (n+1)
(NC.Put s1, NC.Put s2) -> loop (putS (s1++s2) :: rs') acc (n+1)
(NT.Exit n, _) -> loop [] (r1::acc) (n+1)
(NT.Put s1, NT.Put s2) -> loop (putS (s1++s2) :: rs') acc (n+1)
_ -> loop (r2::rs') (r1::acc) (n+1)
in Trampoline.trampoline <| loop rs [] 0

Expand All @@ -82,8 +85,9 @@ step : IResponse ->
(() -> IO a, IOState, List IRequest)
step resp io st =
let newST = case resp of
Nothing -> st
Just s -> { st | buffer <- String.append st.buffer s }
NT.Empty -> st
NT.EOF -> { st | eof <- True }
NT.Data s -> { st | buffer <- String.append st.buffer s }
(newST', (rs, k)) = extractRequests (io ()) newST
in (k, newST', rs)

Expand Down
28 changes: 23 additions & 5 deletions src/Native/Console/NativeCom.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Elm.Native.Console.NativeCom.make = function(localRuntime) {
return localRuntime.Native.Console.NativeCom.values;
}

/* Module imports */
var NT = Elm.Console.NativeTypes.make(localRuntime);

/* Elm imports */
var List = Elm.Native.List.make(localRuntime);
var Maybe = Elm.Maybe.make(localRuntime);
Expand All @@ -21,20 +24,31 @@ Elm.Native.Console.NativeCom.make = function(localRuntime) {
var stdin = process.stdin;

var sendResponseString = function(str) {
var value = Maybe.Nothing;
var value = NT.Empty;
if (str !== null && str.length > 0) {
value = Maybe.Just(str);
value = NT.Data(str);
}
sendResponse(value);
};

var sendResponse = function(value) {
setTimeout(function() {
localRuntime.notify(responsesSignal.id, value);
}, 0);
}
};

var responsesSignal = NS.input(
'Console.NativeCom.responses', Maybe.Nothing);

var responsesSignal = NS.input('Console.NativeCom.responses', Maybe.Nothing);
stdin.on('data', function(chunk) {
stdin.pause();
sendResponseString(chunk.toString());
})
});

var eof = false;
stdin.on('end', function() {
eof = true;
});

var sendRequestBatch = function(list) {
var requests = List.toArray(list);
Expand Down Expand Up @@ -63,6 +77,10 @@ Elm.Native.Console.NativeCom.make = function(localRuntime) {
process.stdout.write(request._0);
break;
case 'Get':
// shared state
if (eof) {
sendResponse(NT.EOF);
}
stdin.resume();
break;
case 'Exit':
Expand Down
9 changes: 9 additions & 0 deletions test/Stdin.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Main where

import Console exposing ((>>=), putStr, getContents)
import String
import Task


port runner : Signal (Task.Task x ())
port runner = Console.run (getContents >>= putStr)
21 changes: 19 additions & 2 deletions test/run-tests.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

set -e
set -ev

cd "$(dirname "$0")"

Expand All @@ -17,4 +17,21 @@ node build/test2.js
elm-make --yes --output build/test3.js BigString.elm
echo "Elm.worker(Elm.Main);" >> build/test3.js
node build/test3.js > /dev/null
echo "Success"

elm-make --yes --output build/test4.js Stdin.elm
echo "Elm.worker(Elm.Main);" >> build/test4.js
INPUT=`cat <<EOF
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
EOF`
RESULT=`echo "$INPUT" | node build/test4.js`
if [ ! "$INPUT" = "$RESULT" ]; then
echo "Unexpected output: $RESULT"
exit 1
fi

echo "Success"