mirror of
https://github.com/edwinb/Idris2-boot.git
synced 2024-09-20 04:17:38 +03:00
Merge branch 'master' of https://github.com/edwinb/Idris2
This commit is contained in:
commit
b682accc0b
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,6 +6,8 @@
|
|||||||
idris2
|
idris2
|
||||||
runtests
|
runtests
|
||||||
|
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
libs/**/build
|
libs/**/build
|
||||||
tests/**/output
|
tests/**/output
|
||||||
|
|
||||||
|
8
Makefile
8
Makefile
@ -26,7 +26,11 @@ prelude:
|
|||||||
base: prelude
|
base: prelude
|
||||||
make -C libs/base IDRIS2=../../idris2
|
make -C libs/base IDRIS2=../../idris2
|
||||||
|
|
||||||
libs : prelude base
|
network: prelude
|
||||||
|
make -C libs/network IDRIS2=../../idris2
|
||||||
|
make -C libs/network test IDRIS2=../../idris2
|
||||||
|
|
||||||
|
libs : prelude base network
|
||||||
|
|
||||||
clean: clean-libs
|
clean: clean-libs
|
||||||
make -C src clean
|
make -C src clean
|
||||||
@ -37,6 +41,7 @@ clean: clean-libs
|
|||||||
clean-libs:
|
clean-libs:
|
||||||
make -C libs/prelude clean
|
make -C libs/prelude clean
|
||||||
make -C libs/base clean
|
make -C libs/base clean
|
||||||
|
make -C libs/network clean
|
||||||
|
|
||||||
test:
|
test:
|
||||||
idris --build tests.ipkg
|
idris --build tests.ipkg
|
||||||
@ -58,3 +63,4 @@ install-exec: idris2
|
|||||||
install-libs: libs
|
install-libs: libs
|
||||||
make -C libs/prelude install IDRIS2=../../idris2
|
make -C libs/prelude install IDRIS2=../../idris2
|
||||||
make -C libs/base install IDRIS2=../../idris2
|
make -C libs/base install IDRIS2=../../idris2
|
||||||
|
make -C libs/network install IDRIS2=../../idris2
|
||||||
|
@ -99,3 +99,49 @@ the elaborator is working on, up to `5` gives you more specific details of
|
|||||||
each stage of the elaboration, and up to `10` gives you full details of
|
each stage of the elaboration, and up to `10` gives you full details of
|
||||||
type checking including progress on unification problems
|
type checking including progress on unification problems
|
||||||
|
|
||||||
|
## Code Generator Directives
|
||||||
|
|
||||||
|
### %cg
|
||||||
|
|
||||||
|
#### Backend code injection
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
* `%cg <backend> <single-line instruction>`
|
||||||
|
* `%cg <backend> { <multi-line instructions> }`
|
||||||
|
|
||||||
|
The given instructions are passed directly to the code generator, to be
|
||||||
|
interpreted in a backend-specific way.
|
||||||
|
|
||||||
|
Currently known backends are: `chez`, `chicken` and `racket`.
|
||||||
|
|
||||||
|
## Backend Specific Directives
|
||||||
|
|
||||||
|
### Chez
|
||||||
|
|
||||||
|
#### Dynamic library loading
|
||||||
|
|
||||||
|
Syntax: `%cg chez lib<library_name>`
|
||||||
|
|
||||||
|
Instructs chez to load given external library at
|
||||||
|
runtime, eg. using dynamic library loader. The compiler looks for a
|
||||||
|
file called `library_name` in the following directories and generates
|
||||||
|
corresponding file-loading code:
|
||||||
|
|
||||||
|
* current directory `.`
|
||||||
|
* directories listed in the `IDRIS2_LIBS` environment variable, if
|
||||||
|
set
|
||||||
|
* directories `lib/` in each package the code depends on (`base` and
|
||||||
|
`prelude` are automatically added). Loads library as absolute path name of _first matchine file_
|
||||||
|
found.
|
||||||
|
* directory `lib/` in the Idris2 installation path (usually
|
||||||
|
`~/.idris2/idris2`).
|
||||||
|
|
||||||
|
Note the file is loaded referencing an absolute path of the first
|
||||||
|
matching file found, except when it's the current directory in which
|
||||||
|
case the file is simply referenced as `./library_name`
|
||||||
|
|
||||||
|
### Chicken
|
||||||
|
|
||||||
|
The directive should be scheme code which is inserted directly into the generated
|
||||||
|
code. This can be used, for example, to import external libraries, e.g.
|
||||||
|
`%cg chicken (use posix)`.
|
||||||
|
@ -35,13 +35,14 @@ extensions = [
|
|||||||
'sphinx.ext.todo',
|
'sphinx.ext.todo',
|
||||||
# 'sphinx.ext.pngmath', # imgmath is not supported on readthedocs.
|
# 'sphinx.ext.pngmath', # imgmath is not supported on readthedocs.
|
||||||
'sphinx.ext.ifconfig',
|
'sphinx.ext.ifconfig',
|
||||||
|
'recommonmark'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ['.rst','.md']
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
#source_encoding = 'utf-8-sig'
|
#source_encoding = 'utf-8-sig'
|
||||||
|
@ -111,6 +111,7 @@ modules =
|
|||||||
TTImp.TTImp,
|
TTImp.TTImp,
|
||||||
TTImp.Unelab,
|
TTImp.Unelab,
|
||||||
TTImp.Utils,
|
TTImp.Utils,
|
||||||
|
TTImp.WithClause,
|
||||||
|
|
||||||
Utils.Binary,
|
Utils.Binary,
|
||||||
Utils.Shunting,
|
Utils.Shunting,
|
||||||
|
@ -85,8 +85,8 @@ export
|
|||||||
strengthen : Fin (S n) -> Either (Fin (S n)) (Fin n)
|
strengthen : Fin (S n) -> Either (Fin (S n)) (Fin n)
|
||||||
strengthen {n = S k} FZ = Right FZ
|
strengthen {n = S k} FZ = Right FZ
|
||||||
strengthen {n = S k} (FS i) with (strengthen i)
|
strengthen {n = S k} (FS i) with (strengthen i)
|
||||||
strengthen (FS k) | Left x = Left (FS x)
|
strengthen (FS i) | Left x = Left (FS x)
|
||||||
strengthen (FS k) | Right x = Right (FS x)
|
strengthen (FS i) | Right x = Right (FS x)
|
||||||
strengthen f = Left f
|
strengthen f = Left f
|
||||||
|
|
||||||
||| Add some natural number to a Fin, extending the bound accordingly
|
||| Add some natural number to a Fin, extending the bound accordingly
|
||||||
|
@ -40,11 +40,28 @@ dropWhile p (x::xs) = if p x then dropWhile p xs else x::xs
|
|||||||
public export
|
public export
|
||||||
filter : (p : a -> Bool) -> List a -> List a
|
filter : (p : a -> Bool) -> List a -> List a
|
||||||
filter p [] = []
|
filter p [] = []
|
||||||
filter p (x :: xs)
|
filter p (x :: xs)
|
||||||
= if p x
|
= if p x
|
||||||
then x :: filter p xs
|
then x :: filter p xs
|
||||||
else filter p xs
|
else filter p xs
|
||||||
|
|
||||||
|
||| Find associated information in a list using a custom comparison.
|
||||||
|
public export
|
||||||
|
lookupBy : (a -> a -> Bool) -> a -> List (a, b) -> Maybe b
|
||||||
|
lookupBy p e [] = Nothing
|
||||||
|
lookupBy p e (x::xs) =
|
||||||
|
let (l, r) = x in
|
||||||
|
if p e l then
|
||||||
|
Just r
|
||||||
|
else
|
||||||
|
lookupBy p e xs
|
||||||
|
|
||||||
|
||| Find associated information in a list using Boolean equality.
|
||||||
|
public export
|
||||||
|
lookup : Eq a => a -> List (a, b) -> Maybe b
|
||||||
|
lookup = lookupBy (==)
|
||||||
|
|
||||||
|
|
||||||
public export
|
public export
|
||||||
span : (a -> Bool) -> List a -> (List a, List a)
|
span : (a -> Bool) -> List a -> (List a, List a)
|
||||||
span p [] = ([], [])
|
span p [] = ([], [])
|
||||||
@ -70,7 +87,7 @@ public export
|
|||||||
splitAt : (n : Nat) -> (xs : List a) -> (List a, List a)
|
splitAt : (n : Nat) -> (xs : List a) -> (List a, List a)
|
||||||
splitAt Z xs = ([], xs)
|
splitAt Z xs = ([], xs)
|
||||||
splitAt (S k) [] = ([], [])
|
splitAt (S k) [] = ([], [])
|
||||||
splitAt (S k) (x :: xs)
|
splitAt (S k) (x :: xs)
|
||||||
= let (tk, dr) = splitAt k xs in
|
= let (tk, dr) = splitAt k xs in
|
||||||
(x :: tk, dr)
|
(x :: tk, dr)
|
||||||
|
|
||||||
@ -115,6 +132,22 @@ export
|
|||||||
toList : Foldable t => t a -> List a
|
toList : Foldable t => t a -> List a
|
||||||
toList = foldr (::) []
|
toList = foldr (::) []
|
||||||
|
|
||||||
|
||| Insert some separator between the elements of a list.
|
||||||
|
|||
|
||||||
|
||| ````idris example
|
||||||
|
||| with List (intersperse ',' ['a', 'b', 'c', 'd', 'e'])
|
||||||
|
||| ````
|
||||||
|
|||
|
||||||
|
export
|
||||||
|
intersperse : a -> List a -> List a
|
||||||
|
intersperse sep [] = []
|
||||||
|
intersperse sep (x::xs) = x :: intersperse' sep xs
|
||||||
|
where
|
||||||
|
intersperse' : a -> List a -> List a
|
||||||
|
intersperse' sep [] = []
|
||||||
|
intersperse' sep (y::ys) = sep :: y :: intersperse' sep ys
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Sorting
|
-- Sorting
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@ -181,17 +214,17 @@ Uninhabited ([] = Prelude.(::) x xs) where
|
|||||||
export
|
export
|
||||||
Uninhabited (Prelude.(::) x xs = []) where
|
Uninhabited (Prelude.(::) x xs = []) where
|
||||||
uninhabited Refl impossible
|
uninhabited Refl impossible
|
||||||
--
|
--
|
||||||
-- ||| (::) is injective
|
-- ||| (::) is injective
|
||||||
-- consInjective : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
|
-- consInjective : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
|
||||||
-- (x :: xs) = (y :: ys) -> (x = y, xs = ys)
|
-- (x :: xs) = (y :: ys) -> (x = y, xs = ys)
|
||||||
-- consInjective Refl = (Refl, Refl)
|
-- consInjective Refl = (Refl, Refl)
|
||||||
--
|
--
|
||||||
-- ||| Two lists are equal, if their heads are equal and their tails are equal.
|
-- ||| Two lists are equal, if their heads are equal and their tails are equal.
|
||||||
-- consCong2 : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
|
-- consCong2 : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
|
||||||
-- x = y -> xs = ys -> x :: xs = y :: ys
|
-- x = y -> xs = ys -> x :: xs = y :: ys
|
||||||
-- consCong2 Refl Refl = Refl
|
-- consCong2 Refl Refl = Refl
|
||||||
--
|
--
|
||||||
-- ||| Appending pairwise equal lists gives equal lists
|
-- ||| Appending pairwise equal lists gives equal lists
|
||||||
-- appendCong2 : {x1 : List a} -> {x2 : List a} ->
|
-- appendCong2 : {x1 : List a} -> {x2 : List a} ->
|
||||||
-- {y1 : List b} -> {y2 : List b} ->
|
-- {y1 : List b} -> {y2 : List b} ->
|
||||||
@ -203,13 +236,13 @@ Uninhabited (Prelude.(::) x xs = []) where
|
|||||||
-- consCong2
|
-- consCong2
|
||||||
-- (fst $ consInjective eq1)
|
-- (fst $ consInjective eq1)
|
||||||
-- (appendCong2 (snd $ consInjective eq1) eq2)
|
-- (appendCong2 (snd $ consInjective eq1) eq2)
|
||||||
--
|
--
|
||||||
-- ||| List.map is distributive over appending.
|
-- ||| List.map is distributive over appending.
|
||||||
-- mapAppendDistributive : (f : a -> b) -> (x : List a) -> (y : List a) ->
|
-- mapAppendDistributive : (f : a -> b) -> (x : List a) -> (y : List a) ->
|
||||||
-- map f (x ++ y) = map f x ++ map f y
|
-- map f (x ++ y) = map f x ++ map f y
|
||||||
-- mapAppendDistributive _ [] _ = Refl
|
-- mapAppendDistributive _ [] _ = Refl
|
||||||
-- mapAppendDistributive f (_ :: xs) y = cong $ mapAppendDistributive f xs y
|
-- mapAppendDistributive f (_ :: xs) y = cong $ mapAppendDistributive f xs y
|
||||||
--
|
--
|
||||||
||| The empty list is a right identity for append.
|
||| The empty list is a right identity for append.
|
||||||
export
|
export
|
||||||
appendNilRightNeutral : (l : List a) ->
|
appendNilRightNeutral : (l : List a) ->
|
||||||
|
@ -72,6 +72,17 @@ public export
|
|||||||
break : (Char -> Bool) -> String -> (String, String)
|
break : (Char -> Bool) -> String -> (String, String)
|
||||||
break p = span (not . p)
|
break p = span (not . p)
|
||||||
|
|
||||||
|
|
||||||
|
||| Splits the string into parts with the predicate
|
||||||
|
||| indicating separator characters.
|
||||||
|
|||
|
||||||
|
||| ```idris example
|
||||||
|
||| split (== '.') ".AB.C..D"
|
||||||
|
||| ```
|
||||||
|
public export
|
||||||
|
split : (Char -> Bool) -> String -> List String
|
||||||
|
split p xs = map pack (split p (unpack xs))
|
||||||
|
|
||||||
export
|
export
|
||||||
stringToNatOrZ : String -> Nat
|
stringToNatOrZ : String -> Nat
|
||||||
stringToNatOrZ = fromInteger . prim__cast_StringInteger
|
stringToNatOrZ = fromInteger . prim__cast_StringInteger
|
||||||
|
57
libs/network/Echo.idr
Normal file
57
libs/network/Echo.idr
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
module Main
|
||||||
|
|
||||||
|
import System
|
||||||
|
import Network.Socket
|
||||||
|
import Network.Socket.Data
|
||||||
|
import Network.Socket.Raw
|
||||||
|
|
||||||
|
%cg chez libidris_net.so
|
||||||
|
|
||||||
|
runServer : IO (Either String (Port, ThreadID))
|
||||||
|
runServer = do
|
||||||
|
Right sock <- socket AF_INET Stream 0
|
||||||
|
| Left fail => pure (Left $ "Failed to open socket: " ++ show fail)
|
||||||
|
res <- bind sock (Just (Hostname "localhost")) 0
|
||||||
|
if res /= 0
|
||||||
|
then pure (Left $ "Failed to bind socket with error: " ++ show res)
|
||||||
|
else do
|
||||||
|
port <- getSockPort sock
|
||||||
|
res <- listen sock
|
||||||
|
if res /= 0
|
||||||
|
then pure (Left $ "Failed to listen on socket with error: " ++ show res)
|
||||||
|
else do
|
||||||
|
forked <- fork (serve port sock)
|
||||||
|
pure $ Right (port, forked)
|
||||||
|
|
||||||
|
where
|
||||||
|
serve : Port -> Socket -> IO ()
|
||||||
|
serve port sock = do
|
||||||
|
Right (s, _) <- accept sock
|
||||||
|
| Left err => putStrLn ("Failed to accept on socket with error: " ++ show err)
|
||||||
|
Right (str, _) <- recv s 1024
|
||||||
|
| Left err => putStrLn ("Failed to accept on socket with error: " ++ show err)
|
||||||
|
putStrLn ("Received: " ++ str)
|
||||||
|
Right n <- send s ("echo: " ++ str)
|
||||||
|
| Left err => putStrLn ("Server failed to send data with error: " ++ show err)
|
||||||
|
putStrLn ("Server sent " ++ show n ++ " bytes")
|
||||||
|
|
||||||
|
runClient : Port -> IO ()
|
||||||
|
runClient serverPort = do
|
||||||
|
Right sock <- socket AF_INET Stream 0
|
||||||
|
| Left fail => putStrLn ("Failed to open socket: " ++ show fail)
|
||||||
|
res <- connect sock (Hostname "localhost") serverPort
|
||||||
|
if res /= 0
|
||||||
|
then putStrLn ("Failed to connect client to port " ++ show serverPort ++ ": " ++ show res)
|
||||||
|
else do
|
||||||
|
Right n <- send sock ("hello world!")
|
||||||
|
| Left err => putStrLn ("Client failed to send data with error: " ++ show err)
|
||||||
|
putStrLn ("Client sent " ++ show n ++ " bytes")
|
||||||
|
Right (str, _) <- recv sock 1024
|
||||||
|
| Left err => putStrLn ("Client failed to receive on socket with error: " ++ show err)
|
||||||
|
putStrLn ("Received: " ++ str)
|
||||||
|
|
||||||
|
main : IO ()
|
||||||
|
main = do
|
||||||
|
Right (serverPort, tid) <- runServer
|
||||||
|
| Left err => putStrLn $ "[server] " ++ err
|
||||||
|
runClient serverPort
|
61
libs/network/Makefile
Normal file
61
libs/network/Makefile
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
RANLIB ?=ranlib
|
||||||
|
AR ?=ar
|
||||||
|
|
||||||
|
MACHINE := $(shell $(CC) -dumpmachine)
|
||||||
|
ifneq (, $(findstring darwin, $(MACHINE)))
|
||||||
|
OS :=darwin
|
||||||
|
else ifneq (, $(findstring cygwin, $(MACHINE)))
|
||||||
|
OS :=windows
|
||||||
|
else ifneq (, $(findstring mingw, $(MACHINE)))
|
||||||
|
OS :=windows
|
||||||
|
else ifneq (, $(findstring windows, $(MACHINE)))
|
||||||
|
OS :=windows
|
||||||
|
else
|
||||||
|
OS :=unix
|
||||||
|
endif
|
||||||
|
|
||||||
|
SHLIB_SUFFIX :=.so
|
||||||
|
|
||||||
|
LIBNAME=idris_net
|
||||||
|
OBJS = $(LIBNAME).o
|
||||||
|
HDRS = $(LIBNAME).h
|
||||||
|
CFLAGS := $(CFLAGS)
|
||||||
|
IDRIS_SRCS = Network/Socket.idr Network/Socket/Data.idr Network/Socket/Raw.idr
|
||||||
|
|
||||||
|
ifneq ($(OS), windows)
|
||||||
|
CFLAGS += -fPIC
|
||||||
|
endif
|
||||||
|
|
||||||
|
DYLIBTARGET = $(LIBNAME)$(SHLIB_SUFFIX)
|
||||||
|
LIBTARGET = $(LIBNAME).a
|
||||||
|
TARGET=${HOME}/.idris2
|
||||||
|
|
||||||
|
build: $(DYLIBTARGET) $(IDRIS_SRCS)
|
||||||
|
@if ! [ -d build ]; then echo "creating 'build' directory"; mkdir build ; else echo "directory 'build' exists"; fi
|
||||||
|
@if [ -z "${IDRIS2}" ]; then echo 'variable $$IDRIS2 is not set, aborting'; exit 1; fi
|
||||||
|
${IDRIS2} --build network.ipkg
|
||||||
|
|
||||||
|
$(DYLIBTARGET) : $(OBJS)
|
||||||
|
$(CC) -o $(DYLIBTARGET) $(LIBFLAGS) -shared $(OBJS)
|
||||||
|
|
||||||
|
install:
|
||||||
|
@if [ -z "${IDRIS2}" ]; then echo 'variable $$IDRIS2 is not set, aborting'; exit 1; fi
|
||||||
|
${IDRIS2} --install network.ipkg
|
||||||
|
@if ! [ -d $(TARGET)/idris2/network/lib ]; then mkdir $(TARGET)/idris2/network/lib; fi
|
||||||
|
install $(DYLIBTARGET) $(HDRS) $(TARGET)/idris2/network/lib
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -rf $(OBJS) $(LIBTARGET) $(DYLIBTARGET) build
|
||||||
|
|
||||||
|
test: build test.c
|
||||||
|
cp $(DYLIBTARGET) lib$(DYLIBTARGET) # to make C linker happy
|
||||||
|
$(CC) -o network-tests -L. -I. test.c -l$(LIBNAME)
|
||||||
|
LD_LIBRARY_PATH=. ./network-tests
|
||||||
|
./lib-tests
|
||||||
|
|
||||||
|
$(OBJS): $(HDRS)
|
||||||
|
|
||||||
|
all: $(DYLIBTARGET) $(LIBTARGET)
|
||||||
|
${IDRIS2} --build network.ipkg
|
||||||
|
|
||||||
|
.PHONY: install clean
|
223
libs/network/Network/Socket.idr
Normal file
223
libs/network/Network/Socket.idr
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
||| Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
|
||||||
|
|||
|
||||||
|
||| Original (C) SimonJF, MIT Licensed, 2014
|
||||||
|
||| Modified (C) The Idris Community, 2015, 2016, 2019
|
||||||
|
module Network.Socket
|
||||||
|
|
||||||
|
import public Network.Socket.Data
|
||||||
|
import Network.Socket.Raw
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
-- ----------------------------------------------------- [ Network Socket API. ]
|
||||||
|
|
||||||
|
||| Creates a UNIX socket with the given family, socket type and protocol
|
||||||
|
||| number. Returns either a socket or an error.
|
||||||
|
socket : (fam : SocketFamily)
|
||||||
|
-> (ty : SocketType)
|
||||||
|
-> (pnum : ProtocolNumber)
|
||||||
|
-> IO (Either SocketError Socket)
|
||||||
|
socket sf st pn = do
|
||||||
|
socket_res <- cCall Int "idrnet_socket" [toCode sf, toCode st, pn]
|
||||||
|
|
||||||
|
if socket_res == -1
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right (MkSocket socket_res sf st pn)
|
||||||
|
|
||||||
|
||| Close a socket
|
||||||
|
close : Socket -> IO ()
|
||||||
|
close sock = cCall () "close" [descriptor sock]
|
||||||
|
|
||||||
|
||| Binds a socket to the given socket address and port.
|
||||||
|
||| Returns 0 on success, an error code otherwise.
|
||||||
|
bind : (sock : Socket)
|
||||||
|
-> (addr : Maybe SocketAddress)
|
||||||
|
-> (port : Port)
|
||||||
|
-> IO Int
|
||||||
|
bind sock addr port = do
|
||||||
|
bind_res <- cCall Int "idrnet_bind"
|
||||||
|
[ descriptor sock, toCode $ family sock
|
||||||
|
, toCode $ socketType sock, saString addr, port
|
||||||
|
]
|
||||||
|
if bind_res == (-1)
|
||||||
|
then getErrno
|
||||||
|
else pure 0
|
||||||
|
where
|
||||||
|
saString : Maybe SocketAddress -> String
|
||||||
|
saString (Just sa) = show sa
|
||||||
|
saString Nothing = ""
|
||||||
|
|
||||||
|
||| Connects to a given address and port.
|
||||||
|
||| Returns 0 on success, and an error number on error.
|
||||||
|
connect : (sock : Socket)
|
||||||
|
-> (addr : SocketAddress)
|
||||||
|
-> (port : Port)
|
||||||
|
-> IO ResultCode
|
||||||
|
connect sock addr port = do
|
||||||
|
conn_res <- cCall Int "idrnet_connect"
|
||||||
|
[ descriptor sock, toCode $ family sock, toCode $ socketType sock, show addr, port]
|
||||||
|
|
||||||
|
if conn_res == (-1)
|
||||||
|
then getErrno
|
||||||
|
else pure 0
|
||||||
|
|
||||||
|
||| Listens on a bound socket.
|
||||||
|
|||
|
||||||
|
||| @sock The socket to listen on.
|
||||||
|
listen : (sock : Socket) -> IO Int
|
||||||
|
listen sock = do
|
||||||
|
listen_res <- cCall Int "listen" [ descriptor sock, BACKLOG ]
|
||||||
|
if listen_res == (-1)
|
||||||
|
then getErrno
|
||||||
|
else pure 0
|
||||||
|
|
||||||
|
||| Accept a connection on the provided socket.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success a pairing of:
|
||||||
|
||| + `Socket` :: The socket representing the connection.
|
||||||
|
||| + `SocketAddress` :: The
|
||||||
|
|||
|
||||||
|
||| @sock The socket used to establish connection.
|
||||||
|
accept : (sock : Socket)
|
||||||
|
-> IO (Either SocketError (Socket, SocketAddress))
|
||||||
|
accept sock = do
|
||||||
|
|
||||||
|
-- We need a pointer to a sockaddr structure. This is then passed into
|
||||||
|
-- idrnet_accept and populated. We can then query it for the SocketAddr and free it.
|
||||||
|
|
||||||
|
sockaddr_ptr <- cCall Ptr "idrnet_create_sockaddr" []
|
||||||
|
|
||||||
|
accept_res <- cCall Int "idrnet_accept" [ descriptor sock, sockaddr_ptr ]
|
||||||
|
if accept_res == (-1)
|
||||||
|
then map Left getErrno
|
||||||
|
else do
|
||||||
|
let (MkSocket _ fam ty p_num) = sock
|
||||||
|
sockaddr <- getSockAddr (SAPtr sockaddr_ptr)
|
||||||
|
sockaddr_free (SAPtr sockaddr_ptr)
|
||||||
|
pure $ Right ((MkSocket accept_res fam ty p_num), sockaddr)
|
||||||
|
|
||||||
|
||| Send data on the specified socket.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`.
|
||||||
|
||| Returns on success the `ResultCode`.
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to send the message.
|
||||||
|
||| @msg The data to send.
|
||||||
|
send : (sock : Socket)
|
||||||
|
-> (msg : String)
|
||||||
|
-> IO (Either SocketError ResultCode)
|
||||||
|
send sock dat = do
|
||||||
|
send_res <- cCall Int "idrnet_send" [ descriptor sock, dat ]
|
||||||
|
|
||||||
|
if send_res == (-1)
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right send_res
|
||||||
|
|
||||||
|
||| Receive data on the specified socket.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success a pairing of:
|
||||||
|
||| + `String` :: The payload.
|
||||||
|
||| + `ResultCode` :: The result of the underlying function.
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to receive the message.
|
||||||
|
||| @len How much of the data to receive.
|
||||||
|
recv : (sock : Socket)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError (String, ResultCode))
|
||||||
|
recv sock len = do
|
||||||
|
-- Firstly make the request, get some kind of recv structure which
|
||||||
|
-- contains the result of the recv and possibly the retrieved payload
|
||||||
|
recv_struct_ptr <- cCall Ptr "idrnet_recv" [ descriptor sock, len]
|
||||||
|
recv_res <- cCall Int "idrnet_get_recv_res" [ recv_struct_ptr ]
|
||||||
|
|
||||||
|
if recv_res == (-1)
|
||||||
|
then do
|
||||||
|
errno <- getErrno
|
||||||
|
freeRecvStruct (RSPtr recv_struct_ptr)
|
||||||
|
pure $ Left errno
|
||||||
|
else
|
||||||
|
if recv_res == 0
|
||||||
|
then do
|
||||||
|
freeRecvStruct (RSPtr recv_struct_ptr)
|
||||||
|
pure $ Left 0
|
||||||
|
else do
|
||||||
|
payload <- cCall String "idrnet_get_recv_payload" [ recv_struct_ptr ]
|
||||||
|
freeRecvStruct (RSPtr recv_struct_ptr)
|
||||||
|
pure $ Right (payload, recv_res)
|
||||||
|
|
||||||
|
||| Receive all the remaining data on the specified socket.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success the payload `String`
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to receive the message.
|
||||||
|
partial
|
||||||
|
recvAll : (sock : Socket) -> IO (Either SocketError String)
|
||||||
|
recvAll sock = recvRec sock [] 64
|
||||||
|
where
|
||||||
|
partial
|
||||||
|
recvRec : Socket -> List String -> ByteLength -> IO (Either SocketError String)
|
||||||
|
recvRec sock acc n = do res <- recv sock n
|
||||||
|
case res of
|
||||||
|
Left 0 => pure (Right $ concat $ reverse acc)
|
||||||
|
Left c => pure (Left c)
|
||||||
|
Right (str, _) => let n' = min (n * 2) 65536 in
|
||||||
|
recvRec sock (str :: acc) n'
|
||||||
|
|
||||||
|
||| Send a message.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success the `ResultCode`
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to send the message.
|
||||||
|
||| @addr Address of the recipient.
|
||||||
|
||| @port The port on which to send the message.
|
||||||
|
||| @msg The message to send.
|
||||||
|
sendTo : (sock : Socket)
|
||||||
|
-> (addr : SocketAddress)
|
||||||
|
-> (port : Port)
|
||||||
|
-> (msg : String)
|
||||||
|
-> IO (Either SocketError ByteLength)
|
||||||
|
sendTo sock addr p dat = do
|
||||||
|
sendto_res <- cCall Int "idrnet_sendto"
|
||||||
|
[ descriptor sock, dat, show addr, p ,toCode $ family sock ]
|
||||||
|
|
||||||
|
if sendto_res == (-1)
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right sendto_res
|
||||||
|
|
||||||
|
||| Receive a message.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`.
|
||||||
|
||| Returns on success a triple of
|
||||||
|
||| + `UDPAddrInfo` :: The address of the sender.
|
||||||
|
||| + `String` :: The payload.
|
||||||
|
||| + `Int` :: Result value from underlying function.
|
||||||
|
|||
|
||||||
|
||| @sock The channel on which to receive.
|
||||||
|
||| @len Size of the expected message.
|
||||||
|
|||
|
||||||
|
recvFrom : (sock : Socket)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError (UDPAddrInfo, String, ResultCode))
|
||||||
|
recvFrom sock bl = do
|
||||||
|
recv_ptr <- cCall Ptr "idrnet_recvfrom"
|
||||||
|
[ descriptor sock, bl ]
|
||||||
|
|
||||||
|
let recv_ptr' = RFPtr recv_ptr
|
||||||
|
isNull <- (nullPtr recv_ptr)
|
||||||
|
if isNull
|
||||||
|
then map Left getErrno
|
||||||
|
else do
|
||||||
|
result <- cCall Int "idrnet_get_recvfrom_res" [ recv_ptr ]
|
||||||
|
if result == -1
|
||||||
|
then do
|
||||||
|
freeRecvfromStruct recv_ptr'
|
||||||
|
map Left getErrno
|
||||||
|
else do
|
||||||
|
payload <- foreignGetRecvfromPayload recv_ptr'
|
||||||
|
port <- foreignGetRecvfromPort recv_ptr'
|
||||||
|
addr <- foreignGetRecvfromAddr recv_ptr'
|
||||||
|
freeRecvfromStruct recv_ptr'
|
||||||
|
pure $ Right (MkUDPAddrInfo addr port, payload, result)
|
214
libs/network/Network/Socket/Data.idr
Normal file
214
libs/network/Network/Socket/Data.idr
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
||| Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
|
||||||
|
||| Types used by Network.Socket.Raw and Network.Socket.
|
||||||
|
|||
|
||||||
|
||| Original (C) SimonJF, MIT Licensed, 2014
|
||||||
|
||| Modified (C) The Idris Community, 2015, 2016, 2019
|
||||||
|
module Network.Socket.Data
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Strings
|
||||||
|
|
||||||
|
-- ------------------------------------------------------------ [ Type Aliases ]
|
||||||
|
|
||||||
|
-- FIXME should be generic name with OS-dependent suffix
|
||||||
|
%cg chez "libidris_net.so"
|
||||||
|
|
||||||
|
public export
|
||||||
|
ByteLength : Type
|
||||||
|
ByteLength = Int
|
||||||
|
|
||||||
|
public export
|
||||||
|
ResultCode : Type
|
||||||
|
ResultCode = Int
|
||||||
|
|
||||||
|
||| Protocol Number.
|
||||||
|
|||
|
||||||
|
||| Generally good enough to just set it to 0.
|
||||||
|
public export
|
||||||
|
ProtocolNumber : Type
|
||||||
|
ProtocolNumber = Int
|
||||||
|
|
||||||
|
||| SocketError: Error thrown by a socket operation
|
||||||
|
public export
|
||||||
|
SocketError : Type
|
||||||
|
SocketError = Int
|
||||||
|
|
||||||
|
||| SocketDescriptor: Native C Socket Descriptor
|
||||||
|
public export
|
||||||
|
SocketDescriptor : Type
|
||||||
|
SocketDescriptor = Int
|
||||||
|
|
||||||
|
public export
|
||||||
|
Port : Type
|
||||||
|
Port = Int
|
||||||
|
|
||||||
|
-- --------------------------------------------------------------- [ Constants ]
|
||||||
|
|
||||||
|
||| Backlog used within listen() call -- number of incoming calls
|
||||||
|
export
|
||||||
|
BACKLOG : Int
|
||||||
|
BACKLOG = 20
|
||||||
|
|
||||||
|
export
|
||||||
|
EAGAIN : Int
|
||||||
|
EAGAIN =
|
||||||
|
-- I'm sorry
|
||||||
|
-- maybe
|
||||||
|
unsafePerformIO $ cCall Int "idrnet_geteagain" []
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------- [ Error Code ]
|
||||||
|
|
||||||
|
export
|
||||||
|
getErrno : IO SocketError
|
||||||
|
getErrno = cCall Int "idrnet_errno" []
|
||||||
|
|
||||||
|
export
|
||||||
|
nullPtr : Ptr -> IO Bool
|
||||||
|
nullPtr p = cCall Bool "isNull" [p]
|
||||||
|
|
||||||
|
-- -------------------------------------------------------------- [ Interfaces ]
|
||||||
|
|
||||||
|
public export
|
||||||
|
interface ToCode a where
|
||||||
|
toCode : a -> Int
|
||||||
|
|
||||||
|
-- --------------------------------------------------------- [ Socket Families ]
|
||||||
|
|
||||||
|
||| Socket Families
|
||||||
|
|||
|
||||||
|
||| The ones that people might actually use. We're not going to need US
|
||||||
|
||| Government proprietary ones.
|
||||||
|
public export
|
||||||
|
data SocketFamily : Type where
|
||||||
|
||| Unspecified
|
||||||
|
AF_UNSPEC : SocketFamily
|
||||||
|
|
||||||
|
||| Unix type sockets
|
||||||
|
AF_UNIX : SocketFamily
|
||||||
|
|
||||||
|
||| IP / UDP etc. IPv4
|
||||||
|
AF_INET : SocketFamily
|
||||||
|
|
||||||
|
||| IP / UDP etc. IPv6
|
||||||
|
AF_INET6 : SocketFamily
|
||||||
|
|
||||||
|
Show SocketFamily where
|
||||||
|
show AF_UNSPEC = "AF_UNSPEC"
|
||||||
|
show AF_UNIX = "AF_UNIX"
|
||||||
|
show AF_INET = "AF_INET"
|
||||||
|
show AF_INET6 = "AF_INET6"
|
||||||
|
|
||||||
|
export
|
||||||
|
ToCode SocketFamily where
|
||||||
|
-- Don't know how to read a constant value from C code in idris2...
|
||||||
|
-- gotta to hardcode those for now
|
||||||
|
toCode AF_UNSPEC = 0 -- unsafePerformIO (cMacro "#AF_UNSPEC" Int)
|
||||||
|
toCode AF_UNIX = 1
|
||||||
|
toCode AF_INET = 2
|
||||||
|
toCode AF_INET6 = 10
|
||||||
|
|
||||||
|
export
|
||||||
|
getSocketFamily : Int -> Maybe SocketFamily
|
||||||
|
getSocketFamily i =
|
||||||
|
lookup i [ (toCode AF_UNSPEC, AF_UNSPEC)
|
||||||
|
, (toCode AF_UNIX, AF_UNIX)
|
||||||
|
, (toCode AF_INET, AF_INET)
|
||||||
|
, (toCode AF_INET6, AF_INET6)
|
||||||
|
]
|
||||||
|
|
||||||
|
-- ------------------------------------------------------------ [ Socket Types ]
|
||||||
|
|
||||||
|
||| Socket Types.
|
||||||
|
public export
|
||||||
|
data SocketType : Type where
|
||||||
|
||| Not a socket, used in certain operations
|
||||||
|
NotASocket : SocketType
|
||||||
|
|
||||||
|
||| TCP
|
||||||
|
Stream : SocketType
|
||||||
|
|
||||||
|
||| UDP
|
||||||
|
Datagram : SocketType
|
||||||
|
|
||||||
|
||| Raw sockets
|
||||||
|
RawSocket : SocketType
|
||||||
|
|
||||||
|
export
|
||||||
|
Show SocketType where
|
||||||
|
show NotASocket = "Not a socket"
|
||||||
|
show Stream = "Stream"
|
||||||
|
show Datagram = "Datagram"
|
||||||
|
show RawSocket = "Raw"
|
||||||
|
|
||||||
|
export
|
||||||
|
ToCode SocketType where
|
||||||
|
toCode NotASocket = 0
|
||||||
|
toCode Stream = 1
|
||||||
|
toCode Datagram = 2
|
||||||
|
toCode RawSocket = 3
|
||||||
|
|
||||||
|
-- --------------------------------------------------------------- [ Addresses ]
|
||||||
|
|
||||||
|
||| Network Addresses
|
||||||
|
public export
|
||||||
|
data SocketAddress : Type where
|
||||||
|
IPv4Addr : Int -> Int -> Int -> Int -> SocketAddress
|
||||||
|
|
||||||
|
||| Not implemented (yet)
|
||||||
|
IPv6Addr : SocketAddress
|
||||||
|
|
||||||
|
Hostname : String -> SocketAddress
|
||||||
|
|
||||||
|
||| Used when there's a parse error
|
||||||
|
InvalidAddress : SocketAddress
|
||||||
|
|
||||||
|
export
|
||||||
|
Show SocketAddress where
|
||||||
|
show (IPv4Addr i1 i2 i3 i4) = concat $ intersperse "." (map show [i1, i2, i3, i4])
|
||||||
|
show IPv6Addr = "NOT IMPLEMENTED YET"
|
||||||
|
show (Hostname host) = host
|
||||||
|
show InvalidAddress = "Invalid"
|
||||||
|
|
||||||
|
||| Parses a textual representation of an IPv4 address into a SocketAddress
|
||||||
|
export
|
||||||
|
parseIPv4 : String -> SocketAddress
|
||||||
|
parseIPv4 str =
|
||||||
|
case splitted of
|
||||||
|
(i1 :: i2 :: i3 :: i4 :: _) => IPv4Addr i1 i2 i3 i4
|
||||||
|
otherwise => InvalidAddress
|
||||||
|
where
|
||||||
|
toInt' : String -> Integer
|
||||||
|
toInt' = cast
|
||||||
|
|
||||||
|
toInt : String -> Int
|
||||||
|
toInt s = fromInteger $ toInt' s
|
||||||
|
|
||||||
|
splitted : List Int
|
||||||
|
splitted = map toInt (split (\c => c == '.') str)
|
||||||
|
|
||||||
|
-- --------------------------------------------------------- [ UDP Information ]
|
||||||
|
|
||||||
|
-- TODO: Expand to non-string payloads
|
||||||
|
public export
|
||||||
|
record UDPRecvData where
|
||||||
|
constructor MkUDPRecvData
|
||||||
|
remote_addr : SocketAddress
|
||||||
|
remote_port : Port
|
||||||
|
recv_data : String
|
||||||
|
data_len : Int
|
||||||
|
|
||||||
|
public export
|
||||||
|
record UDPAddrInfo where
|
||||||
|
constructor MkUDPAddrInfo
|
||||||
|
remote_addr : SocketAddress
|
||||||
|
remote_port : Port
|
||||||
|
|
||||||
|
-- ----------------------------------------------------------------- [ Sockets ]
|
||||||
|
||| The metadata about a socket
|
||||||
|
public export
|
||||||
|
record Socket where
|
||||||
|
constructor MkSocket
|
||||||
|
descriptor : SocketDescriptor
|
||||||
|
family : SocketFamily
|
||||||
|
socketType : SocketType
|
||||||
|
protocolNumber : ProtocolNumber
|
190
libs/network/Network/Socket/Raw.idr
Normal file
190
libs/network/Network/Socket/Raw.idr
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
||| Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
|
||||||
|
||| Type-unsafe parts. Use Network.Socket for a safe variant.
|
||||||
|
|||
|
||||||
|
||| Original (C) SimonJF, MIT Licensed, 2014
|
||||||
|
||| Modified (C) The Idris Community, 2015, 2016
|
||||||
|
module Network.Socket.Raw
|
||||||
|
|
||||||
|
import public Network.Socket.Data
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------- [ Pointers ]
|
||||||
|
|
||||||
|
public export
|
||||||
|
data RecvStructPtr = RSPtr Ptr
|
||||||
|
|
||||||
|
public export
|
||||||
|
data RecvfromStructPtr = RFPtr Ptr
|
||||||
|
|
||||||
|
public export
|
||||||
|
data BufPtr = BPtr Ptr
|
||||||
|
|
||||||
|
public export
|
||||||
|
data SockaddrPtr = SAPtr Ptr
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------- [ Socket Utilies ]
|
||||||
|
|
||||||
|
||| Frees a given pointer
|
||||||
|
sock_free : BufPtr -> IO ()
|
||||||
|
sock_free (BPtr ptr) = cCall () "idrnet_free" [ptr]
|
||||||
|
|
||||||
|
export
|
||||||
|
sockaddr_free : SockaddrPtr -> IO ()
|
||||||
|
sockaddr_free (SAPtr ptr) = cCall () "idrnet_free" [ptr]
|
||||||
|
|
||||||
|
||| Allocates an amount of memory given by the ByteLength parameter.
|
||||||
|
|||
|
||||||
|
||| Used to allocate a mutable pointer to be given to the Recv functions.
|
||||||
|
sock_alloc : ByteLength -> IO BufPtr
|
||||||
|
sock_alloc bl = map BPtr $ cCall Ptr "idrnet_malloc" [bl]
|
||||||
|
|
||||||
|
||| Retrieves the port the given socket is bound to
|
||||||
|
export
|
||||||
|
getSockPort : Socket -> IO Port
|
||||||
|
getSockPort sock = cCall Int "idrnet_sockaddr_port" [descriptor sock]
|
||||||
|
|
||||||
|
|
||||||
|
||| Retrieves a socket address from a sockaddr pointer
|
||||||
|
export
|
||||||
|
getSockAddr : SockaddrPtr -> IO SocketAddress
|
||||||
|
getSockAddr (SAPtr ptr) = do
|
||||||
|
addr_family_int <- cCall Int "idrnet_sockaddr_family" [ptr]
|
||||||
|
|
||||||
|
-- ASSUMPTION: Foreign call returns a valid int
|
||||||
|
assert_total (case getSocketFamily addr_family_int of
|
||||||
|
Just AF_INET => do
|
||||||
|
ipv4_addr <- cCall String "idrnet_sockaddr_ipv4" [ptr]
|
||||||
|
|
||||||
|
pure $ parseIPv4 ipv4_addr
|
||||||
|
Just AF_INET6 => pure IPv6Addr
|
||||||
|
Just AF_UNSPEC => pure InvalidAddress)
|
||||||
|
|
||||||
|
export
|
||||||
|
freeRecvStruct : RecvStructPtr -> IO ()
|
||||||
|
freeRecvStruct (RSPtr p) = cCall () "idrnet_free_recv_struct" [p]
|
||||||
|
|
||||||
|
||| Utility to extract data.
|
||||||
|
export
|
||||||
|
freeRecvfromStruct : RecvfromStructPtr -> IO ()
|
||||||
|
freeRecvfromStruct (RFPtr p) = cCall () "idrnet_free_recvfrom_struct" [p]
|
||||||
|
|
||||||
|
||| Sends the data in a given memory location
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success the `ResultCode`
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to send the message.
|
||||||
|
||| @ptr The location containing the data to send.
|
||||||
|
||| @len How much of the data to send.
|
||||||
|
export
|
||||||
|
sendBuf : (sock : Socket)
|
||||||
|
-> (ptr : BufPtr)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError ResultCode)
|
||||||
|
sendBuf sock (BPtr ptr) len = do
|
||||||
|
send_res <- cCall Int "idrnet_send_buf" [ descriptor sock, ptr, len]
|
||||||
|
|
||||||
|
if send_res == (-1)
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right send_res
|
||||||
|
|
||||||
|
||| Receive data from a given memory location.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success the `ResultCode`
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to receive the message.
|
||||||
|
||| @ptr The location containing the data to receive.
|
||||||
|
||| @len How much of the data to receive.
|
||||||
|
export
|
||||||
|
recvBuf : (sock : Socket)
|
||||||
|
-> (ptr : BufPtr)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError ResultCode)
|
||||||
|
recvBuf sock (BPtr ptr) len = do
|
||||||
|
recv_res <- cCall Int "idrnet_recv_buf" [ descriptor sock, ptr, len ]
|
||||||
|
|
||||||
|
if (recv_res == (-1))
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right recv_res
|
||||||
|
|
||||||
|
||| Send a message stored in some buffer.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`
|
||||||
|
||| Returns on success the `ResultCode`
|
||||||
|
|||
|
||||||
|
||| @sock The socket on which to send the message.
|
||||||
|
||| @addr Address of the recipient.
|
||||||
|
||| @port The port on which to send the message.
|
||||||
|
||| @ptr A Pointer to the buffer containing the message.
|
||||||
|
||| @len The size of the message.
|
||||||
|
export
|
||||||
|
sendToBuf : (sock : Socket)
|
||||||
|
-> (addr : SocketAddress)
|
||||||
|
-> (port : Port)
|
||||||
|
-> (ptr : BufPtr)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError ResultCode)
|
||||||
|
sendToBuf sock addr p (BPtr dat) len = do
|
||||||
|
sendto_res <- cCall Int "idrnet_sendto_buf"
|
||||||
|
[ descriptor sock, dat, len, show addr, p, toCode $ family sock ]
|
||||||
|
|
||||||
|
if sendto_res == (-1)
|
||||||
|
then map Left getErrno
|
||||||
|
else pure $ Right sendto_res
|
||||||
|
|
||||||
|
||| Utility function to get the payload of the sent message as a `String`.
|
||||||
|
export
|
||||||
|
foreignGetRecvfromPayload : RecvfromStructPtr -> IO String
|
||||||
|
foreignGetRecvfromPayload (RFPtr p) = cCall String "idrnet_get_recvfrom_payload" [ p ]
|
||||||
|
|
||||||
|
||| Utility function to return senders socket address.
|
||||||
|
export
|
||||||
|
foreignGetRecvfromAddr : RecvfromStructPtr -> IO SocketAddress
|
||||||
|
foreignGetRecvfromAddr (RFPtr p) = do
|
||||||
|
sockaddr_ptr <- map SAPtr $ cCall Ptr "idrnet_get_recvfrom_sockaddr" [p]
|
||||||
|
getSockAddr sockaddr_ptr
|
||||||
|
|
||||||
|
||| Utility function to return sender's IPV4 port.
|
||||||
|
export
|
||||||
|
foreignGetRecvfromPort : RecvfromStructPtr -> IO Port
|
||||||
|
foreignGetRecvfromPort (RFPtr p) = do
|
||||||
|
sockaddr_ptr <- cCall Ptr "idrnet_get_recvfrom_sockaddr" [p]
|
||||||
|
port <- cCall Int "idrnet_sockaddr_ipv4_port" [sockaddr_ptr]
|
||||||
|
pure port
|
||||||
|
|
||||||
|
||| Receive a message placed on a 'known' buffer.
|
||||||
|
|||
|
||||||
|
||| Returns on failure a `SocketError`.
|
||||||
|
||| Returns on success a pair of
|
||||||
|
||| + `UDPAddrInfo` :: The address of the sender.
|
||||||
|
||| + `Int` :: Result value from underlying function.
|
||||||
|
|||
|
||||||
|
||| @sock The channel on which to receive.
|
||||||
|
||| @ptr Pointer to the buffer to place the message.
|
||||||
|
||| @len Size of the expected message.
|
||||||
|
|||
|
||||||
|
export
|
||||||
|
recvFromBuf : (sock : Socket)
|
||||||
|
-> (ptr : BufPtr)
|
||||||
|
-> (len : ByteLength)
|
||||||
|
-> IO (Either SocketError (UDPAddrInfo, ResultCode))
|
||||||
|
recvFromBuf sock (BPtr ptr) bl = do
|
||||||
|
recv_ptr <- cCall Ptr "idrnet_recvfrom_buf" [ descriptor sock, ptr, bl]
|
||||||
|
|
||||||
|
let recv_ptr' = RFPtr recv_ptr
|
||||||
|
|
||||||
|
isnull <- nullPtr recv_ptr
|
||||||
|
|
||||||
|
if isnull
|
||||||
|
then map Left getErrno
|
||||||
|
else do
|
||||||
|
result <- cCall Int "idrnet_get_recvfrom_res" [recv_ptr]
|
||||||
|
if result == -1
|
||||||
|
then do
|
||||||
|
freeRecvfromStruct recv_ptr'
|
||||||
|
map Left getErrno
|
||||||
|
else do
|
||||||
|
port <- foreignGetRecvfromPort recv_ptr'
|
||||||
|
addr <- foreignGetRecvfromAddr recv_ptr'
|
||||||
|
freeRecvfromStruct recv_ptr'
|
||||||
|
pure $ Right (MkUDPAddrInfo addr port, result + 1)
|
4
libs/network/expected
Normal file
4
libs/network/expected
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Client sent 12 bytes
|
||||||
|
Received: hello world!
|
||||||
|
Server sent 18 bytes
|
||||||
|
Received: echo: hello world!
|
383
libs/network/idris_net.c
Normal file
383
libs/network/idris_net.c
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
// C-Side of the Idris network library
|
||||||
|
// (C) Simon Fowler, 2014
|
||||||
|
// MIT Licensed. Have fun!
|
||||||
|
#include "idris_net.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#else
|
||||||
|
static int socket_inited = 0;
|
||||||
|
static WSADATA wsa_data;
|
||||||
|
|
||||||
|
static void clean_sockets(void) {
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_init(void) {
|
||||||
|
if (!socket_inited) {
|
||||||
|
int result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||||
|
if (result == NO_ERROR) {
|
||||||
|
socket_inited = 1;
|
||||||
|
atexit(clean_sockets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return socket_inited;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void buf_htonl(void* buf, int len) {
|
||||||
|
int* buf_i = (int*) buf;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < (len / sizeof(int)) + 1; i++) {
|
||||||
|
buf_i[i] = htonl(buf_i[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void buf_ntohl(void* buf, int len) {
|
||||||
|
int* buf_i = (int*) buf;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < (len / sizeof(int)) + 1; i++) {
|
||||||
|
buf_i[i] = ntohl(buf_i[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* idrnet_malloc(int size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void idrnet_free(void* ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_socket(int domain, int type, int protocol) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!check_init()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return socket(domain, type, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We call this from quite a few functions. Given a textual host and an int port,
|
||||||
|
// populates a struct addrinfo.
|
||||||
|
int idrnet_getaddrinfo(struct addrinfo** address_res, char* host, int port,
|
||||||
|
int family, int socket_type) {
|
||||||
|
|
||||||
|
struct addrinfo hints;
|
||||||
|
// Convert port into string
|
||||||
|
char str_port[8];
|
||||||
|
sprintf(str_port, "%d", port);
|
||||||
|
|
||||||
|
// Set up hints structure
|
||||||
|
memset(&hints, 0, sizeof(hints)); // zero out hints
|
||||||
|
hints.ai_family = family;
|
||||||
|
hints.ai_socktype = socket_type;
|
||||||
|
|
||||||
|
// If the length of the hostname is 0 (i.e, it was set to Nothing in Idris)
|
||||||
|
// then we want to instruct the C library to fill in the IP automatically
|
||||||
|
if (strlen(host) == 0) {
|
||||||
|
hints.ai_flags = AI_PASSIVE; // fill in IP automatically
|
||||||
|
return getaddrinfo(NULL, str_port, &hints, address_res);
|
||||||
|
}
|
||||||
|
return getaddrinfo(host, str_port, &hints, address_res);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_bind(int sockfd, int family, int socket_type, char* host, int port) {
|
||||||
|
struct addrinfo *address_res;
|
||||||
|
int addr_res = idrnet_getaddrinfo(&address_res, host, port, family, socket_type);
|
||||||
|
if (addr_res != 0) {
|
||||||
|
//printf("Lib err: bind getaddrinfo\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bind_res = bind(sockfd, address_res->ai_addr, address_res->ai_addrlen);
|
||||||
|
if (bind_res == -1) {
|
||||||
|
//freeaddrinfo(address_res);
|
||||||
|
//printf("Lib err: bind\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// to retrieve information about a socket bound to port 0
|
||||||
|
int idrnet_getsockname(int sockfd, void *address, void *len) {
|
||||||
|
int res = getsockname(sockfd, address, len);
|
||||||
|
if(res != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_sockaddr_port(int sockfd) {
|
||||||
|
struct sockaddr address;
|
||||||
|
socklen_t addrlen = sizeof(struct sockaddr);
|
||||||
|
int res = getsockname(sockfd, &address, &addrlen);
|
||||||
|
if(res < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(address.sa_family) {
|
||||||
|
case AF_INET:
|
||||||
|
return ntohs(((struct sockaddr_in*)&address)->sin_port);
|
||||||
|
case AF_INET6:
|
||||||
|
return ntohs(((struct sockaddr_in6*)&address)->sin6_port);
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_connect(int sockfd, int family, int socket_type, char* host, int port) {
|
||||||
|
struct addrinfo* remote_host;
|
||||||
|
int addr_res = idrnet_getaddrinfo(&remote_host, host, port, family, socket_type);
|
||||||
|
if (addr_res != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connect_res = connect(sockfd, remote_host->ai_addr, remote_host->ai_addrlen);
|
||||||
|
if (connect_res == -1) {
|
||||||
|
freeaddrinfo(remote_host);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
freeaddrinfo(remote_host);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_sockaddr_family(void* sockaddr) {
|
||||||
|
struct sockaddr* addr = (struct sockaddr*) sockaddr;
|
||||||
|
return (int) addr->sa_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* idrnet_sockaddr_ipv4(void* sockaddr) {
|
||||||
|
struct sockaddr_in* addr = (struct sockaddr_in*) sockaddr;
|
||||||
|
char* ip_addr = (char*) malloc(sizeof(char) * INET_ADDRSTRLEN);
|
||||||
|
inet_ntop(AF_INET, &(addr->sin_addr), ip_addr, INET_ADDRSTRLEN);
|
||||||
|
//printf("Lib: ip_addr: %s\n", ip_addr);
|
||||||
|
return ip_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_sockaddr_ipv4_port(void* sockaddr) {
|
||||||
|
struct sockaddr_in* addr = (struct sockaddr_in*) sockaddr;
|
||||||
|
return ((int) ntohs(addr->sin_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* idrnet_create_sockaddr() {
|
||||||
|
return malloc(sizeof(struct sockaddr_storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_accept(int sockfd, void* sockaddr) {
|
||||||
|
struct sockaddr* addr = (struct sockaddr*) sockaddr;
|
||||||
|
socklen_t addr_size = sizeof(struct sockaddr_storage);
|
||||||
|
return accept(sockfd, addr, &addr_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_send(int sockfd, char* data) {
|
||||||
|
int len = strlen(data); // For now.
|
||||||
|
return send(sockfd, (void*) data, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_send_buf(int sockfd, void* data, int len) {
|
||||||
|
void* buf_cpy = malloc(len);
|
||||||
|
memset(buf_cpy, 0, len);
|
||||||
|
memcpy(buf_cpy, data, len);
|
||||||
|
buf_htonl(buf_cpy, len);
|
||||||
|
int res = send(sockfd, buf_cpy, len, 0);
|
||||||
|
free(buf_cpy);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* idrnet_recv(int sockfd, int len) {
|
||||||
|
idrnet_recv_result* res_struct =
|
||||||
|
(idrnet_recv_result*) malloc(sizeof(idrnet_recv_result));
|
||||||
|
char* buf = malloc(len + 1);
|
||||||
|
memset(buf, 0, len + 1);
|
||||||
|
int recv_res = recv(sockfd, buf, len, 0);
|
||||||
|
res_struct->result = recv_res;
|
||||||
|
|
||||||
|
if (recv_res > 0) { // Data was received
|
||||||
|
buf[recv_res + 1] = 0x00; // Null-term, so Idris can interpret it
|
||||||
|
}
|
||||||
|
res_struct->payload = buf;
|
||||||
|
return (void*) res_struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_recv_buf(int sockfd, void* buf, int len) {
|
||||||
|
int recv_res = recv(sockfd, buf, len, 0);
|
||||||
|
if (recv_res != -1) {
|
||||||
|
buf_ntohl(buf, len);
|
||||||
|
}
|
||||||
|
return recv_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_get_recv_res(void* res_struct) {
|
||||||
|
return (((idrnet_recv_result*) res_struct)->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* idrnet_get_recv_payload(void* res_struct) {
|
||||||
|
return (((idrnet_recv_result*) res_struct)->payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void idrnet_free_recv_struct(void* res_struct) {
|
||||||
|
idrnet_recv_result* i_res_struct =
|
||||||
|
(idrnet_recv_result*) res_struct;
|
||||||
|
if (i_res_struct->payload != NULL) {
|
||||||
|
free(i_res_struct->payload);
|
||||||
|
}
|
||||||
|
free(res_struct);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_errno() {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_sendto(int sockfd, char* data, char* host, int port, int family) {
|
||||||
|
|
||||||
|
struct addrinfo* remote_host;
|
||||||
|
int addr_res = idrnet_getaddrinfo(&remote_host, host, port, family, SOCK_DGRAM);
|
||||||
|
if (addr_res != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_res = sendto(sockfd, data, strlen(data), 0,
|
||||||
|
remote_host->ai_addr, remote_host->ai_addrlen);
|
||||||
|
freeaddrinfo(remote_host);
|
||||||
|
return send_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_sendto_buf(int sockfd, void* buf, int buf_len, char* host, int port, int family) {
|
||||||
|
|
||||||
|
struct addrinfo* remote_host;
|
||||||
|
int addr_res = idrnet_getaddrinfo(&remote_host, host, port, family, SOCK_DGRAM);
|
||||||
|
if (addr_res != 0) {
|
||||||
|
//printf("lib err: sendto getaddrinfo \n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_htonl(buf, buf_len);
|
||||||
|
|
||||||
|
int send_res = sendto(sockfd, buf, buf_len, 0,
|
||||||
|
remote_host->ai_addr, remote_host->ai_addrlen);
|
||||||
|
if (send_res == -1) {
|
||||||
|
perror("lib err: sendto \n");
|
||||||
|
}
|
||||||
|
//freeaddrinfo(remote_host);
|
||||||
|
return send_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void* idrnet_recvfrom(int sockfd, int len) {
|
||||||
|
/*
|
||||||
|
* int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
|
||||||
|
struct sockaddr *from, int *fromlen);
|
||||||
|
*/
|
||||||
|
// Allocate the required structures, and nuke them
|
||||||
|
struct sockaddr_storage* remote_addr =
|
||||||
|
(struct sockaddr_storage*) malloc(sizeof(struct sockaddr_storage));
|
||||||
|
char* buf = (char*) malloc(len + 1);
|
||||||
|
idrnet_recvfrom_result* ret =
|
||||||
|
(idrnet_recvfrom_result*) malloc(sizeof(idrnet_recvfrom_result));
|
||||||
|
memset(remote_addr, 0, sizeof(struct sockaddr_storage));
|
||||||
|
memset(buf, 0, len + 1);
|
||||||
|
memset(ret, 0, sizeof(idrnet_recvfrom_result));
|
||||||
|
socklen_t fromlen = sizeof(struct sockaddr_storage);
|
||||||
|
|
||||||
|
int recv_res = recvfrom(sockfd, buf, len, 0, (struct sockaddr*) remote_addr, &fromlen);
|
||||||
|
ret->result = recv_res;
|
||||||
|
// Check for failure...
|
||||||
|
if (recv_res == -1) {
|
||||||
|
free(buf);
|
||||||
|
free(remote_addr);
|
||||||
|
} else {
|
||||||
|
// If data was received, process and populate
|
||||||
|
ret->result = recv_res;
|
||||||
|
ret->remote_addr = remote_addr;
|
||||||
|
// Ensure the last byte is NULL, since in this mode we're sending strings
|
||||||
|
buf[len] = 0x00;
|
||||||
|
ret->payload = (void*) buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* idrnet_recvfrom_buf(int sockfd, void* buf, int len) {
|
||||||
|
// Allocate the required structures, and nuke them
|
||||||
|
struct sockaddr_storage* remote_addr =
|
||||||
|
(struct sockaddr_storage*) malloc(sizeof(struct sockaddr_storage));
|
||||||
|
idrnet_recvfrom_result* ret =
|
||||||
|
(idrnet_recvfrom_result*) malloc(sizeof(idrnet_recvfrom_result));
|
||||||
|
memset(remote_addr, 0, sizeof(struct sockaddr_storage));
|
||||||
|
memset(ret, 0, sizeof(idrnet_recvfrom_result));
|
||||||
|
socklen_t fromlen = 0;
|
||||||
|
|
||||||
|
int recv_res = recvfrom(sockfd, buf, len, 0, (struct sockaddr*) remote_addr, &fromlen);
|
||||||
|
// Check for failure... But don't free the buffer! Not our job.
|
||||||
|
ret->result = recv_res;
|
||||||
|
if (recv_res == -1) {
|
||||||
|
free(remote_addr);
|
||||||
|
}
|
||||||
|
// Payload will be NULL -- since it's been put into the user-specified buffer. We
|
||||||
|
// still need the return struct to get our hands on the remote address, though.
|
||||||
|
if (recv_res > 0) {
|
||||||
|
buf_ntohl(buf, len);
|
||||||
|
ret->payload = NULL;
|
||||||
|
ret->remote_addr = remote_addr;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_get_recvfrom_res(void* res_struct) {
|
||||||
|
return (((idrnet_recvfrom_result*) res_struct)->result);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* idrnet_get_recvfrom_payload(void* res_struct) {
|
||||||
|
return (((idrnet_recvfrom_result*) res_struct)->payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* idrnet_get_recvfrom_sockaddr(void* res_struct) {
|
||||||
|
idrnet_recvfrom_result* recv_struct = (idrnet_recvfrom_result*) res_struct;
|
||||||
|
return recv_struct->remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_get_recvfrom_port(void* res_struct) {
|
||||||
|
idrnet_recvfrom_result* recv_struct = (idrnet_recvfrom_result*) res_struct;
|
||||||
|
if (recv_struct->remote_addr != NULL) {
|
||||||
|
struct sockaddr_in* remote_addr_in =
|
||||||
|
(struct sockaddr_in*) recv_struct->remote_addr;
|
||||||
|
return ((int) ntohs(remote_addr_in->sin_port));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void idrnet_free_recvfrom_struct(void* res_struct) {
|
||||||
|
idrnet_recvfrom_result* recv_struct = (idrnet_recvfrom_result*) res_struct;
|
||||||
|
if (recv_struct != NULL) {
|
||||||
|
if (recv_struct->payload != NULL) {
|
||||||
|
free(recv_struct->payload);
|
||||||
|
}
|
||||||
|
if (recv_struct->remote_addr != NULL) {
|
||||||
|
free(recv_struct->remote_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int idrnet_geteagain() {
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isNull(void* ptr) {
|
||||||
|
return ptr==NULL;
|
||||||
|
}
|
98
libs/network/idris_net.h
Normal file
98
libs/network/idris_net.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#ifndef IDRISNET_H
|
||||||
|
#define IDRISNET_H
|
||||||
|
|
||||||
|
// Includes used by the idris-file.
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <Ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct sockaddr_storage;
|
||||||
|
struct addrinfo;
|
||||||
|
|
||||||
|
typedef struct idrnet_recv_result {
|
||||||
|
int result;
|
||||||
|
void* payload;
|
||||||
|
} idrnet_recv_result;
|
||||||
|
|
||||||
|
// Same type of thing as idrnet_recv_result, but for UDP, so stores some
|
||||||
|
// address information
|
||||||
|
typedef struct idrnet_recvfrom_result {
|
||||||
|
int result;
|
||||||
|
void* payload;
|
||||||
|
struct sockaddr_storage* remote_addr;
|
||||||
|
} idrnet_recvfrom_result;
|
||||||
|
|
||||||
|
// Memory management functions
|
||||||
|
void* idrnet_malloc(int size);
|
||||||
|
void idrnet_free(void* ptr);
|
||||||
|
|
||||||
|
// Gets value of errno
|
||||||
|
int idrnet_errno();
|
||||||
|
|
||||||
|
int idrnet_socket(int domain, int type, int protocol);
|
||||||
|
|
||||||
|
// Bind
|
||||||
|
int idrnet_bind(int sockfd, int family, int socket_type, char* host, int port);
|
||||||
|
|
||||||
|
// Retrieve information about socket
|
||||||
|
int idrnet_getsockname(int sockfd, void *address, void *len);
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
int idrnet_connect(int sockfd, int family, int socket_type, char* host, int port);
|
||||||
|
|
||||||
|
// Accessor functions for struct_sockaddr
|
||||||
|
int idrnet_sockaddr_family(void* sockaddr);
|
||||||
|
char* idrnet_sockaddr_ipv4(void* sockaddr);
|
||||||
|
int idrnet_sockaddr_ipv4_port(void* sockaddr);
|
||||||
|
void* idrnet_create_sockaddr();
|
||||||
|
|
||||||
|
int idrnet_sockaddr_port(int sockfd);
|
||||||
|
|
||||||
|
// Accept
|
||||||
|
int idrnet_accept(int sockfd, void* sockaddr);
|
||||||
|
|
||||||
|
// Send
|
||||||
|
int idrnet_send(int sockfd, char* data);
|
||||||
|
int idrnet_send_buf(int sockfd, void* data, int len);
|
||||||
|
|
||||||
|
// Receive
|
||||||
|
// Creates a result structure containing result and payload
|
||||||
|
void* idrnet_recv(int sockfd, int len);
|
||||||
|
// Receives directly into a buffer
|
||||||
|
int idrnet_recv_buf(int sockfd, void* buf, int len);
|
||||||
|
|
||||||
|
// UDP Send
|
||||||
|
int idrnet_sendto(int sockfd, char* data, char* host, int port, int family);
|
||||||
|
int idrnet_sendto_buf(int sockfd, void* buf, int buf_len, char* host, int port, int family);
|
||||||
|
|
||||||
|
|
||||||
|
// UDP Receive
|
||||||
|
void* idrnet_recvfrom(int sockfd, int len);
|
||||||
|
void* idrnet_recvfrom_buf(int sockfd, void* buf, int len);
|
||||||
|
|
||||||
|
// Receive structure accessors
|
||||||
|
int idrnet_get_recv_res(void* res_struct);
|
||||||
|
char* idrnet_get_recv_payload(void* res_struct);
|
||||||
|
void idrnet_free_recv_struct(void* res_struct);
|
||||||
|
|
||||||
|
// Recvfrom structure accessors
|
||||||
|
int idrnet_get_recvfrom_res(void* res_struct);
|
||||||
|
char* idrnet_get_recvfrom_payload(void* res_struct);
|
||||||
|
void* idrnet_get_recvfrom_sockaddr(void* res_struct);
|
||||||
|
void idrnet_free_recvfrom_struct(void* res_struct);
|
||||||
|
|
||||||
|
|
||||||
|
int idrnet_getaddrinfo(struct addrinfo** address_res, char* host,
|
||||||
|
int port, int family, int socket_type);
|
||||||
|
|
||||||
|
int idrnet_geteagain();
|
||||||
|
|
||||||
|
int isNull(void* ptr);
|
||||||
|
|
||||||
|
#endif
|
10
libs/network/lib-tests
Executable file
10
libs/network/lib-tests
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
${IDRIS2} --exec main Echo.idr > build/output
|
||||||
|
|
||||||
|
if ! [ -z "$(diff build/output expected)" ]; then
|
||||||
|
echo "TEST FAILURE: unexpected build/output"
|
||||||
|
exit 2
|
||||||
|
else
|
||||||
|
echo "TEST SUCCESS"
|
||||||
|
fi
|
5
libs/network/network.ipkg
Normal file
5
libs/network/network.ipkg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
modules = Network.Socket,
|
||||||
|
Network.Socket.Data,
|
||||||
|
Network.Socket.Raw
|
41
libs/network/test.c
Normal file
41
libs/network/test.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "idris_net.h"
|
||||||
|
|
||||||
|
void test_sockaddr_port_returns_random_port_when_bind_port_is_0() {
|
||||||
|
int sock = idrnet_socket(AF_INET, 1, 0);
|
||||||
|
assert(sock > 0);
|
||||||
|
|
||||||
|
int res = idrnet_bind(sock, AF_INET, 1, "127.0.0.1", 0);
|
||||||
|
assert(res == 0);
|
||||||
|
|
||||||
|
res = idrnet_sockaddr_port(sock);
|
||||||
|
assert(res > 0);
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_sockaddr_port_returns_explicitly_assigned_port() {
|
||||||
|
int sock = idrnet_socket(AF_INET, 1, 0);
|
||||||
|
assert(sock > 0);
|
||||||
|
|
||||||
|
int res = idrnet_bind(sock, AF_INET, 1, "127.0.0.1", 34567);
|
||||||
|
assert(res == 0);
|
||||||
|
|
||||||
|
res = idrnet_sockaddr_port(sock);
|
||||||
|
assert(res == 34567);
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char**argv) {
|
||||||
|
test_sockaddr_port_returns_explicitly_assigned_port();
|
||||||
|
test_sockaddr_port_returns_random_port_when_bind_port_is_0();
|
||||||
|
|
||||||
|
printf("network library tests SUCCESS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
@ -861,7 +861,7 @@ strUncons : String -> Maybe (Char, String)
|
|||||||
strUncons "" = Nothing
|
strUncons "" = Nothing
|
||||||
strUncons str = Just (prim__strHead str, prim__strTail str)
|
strUncons str = Just (prim__strHead str, prim__strTail str)
|
||||||
|
|
||||||
export
|
public export
|
||||||
pack : List Char -> String
|
pack : List Char -> String
|
||||||
pack [] = ""
|
pack [] = ""
|
||||||
pack (x :: xs) = strCons x (pack xs)
|
pack (x :: xs) = strCons x (pack xs)
|
||||||
@ -875,7 +875,7 @@ fastPack xs
|
|||||||
toFArgs [] = []
|
toFArgs [] = []
|
||||||
toFArgs (x :: xs) = x :: toFArgs xs
|
toFArgs (x :: xs) = x :: toFArgs xs
|
||||||
|
|
||||||
export
|
public export
|
||||||
unpack : String -> List Char
|
unpack : String -> List Char
|
||||||
unpack str = unpack' 0 (prim__cast_IntegerInt (natToInteger (length str))) str
|
unpack str = unpack' 0 (prim__cast_IntegerInt (natToInteger (length str))) str
|
||||||
where
|
where
|
||||||
|
@ -300,10 +300,12 @@ elabInterface {vars} fc vis env nest constraints iname params dets mcon body
|
|||||||
let dn_in = UN ("Default implementation of " ++ show n)
|
let dn_in = UN ("Default implementation of " ++ show n)
|
||||||
dn <- inCurrentNS dn_in
|
dn <- inCurrentNS dn_in
|
||||||
|
|
||||||
dty <- case lookup n tydecls of
|
(rig, dty) <-
|
||||||
Just (_, _, t) => pure t
|
the (Core (RigCount, RawImp)) $
|
||||||
Nothing => throw (GenericMsg fc ("No method named " ++ show n ++ " in interface " ++ show iname))
|
case lookup n tydecls of
|
||||||
|
Just (r, (_, t)) => pure (r, t)
|
||||||
|
Nothing => throw (GenericMsg fc ("No method named " ++ show n ++ " in interface " ++ show iname))
|
||||||
|
|
||||||
let ity = apply (IVar fc iname) (map (IVar fc) (map fst params))
|
let ity = apply (IVar fc iname) (map (IVar fc) (map fst params))
|
||||||
|
|
||||||
-- Substitute the method names with their top level function
|
-- Substitute the method names with their top level function
|
||||||
@ -318,7 +320,7 @@ elabInterface {vars} fc vis env nest constraints iname params dets mcon body
|
|||||||
dty_imp <- bindTypeNames (map fst tydecls ++ vars)
|
dty_imp <- bindTypeNames (map fst tydecls ++ vars)
|
||||||
(bindIFace fc ity dty)
|
(bindIFace fc ity dty)
|
||||||
log 5 $ "Default method " ++ show dn ++ " : " ++ show dty_imp
|
log 5 $ "Default method " ++ show dn ++ " : " ++ show dty_imp
|
||||||
let dtydecl = IClaim fc RigW vis [] (MkImpTy fc dn dty_imp)
|
let dtydecl = IClaim fc rig vis [] (MkImpTy fc dn dty_imp)
|
||||||
processDecl [] nest env dtydecl
|
processDecl [] nest env dtydecl
|
||||||
|
|
||||||
let cs' = map (changeName dn) cs
|
let cs' = map (changeName dn) cs
|
||||||
|
@ -126,9 +126,11 @@ stMain opts
|
|||||||
|
|
||||||
u <- newRef UST initUState
|
u <- newRef UST initUState
|
||||||
updateREPLOpts
|
updateREPLOpts
|
||||||
|
session <- getSession
|
||||||
case fname of
|
case fname of
|
||||||
Nothing => logTime "Loading prelude" $
|
Nothing => logTime "Loading prelude" $
|
||||||
readPrelude
|
when (not $ noprelude session) $
|
||||||
|
readPrelude
|
||||||
Just f => logTime "Loading main file" $
|
Just f => logTime "Loading main file" $
|
||||||
loadMainFile f
|
loadMainFile f
|
||||||
|
|
||||||
|
@ -188,8 +188,9 @@ checkLet rigc_in elabinfo nest env fc rigl n nTy nVal scope expty
|
|||||||
inScope fc env' (\e' =>
|
inScope fc env' (\e' =>
|
||||||
check {e=e'} rigc elabinfo nest' env' scope expScope)
|
check {e=e'} rigc elabinfo nest' env' scope expScope)
|
||||||
scopet <- getTerm gscopet
|
scopet <- getTerm gscopet
|
||||||
checkExp rigc elabinfo env fc
|
|
||||||
(Bind fc n (Let rigb valv tyv) scopev)
|
|
||||||
(gnf env (Bind fc n (Let rigb valv tyv) scopet))
|
|
||||||
expty
|
|
||||||
|
|
||||||
|
-- No need to 'checkExp' here - we've already checked scopet
|
||||||
|
-- against the expected type when checking the scope, so just
|
||||||
|
-- build the term directly
|
||||||
|
pure (Bind fc n (Let rigb valv tyv) scopev,
|
||||||
|
gnf env (Bind fc n (Let rigb valv tyv) scopet))
|
||||||
|
@ -20,6 +20,7 @@ import TTImp.Elab.Check
|
|||||||
import TTImp.TTImp
|
import TTImp.TTImp
|
||||||
import TTImp.Unelab
|
import TTImp.Unelab
|
||||||
import TTImp.Utils
|
import TTImp.Utils
|
||||||
|
import TTImp.WithClause
|
||||||
|
|
||||||
import Data.NameMap
|
import Data.NameMap
|
||||||
|
|
||||||
@ -202,7 +203,6 @@ combineLinear loc ((n, count) :: cs)
|
|||||||
= do newc <- combine c c'
|
= do newc <- combine c c'
|
||||||
combineAll newc cs
|
combineAll newc cs
|
||||||
|
|
||||||
export
|
|
||||||
checkLHS : {vars : _} ->
|
checkLHS : {vars : _} ->
|
||||||
{auto c : Ref Ctxt Defs} ->
|
{auto c : Ref Ctxt Defs} ->
|
||||||
{auto m : Ref MD Metadata} ->
|
{auto m : Ref MD Metadata} ->
|
||||||
@ -210,9 +210,10 @@ checkLHS : {vars : _} ->
|
|||||||
(mult : RigCount) -> (hashit : Bool) ->
|
(mult : RigCount) -> (hashit : Bool) ->
|
||||||
Int -> List ElabOpt -> NestedNames vars -> Env Term vars ->
|
Int -> List ElabOpt -> NestedNames vars -> Env Term vars ->
|
||||||
FC -> RawImp ->
|
FC -> RawImp ->
|
||||||
Core (vars' ** (SubVars vars vars',
|
Core (RawImp, -- checked LHS with implicits added
|
||||||
|
(vars' ** (SubVars vars vars',
|
||||||
Env Term vars', NestedNames vars',
|
Env Term vars', NestedNames vars',
|
||||||
Term vars', Term vars'))
|
Term vars', Term vars')))
|
||||||
checkLHS {vars} mult hashit n opts nest env fc lhs_in
|
checkLHS {vars} mult hashit n opts nest env fc lhs_in
|
||||||
= do defs <- get Ctxt
|
= do defs <- get Ctxt
|
||||||
lhs_raw <- lhsInCurrentNS nest lhs_in
|
lhs_raw <- lhsInCurrentNS nest lhs_in
|
||||||
@ -250,7 +251,8 @@ checkLHS {vars} mult hashit n opts nest env fc lhs_in
|
|||||||
logTerm 5 "LHS type" lhsty_lin
|
logTerm 5 "LHS type" lhsty_lin
|
||||||
setHoleLHS (bindEnv fc env lhstm_lin)
|
setHoleLHS (bindEnv fc env lhstm_lin)
|
||||||
|
|
||||||
extendEnv env SubRefl nest lhstm_lin lhsty_lin
|
ext <- extendEnv env SubRefl nest lhstm_lin lhsty_lin
|
||||||
|
pure (lhs, ext)
|
||||||
|
|
||||||
plicit : Binder (Term vars) -> PiInfo
|
plicit : Binder (Term vars) -> PiInfo
|
||||||
plicit (Pi _ p _) = p
|
plicit (Pi _ p _) = p
|
||||||
@ -303,110 +305,6 @@ hasEmptyPat defs env (Bind fc x (PVar c p ty) sc)
|
|||||||
|| !(hasEmptyPat defs (PVar c p ty :: env) sc)
|
|| !(hasEmptyPat defs (PVar c p ty :: env) sc)
|
||||||
hasEmptyPat defs env _ = pure False
|
hasEmptyPat defs env _ = pure False
|
||||||
|
|
||||||
-- Get the arguments for the rewritten pattern clause of a with by looking
|
|
||||||
-- up how the argument names matched
|
|
||||||
getArgMatch : FC -> Bool -> RawImp -> List (String, RawImp) ->
|
|
||||||
Maybe (PiInfo, Name) -> RawImp
|
|
||||||
getArgMatch ploc search warg ms Nothing = warg
|
|
||||||
getArgMatch ploc True warg ms (Just (AutoImplicit, UN n))
|
|
||||||
= case lookup n ms of
|
|
||||||
Nothing => ISearch ploc 500
|
|
||||||
Just tm => tm
|
|
||||||
getArgMatch ploc True warg ms (Just (AutoImplicit, _))
|
|
||||||
= ISearch ploc 500
|
|
||||||
getArgMatch ploc search warg ms (Just (_, UN n))
|
|
||||||
= case lookup n ms of
|
|
||||||
Nothing => Implicit ploc True
|
|
||||||
Just tm => tm
|
|
||||||
getArgMatch ploc search warg ms _ = Implicit ploc True
|
|
||||||
|
|
||||||
getNewLHS : {auto c : Ref Ctxt Defs} ->
|
|
||||||
FC -> (drop : Nat) -> Name -> List (Maybe (PiInfo, Name)) ->
|
|
||||||
RawImp -> RawImp -> Core RawImp
|
|
||||||
getNewLHS ploc drop wname wargnames lhs_raw patlhs
|
|
||||||
= do (mlhs_raw, wrest) <- dropWithArgs drop patlhs
|
|
||||||
autoimp <- isAutoImplicits
|
|
||||||
autoImplicits True
|
|
||||||
(_, lhs) <- bindNames False lhs_raw
|
|
||||||
(_, mlhs) <- bindNames False mlhs_raw
|
|
||||||
autoImplicits autoimp
|
|
||||||
|
|
||||||
let (warg :: rest) = reverse wrest
|
|
||||||
| _ => throw (GenericMsg ploc "Badly formed 'with' clause")
|
|
||||||
log 10 $ show lhs ++ " against " ++ show mlhs ++
|
|
||||||
" dropping " ++ show (warg :: rest)
|
|
||||||
ms <- getMatch lhs mlhs
|
|
||||||
log 10 $ "Matches: " ++ show ms
|
|
||||||
let newlhs = apply (IVar ploc wname)
|
|
||||||
(map (getArgMatch ploc False warg ms) wargnames ++ rest)
|
|
||||||
log 10 $ "New LHS: " ++ show newlhs
|
|
||||||
pure newlhs
|
|
||||||
where
|
|
||||||
dropWithArgs : Nat -> RawImp ->
|
|
||||||
Core (RawImp, List RawImp)
|
|
||||||
dropWithArgs Z tm = pure (tm, [])
|
|
||||||
dropWithArgs (S k) (IApp _ f arg)
|
|
||||||
= do (tm, rest) <- dropWithArgs k f
|
|
||||||
pure (tm, arg :: rest)
|
|
||||||
-- Shouldn't happen if parsed correctly, but there's no guarantee that
|
|
||||||
-- inputs come from parsed source so throw an error.
|
|
||||||
dropWithArgs _ _ = throw (GenericMsg ploc "Badly formed 'with' clause")
|
|
||||||
|
|
||||||
-- Find a 'with' application on the RHS and update it
|
|
||||||
withRHS : {auto c : Ref Ctxt Defs} ->
|
|
||||||
FC -> (drop : Nat) -> Name -> List (Maybe (PiInfo, Name)) ->
|
|
||||||
RawImp -> RawImp ->
|
|
||||||
Core RawImp
|
|
||||||
withRHS fc drop wname wargnames tm toplhs
|
|
||||||
= wrhs tm
|
|
||||||
where
|
|
||||||
withApply : FC -> RawImp -> List RawImp -> RawImp
|
|
||||||
withApply fc f [] = f
|
|
||||||
withApply fc f (a :: as) = withApply fc (IWithApp fc f a) as
|
|
||||||
|
|
||||||
updateWith : FC -> RawImp -> List RawImp -> Core RawImp
|
|
||||||
updateWith fc (IWithApp _ f a) ws = updateWith fc f (a :: ws)
|
|
||||||
updateWith fc tm []
|
|
||||||
= throw (GenericMsg fc "Badly formed 'with' application")
|
|
||||||
updateWith fc tm (arg :: args)
|
|
||||||
= do log 10 $ "With-app: Matching " ++ show toplhs ++ " against " ++ show tm
|
|
||||||
ms <- getMatch toplhs tm
|
|
||||||
log 10 $ "Result: " ++ show ms
|
|
||||||
let newrhs = apply (IVar fc wname)
|
|
||||||
(map (getArgMatch fc True arg ms) wargnames)
|
|
||||||
log 10 $ "With args for RHS: " ++ show wargnames
|
|
||||||
log 10 $ "New RHS: " ++ show newrhs
|
|
||||||
pure (withApply fc newrhs args)
|
|
||||||
|
|
||||||
mutual
|
|
||||||
wrhs : RawImp -> Core RawImp
|
|
||||||
wrhs (IPi fc c p n ty sc)
|
|
||||||
= pure $ IPi fc c p n !(wrhs ty) !(wrhs sc)
|
|
||||||
wrhs (ILam fc c p n ty sc)
|
|
||||||
= pure $ ILam fc c p n !(wrhs ty) !(wrhs sc)
|
|
||||||
wrhs (ILet fc c n ty val sc)
|
|
||||||
= pure $ ILet fc c n !(wrhs ty) !(wrhs val) !(wrhs sc)
|
|
||||||
wrhs (ICase fc sc ty clauses)
|
|
||||||
= pure $ ICase fc !(wrhs sc) !(wrhs ty) !(traverse wrhsC clauses)
|
|
||||||
wrhs (ILocal fc decls sc)
|
|
||||||
= pure $ ILocal fc decls !(wrhs sc) -- TODO!
|
|
||||||
wrhs (IUpdate fc upds tm)
|
|
||||||
= pure $ IUpdate fc upds !(wrhs tm) -- TODO!
|
|
||||||
wrhs (IApp fc f a)
|
|
||||||
= pure $ IApp fc !(wrhs f) !(wrhs a)
|
|
||||||
wrhs (IImplicitApp fc f n a)
|
|
||||||
= pure $ IImplicitApp fc !(wrhs f) n !(wrhs a)
|
|
||||||
wrhs (IWithApp fc f a) = updateWith fc f [a]
|
|
||||||
wrhs (IRewrite fc rule tm) = pure $ IRewrite fc !(wrhs rule) !(wrhs tm)
|
|
||||||
wrhs (IDelayed fc r tm) = pure $ IDelayed fc r !(wrhs tm)
|
|
||||||
wrhs (IDelay fc tm) = pure $ IDelay fc !(wrhs tm)
|
|
||||||
wrhs (IForce fc tm) = pure $ IForce fc !(wrhs tm)
|
|
||||||
wrhs tm = pure tm
|
|
||||||
|
|
||||||
wrhsC : ImpClause -> Core ImpClause
|
|
||||||
wrhsC (PatClause fc lhs rhs) = pure $ PatClause fc lhs !(wrhs rhs)
|
|
||||||
wrhsC c = pure c
|
|
||||||
|
|
||||||
-- For checking with blocks as nested names
|
-- For checking with blocks as nested names
|
||||||
applyEnv : {auto c : Ref Ctxt Defs} ->
|
applyEnv : {auto c : Ref Ctxt Defs} ->
|
||||||
Env Term vars -> Name ->
|
Env Term vars -> Name ->
|
||||||
@ -453,7 +351,7 @@ checkClause mult hashit n opts nest env (ImpossibleClause fc lhs)
|
|||||||
then pure Nothing
|
then pure Nothing
|
||||||
else throw (ValidCase fc env (Right err)))
|
else throw (ValidCase fc env (Right err)))
|
||||||
checkClause {vars} mult hashit n opts nest env (PatClause fc lhs_in rhs)
|
checkClause {vars} mult hashit n opts nest env (PatClause fc lhs_in rhs)
|
||||||
= do (vars' ** (sub', env', nest', lhstm', lhsty')) <-
|
= do (_, (vars' ** (sub', env', nest', lhstm', lhsty'))) <-
|
||||||
checkLHS mult hashit n opts nest env fc lhs_in
|
checkLHS mult hashit n opts nest env fc lhs_in
|
||||||
let rhsMode = case mult of
|
let rhsMode = case mult of
|
||||||
Rig0 => InType
|
Rig0 => InType
|
||||||
@ -478,7 +376,7 @@ checkClause {vars} mult hashit n opts nest env (PatClause fc lhs_in rhs)
|
|||||||
pure (Just (MkClause env' lhstm' rhstm))
|
pure (Just (MkClause env' lhstm' rhstm))
|
||||||
-- TODO: (to decide) With is complicated. Move this into its own module?
|
-- TODO: (to decide) With is complicated. Move this into its own module?
|
||||||
checkClause {vars} mult hashit n opts nest env (WithClause fc lhs_in wval_raw cs)
|
checkClause {vars} mult hashit n opts nest env (WithClause fc lhs_in wval_raw cs)
|
||||||
= do (vars' ** (sub', env', nest', lhspat, reqty)) <-
|
= do (lhs, (vars' ** (sub', env', nest', lhspat, reqty))) <-
|
||||||
checkLHS mult hashit n opts nest env fc lhs_in
|
checkLHS mult hashit n opts nest env fc lhs_in
|
||||||
let wmode
|
let wmode
|
||||||
= case mult of
|
= case mult of
|
||||||
@ -549,7 +447,7 @@ checkClause {vars} mult hashit n opts nest env (WithClause fc lhs_in wval_raw cs
|
|||||||
(gnf env' reqty)
|
(gnf env' reqty)
|
||||||
|
|
||||||
-- Generate new clauses by rewriting the matched arguments
|
-- Generate new clauses by rewriting the matched arguments
|
||||||
cs' <- traverse (mkClauseWith 1 wname wargNames lhs_in) cs
|
cs' <- traverse (mkClauseWith 1 wname wargNames lhs) cs
|
||||||
log 3 $ "With clauses: " ++ show cs'
|
log 3 $ "With clauses: " ++ show cs'
|
||||||
|
|
||||||
-- Elaborate the new definition here
|
-- Elaborate the new definition here
|
||||||
@ -588,16 +486,16 @@ checkClause {vars} mult hashit n opts nest env (WithClause fc lhs_in wval_raw cs
|
|||||||
RawImp -> ImpClause ->
|
RawImp -> ImpClause ->
|
||||||
Core ImpClause
|
Core ImpClause
|
||||||
mkClauseWith drop wname wargnames lhs (PatClause ploc patlhs rhs)
|
mkClauseWith drop wname wargnames lhs (PatClause ploc patlhs rhs)
|
||||||
= do newlhs <- getNewLHS ploc drop wname wargnames lhs patlhs
|
= do newlhs <- getNewLHS ploc drop nest wname wargnames lhs patlhs
|
||||||
newrhs <- withRHS ploc drop wname wargnames rhs lhs
|
newrhs <- withRHS ploc drop wname wargnames rhs lhs
|
||||||
pure (PatClause ploc newlhs newrhs)
|
pure (PatClause ploc newlhs newrhs)
|
||||||
mkClauseWith drop wname wargnames lhs (WithClause ploc patlhs rhs ws)
|
mkClauseWith drop wname wargnames lhs (WithClause ploc patlhs rhs ws)
|
||||||
= do newlhs <- getNewLHS ploc drop wname wargnames lhs patlhs
|
= do newlhs <- getNewLHS ploc drop nest wname wargnames lhs patlhs
|
||||||
newrhs <- withRHS ploc drop wname wargnames rhs lhs
|
newrhs <- withRHS ploc drop wname wargnames rhs lhs
|
||||||
ws' <- traverse (mkClauseWith (S drop) wname wargnames lhs) ws
|
ws' <- traverse (mkClauseWith (S drop) wname wargnames lhs) ws
|
||||||
pure (WithClause ploc newlhs newrhs ws')
|
pure (WithClause ploc newlhs newrhs ws')
|
||||||
mkClauseWith drop wname wargnames lhs (ImpossibleClause ploc patlhs)
|
mkClauseWith drop wname wargnames lhs (ImpossibleClause ploc patlhs)
|
||||||
= do newlhs <- getNewLHS ploc drop wname wargnames lhs patlhs
|
= do newlhs <- getNewLHS ploc drop nest wname wargnames lhs patlhs
|
||||||
pure (ImpossibleClause ploc newlhs)
|
pure (ImpossibleClause ploc newlhs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,62 +252,3 @@ uniqueName defs used n
|
|||||||
next str
|
next str
|
||||||
= let (n, i) = nameNum str in
|
= let (n, i) = nameNum str in
|
||||||
n ++ "_" ++ show (i + 1)
|
n ++ "_" ++ show (i + 1)
|
||||||
|
|
||||||
matchFail : FC -> Core a
|
|
||||||
matchFail loc = throw (GenericMsg loc "Clauses do not match")
|
|
||||||
|
|
||||||
mutual
|
|
||||||
export
|
|
||||||
getMatch : RawImp -> RawImp ->
|
|
||||||
Core (List (String, RawImp))
|
|
||||||
getMatch (IBindVar _ n) tm = pure [(n, tm)]
|
|
||||||
getMatch (Implicit _ _) tm = pure []
|
|
||||||
|
|
||||||
getMatch (IVar _ n) (IVar loc n')
|
|
||||||
= if n == n' then pure [] else matchFail loc
|
|
||||||
getMatch (IPi _ c p n arg ret) (IPi loc c' p' n' arg' ret')
|
|
||||||
= if c == c' && p == p' && n == n'
|
|
||||||
then matchAll [(arg, arg'), (ret, ret')]
|
|
||||||
else matchFail loc
|
|
||||||
-- TODO: Lam, Let, Case, Local, Update
|
|
||||||
getMatch (IApp _ f a) (IApp loc f' a')
|
|
||||||
= matchAll [(f, f'), (a, a')]
|
|
||||||
getMatch (IImplicitApp _ f n a) (IImplicitApp loc f' n' a')
|
|
||||||
= if n == n'
|
|
||||||
then matchAll [(f, f'), (a, a')]
|
|
||||||
else matchFail loc
|
|
||||||
getMatch (IWithApp _ f a) (IWithApp loc f' a')
|
|
||||||
= matchAll [(f, f'), (a, a')]
|
|
||||||
-- It's okay to skip implicits
|
|
||||||
getMatch (IImplicitApp fc f n a) f'
|
|
||||||
= getMatch f f;
|
|
||||||
getMatch f (IImplicitApp fc f' n a)
|
|
||||||
= getMatch f f;
|
|
||||||
-- Alternatives are okay as long as all corresponding alternatives are okay
|
|
||||||
getMatch (IAlternative _ _ as) (IAlternative _ _ as')
|
|
||||||
= matchAll (zip as as')
|
|
||||||
getMatch (IAs _ _ _ p) p' = getMatch p p'
|
|
||||||
getMatch p (IAs _ _ _ p') = getMatch p p'
|
|
||||||
getMatch (IType _) (IType _) = pure []
|
|
||||||
getMatch pat spec = matchFail (getFC pat)
|
|
||||||
|
|
||||||
matchAll : List (RawImp, RawImp) ->
|
|
||||||
Core (List (String, RawImp))
|
|
||||||
matchAll [] = pure []
|
|
||||||
matchAll ((x, y) :: ms)
|
|
||||||
= do matches <- matchAll ms
|
|
||||||
mxy <- getMatch x y
|
|
||||||
mergeMatches (mxy ++ matches)
|
|
||||||
|
|
||||||
mergeMatches : List (String, RawImp) ->
|
|
||||||
Core (List (String, RawImp))
|
|
||||||
mergeMatches [] = pure []
|
|
||||||
mergeMatches ((n, tm) :: rest)
|
|
||||||
= do rest' <- mergeMatches rest
|
|
||||||
case lookup n rest' of
|
|
||||||
Nothing => pure ((n, tm) :: rest')
|
|
||||||
Just tm' =>
|
|
||||||
do getMatch tm tm' -- just need to know it succeeds
|
|
||||||
mergeMatches rest
|
|
||||||
|
|
||||||
|
|
||||||
|
189
src/TTImp/WithClause.idr
Normal file
189
src/TTImp/WithClause.idr
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
module TTImp.WithClause
|
||||||
|
|
||||||
|
import Core.Context
|
||||||
|
import Core.TT
|
||||||
|
import TTImp.BindImplicits
|
||||||
|
import TTImp.TTImp
|
||||||
|
|
||||||
|
matchFail : FC -> Core a
|
||||||
|
matchFail loc = throw (GenericMsg loc "With clause does not match parent")
|
||||||
|
|
||||||
|
mutual
|
||||||
|
export
|
||||||
|
getMatch : (lhs : Bool) -> RawImp -> RawImp ->
|
||||||
|
Core (List (String, RawImp))
|
||||||
|
getMatch lhs (IBindVar _ n) tm = pure [(n, tm)]
|
||||||
|
getMatch lhs (Implicit _ _) tm = pure []
|
||||||
|
|
||||||
|
getMatch lhs (IVar _ (NS ns n)) (IVar loc (NS ns' n'))
|
||||||
|
= if n == n' && isSuffixOf ns' ns then pure [] else matchFail loc
|
||||||
|
getMatch lhs (IVar _ (NS ns n)) (IVar loc n')
|
||||||
|
= if n == n' then pure [] else matchFail loc
|
||||||
|
getMatch lhs (IVar _ n) (IVar loc n')
|
||||||
|
= if n == n' then pure [] else matchFail loc
|
||||||
|
getMatch lhs (IPi _ c p n arg ret) (IPi loc c' p' n' arg' ret')
|
||||||
|
= if c == c' && p == p' && n == n'
|
||||||
|
then matchAll lhs [(arg, arg'), (ret, ret')]
|
||||||
|
else matchFail loc
|
||||||
|
-- TODO: Lam, Let, Case, Local, Update
|
||||||
|
getMatch lhs (IApp _ f a) (IApp loc f' a')
|
||||||
|
= matchAll lhs [(f, f'), (a, a')]
|
||||||
|
getMatch lhs (IImplicitApp _ f n a) (IImplicitApp loc f' n' a')
|
||||||
|
= if n == n'
|
||||||
|
then matchAll lhs [(f, f'), (a, a')]
|
||||||
|
else matchFail loc
|
||||||
|
getMatch lhs (IWithApp _ f a) (IWithApp loc f' a')
|
||||||
|
= matchAll lhs [(f, f'), (a, a')]
|
||||||
|
-- On LHS: If there's an implicit in the parent, but not the clause, add the
|
||||||
|
-- implicit to the clause. This will propagate the implicit through to the
|
||||||
|
-- body
|
||||||
|
getMatch True (IImplicitApp fc f n a) f'
|
||||||
|
= matchAll True [(f, f'), (a, a)]
|
||||||
|
-- On RHS: Rely on unification to fill in the implicit
|
||||||
|
getMatch False (IImplicitApp fc f n a) f'
|
||||||
|
= getMatch False f f
|
||||||
|
-- Can't have an implicit in the clause if there wasn't a matching
|
||||||
|
-- implicit in the parent
|
||||||
|
getMatch lhs f (IImplicitApp fc f' n a)
|
||||||
|
= matchFail fc
|
||||||
|
-- Alternatives are okay as long as all corresponding alternatives are okay
|
||||||
|
getMatch lhs (IAlternative _ _ as) (IAlternative _ _ as')
|
||||||
|
= matchAll lhs (zip as as')
|
||||||
|
getMatch lhs (IAs _ _ (UN n) p) (IAs fc _ (UN n') p')
|
||||||
|
= do ms <- getMatch lhs p p'
|
||||||
|
mergeMatches lhs ((n, IBindVar fc n') :: ms)
|
||||||
|
getMatch lhs (IAs _ _ (UN n) p) p'
|
||||||
|
= do ms <- getMatch lhs p p'
|
||||||
|
mergeMatches lhs ((n, p') :: ms)
|
||||||
|
getMatch lhs (IAs _ _ _ p) p' = getMatch lhs p p'
|
||||||
|
getMatch lhs p (IAs _ _ _ p') = getMatch lhs p p'
|
||||||
|
getMatch lhs (IType _) (IType _) = pure []
|
||||||
|
getMatch lhs pat spec = matchFail (getFC pat)
|
||||||
|
|
||||||
|
matchAll : (lhs : Bool) -> List (RawImp, RawImp) ->
|
||||||
|
Core (List (String, RawImp))
|
||||||
|
matchAll lhs [] = pure []
|
||||||
|
matchAll lhs ((x, y) :: ms)
|
||||||
|
= do matches <- matchAll lhs ms
|
||||||
|
mxy <- getMatch lhs x y
|
||||||
|
mergeMatches lhs (mxy ++ matches)
|
||||||
|
|
||||||
|
mergeMatches : (lhs : Bool) -> List (String, RawImp) ->
|
||||||
|
Core (List (String, RawImp))
|
||||||
|
mergeMatches lhs [] = pure []
|
||||||
|
mergeMatches lhs ((n, tm) :: rest)
|
||||||
|
= do rest' <- mergeMatches lhs rest
|
||||||
|
case lookup n rest' of
|
||||||
|
Nothing => pure ((n, tm) :: rest')
|
||||||
|
Just tm' =>
|
||||||
|
do getMatch lhs tm tm' -- just need to know it succeeds
|
||||||
|
mergeMatches lhs rest
|
||||||
|
|
||||||
|
-- Get the arguments for the rewritten pattern clause of a with by looking
|
||||||
|
-- up how the argument names matched
|
||||||
|
getArgMatch : FC -> Bool -> RawImp -> List (String, RawImp) ->
|
||||||
|
Maybe (PiInfo, Name) -> RawImp
|
||||||
|
getArgMatch ploc search warg ms Nothing = warg
|
||||||
|
getArgMatch ploc True warg ms (Just (AutoImplicit, UN n))
|
||||||
|
= case lookup n ms of
|
||||||
|
Nothing => ISearch ploc 500
|
||||||
|
Just tm => tm
|
||||||
|
getArgMatch ploc True warg ms (Just (AutoImplicit, _))
|
||||||
|
= ISearch ploc 500
|
||||||
|
getArgMatch ploc search warg ms (Just (_, UN n))
|
||||||
|
= case lookup n ms of
|
||||||
|
Nothing => Implicit ploc True
|
||||||
|
Just tm => tm
|
||||||
|
getArgMatch ploc search warg ms _ = Implicit ploc True
|
||||||
|
|
||||||
|
export
|
||||||
|
getNewLHS : {auto c : Ref Ctxt Defs} ->
|
||||||
|
FC -> (drop : Nat) -> NestedNames vars ->
|
||||||
|
Name -> List (Maybe (PiInfo, Name)) ->
|
||||||
|
RawImp -> RawImp -> Core RawImp
|
||||||
|
getNewLHS ploc drop nest wname wargnames lhs_raw patlhs
|
||||||
|
= do (mlhs_raw, wrest) <- dropWithArgs drop patlhs
|
||||||
|
autoimp <- isAutoImplicits
|
||||||
|
autoImplicits True
|
||||||
|
(_, lhs) <- bindNames False lhs_raw
|
||||||
|
(_, mlhs) <- bindNames False mlhs_raw
|
||||||
|
autoImplicits autoimp
|
||||||
|
|
||||||
|
let (warg :: rest) = reverse wrest
|
||||||
|
| _ => throw (GenericMsg ploc "Badly formed 'with' clause")
|
||||||
|
log 5 $ show lhs ++ " against " ++ show mlhs ++
|
||||||
|
" dropping " ++ show (warg :: rest)
|
||||||
|
ms <- getMatch True lhs mlhs
|
||||||
|
log 5 $ "Matches: " ++ show ms
|
||||||
|
let newlhs = apply (IVar ploc wname)
|
||||||
|
(map (getArgMatch ploc False warg ms) wargnames ++ rest)
|
||||||
|
log 5 $ "New LHS: " ++ show newlhs
|
||||||
|
pure newlhs
|
||||||
|
where
|
||||||
|
dropWithArgs : Nat -> RawImp ->
|
||||||
|
Core (RawImp, List RawImp)
|
||||||
|
dropWithArgs Z tm = pure (tm, [])
|
||||||
|
dropWithArgs (S k) (IApp _ f arg)
|
||||||
|
= do (tm, rest) <- dropWithArgs k f
|
||||||
|
pure (tm, arg :: rest)
|
||||||
|
-- Shouldn't happen if parsed correctly, but there's no guarantee that
|
||||||
|
-- inputs come from parsed source so throw an error.
|
||||||
|
dropWithArgs _ _ = throw (GenericMsg ploc "Badly formed 'with' clause")
|
||||||
|
|
||||||
|
-- Find a 'with' application on the RHS and update it
|
||||||
|
export
|
||||||
|
withRHS : {auto c : Ref Ctxt Defs} ->
|
||||||
|
FC -> (drop : Nat) -> Name -> List (Maybe (PiInfo, Name)) ->
|
||||||
|
RawImp -> RawImp ->
|
||||||
|
Core RawImp
|
||||||
|
withRHS fc drop wname wargnames tm toplhs
|
||||||
|
= wrhs tm
|
||||||
|
where
|
||||||
|
withApply : FC -> RawImp -> List RawImp -> RawImp
|
||||||
|
withApply fc f [] = f
|
||||||
|
withApply fc f (a :: as) = withApply fc (IWithApp fc f a) as
|
||||||
|
|
||||||
|
updateWith : FC -> RawImp -> List RawImp -> Core RawImp
|
||||||
|
updateWith fc (IWithApp _ f a) ws = updateWith fc f (a :: ws)
|
||||||
|
updateWith fc tm []
|
||||||
|
= throw (GenericMsg fc "Badly formed 'with' application")
|
||||||
|
updateWith fc tm (arg :: args)
|
||||||
|
= do log 10 $ "With-app: Matching " ++ show toplhs ++ " against " ++ show tm
|
||||||
|
ms <- getMatch False toplhs tm
|
||||||
|
log 10 $ "Result: " ++ show ms
|
||||||
|
let newrhs = apply (IVar fc wname)
|
||||||
|
(map (getArgMatch fc True arg ms) wargnames)
|
||||||
|
log 10 $ "With args for RHS: " ++ show wargnames
|
||||||
|
log 10 $ "New RHS: " ++ show newrhs
|
||||||
|
pure (withApply fc newrhs args)
|
||||||
|
|
||||||
|
mutual
|
||||||
|
wrhs : RawImp -> Core RawImp
|
||||||
|
wrhs (IPi fc c p n ty sc)
|
||||||
|
= pure $ IPi fc c p n !(wrhs ty) !(wrhs sc)
|
||||||
|
wrhs (ILam fc c p n ty sc)
|
||||||
|
= pure $ ILam fc c p n !(wrhs ty) !(wrhs sc)
|
||||||
|
wrhs (ILet fc c n ty val sc)
|
||||||
|
= pure $ ILet fc c n !(wrhs ty) !(wrhs val) !(wrhs sc)
|
||||||
|
wrhs (ICase fc sc ty clauses)
|
||||||
|
= pure $ ICase fc !(wrhs sc) !(wrhs ty) !(traverse wrhsC clauses)
|
||||||
|
wrhs (ILocal fc decls sc)
|
||||||
|
= pure $ ILocal fc decls !(wrhs sc) -- TODO!
|
||||||
|
wrhs (IUpdate fc upds tm)
|
||||||
|
= pure $ IUpdate fc upds !(wrhs tm) -- TODO!
|
||||||
|
wrhs (IApp fc f a)
|
||||||
|
= pure $ IApp fc !(wrhs f) !(wrhs a)
|
||||||
|
wrhs (IImplicitApp fc f n a)
|
||||||
|
= pure $ IImplicitApp fc !(wrhs f) n !(wrhs a)
|
||||||
|
wrhs (IWithApp fc f a) = updateWith fc f [a]
|
||||||
|
wrhs (IRewrite fc rule tm) = pure $ IRewrite fc !(wrhs rule) !(wrhs tm)
|
||||||
|
wrhs (IDelayed fc r tm) = pure $ IDelayed fc r !(wrhs tm)
|
||||||
|
wrhs (IDelay fc tm) = pure $ IDelay fc !(wrhs tm)
|
||||||
|
wrhs (IForce fc tm) = pure $ IForce fc !(wrhs tm)
|
||||||
|
wrhs tm = pure tm
|
||||||
|
|
||||||
|
wrhsC : ImpClause -> Core ImpClause
|
||||||
|
wrhsC (PatClause fc lhs rhs) = pure $ PatClause fc lhs !(wrhs rhs)
|
||||||
|
wrhsC c = pure c
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ idrisTests
|
|||||||
"basic011", "basic012", "basic013", "basic014", "basic015",
|
"basic011", "basic012", "basic013", "basic014", "basic015",
|
||||||
"basic016", "basic017", "basic018", "basic019", "basic020",
|
"basic016", "basic017", "basic018", "basic019", "basic020",
|
||||||
"basic021", "basic022", "basic023", "basic024", "basic025",
|
"basic021", "basic022", "basic023", "basic024", "basic025",
|
||||||
"basic026", "basic027",
|
"basic026", "basic027", "basic028",
|
||||||
"coverage001", "coverage002", "coverage003", "coverage004",
|
"coverage001", "coverage002", "coverage003", "coverage004",
|
||||||
"error001", "error002", "error003", "error004", "error005",
|
"error001", "error002", "error003", "error004", "error005",
|
||||||
"error006", "error007", "error008", "error009", "error010",
|
"error006", "error007", "error008", "error009", "error010",
|
||||||
@ -43,12 +43,13 @@ idrisTests
|
|||||||
"lazy001",
|
"lazy001",
|
||||||
"linear001", "linear002", "linear003", "linear004", "linear005",
|
"linear001", "linear002", "linear003", "linear004", "linear005",
|
||||||
"linear006", "linear007",
|
"linear006", "linear007",
|
||||||
"perf001",
|
"perf001", "perf002",
|
||||||
"perror001", "perror002", "perror003", "perror004", "perror005",
|
"perror001", "perror002", "perror003", "perror004", "perror005",
|
||||||
"perror006",
|
"perror006",
|
||||||
"record001", "record002",
|
"record001", "record002",
|
||||||
"total001", "total002", "total003", "total004", "total005",
|
"total001", "total002", "total003", "total004", "total005",
|
||||||
"total006"]
|
"total006",
|
||||||
|
"with001"]
|
||||||
|
|
||||||
typeddTests : List String
|
typeddTests : List String
|
||||||
typeddTests
|
typeddTests
|
||||||
|
49
tests/idris2/basic028/Do.idr
Normal file
49
tests/idris2/basic028/Do.idr
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
data Maybe a = Nothing
|
||||||
|
| Just a
|
||||||
|
|
||||||
|
infixl 1 >>=
|
||||||
|
|
||||||
|
(>>=) : Maybe a -> (a -> Maybe b) -> Maybe b
|
||||||
|
(>>=) Nothing k = Nothing
|
||||||
|
(>>=) (Just x) k = k x
|
||||||
|
|
||||||
|
data Nat : Type where
|
||||||
|
Z : Nat
|
||||||
|
S : Nat -> Nat
|
||||||
|
|
||||||
|
plus : Nat -> Nat -> Nat
|
||||||
|
plus Z y = y
|
||||||
|
plus (S k) y = S (plus k y)
|
||||||
|
|
||||||
|
maybeAdd' : Maybe Nat -> Maybe Nat -> Maybe Nat
|
||||||
|
maybeAdd' x y
|
||||||
|
= x >>= \x' =>
|
||||||
|
y >>= \y' =>
|
||||||
|
Just (plus x' y')
|
||||||
|
|
||||||
|
maybeAdd : Maybe Nat -> Maybe Nat -> Maybe Nat
|
||||||
|
maybeAdd x y
|
||||||
|
= do x' <- x
|
||||||
|
y' <- y
|
||||||
|
Just (plus x' y')
|
||||||
|
|
||||||
|
data Either : Type -> Type -> Type where
|
||||||
|
Left : a -> Either a b
|
||||||
|
Right : b -> Either a b
|
||||||
|
|
||||||
|
mEmbed : Maybe a -> Maybe (Either String a)
|
||||||
|
mEmbed Nothing = Just (Left "FAIL")
|
||||||
|
mEmbed (Just x) = Just (Right x)
|
||||||
|
|
||||||
|
mPatBind : Maybe Nat -> Maybe Nat -> Maybe Nat
|
||||||
|
mPatBind x y
|
||||||
|
= do Right res <- mEmbed (maybeAdd x y)
|
||||||
|
| Left err => Just Z
|
||||||
|
Just res
|
||||||
|
|
||||||
|
mLetBind : Maybe Nat -> Maybe Nat -> Maybe Nat
|
||||||
|
mLetBind x y
|
||||||
|
= do let Just res = maybeAdd x y
|
||||||
|
| Nothing => Just Z
|
||||||
|
Just res
|
||||||
|
|
7
tests/idris2/basic028/expected
Normal file
7
tests/idris2/basic028/expected
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Welcome to Idris 2 version 0.0. Enjoy yourself!
|
||||||
|
Main> 1/1: Building Do (Do.idr)
|
||||||
|
Main> Just (S (S (S Z)))
|
||||||
|
Main> Just Z
|
||||||
|
Main> Just (S (S (S Z)))
|
||||||
|
Main> Just Z
|
||||||
|
Main> Bye for now!
|
6
tests/idris2/basic028/input
Normal file
6
tests/idris2/basic028/input
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
:l Do.idr
|
||||||
|
mPatBind (Just (S Z)) (Just (S (S Z)))
|
||||||
|
mPatBind (Just (S Z)) Nothing
|
||||||
|
mLetBind (Just (S Z)) (Just (S (S Z)))
|
||||||
|
mLetBind (Just (S Z)) Nothing
|
||||||
|
:q
|
5
tests/idris2/basic028/run
Executable file
5
tests/idris2/basic028/run
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
unset IDRIS2_PATH
|
||||||
|
|
||||||
|
$1 --no-prelude < input
|
||||||
|
|
||||||
|
rm -rf build
|
35
tests/idris2/perf002/Big.idr
Normal file
35
tests/idris2/perf002/Big.idr
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Data.Vect
|
||||||
|
|
||||||
|
test : Vect 2 () -> IO ()
|
||||||
|
test b =
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
let i = index 1 b in
|
||||||
|
pure ()
|
||||||
|
|
||||||
|
main : IO ()
|
||||||
|
main = do
|
||||||
|
pure ()
|
1
tests/idris2/perf002/expected
Normal file
1
tests/idris2/perf002/expected
Normal file
@ -0,0 +1 @@
|
|||||||
|
1/1: Building Big (Big.idr)
|
3
tests/idris2/perf002/run
Executable file
3
tests/idris2/perf002/run
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
$1 --check Big.idr
|
||||||
|
|
||||||
|
rm -rf build
|
29
tests/idris2/with001/Temp.idr
Normal file
29
tests/idris2/with001/Temp.idr
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
module Temp
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
|
||||||
|
safeStrHead : (s : String) -> {pr : NonEmpty (unpack s)} -> Char
|
||||||
|
safeStrHead s with (unpack s)
|
||||||
|
safeStrHead s | [] = absurd pr
|
||||||
|
safeStrHead s | (c::_) = c
|
||||||
|
|
||||||
|
safeStrHead1 : (s : String) -> {pr : NonEmpty (unpack s)} -> Char
|
||||||
|
safeStrHead1 s with (unpack s)
|
||||||
|
safeStrHead1 {pr} s | [] = absurd pr
|
||||||
|
safeStrHead1 s | (c::_) = c
|
||||||
|
|
||||||
|
safeStrHead2 : (s : String) -> {pr : NonEmpty (unpack s)} -> Char
|
||||||
|
safeStrHead2 s with (unpack s)
|
||||||
|
safeStrHead2 {pr=foo} s | [] = absurd foo
|
||||||
|
safeStrHead2 s | (c::_) = c
|
||||||
|
|
||||||
|
safeStrHead3 : (s : String) -> {pr : NonEmpty (unpack s)} -> Char
|
||||||
|
safeStrHead3 {pr=foo} s with (unpack s)
|
||||||
|
safeStrHead3 s | [] = absurd foo
|
||||||
|
safeStrHead3 s | (c::_) = c
|
||||||
|
|
||||||
|
safeStrHead4 : (s : String) -> {pr : NonEmpty (unpack s)} -> Char
|
||||||
|
safeStrHead4 {pr=foo} s with (unpack s)
|
||||||
|
safeStrHead4 {pr=bar} s | [] = absurd bar
|
||||||
|
safeStrHead4 s | (c::_) = c
|
||||||
|
|
1
tests/idris2/with001/expected
Normal file
1
tests/idris2/with001/expected
Normal file
@ -0,0 +1 @@
|
|||||||
|
1/1: Building Temp (Temp.idr)
|
3
tests/idris2/with001/run
Executable file
3
tests/idris2/with001/run
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
$1 --check Temp.idr
|
||||||
|
|
||||||
|
rm -rf build
|
Loading…
Reference in New Issue
Block a user