Writing Meck testcases for gen_tcp function - erlang

Here is a simple IRC bot module written by Erlang:
IRC Bot
Could someone helps me write the testcase for the function connect and parse_line with MECK
connect(Host, Port) ->
{ok, Sock} = gen_tcp:connect(Host, Port, [{packet, line}]),
% According to RFC1459, we need to tell the server our nickname and username
gen_tcp:send(Sock, "NICK " ++ ?nickname ++ "\r\n"),
gen_tcp:send(Sock, "USER " ++ ?nickname ++ " blah blah blah blah\r\n"),
loop(Sock).
parse_line(Sock, [User,"PRIVMSG",Channel,?nickname|_]) ->
Nick = lists:nth(1, string:tokens(User, "!")),
irc_privmsg(Sock, Channel, "You talkin to me, " ++ Nick ++ "?");
parse_line(Sock, [_,"376"|_]) ->
gen_tcp:send(Sock, "JOIN :" ++ ?channel ++ "\r\n");
parse_line(Sock, ["PING"|Rest]) ->
gen_tcp:send(Sock, "PONG " ++ Rest ++ "\r\n");
parse_line(_, _) ->
0.
Thanks you very much, I have already know how to use MECK to write some simple Erlang testcases about input/ otput, lists...but this IRC bot seems tobe quite beyond my current ability.

I would suggest splitting your parsing code and output code from the rest of your logic. There is hardly any reason to test such low-level functionality, but if you added functions "in between" the low-level interface and your code, you could easily write test cases without even using Meck.

Related

How do you read from stdout of an external program in Erlang?

