Verified Commit b2f995e0 authored by Camil Staps's avatar Camil Staps 🚀

Merge branch 'master' into ssl

parents 5f4beac4 e9a74224
test:
before_script:
- install_clean.sh bundle-complete && apt-get update -qq && apt-get install -y -qq build-essential
image: "camilstaps/clean:nightly"
script:
- make -C tests/linux64
# Standards
The following guidelines should be adhered to when developing libraries for the
Clean Platform library collection.
## What is the purpose of Clean Platform
Clean Platform was created to have a central place where commonly used
functionality was stored so that people didn't have to look for it. All the
functionality should be available on all platforms. This means that
functionality only working on Windows has no place here. It is allowed to
simulate functionality across systems. Examples of this is the System.Process
module that offers the same API across platforms.
## Type names
The names of types should be clear and informative, and should always start
with a capital. If the name of a type consists of multiple words, each new
word should start with a capital. Whenever the name is an abbreviation the
abbreviation should be written using only capitals (e.g. GUI,SQL,HTTP).
## Function names
Function names should be written in lowerCamelCase. By starting types and
constructors with a capital and functions without one, the difference between
a constructor and a function is immediately clear for the reader of a program.
Generic function names should normally start with `g`, and the next character
should be a capital.
## Module names
For modules, the same guidelines apply as for naming types. Names should be
informative and preferably short.
- When a library module is not meant for direct imports by end users, but
should only used by experts in modules that for example provide a more
friendly interface, you should prefix the name of that module with an
underscore character (`_`) or place it in a separate `Internal` submodule.
- When a module (mainly) provides generic functions for functionality that
could also be reasonably implemented differently, it should be prefixed with
`Gen`.
## Argument order
While there are no hard demands on the order in which you specify the arguments
of functions, there are two rules which make your functions easier to use and
somewhat more clear:
- State representing arguments such as the common `*World` type argument,
should be at the end of the argument list.
- Arguments which are used as "options" in some way should be at the beginning
of the arguments. This makes it easy to pass in options by currying.
## Comments
A concise description of the purpose of a function and the meaning of its
arguments and result should be present in the .dcl file for all exported
functions. The documentation should not be included in the .icl file for
maintainability. Comments are specified as follows:
```clean
/**
* This function is the identity.
* @param Some value
* @result The same value
*/
id :: a -> a
id x = x
```
Several JavaDoc like parameters are supported such as `@param`, `@result`,
`@type`, `@var` and `@representation`. More info about this can be found
[here](https://github.com/clean-cloogle/Cloogle#clean-documentation).
We use `@complexity` for the complexity order. Some other special fields are
used, like `@gin-icon`, but one should be reluctant with inventing new field
names. If there is a general use case, adding it can be discussed.
## Layout
- Tabs should be used for indentation. Spaces for alignment.
- The `where` keyword should be at the same level as the parent code block.
## Exporting functions and types
Definition modules (.dcl) must be very specific about the modules they import
because everything imported in a definition module is exported as well,
increasing the chance of name collisions. To minimize the chance for
collisions, adhere to the following conventions:
- Explicitly import the types and classes you need for specifying the type
signatures by using the `from ... import ...` notation.
- Only ever import an entire module with the `import ...` notation if you
really truly want to re-export the entire module.
Implementation modules may import anything they like.
## Implementing class instances and generic derives
The applicable instances for the _general_ classes should be exported in the module of the type and not of the class.
This means that for example the `Functor` instance of `Maybe` should be defined in `Data.Maybe` and not in `Data.Functor`.
For _specific_ classes the instances for types should be exported in submodules.
For example, `JSONEncode` for `Map` should be exported in `Data.Map.JSONEncode` and not in `Data.Map` nor in `Text.JSON`.
This rule also holds for types that have multiple valid instances such as the `Monoid` for `Int`.
_general_ classes are:
- [ ] `Functor` from `Data.Functor`
- [ ] `Monoid, Semigroup` from `Data.Monoid`
- [ ] `Monad` from `Control.Monad` and applicable monads from `Control.Monad.*`
- [ ] `Applicative, Alternative` from `Control.Applicative`
- [ ] `gEq{|*|}` from `Data.GenEq`
- [ ] `gDefault{|*|}` from `Data.GenDefault`
- [ ] `GenFDomain` from `Data.GenFDomain`
- [ ] everything from `StdOverloaded`
- [ ] ...
_specific_ classes are for example:
- [ ] `JSONEncode, JSONDecode` from `Text.JSON`
- [ ] ...
## OS/Architecture specific functionality
When implementing functionality that is OS or Architecture specific it is preferred to implement it for all platforms.
This means that it is preferred to define a common interface to offer the same functionality on all platforms.
However, this is not always possible (e.g. PseudoTTY support is not available on windows) and therefore this is not mandatory.
The following guidelines should be adhered to when developing libraries for the
Clean Platform library collection.
== Type names ==
The names of types should be clear and informative, and should always start
with a capital. If the name of a type consists of multiple words, each new
word should start with a capital. The constructors of a type should also start
with a capital. Whenever the name is an abbreviation the abbreviation should be
written using only capitals (e.g. GUI,SQL,HTTP).
== Function names ==
Function names should *not* start with a capital, but when a function consists
of multiple words, each new word, except the first one should start with a
capital. By starting types and constructors with a capital and, functions
without one, the difference between a constructor and a function is immediately
clear for the reader of a program.
== Module names ==
For modules, the same guidelines apply as for naming types. Names should be
informative and preferably short. When a library module is not meant for direct
imports by end users, but should only used by experts in modules that for
example provide a more friendly interface, you should prefix the name of that
module with an underscore character.
== Argument order ==
While there are no hard demands on the order in which you specify the arguments
of functions, there are two rules which make your functions easier to use and
somewhat more clear:
* State representing arguments such as the common *World type argument,
should be at the end of the argument list.
* Arguments which are used as "options" in some way should be at the
beginning of the arguments. This makes it easy to pass in options by
currying.
== Comments ==
A concise description of the purpose of a function and the meaning of its input
and output parameters should be present in the .dcl file for all exported
functions. Comments are specified as follows:
/**
* This function is the identity.
*/
id :: a -> a
id x = x
The @param notation (similar to JavaDocs) can be used to clarify the arguments
separately.
== Layout ==
Whitespace and new lines can be used to format code nicely. When tabs are used
for alignment, they are considered to be fixed to four spaces.
== Exporting functions and types ===
Definition modules (.dcl) must be very specific about the modules they import
because everything imported in a definition module is exported as well,
increasing the chance of name collisions. To minimize the chance for
collisions, adhere to the following conventions:
* Explicitly import the types and classes you need for specifying the type
signatures by using the "from ... import ..." notation.
* Only ever import an entire module with the "import ..." notation if you
really truly want to re-export the entire module.
Implementation modules can be more liberal with what they import.
......@@ -4,6 +4,7 @@ Environments
EnvironmentName: Clean Platform
EnvironmentPaths
Path: {Application}\Libraries\StdEnv
Path: {Application}\Platform\src\libaries\Platform-x86
Path: {Application}\Platform\src\libraries\OS-Independent
Path: {Application}\Platform\src\libraries\OS-Independent\Data
Path: {Application}\Platform\src\libraries\OS-Independent\Database
......
......@@ -4,6 +4,7 @@
Path: {Application}/lib/StdEnv
Path: {Application}/lib/Generics
Path: {Application}/lib/StdLib
Path: {Application}/lib/clean-platform/Platform-x86
Path: {Application}/lib/clean-platform/OS-Independent
Path: {Application}/lib/clean-platform/OS-Independent/Data
Path: {Application}/lib/clean-platform/OS-Independent/Database
......
......@@ -4,6 +4,7 @@
Path: {Application}/lib/StdEnv
Path: {Application}/lib/Generics
Path: {Application}/lib/StdLib
Path: {Application}/lib/clean-platform/Platform-x86
Path: {Application}/lib/clean-platform/OS-Independent
Path: {Application}/lib/clean-platform/OS-Independent/Data
Path: {Application}/lib/clean-platform/OS-Independent/Database
......
definition module System.OS
OS_NAME :== "Android (32-bit)"
OS_PATH_SEPARATOR :== '/'
OS_NEWLINE :== "\n"
IF_POSIX_OR_WINDOWS posix windows :== posix
IF_WINDOWS win other :== other
IF_WINDOWS32 win other :== other
IF_WINDOWS64 win other :== other
IF_POSIX posix other :== posix
IF_LINUX linux other :== linux
IF_LINUX32 linux other :== linux
IF_LINUX64 linux other :== other
IF_MAC mac other :== other
IF_ANDROID android other :== android
implementation module System.OS
definition module System._Platform
import System.Platform
CURRENT_PLATFORM :== Android32
implementation module System._Platform
definition module System.OS
OS_NAME :== "Android (64-bit)"
OS_PATH_SEPARATOR :== '/'
OS_NEWLINE :== "\n"
IF_POSIX_OR_WINDOWS posix windows :== posix
IF_WINDOWS win other :== other
IF_WINDOWS32 win other :== other
IF_WINDOWS64 win other :== other
IF_POSIX posix other :== posix
IF_LINUX linux other :== linux
IF_LINUX32 linux other :== other
IF_LINUX64 linux other :== linux
IF_MAC mac other :== other
IF_ANDROID android other :== android
implementation module System.OS
definition module System._Platform
import System.Platform
CURRENT_PLATFORM :== Android64
implementation module System._Platform
definition module System._Posix
from System._Pointer import :: Pointer
from StdInt import IF_INT_64_OR_32
from System.Time import :: Tm
WNOHANG :== 0x00000001
WUNTRACED :== 0x00000002
MAXPATHLEN :== 1024
DIRENT_D_NAME_OFFSET :== 19
S_IFMT :== 0170000
S_IFIFO :== 0010000
S_IFCHR :== 0020000
S_IFDIR :== 0040000
S_IFBLK :== 0060000
S_IFREG :== 0100000
S_IFLNK :== 0120000
S_IFSOCK :== 0140000
S_IFWHT :== 0160000
STDIN_FILENO :== 0
STDOUT_FILENO :== 1
STDERR_FILENO :== 2
FIONREAD :== 0x541B
F_SETFD :== 2
O_CLOEXEC :== 02000000
//Posix API calls
errno :: !*w -> (!Int,!*w)
strerr :: !Int -> Pointer
stat :: !{#Char} !{#Char} !*w -> (!Int,!*w)
unlink :: !{#Char} !*w -> (!Int,!*w)
fork :: !*w -> (!Int,!*w)
execvp :: !{#Char} !{#Pointer} !*w -> (!Int,!*w)
waitpid :: !Int !{#Int} !Int !*w -> (!Int,!*w)
exit :: !Int !*w -> (!.a,!*w)
getcwd :: !{#Char} !Int !*w -> (!Pointer,!*w)
chdir :: !{#Char} !*w -> (!Int,!*w)
mkdir :: !{#Char} !Int !*w -> (!Int,!*w)
rmdir :: !{#Char} !*w -> (!Int,!*w)
rename :: !{#Char} !{#Char} !*w -> (!Int,!*w)
opendir :: !{#Char} !*w -> (!Pointer,!*w)
closedir :: !Pointer !*w -> (!Int,!*w)
readdir :: !Pointer !*w -> (!Pointer,!*w)
pipe :: !Pointer !*w -> (!Int, !*w)
dup2 :: !Int !Int !*w -> (!Int, !*w)
close :: !Int !*w -> (!Int, !*w)
ioctl :: !Int !Int !Pointer !*w -> (!Int, !*w)
// variant requiring an argument as third parameter
fcntlArg :: !Int !Int !Int !*w -> (!Int, !*w)
read :: !Int !Pointer !Int !*w -> (!Int, !*w)
write :: !Int !{#Char} !Int !*w -> (!Int, !*w)
select_ :: !Int !Pointer !Pointer !Pointer !Pointer !*w -> (!Int, !*w)
kill :: !Int !Int !*w -> (!Int, !*w)
//Memory (impure)
malloc :: !Int -> Pointer
free :: !Pointer -> Int
freeSt :: !Pointer !*w -> *w
memcpy_string_to_pointer :: !Pointer !{#Char} !Int -> Pointer
//Posix datastructures
:: Stat =
{ st_dev :: !Int
, st_ino :: !Int
, st_mode :: !Int
, st_nlink :: !Int
, st_uid :: !Int
, st_gid :: !Int
, st_rdev :: !Int
, st_size :: !Int
, st_blocks :: !Int
, st_blksize :: !Int
, st_ctimespec :: !Int
, st_mtimespec :: !Int
, st_atimespec :: !Int
}
//Mapping to/from byte arrays
unpackStat :: !{#Char} -> Stat
sizeOfStat :: Int
implementation module System._Posix
import System._Pointer, System.Time
import StdInt
errno :: !*w -> (!Int,!*w)
errno world = (getErrno,world)
where
getErrno :: Int
getErrno = readInt4S errnoAddr 0
errnoAddr :: Pointer
errnoAddr = code {
ccall __errno ":p"
}
strerr :: !Int -> Pointer
strerr world = code {
ccall strerror "I:p"
}
stat :: !{#Char} !{#Char} !*w -> (!Int,!*w)
stat path buf world = code {
ccall stat "ss:I:A"
}
unlink :: !{#Char} !*w -> (!Int,!*w)
unlink path world = code {
ccall unlink "s:I:A"
}
fork :: !*w -> (!Int,!*w)
fork world = code {
ccall fork ":I:A"
}
execvp :: !{#Char} !{#Pointer} !*w -> (!Int,!*w)
execvp name argv world = code {
ccall execvp "sA:I:A"
}
waitpid :: !Int !{#Int} !Int !*w -> (!Int,!*w)
waitpid pid status_p options world = code {
ccall waitpid "IAI:I:A"
}
exit :: !Int !*w -> (!.a,!*w)
exit num world = code {
ccall exit "I:p:A"
}
getcwd :: !{#Char} !Int !*w -> (!Pointer,!*w)
getcwd buf size_t world = code {
ccall getcwd "sI:p:A"
}
chdir :: !{#Char} !*w -> (!Int,!*w)
chdir name world = code {
ccall chdir "s:I:A"
}
mkdir :: !{#Char} !Int !*w -> (!Int,!*w)
mkdir name mode world = code {
ccall mkdir "sI:I:A"
}
rmdir :: !{#Char} !*w -> (!Int,!*w)
rmdir name world = code {
ccall rmdir "s:I:A"
}
rename :: !{#Char} !{#Char} !*w -> (!Int,!*w)
rename old new world = code {
ccall rename "ss:I:A"
}
opendir :: !{#Char} !*w -> (!Pointer,!*w)
opendir path world = code {
ccall opendir "s:p:A"
}
closedir :: !Pointer !*w -> (!Int,!*w)
closedir dir world = code {
ccall closedir "p:I:A"
}
readdir :: !Pointer !*w -> (!Pointer,!*w)
readdir dir world = code {
ccall readdir "p:p:A"
}
pipe :: !Pointer !*w -> (!Int, !*w)
pipe arr world = code {
ccall pipe "p:I:A"
}
dup2 :: !Int !Int !*w -> (!Int, !*w)
dup2 old new world = code {
ccall dup2 "II:I:A"
}
close :: !Int !*w -> (!Int, !*w)
close fd world = code {
ccall close "I:I:A"
}
ioctl :: !Int !Int !Pointer !*w -> (!Int, !*w)
ioctl fd op ptr world = code {
ccall ioctl "IIp:I:A"
}
fcntlArg :: !Int !Int !Int !*w -> (!Int, !*w)
fcntlArg fd op arg world = code {
ccall fcntl "III:I:A"
}
read :: !Int !Pointer !Int !*w -> (!Int, !*w)
read fd buffer nBuffer world = code {
ccall read "IpI:I:A"
}
write :: !Int !{#Char} !Int !*w -> (!Int, !*w)
write fd buffer nBuffer world = code {
ccall write "IsI:I:A"
}
select_ :: !Int !Pointer !Pointer !Pointer !Pointer !*w -> (!Int, !*w)
select_ nfds readfds writefds exceptfds timeout world = code {
ccall select "Ipppp:I:A"
}
kill :: !Int !Int !*w -> (!Int, !*w)
kill pid sig world = code {
ccall kill "II:I:A"