pi-calculus 0.0.4 → 0.0.5
raw patch · 16 files changed
+182/−173 lines, 16 filesdep +AESdep +RSAdep +binary
Dependencies added: AES, RSA, binary, byteable, cryptohash
Files
- Channel.hs +1/−22
- Parser.hs +4/−6
- PatternMatching.hs +6/−5
- PiCalculus.hs +15/−34
- Primitives.hs +58/−38
- TypDefs.hs +49/−48
- changes.md +10/−0
- pi-calculus.cabal +5/−4
- pilude.pi +6/−1
- testing/client.pi +6/−6
- testing/follow.pi +2/−2
- testing/get.pi +6/−0
- testing/index.pi +0/−4
- testing/p_chan_client.pi +6/−0
- testing/p_chan_server.pi +3/−0
- testing/server.pi +5/−3
Channel.hs view
@@ -35,26 +35,6 @@ chan <- Ch.newChan return $ Channel (Ch.writeChan chan) (Ch.readChan chan) [] -{-newInternalChan :: String -> String -> Integer -> IO Channel-newInternalChan hostName hostPort cp = do- currentHost <- getHostName- let ex = makeExtra ["host","clientPort"] [currentHost ++ ":" ++ hostPort,show cp]- return $ Channel s r True ex- where- r = N.withSocketsDo $ do- sock <- N.listenOn $ N.PortNumber $ fromIntegral cp- (inHandle,_,_) <- N.accept sock- msg <- unlines <$> emptyHandle inHandle- putStrLn $ "got: " ++ msg- N.sClose sock- return msg- s v = N.withSocketsDo $ do- _ <- forkIO $ do- outHandle <- waitForConnect hostName $ N.PortNumber $ port hostPort- putStrLn $ "sending: " ++ v- hPutStr outHandle v- return ()-}- newChanServer :: Integer -> IO Channel newChanServer cp = N.withSocketsDo $ do hanVar <- newEmptyMVar@@ -72,9 +52,8 @@ _ <- forkIO $ do serverHandle <- waitForConnect hostName $ N.PortNumber $ fromIntegral hostPort putMVar hanVar serverHandle+ let ex = makeExtra [hostSig, portSig] [hostName, show hostPort] return $ Channel (send' hanVar) (receive' hanVar) ex- where- ex = makeExtra [hostSig, portSig] [hostName, show hostPort] waitForConnect :: N.HostName -> N.PortID -> IO Handle waitForConnect h p = N.connectTo h p `catchIOError`
Parser.hs view
@@ -200,11 +200,11 @@ parseType :: Parser Type parseType = try (str HttpRequest) <|> try (str HttpResponse)- <|> try (str Header)- <|> parseListType+ -- <|> try (str Header)+ -- <|> parseListType where str t = string (show t) >> return t- parseListType = string "List" >> many1 space >> fmap List parseType + --parseListType = string "List" >> many1 space >> fmap List parseType bracketed :: Parser a -> Parser a@@ -222,6 +222,4 @@ readProcesses = readOrThrow parseProcesses "multiple-processes" readTerm :: String -> ThrowsError Term -readTerm str = case parse parseTerm "term" str of- Left err -> throwError $ Parser err- Right val -> return val +readTerm = readOrThrow parseTerm "term"
PatternMatching.hs view
@@ -1,4 +1,4 @@-module PatternMatching (match, matchTest) where +module PatternMatching (match) where import Control.Arrow (second) import Control.Monad (liftM,liftM2)@@ -7,18 +7,19 @@ import Parser (readTerm) import Network.HTTP.Base -match :: Term -> Term -> ThrowsError [(String,Value)]+match :: Term -> Term -> ThrowsError [(Name,Value)] match a b = liftM (map (second Term)) $ match' a b -match' :: Term -> Term -> ThrowsError [(String,Term)]+match' :: Term -> Term -> ThrowsError [(Name,Term)] match' (TVar name _) term = case name of '_':_ -> return [] _ -> return [(name,term)] match' (TPair (m1, m2)) (TPair (t1,t2)) = liftM2 (++) (match' m1 t1) (match' m2 t2) match' (TList (m:ms)) (TList (t:ts)) = do bind <- match' m t- rest <- case ms of- [v] -> match' v $ TList ts+ rest <- case (ms,ts) of+ ([v],[t']) -> match' v t'+ ([v], _ ) -> match' v $ TList ts _ -> match' (TList ms) (TList ts) return $ bind ++ rest match' l@(TList _) (TData d) = match' l $ dataToList d
PiCalculus.hs view
@@ -7,7 +7,6 @@ import Control.Monad.Trans (liftIO) import Control.Monad.Trans.Except (catchE, runExceptT, throwE) import Data.IORef (newIORef, readIORef,writeIORef)-import Data.Maybe (isJust) import Network.HTTP.Base (Request(..),Response(..), parseResponseHead, parseRequestHead) import System.Environment (getArgs, getProgName) import System.IO (hFlush, stderr, stdin, stdout)@@ -25,32 +24,16 @@ nullEnv :: IO Env nullEnv = newIORef Map.empty -isBound :: Env -> String -> IO Bool-isBound envRef var = liftM (isJust . Map.lookup var) $ readIORef envRef- getVar :: Env -> String -> IOThrowsError Value getVar envRef var = do env <- liftIO $ readIORef envRef maybe (throwE $ UnboundVar "Getting an unbound variable" var) return (Map.lookup var env)--setVar :: Env -> String -> Value -> IOThrowsError Value-setVar _ "_" _ = return $ Term $ TVar "_" Nothing -- to allow wildcard matching-setVar envRef var val = do env <- liftIO $ readIORef envRef- maybe (throwE $ UnboundVar "Setting an unbound variable" var)- (return $ liftIO $ writeIORef envRef $ Map.insert var val env)- (Map.lookup var env)- return val -defineVar :: Env -> String -> Value -> IOThrowsError Value-defineVar envRef var val = do- alreadyDefined <- liftIO $ isBound envRef var- if alreadyDefined- then setVar envRef var val >> return val- else liftIO $ do- env <- readIORef envRef- writeIORef envRef $ Map.insert var val env- return val+defineVar :: Env -> String -> Value -> IOThrowsError ()+defineVar envRef var val = liftIO $ do+ env <- readIORef envRef+ writeIORef envRef $ Map.insert var val env bindVars :: Env -> [(String , Value)] -> IO Env bindVars envRef bindings = do@@ -85,7 +68,7 @@ pilude <- getDataFileName "pilude.pi" case args of [] -> runRepl coreBindings- [x] -> liftM (("&load("++pilude++");")++) (readFile x) >>= runProcess coreBindings + [x] -> readFile x >>= runProcess coreBindings _ -> do putStrLn "Use:" putStrLn $ name ++ " -- Enter the REPL"@@ -103,10 +86,11 @@ evalTerm :: Env -> Term -> IOThrowsError Value evalTerm env (TVar name _) = getVar env name-evalTerm _ (TNum num) = return $ Term $ TNum num-evalTerm _ (TStr str) = return $ Term $ TStr str-evalTerm _ (TBool b ) = return $ Term $ TBool b-evalTerm _ (TData d ) = return $ Term $ TData d+evalTerm _ t@(TNum _ ) = return $ Term t+evalTerm _ t@(TStr _ ) = return $ Term t+evalTerm _ t@(TBool _ ) = return $ Term t+evalTerm _ t@(TBS _ ) = return $ Term t+evalTerm _ t@(TData _ ) = return $ Term t evalTerm env (TList ls) = do vs <- mapM (evalTerm env) ls ts <- extractTerms vs@@ -163,7 +147,7 @@ assignFreePort :: Env -> IOThrowsError Integer assignFreePort env = do Term (TNum port) <- getVar env counterRef- _ <- setVar env counterRef $ Term $ TNum $ port + 1+ defineVar env counterRef $ Term $ TNum $ port + 1 if port == 2 ^ (16 :: Integer) then error "HOW MANY CHANNELS DO YOU WANT?!" else return port@@ -240,7 +224,7 @@ eval env (p1 `Seq` p2) = do eval env p1 eval env p2-eval env (New var@(TVar name _)) = void $ defineVar env name $ Term var+eval env (New var@(TVar name _)) = defineVar env name $ Term var eval env (If b p1 p2) = do cond <- evalCond env b eval env (if cond then p1 else p2)@@ -250,14 +234,11 @@ eval newEnv p eval env (Let (TVar name _) (Term t2) Nothing) = do val <- evalTerm env t2- _ <- defineVar env name val- return ()+ defineVar env name val eval env (Let (TVar name _) proc@(Proc _) (Just p)) = do newEnv <- liftIO $ bindVars env [(name,proc)] eval newEnv p-eval env (Let (TVar name _) proc@(Proc _) Nothing) = do- _ <- defineVar env name proc- return ()+eval env (Let (TVar name _) proc@(Proc _) Nothing) = defineVar env name proc eval env (Let (TFun name args) t2 (Just p)) = defineLocalFun env name args t2 p eval env (Let (TFun name args) t2 Nothing) = @@ -293,7 +274,7 @@ eval _ _ = throwE $ Default "undefined action" defineGlobalFun :: Env -> String -> [Term] -> Value -> IOThrowsError ()-defineGlobalFun env name args term = void $ defineVar env name $ makeFun args term env+defineGlobalFun env name args term = defineVar env name $ makeFun args term env defineLocalFun :: Env -> String -> [Term] -> Value -> PiProcess -> IOThrowsError () defineLocalFun env name args term p = do
Primitives.hs view
@@ -2,6 +2,9 @@ import Control.Applicative ((<$>)) import Control.Monad.Error (throwError)+import qualified Crypto.Hash as Crypto+import Data.Byteable (toBytes)+import qualified Data.ByteString.Char8 as C8 import Network.HTTP.Base (Request(..), Response(..), RequestMethod(..), mkRequest) import Network.HTTP.Cookie (Cookie, cookiesToHeader) import Network.HTTP.Headers (HasHeaders(..), Header, HeaderName(..), hdrName, hdrValue, findHeader, parseHeader, replaceHeader, setHeaders)@@ -9,41 +12,43 @@ import TypDefs -primitives :: [(String , TermFun)]-primitives = [ ("fst" , first)- , ("snd" , secnd)- , ("hash" , unaryId "hash")- , ("pk" , unaryId "pk")- , ("httpReq" , httpReq)- , ("httpResp" , httpResp)- , ("getmsg" , getmsg)- , ("pair" , binaryId "pair")- , ("sdec" , sdec)- , ("senc" , binaryId "senc")- , ("adec" , adec)- , ("aenc" , binaryId "aenc")- , ("sign" , binaryId "sign")- , ("checksign" , checksign)- , ("mac" , binaryId "mac")- , ("headers" , listId)- , ("header" , header)- , ("cookies" , listId)- , ("httpGet" , constId "httpGet")- , ("httpHead" , constId "httpHead")- , ("httpPost" , constId "httpPost")- , ("getHeaders", getHeadersFromData)- , ("setHeader" , setHeader)- , ("getHeader" , getHeader)- , ("getCookie" , getCookie)- , ("setCookie" , setCookie)- , ("uri" , makeUri)- , ("rspCode" , responseCode)- , ("add" , add)- , ("cons" , cons)- , ("head" , head')- , ("tail" , tail')+primitives :: [(Name , TermFun)]+primitives = [ ("fst" , first)+ , ("snd" , secnd)+ , ("hash" , hash)+ , ("pk" , unaryId "pk")+ , ("httpReq" , httpReq)+ , ("httpResp" , httpResp)+ , ("getmsg" , getmsg)+ , ("sdec" , sdec)+ , ("senc" , binaryId "senc")+ , ("adec" , adec)+ , ("aenc" , binaryId "aenc")+ , ("sign" , binaryId "sign")+ , ("checksign" , checksign)+ , ("mac" , mac)+ , ("headers" , listId)+ , ("header" , header)+ , ("cookies" , listId)+ , ("httpGet" , constId "httpGet")+ , ("httpHead" , constId "httpHead")+ , ("httpPost" , constId "httpPost")+ , ("getHeaders" , getHeadersFromData)+ , ("insertHeader", insertHeader)+ , ("getHeader" , getHeader)+ , ("getCookie" , getCookie)+ , ("setCookie" , setCookie)+ , ("uri" , makeUri)+ , ("rspCode" , responseCode)+ , ("add" , add)+ , ("cons" , cons)+ , ("head" , head')+ , ("tail" , tail')+ , ("append" , append) ] ++ constId :: String -> TermFun constId name [] = return $ TFun name [] constId name e = throwError $ NumArgs name 0 (map Term e)@@ -87,6 +92,7 @@ checksign [TFun "pk" [k1], TFun "sign" [k2,_] ] = return $ TBool (k1 == k2) checksign e = throwError $ TypeMismatch "(pk(var),sign(var,var))" $ map Term e + httpReq :: TermFun httpReq [TStr url, TList hs , reqMethod] = do uri <- case parseURI http' of @@ -149,14 +155,14 @@ _ -> throwError $ Default $ headr ++ " not in " ++ show d getHeader e = throwError $ TypeMismatch "httpData" $ map Term e -setHeader :: TermFun-setHeader [h@(TStr _), TData d] = do +insertHeader :: TermFun+insertHeader [h@(TStr _), TData d] = do headr <- makeHeader h let name = hdrName headr let val = hdrValue headr return $ TData $ replaceHeader name val d -setHeader [h@(TStr _), TList hs] = return $ TList $ h : hs -setHeader e = throwError $ TypeMismatch "(header,httpData|headers)" $ map Term e+insertHeader [h@(TStr _), TList hs] = return $ TList $ h : hs +insertHeader e = throwError $ TypeMismatch "(header,httpData|headers)" $ map Term e getCookie :: TermFun getCookie [TData d] = @@ -166,7 +172,7 @@ getCookie e = throwError $ TypeMismatch "httpData" $ map Term e setCookie :: TermFun-setCookie [TData d, c] = do+setCookie [c, TData d] = do cookie <- makeCookie c let cookieHeader = cookiesToHeader [cookie] let n = hdrName cookieHeader@@ -204,3 +210,17 @@ tail' [TList []] = throwError $ Default "tail of empty list" tail' [TList x] = return $ TList $ tail x tail' e = throwError $ TypeMismatch "list" $ map Term e++hash :: TermFun+hash [TBS msg] = return $ TBS $ toBytes (Crypto.hash msg :: Crypto.Digest Crypto.MD5)+hash [TStr msg] = let mBS = C8.pack msg in return $ TBS $ toBytes (Crypto.hash mBS :: Crypto.Digest Crypto.MD5)+hash e = throwError $ TypeMismatch "bytestring" $ map Term e+ +mac :: TermFun+mac [TBS key,TBS msg] = return $ TBS $ toBytes $ Crypto.hmacGetDigest $ Crypto.hmacAlg Crypto.MD5 key msg+mac [TBS key,TStr msg] = let mBS = C8.pack msg in return $ TBS $ toBytes $ Crypto.hmacGetDigest $ Crypto.hmacAlg Crypto.MD5 key mBS+mac e = throwError $ TypeMismatch "(bytestring,bytestring)" $ map Term e++append :: TermFun+append [TList x, TList y] = return $ TList $ x ++ y +append e = throwError $ TypeMismatch "(list,list)" $ map Term e
TypDefs.hs view
@@ -17,6 +17,7 @@ import Control.Monad.Trans.Except(ExceptT(..)) import Data.Char (toLower)+import Data.ByteString (ByteString) import Data.IORef (IORef) import Data.List (intercalate) import Data.Map (Map)@@ -43,6 +44,7 @@ | TBool Bool | TPair (Term, Term) | TList [Term]+ | TBS ByteString | TData HttpData | TFun Name [Term] deriving (Eq)@@ -50,22 +52,13 @@ data HttpData = Resp (Response String) | Req (Request String) -instance Eq HttpData where (==) = eqHttpData-instance Show HttpData where show = showHttpData- instance HasHeaders HttpData where getHeaders (Resp r) = getHeaders r getHeaders (Req r) = getHeaders r setHeaders (Resp r) = Resp . setHeaders r setHeaders (Req r) = Req . setHeaders r -showHttpData :: HttpData -> String-showHttpData (Resp r) = show r-showHttpData (Req r) = show r -eqHttpData :: HttpData -> HttpData -> Bool-eqHttpData _ _ = False- type TermFun = [Term] -> ThrowsError Term type Name = String@@ -93,27 +86,9 @@ data Type = HttpRequest | HttpResponse- | Header- | List Type deriving (Eq, Read, Show) -instance Show PiError where show = showError--showError :: PiError -> String-showError (UnboundVar message var) = message ++ ": " ++ var-showError (NotFunction message fun) = message ++ ": " ++ fun-showError (NotChannel chan) = "Not a channel: " ++ chan -showError (NotProcess proc) = "Not a Process: " ++ proc-showError (NotTerm name var) = "Expecting " ++ name ++ " to be a Term, found: " ++ show var-showError (NumArgs name expected found) = "Expected " ++ show name ++ show expected ++ " args; found values "- ++ unwordsList found-showError (TypeMismatch expected found) = "Invalid type: expected " ++ expected ++ ", found "- ++ show found-showError (Parser parseErr) = "Parse error at " ++ show parseErr-showError (PatternMatch t1 t2) = "Pattern match failure: cannot match " ++ show t1 ++ " and \n" ++ show t2-showError (Default msg) = msg--type Env = IORef (Map String Value)+type Env = IORef (Map Name Value) data Channel = Channel { send :: String -> IO ()@@ -125,22 +100,12 @@ | Connect deriving (Eq, Show, Read) -showValue :: Value -> String-showValue (Proc p) = show p-showValue (Term t) = show t-showValue (Chan c) = show $ convert c- where - convert ch = TFun "<chan>" (map TStr ex) - where ex = extra ch-showValue (PrimitiveFunc _) = "<primitive>" -showValue (Func {}) = "<user function>" --eqvVal :: Value -> Value -> Bool-eqvVal (Proc p1) (Proc p2) = p1 == p2-eqvVal (Term t1) (Term t2) = t1 == t2-eqvVal _ _ = False--instance Eq Value where (==) = eqvVal+instance Show PiProcess where show = showPi+instance Show Term where show = showTerm+instance Show Value where show = showValue+instance Show Condition where show = showCond+instance Show PiError where show = showError+instance Show HttpData where show = showHttpData showPi :: PiProcess -> String showPi Null = "0"@@ -166,15 +131,51 @@ showTerm (TList ls) = "list(" ++ intercalate "," (map show ls) ++ ")" showTerm (TPair (a,b)) = "pair("++ show a ++ ","++ show b ++ ")" showTerm (TData d) = show d+showTerm (TBS bs) = show bs showTerm (TFun n ts) = n ++ "(" ++ intercalate "," (map show ts) ++ ")" +showValue :: Value -> String+showValue (Proc p) = show p+showValue (Term t) = show t+showValue (Chan c) = show $ convert c+ where + convert ch = TFun "<chan>" (map TStr ex) + where ex = extra ch+showValue (PrimitiveFunc _) = "<primitive>" +showValue (Func {}) = "<user function>" + showCond :: Condition -> String showCond (t1 `Equals` t2) = show t1 ++ " == " ++ show t2 -instance Show PiProcess where show = showPi-instance Show Term where show = showTerm-instance Show Condition where show = showCond-instance Show Value where show = showValue+showError :: PiError -> String+showError (UnboundVar message var) = message ++ ": " ++ var+showError (NotFunction message fun) = message ++ ": " ++ fun+showError (NotChannel chan) = "Not a channel: " ++ chan +showError (NotProcess proc) = "Not a Process: " ++ proc+showError (NotTerm name var) = "Expecting " ++ name ++ " to be a Term, found: " ++ show var+showError (NumArgs name expected found) = "Expected " ++ show name ++ show expected ++ " args; found values "+ ++ unwordsList found+showError (TypeMismatch expected found) = "Invalid type: expected " ++ expected ++ ", found "+ ++ show found+showError (Parser parseErr) = "Parse error at " ++ show parseErr+showError (PatternMatch t1 t2) = "Pattern match failure: cannot match " ++ show t1 ++ " and \n" ++ show t2+showError (Default msg) = msg++showHttpData :: HttpData -> String+showHttpData (Resp r) = show r+showHttpData (Req r) = show r ++instance Eq Value where (==) = eqvVal+instance Eq HttpData where (==) = eqHttpData++eqHttpData :: HttpData -> HttpData -> Bool+eqHttpData _ _ = False++eqvVal :: Value -> Value -> Bool+eqvVal (Proc p1) (Proc p2) = p1 == p2+eqvVal (Term t1) (Term t2) = t1 == t2+eqvVal _ _ = False+ unwordsList :: [Value] -> String unwordsList = unwords . map show
+ changes.md view
@@ -0,0 +1,10 @@+Version 0.0.5:+--------------++Changelog!++- Fixed an issue with pattern matching on lists. Lists of the same length pattern match exactly: i.e.+let list(a,b,c) = list(1,2,3) => a = 1 , b = 2, c = 3 . ++- Added the primitive append(list(a..),list(b..)) => list(a..,b..)+
pi-calculus.cabal view
@@ -2,9 +2,9 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: pi-calculus-version: 0.0.4-synopsis: Applied pi-calculus compiler-description: Phi - A compiler for the applied pi-calculus. Very rough around the edges.+version: 0.0.5+synopsis: Applied pi-calculus interpreter+description: Phi - an interpreter for the applied pi-calculus. Very rough around the edges. license: GPL-3 license-file: LICENSE author: Will de Renzy-Martin@@ -15,8 +15,9 @@ build-type: Simple cabal-version: >=1.8 data-files: testing/*.pi, pilude.pi+extra-source-files: changes.md executable phi main-is: PiCalculus.hs other-modules: Channel, Parser, PatternMatching, Primitives, TypDefs, Paths_pi_calculus- build-depends: base >=4.5 && <=4.8, mtl ==2.2.*, parsec ==3.1.*, containers ==0.5.*, transformers ==0.4.*, network ==2.5.*, HTTP >=2.15, bytestring >= 0.10, io-streams ==1.1.*+ build-depends: base >=4.5 && <=4.8, mtl ==2.2.*, parsec ==3.1.*, containers ==0.5.*, transformers ==0.4.*, network ==2.5.*, HTTP >=2.15, bytestring >=0.10, io-streams ==1.1.*, cryptohash ==0.11.*, byteable ==0.1.*, binary ==0.7.2.*, AES ==0.2.*, RSA ==1.2.2.*
pilude.pi view
@@ -2,4 +2,9 @@ let print(msg) = out(stdout,msg) let error(msg) = out(stderr,msg) let dbgOut(c, msg) = out(c,msg);out(stdout,msg)-let forever(process) = &process;&forever(process)+let fetch_out(c) = in(stdin,msg);out(c,msg)+let in_print(c) = in(c,msg);out(stdout,msg)+let forever(p) = &p;&forever(p)+let getLocation(data) = getHeader("location",data)+let in_and_out(c) = &in_print(c);&fetch_out(c)+let out_and_in(c) = &fetch_out(c);&in_print(c)
testing/client.pi view
@@ -1,6 +1,6 @@-out(stdout,"Enter host:"); in(stdin,host);-out(stdout,"Enter port:"); in(stdin,port);-let ch = chan(host,port) in- let sch = {} in- out(stdout,sch);out(ch,sch);in(sch,msg);out(stdout,msg)-+&load("pilude.pi");+out(stdout,"Enter Host"); in(stdin,host);+out(stdout,"Enter Port"); in(stdin,port);+ let ch = chan(host,port) in+ let proc = out_and_in(ch) in+ &forever(proc)
testing/follow.pi view
@@ -1,5 +1,5 @@ let follow(ch,r) = (out(ch,r);in(ch,resp:HttpResponse);- let list(c,_,h,_) = resp in+ let list(c,_,h,b) = resp in if c = 302 then let req = httpReq(getHeader("location",resp),headers(),httpGet()) in &follow(ch,req) - else out(stdout,h)) + else &print(b))
+ testing/get.pi view
@@ -0,0 +1,6 @@+&load("pilude.pi");+&load("follow.pi");+&print("Host:");in(stdin,site);+&print("Resource:");in(stdin,res);+let siteChan = httpChan(site) in+ let req = httpReq(uri(site,res),headers(),httpGet()) in &follow(siteChan,req)
− testing/index.pi
@@ -1,4 +0,0 @@-&load("follow.pi");-out(stdout,"Host:");in(stdin,site);-let siteChan = httpChan(site) in-let req = httpReq(uri(site,"index.html"),headers(),httpGet()) in &follow(siteChan,req)
+ testing/p_chan_client.pi view
@@ -0,0 +1,6 @@+out(stdout,"Enter host:"); in(stdin,host);+out(stdout,"Enter port:"); in(stdin,port);+let ch = chan(host,port) in+ let sch = {} in+ out(ch,sch);in(sch,msg);out(stdout,msg)+
+ testing/p_chan_server.pi view
@@ -0,0 +1,3 @@+out(stdout,"Enter port:"); in(stdin,port);+let ch = {port} in + in(ch,sch);in(stdin,msg);out(sch,msg)
testing/server.pi view
@@ -1,3 +1,5 @@-out(stdout,"Enter port:"); in(stdin,port);-let ch = {port} in - in(ch,sch);out(stdout,sch);in(stdin,msg);out(sch,msg)+&load("pilude.pi");+out(stdout,"Enter Port");+in(stdin,port);+ let ch = {port} in+ &in_and_out(ch)