Here's the code I wrote:
-module(comments).
-record(comment, {user, contents, id, path, sub_comments}).
-export([sort_comments/1]).
comments2csv(Port, []) -> Port ! {self(), close};
comments2csv(Port, [#comment{user=User, contents=Contents, id=ID, path=Path}|Rest]) when is_integer(User) ->
Port ! {self(), {command, list_to_binary(Path ++ ":" ++ integer_to_list(User) ++ ":" ++ integer_to_list(ID) ++ ":" ++ Contents ++ "\n")}},
comments2csv(Port, Rest).
sort_comments(Comments) ->
Comments2 = [#comment{user=1, contents="hello", id=1, path="/1/", sub_comments=[]}],
Port = open_port({spawn_executable, "./comsort"}, [binary]),
comments2csv(Port, Comments2),
receive
{Port, {data, Data}} ->
io:format("~p~n", [Data]);
{Port, closed} ->
io:format("closed~n"),
receive
X ->
io:format("~p~n", [X])
end
end.
It calls up an external program written in Haskell, and when I run it from the shell it prints "closed" and then hangs. I can't for the life of me figure out why it's not reading the output from the stdout of the program. The Haskell program is set up to print XML data to stdout when it's done receiving CSV data from stdin, and then it exits.
I loosely based it off of the tutorial here:
Ok, so I found this which has the options that I need to give to open_port in order to receive stuff from stdout. Now it works.
Edit: Nevermind. It seems that what I'm trying to do is impossible. Erlang doesn't let you close the file descriptor for stdin to a program called externally so comsort can't ever gather up all of the input and then send out the xml at the end because it never sees an end to the input.

Parse String to Datatype in Haskell

I'm taking a Haskell course at school, and I have to define a Logical Proposition datatype in Haskell. Everything so far Works fine (definition and functions), and i've declared it as an instance of Ord, Eq and show. The problem comes when I'm required to define a program which interacts with the user: I have to parse the input from the user into my datatype:
type Var = String
data FProp = V Var
| No FProp
| Y FProp FProp
| O FProp FProp
| Si FProp FProp
| Sii FProp FProp
where the formula: ¬q ^ p would be: (Y (No (V "q")) (V "p"))
I've been researching, and found that I can declare my datatype as an instance of Read.
Is this advisable? If it is, can I get some help in order to define the parsing method?
Not a complete answer, since this is a homework problem, but here are some hints.
The other answer suggested getLine followed by splitting at words. It sounds like you instead want something more like a conventional tokenizer, which would let you write things like:
(Y
(No (V q))
(V p))
Here’s one implementation that turns a string into tokens that are either a string of alphanumeric characters or a single, non-alphanumeric printable character. You would need to extend it to support quoted strings:
import Data.Char
type Token = String
tokenize :: String -> [Token]
{- Here, a token is either a string of alphanumeric characters, or else one
- non-spacing printable character, such as "(" or ")".
-}
tokenize [] = []
tokenize (x:xs) | isSpace x = tokenize xs
| not (isPrint x) = error $
"Invalid character " ++ show x ++ " in input."
| not (isAlphaNum x) = [x]:(tokenize xs)
| otherwise = let (token, rest) = span isAlphaNum (x:xs)
in token:(tokenize rest)
It turns the example into ["(","Y","(","No","(","V","q",")",")","(","V","p",")",")"]. Note that you have access to the entire repertoire of Unicode.
The main function that evaluates this interactively might look like:
main = interact ( unlines . map show . map evaluate . parse . tokenize )
Where parse turns a list of tokens into a list of ASTs and evaluate turns an AST into a printable expression.
As for implementing the parser, your language appears to have similar syntax to LISP, which is one of the simplest languages to parse; you don’t even need precedence rules. A recursive-descent parser could do it, and is probably the easiest to implement by hand. You can pattern-match on parse ("(":xs) =, but pattern-matching syntax can also implement lookahead very easily, for example parse ("(":x1:xs) = to look ahead one token.
If you’re calling the parser recursively, you would define a helper function that consumes only a single expression, and that has a type signature like :: [Token] -> (AST, [Token]). This lets you parse the inner expression, check that the next token is ")", and proceed with the parse. However, externally, you’ll want to consume all the tokens and return an AST or a list of them.
The stylish way to write a parser is with monadic parser combinators. (And maybe someone will post an example of one.) The industrial-strength solution would be a library like Parsec, but that’s probably overkill here. Still, parsing is (mostly!) a solved problem, and if you just want to get the assignment done on time, using a library off the shelf is a good idea.
the read part of a REPL interpreter typically looks like this
repl :: ForthState -> IO () -- parser definition
repl state
= do putStr "> " -- puts a > character to indicate it's waiting for input
input <- getLine -- this is what you're looking for, to read a line.
if input == "quit" -- allows user to quit the interpreter
then do putStrLn "Bye!"
return ()
else let (is, cs, d, output) = eval (words input) state -- your grammar definition is somewhere down the chain when eval is called on input
in do mapM_ putStrLn output
repl (is, cs, d, [])
main = do putStrLn "Welcome to your very own interpreter!"
repl initialForthState -- runs the parser, starting with read
your eval method will have various loops, stack manipulations, conditionals, etc to actually figure out what the user inputted. hope this helps you with at least the reading input part.

F# write a string of text letter by letter without using loops

I'm doing functional programming with F# at the moment and I'm quite stuck on this. I want to print out a string of text letter by letter without using a loop as that isn't considered functional. I have a very primitive way here:
printf "W"
Thread.Sleep(200)
printf "e"
Thread.Sleep(200)
printf "l"
Thread.Sleep(200)
printf "c"
Thread.Sleep(200)
printf "o"
Thread.Sleep(200)
printf "m"
Thread.Sleep(200)
printf "e "
but obviously, I cannot do that for a 150 character string. If anyone can provide some help or point me in the right direction, it would be most appreciative. Thanks
As mentioned in the comments, blocking a thread and printing are side-effects and so they are not really functional on their own. The nice thing about F# is that you can do side-effects easily if you need to. Typically, you will have some core functional logic, called from a bit of code that does the interaction with user using side-effects.
As you really just need to iterate over a word, for loop does this perfectly:
let msg = "Welcome"
for c in msg do
printf "%c" c
Thread.Sleep(200)
But since you asked about more functional ways, one thing you could learn using this example is recursion (which is quite important in functional programming in general). You can transform for loop into a recursive function like this:
let rec printChars chars =
match chars with
| [] -> ()
| c::rest ->
printfn "%c" c
Thread.Sleep(200)
printChars rest
let msg = "Welcome"
printChars (List.ofSeq msg)
This is not what I'd normally write - because there is no point making code more complicated - but it is useful learning exercise.

Show custom errors while parsing Happy Haskell

I'm writing a monadic parser using Alex and Happy in Haskell.
My error function is defined like this:
parseError :: Token -> Alex a
parseError _ = alexError "error occurred"
How can I send custom errors (like incorrect type while trying to add a string to a number) during parsing?
UPDATE
The parser doesn't need to do the type checking, I'm doing it inside the production since I keep track of the operands type.
As said in a comment, I cannot use the parseError, so is there a way to print an error and stop the parser?
I've solved it by implementing this function:
fatalError :: (Show a1, Show a) => [Char] -> a -> a1 -> t
fatalError s l c = error ("Error at line " ++ (show l) ++ " column " ++ (show c) ++ ": " ++ s)
and I call it from the production when an error is detected

Debugging Haskell read function

I'm new to Haskell and I'm writing a simple AI decision system to play a 20 questions style of game. If the program is unable to guess the correct answer, it will ask for a question to use to distinguish the answer and it will store that question in a tree. It reads the tree in from the file system at the start of the program and writes it back out at the end.
I'm having a lot of problems with the serialization code in Haskell. I'm getting the error "Prelude read: no parse". What's going on? Here's my code:
import Data.Char
import System.IO
-- Defines a type for a binary tree that holds the questions and answers
data Tree a =
Answer String |
Question String (Tree a) (Tree a)
deriving (Read, Show)
-- Starts the game running
main = do
let filePath = "data.txt"
fileContents <- readFile filePath
animals <- return (read fileContents)
putStrLn "Think of an animal. Hit Enter when you are ready. "
_ <- getLine
ask animals
writeFile filePath (show animals)
-- Walks through the animals tree and ask the question at each node
ask :: Tree a -> IO ()
ask (Question q yes no) = do
putStrLn q
answer <- getLine
if answer == "yes" then ask yes
else ask no
ask (Answer a) = do
putStrLn $ "I know! Is your animal a " ++ a ++ "?"
answer <- getLine
if answer == "yes" then computerWins
else playerWins
computerWins = do putStrLn "See? Humans should work, computers should think!"
playerWins = do putStrLn "TODO"
And here's the data file I'm using:
Question "Does it live in the water?"
((Question "Does it hop?") (Answer "frog") (Answer "fish"))
(Answer "cow")
read is not intended to handle things like extra parentheses robustly. Your code works for me with the following data file:
Question "Does it live in the water?"
(Question "Does it hop?" (Answer "frog") (Answer "fish"))
(Answer "cow")

Resources