Owl Lisp is a simple programming language. The main motivation for writing it was to get a portable system for writing standalone programs in a subjectively pleasant dialect of LISP, which in this case means a minimal core language and runtime, purely functional operation and support for asynchronous evaluation.
#index
It should be easy to get owl up and running on most somewhat
POSIX-compliant systems, such as Linux, any BSD. Owl is mainly developed
on OpenBSD and Linux. You should have make
and a working
C-compiler installed. For example in Debian-based Linux distributions
you can use:
$ sudo apt-get install gcc
You may also need git
and make
if you
download the sources from git or want to build the full source
package.
The easiest option is to download the current precompiled C-version
of ol
, and compile with with a C-compiler. Ol is the
standalone repl and compiler, which also has the builtin libraries
described in this manual.
$ curl https://haltp.org/files/ol-0.2.c.gz \
| gzip -d \
| gcc -x c -O2 -o ol -
$ ./ol
You see a prompt.
> (cons 1 (list 2))
'(1 2)
> ,quit
bye bye _o/~
This version of ol is compiled with no C-code optimizations, so the resulting C-code is small and takes very little time and resources to compile. Alternatively you can download all of the sources and make a traditional install.
$ git clone https://gitlab.com/owl-lisp/owl.git
$ cd owl-lisp
$ make
If you just built ol, you can use it from wherever convenient. Usually it is convenient to put such binaries to your home directory under bin/ -directory.
You can install owl and the manual pages with
sudo make install
after building the sources or a release
tarball.
When started, owl greets the user is ready to proceed evaluating terms given to it. This is the REPL, or read-eval-print -loop familiar from many modern programming languages.
$ ol
You see a prompt.
> (+ 1 2)
3
> (reverse (list 1 2 3))
'(3 2 1)
>
You can exit owl by pressing Ctrl-d, denoting end of input in UNIX,
asking the REPL to exit via ,quit
, or by asking the thread
scheduler to stop everything with (halt 1)
.
Compiler mode can be tested for example by doing
$ echo '(lambda (args) (print "hello world") 0)' \
| ol -x c -o - \
| gcc -x c -o test -
$ ./test
hello world
It is possible to write fairly large programs using just definitions, but they tend to become rather messy and hard to maintain. Programming languages usually allow encapsulating related definitions into a library or a module, which has a well defined interface or interfaces. In Owl these are called libraries. R7RS Scheme happened to defined modules almost exactly equally to Owl libraries, so the naming was converted to match that of R7RS.
A library consists of a listing of other libraries it depends on, a listing of values it intends to expose, and the actual definitions of the values. A simple library can be defined and imported for use as follows:
(define-library (my test) (import (owl base)) ;; required (export barrow barrow?) (begin (define barrow (cdr '(wheel barrow))) (define barrow? (lambda (x) (eq? x barrow))))) ;; Library (my test) added (import (my test)) ;; Imported (my test) barrow '(barrow) (barrow? barrow) #true
Owl contains a number of builtin libraries. Some of them are general
purpose ones and others were mainly needed to implement the repl and
compiler. A listing of readily available libraries can be shown with the
,libraries
REPL command. They are also available in the
*libraries*
variable, which is an association list of
library names and the values of the library. There is nothing magical
about libraries - they are just values like everything else.
Libraries can be defined anywhere at toplevel whether using the REPL
or loading files. There is however a naming and loading convention,
which makes it easier to load libraries. If a library
(foo bar baz)
is to be imported and one is not already
loaded, then Owl will automatically attempt to load it from
foo/bar/baz.scm
.
If you know the name of the value you would like to import, but don't
know the library, then you can search for if using
,find
.
,find stat ,find stat current toplevel: state, type-thread-state (owl base): type-thread-state (owl sys): stat (owl core): type-thread-state (import (owl sys)) ;; Imported (owl sys) (assoc 'size (stat "Makefile" #t)) '(size . 3869)
Randomness is an interesting thing to work with in a purely functional setting. Owl builds randomness around streams of typically deterministically generated 24-bit fixnums. These are usually called rands in the code.
A function involving randomness typically receives a rand stream, and also returns one after possibly consuming some rands. Behavior like this would be easy to hide using macros or monadic code, but Owl generally strives to be explicit and simple, so the rand streams are handled just like any other value.
(define rs (seed->rands 9)) (rand rs 10000) '(values ##
3942) ;; the new rand stream and 3942 (lets ((rs a (rand rs 10000))) a) 3942 (lets ((rs elem (rand-elem rs '(a b c d e f g)))) elem) 'c (lets ((rs sub (rand-subset rs '(a b c d e f g)))) sub) '(b e f) (lets ((rs perm (random-permutation rs '(a b c d e f g)))) perm) '(g e c b d a f) (lets ((rs ns (random-numbers rs 100 10))) ns) '(95 39 69 99 2 98 56 85 77 39)
Exported values:
lcg-rands
seed->rands
rands->bits
seed->bits
rands->bytes
seed->bytes
rand
rand-nbit
rand-log
rand-elem
rand-subset
rand-range
random-numbers
reservoir-sample
shuffle
random-permutation
random-subset
rand-occurs?
Association lists a lists of pairs of keys and their corresponding values. This library has functions for processing them following the naming and argument order conventions used also in other data structures.
Exported values:
alist
alkeys
alvalues
setq
getq
delq
edq
ednq
alset
alget
aldel
aled
aledn
Examples:
al
= '((a . 1) (b . 2))
(alget al 'b #false)
= 2
(alget al 'x #false)
= #false
(alset al 'b 20)
=
'((a . 1) (b . 20))
(alset al 'c 3)
=
'((a . 1) (b . 2) (c . 3))
(pipe al (aldel 'a) (aldel 'b) (alset 'x 10))
=
'((x . 10))
(alkeys (alist 'a 1 'b 2))
= '(a b)
(alvalues (alist 'a 1))
= '(1)
Exported values:
string->symbol
symbol?
symbol=?
symbol->string
render-symbol
##false Exported values:
equal?
eqv?
##false Exported values:
vector
vector?
list->vector
make-vector
vector->list
vector-length
vector-ref
vref
vector-set
vset
vector-snoc
vector-map
vector-iter
vector-iterr
vector-fold
vector-foldr
vector-zip
vector-append
Examples:
(vector? (vector 1 2 3))
= #true
(vector? (vector))
= #true
(vector? (list 1 2 3))
= #false
(vector-ref (vector 1 2 3) 0)
= 1
(vector-ref (vector 1 2 3) 1)
= 2
(vector-ref (vector 1 2 3) 2)
= 3
(vector-ref (make-vector 100 42) 99)
=
42
(vector-snoc (vector) 42)
=
(vector 42)
(vector-snoc (vector 42) 43)
=
(vector 42 43)
(vector-snoc (vector 42 43) 44)
=
(vector 42 43 44)
(vector-append (vector 1 2 3) (vector 4 5 6))
=
(vector 1 2 3 4 5 6)
(vector-zip + (vector 1 2 3) (vector 10 20 30))
=
(vector 11 22 33)
(vector->list (list->vector '()))
=
'()
(vector->list (list->vector '(a)))
=
'(a)
(vector->list (list->vector '(a b)))
=
'(a b)
(vector->list (list->vector (iota 0 1 100)))
=
(iota 0 1 100)
(vector-map (λ (x) (+ x 10)) (vector 1 2 3))
=
(vector 11 12 13)
(vector-fold - 0 (vector 1 2 3 4))
=
(fold - 0 (list 1 2 3 4))
(vector-foldr - 0 (vector 1 2 3 4))
=
(foldr - 0 (list 1 2 3 4))
(vset (vector 1 2 3) 0 42)
=
(vector 42 2 3)
(vset (vector 1 2 3) 1 42)
=
(vector 1 42 3)
(vset (vector 1 2 3) 2 42)
=
(vector 1 2 42)
##false Exported values:
set-terminal-rawness
font-normal
font-bright
font-dim
font-standard
font-reverse
font-attrs
font-gray
font-fg-black
font-fg-red
font-fg-green
font-fg-yellow
font-fg-blue
font-fg-magenta
font-fg-cyan
font-fg-white
font-bg-black
font-bg-red
font-bg-green
font-bg-yellow
font-bg-blue
font-bg-magenta
font-bg-cyan
font-bg-white
clear-screen
clear-screen-top
clear-screen-bottom
clear-line
clear-line-left
clear-line-right
set-cursor
cursor-hide
cursor-show
cursor-save
cursor-restore
cursor-up
cursor-down
cursor-left
cursor-right
enable-line-wrap
disable-line-wrap
set-scroll-range
scroll-up
scroll-down
cursor-pos
terminal-input
get-terminal-size
get-cursor-position
This library implements hygienic macro expansion. The role of this library is to construct the transformer functions out of the usual define-syntax definitions as specified in R7RS Scheme.
A macro mainly consists of a set of patterns to be tried on code. If one of them matches, then the corresponding rewrite rule is used to transform the expression. A pattern may contain literals, which means symbols that must be the same as in the pattern, and rest are generally used as syntax variables. A syntax variable is matched to any expression in the input expression, and it can be used to place the expression somewhere in the rewrite rule.
It is also possible to use the ellipsis pattern, denoted by suffixing a pattern ..., which means that the pattern may occur zero or more times. Suffixing a syntax variable bound within such pattern with ... in the rewrite rule causes all of the matches to be added.
Exported values:
define-syntax
new-define-syntax-transformer
Examples:
(new-ellipsis '() 1)
= '(())
(new-ellipsis '(a) 1)
= '(a ())
(new-ellipsis '(a (b c)) 2)
=
'(a (b c ()))
(push-ellipsis '(a b c) 1 'd)
=
'(a b c d)
(push-ellipsis '((a b) (c d)) 2 'e)
=
'((a b) (c d e))
(test-rewrite '(a b c) '((a bound . A) (b bound . B) (c literal)))
= '(ok (A B c))
(test-rewrite '(a ... b ...) '((a 1 a1 a2 a3) (b 1 b1 b2 b3)))
= '(ok (a1 a2 a3 b1 b2 b3))
(test-rewrite '((a b) ...) '((a 1 a1 a2 a3) (b 1 b1 b2 b3)))
= '(ok ((a1 b1) (a2 b2) (a3 b3)))
(test-rewrite '((a b ...) ...) '((a 1 a1 a2) (b 2 (b1 b2 b3) (B1 B2 B3))))
= '(ok ((a1 b1 b2 b3) (a2 B1 B2 B3)))
(test-rewrite '(x y z) '())
=
'(ok (g1 g2 g3))
(test-rewrite '(x (a x) ... x) '((a 1 a1 a2)))
=
'(ok (g1 (a1 g1) (a2 g1) g1))
(test-rewrite '((a x) ...) '((a 1 a1 a2)))
=
'(ok ((a1 g1) (a2 g2)))
(xlet ((foo 100)) foo)
= 42
(xlet ((a 1) (b 2)) (list a b))
=
'(1 2)
Exported values:
sort
isort
quicksort
mergesort
Examples:
(sort < '(1 4 2))
= '(1 2 4)
(sort (λ (a b) (< (>> a 1) (>> b 1))) '(2 8 4 9 0 5 1 6 7 3 5))
= '(0 1 2 3 4 5 5 6 7 8 9)
It is sometimes useful to get symbols, which do not occur elsewhere. This is typically needed in the compiler, but it may also be needed elsewhere. Gensyms in Owl are just regular symbols, which do not occur in a given expression. This requires walking through the whole expression. To avoid having to walk the original expression in many cases when gensyms are needed, they work in a way that ensures that the gensym of the gensym of an expression also does not occur in the original expression.
Exported values:
fresh
gensym
Examples:
(gensym 'x)
= 'g1
(gensym 'g1)
= 'g2
(gensym '(lambda (x) x))
= 'g1
(gensym '(lambda (g10) g11))
= 'g12
##false Exported values:
λ
syntax-error
begin
quasiquote
letrec
let
letrec*
if
when
unless
cond
case
define
define*
prod
lets
or
and
list
ilist
tuple
tuple-case
define-library
case-lambda
define-values
define-record-type
_record-values
B
C
H
I
K
self
null?
type-complex
type-rational
type-int+
type-int-
type-record
immediate?
allocated?
raw?
record?
partial
pipe
piper
if-lets
type-bytecode
type-proc
type-clos
type-fix+
type-fix-
type-pair
type-vector
type-bytevector
type-tuple
type-symbol
type-const
type-port
type-string
type-string-wide
type-string-dispatch
type-thread-state
not
maybe
primops
primop-name
special-bind-primop?
run
set-ticker
next-thread
bind
mkt
halt
wait
set-memory-limit
get-word-size
get-memory-limit
apply
call/cc
lets/cc
call/cc
call/cc2
call/cc3
create-type
object-size
object->list
len
opcode-arity-ok?
This library exports the usual arbitrary precision bignum arithmetic functions. You can also use specific (owl math X) libraries if necessary.
Exported values:
quotient
<<
>>
band
bior
bxor
fx-width
ncar
ncdr
fx-greatest
zero?
integer?
truncate/
even?
odd?
fixnum?
ediv
numerator
denominator
gcd
(exports (owl math complex))
(exports (owl math extra))
This library attempts to implement date processing functions. Dates are typically represented as seconds or milliseconds since UNIX Epoch 1.1.1970. These are generally a good way to work with time, apart from assuming that an absolute time exists at all. Sometimes it is however necessary to convert time stamps to human readable form, which means splitting the time according to various more and less sensible rules.
(time) → current time in seconds since epoch
(time-ms) → current time in milliseconds since epoch
(date-str 0) → "00:00:00 1.1.1970 UTC+00:00"
(date-str (time) 2.5) → "20:49:08 19.3.2018 UTC+02:30"
(date 0) → 1 1 1970 0 0 0 ; as multiple values
(leap-year? 2018) → ##false
Exported values:
date
leap-year?
valid-date?
next-date
next-date-with-week
week-info
day-names-fi
day-names-en
date-str
date-str-tz
year-start-day
year-start-week-info
minute
hour
day
week
year
leap-year
Examples:
(next-date-with-week 31 12 1971 5 1)
=
(values 1 1 1972 6 1)
(next-date-with-week 27 12 1970 7 52)
=
(values 28 12 1970 1 53)
(date-str 0)
=
"00:00:00 1.1.1970 UTC+00:00"
(date-str 0 5/2)
=
"02:30:00 1.1.1970 UTC+02:30"
(date-str 1234567890 0)
=
"23:31:30 13.2.2009 UTC+00:00"
This library implements a simple DSL for constructing parsing functions. The operation is traditional top down backtracking parsing.
Exported values:
parses
byte
imm
imm-or
input-ready?
seq
epsilon
ε
byte-if
peek-byte
rune
rune-if
either
either!
one-of
one-of!
star
plus
greedy-star
star!
greedy-plus
plus!
byte-between
parse-head
backtrack
parse
try-parse
first-match
word
maybe
error-context
syntax-error?
report-syntax-error
byte-stream->exp-stream
fd->exp-stream
file->exp-stream
silent-syntax-fail
syntax-errors-as-values
resuming-syntax-fail
Examples:
(first-match byte '(1 2) 'x)
=
(values 1 '(2) 1)
(first-match (imm 42) '(1 2) 'x)
=
(values 'x '(1 2) 0)
(first-match (seq (imm 1) (imm 2)) '(1 1 2) 'x)
=
(values 'x '(1 1 2) 1)
(first-match (seq (imm 1) (imm 1)) '(1 1 2) 'x)
=
(values '(1 . 1) '(2) 2)
(first-match (star (imm 1)) '(1 1 1 2) 'x)
=
(values '(1 1 1) '(2) 3)
(first-match (plus (byte-if even?)) '(2 4 8 9) 'x)
=
(values '(2 4 8) '(9) 3)
(first-match (plus (one-of (imm 1) (imm 2))) '(1 2 3 4) 'x)
= (values '(1 2) '(3 4) 2)
(first-match (either (seq (imm 1) (imm 1)) (seq (imm 1) (imm 2))) '(1 2 3) 'x)
= (values '(1 . 2) '(3) 2)
(first-match (either (seq (imm 1) (imm 1)) (seq (imm 1) (imm 2))) '(1 3 4) 'x)
= (values 'x '(1 3 4) 1)
(first-match (either (seq (imm 1) (imm 2)) (seq (imm 3) (imm 4))) '(1 3 4) 'x)
= (values 'x '(1 3 4) 1)
(first-match (either! (seq (imm 1) (imm 1)) (seq (imm 1) (imm 2))) '(1 2 3) 'x)
= (values 'x '(1 2 3) 1)
(first-match (either! (seq (imm 2) (imm 1)) (seq (imm 1) (imm 2))) '(1 2 3) 'x)
= (values '(1 . 2) '(3) 2)
(first-match (either! (seq (imm 2) (imm 1)) (seq (imm 1) (imm 2))) '(2 2 3) 'x)
= (values 'x '(2 2 3) 1)
(first-match (seq (imm 1) (peek-byte (λ (x) (eq? x 2)))) '(1 2 3) 'x)
= (values '(1 . 2) '(2 3) 1)
(first-match (seq (imm 1) (peek-byte (λ (x) (eq? x 2)))) '(1 3 3) 'x)
= (values 'x '(1 3 3) 1)
##false Exported values:
eof-object
eof-object?
This library writes programs in formats required from the compiler. Programs can currently be written as FASL-encoded bytecode for use in the VM, or a mixture of C and FASL when compiling to C.
Exported values:
make-compiler
dump-fasl
load-fasl
suspend
Tuples are an early simple data structure for holding multiple values. Values are indexed from 1 and there is little error detection apart from range checks.
(define x (list->tuple '(a b c))) (ref x 1) 'a (size x) 3 (equal? x (tuple 'a 'b 'c)) ##true
Exported values:
tuple?
tuple-length
list->tuple
tuple->list
Evaluation ties together all the separate compiler passes. Each pass is a function of the environment in which the evaluation occurs and the current version of the expression to be evaluated, and returns either an error or next versions of the environment and expression. In the final step the expression has been converted to a thunk, meaning a function of zero arguments, which when called gives the value(s) of the expression.
Evaluate returns the success/failure data structure used in the compiler. Exported-eval is the function typically found at toplevel as eval.
Exported values:
evaluate
exported-eval
Examples:
(evaluate '(car '(11 22)) *toplevel*)
=
(ok 11 *toplevel*)
(exported-eval '(car '(11 . 22)) *toplevel*)
=
11
##false Exported values:
bytecode?
function?
procedure?
Random access lists are a data structure similar to lists, but with very different efficiency characteristics. A regular list built out of cons cells is an optimal solution, if one needs to work mainly with the initial elements or the whole list at a time. However, if you need to frequently find and maybe update values in the middle of the list, you have to perform operations taking time proportional to the length of the list. In other words, those list operations are linear time, or have complexity O(n). Cons, car and cdr on the other hand are very efficient for regular lists. Regardless of the size of a list, it will always take a fixed amount of time to add, take or remove a value from it. In other words, these operations are constant time, or have complexity O(1).
A random access list is a data structure, which unsurprisingly attempts to make random access and update efficient.
The performance characteristics of this random access list library are:
car → O(1) cdr → O(log n) cons → O(log n) get → O(log n) set → O(log n) len → O(log n) fold → O(n) foldr → O(n) map → O(n) rlist->list → O(n) append → O(n log n) del → O(n log n) list->rlist → O(n log n)
The operation is based on two ideas. Firstly, a random access list consists of a sequence of complete binary trees. The binary trees are built out of cons cells when needed. The first tree is always of height 1, meaning it just holds the value, much like a regular cons cell. The next node always holds a binary tree either of the same or next height. There can be at most two trees of the same height next to each other. Therefore, tree heights (1 1), (1 2 4) and (1 1 2 4 4) are valid, whereas (1 1 1), (2 2 4) and (1 2 2 8) are not. (5) is right out.
Secondly, trees can be addressed directly with bits. It takes a n-bit number to address each node of a complete binary tree of height n. Finding a value from a random access list works here by first finding the tree in which the value is held, and then using the remaining bits to find the correct leaf node in the tree.
It is easy to see that it takes O(log n) steps to find the tree in which some particular value is held, and then another O(log n) steps to walk the tree to a given position, Threfore we have a total complexity of O(log n) for access and update.
Exported values:
rnull
rcons
rget
rcar
rcdr
rset
rlen
rmap
rdel
rlist
rfold
rfoldr
rnull?
rpair?
rreverse
rappend
list->rlist
rlist->list
Examples:
rla
= (rlist 1 2)
rlb
= (rlist 3 4 5)
(rcar (rcons 11 rnull))
= 11
(rget rnull 100 'no)
= 'no
(rget rla 1 'no)
= 2
(rget rla 9 'no)
= 'no
(rnull? (rcdr (rcons 11 rnull)))
=
#true
(rlen (rcons 11 rnull))
= 1
(rlist->list (rlist 11 22 33))
=
'(11 22 33)
(rmap dup (rlist 10 20 30))
=
(rlist 20 40 60)
(rappend rla rlb)
= (rlist 1 2 3 4 5)
(rdel rlb 0)
= (rlist 4 5)
(rdel rlb 2)
= (rlist 3 4)
You cannot mutate values, but threads can also be used to encapsulate state. This library introduces seemingly variable values implemented threads which allow setting and getting a value.
Exported values:
make-variable
link-variable
This library makes it easy to write tools which use standard UNIX command line tool conventions. These include accepting short and long versions of flags (-h or --help), packing multiple flags to a single short one (-hai = -h -a -i), handling and verification of flag arguments (--output foo.txt), ability to handle flags anywhere in the arguments and use of -- to stop command line argument processing (e.g. to remove a file called -h or process globbed files coming from an untrusted source).
Exported values:
process-arguments
format-rules
print-rules
parse-args
cl-rules
##false Exported values:
equal-prim?
simple-equal?
These common list processing operations require numbers, so they are defined in a library loaded after math code.
Exported values:
lset
ldel
lget
length
led
ledn
lins
lref
take
drop
iota
list-ref
list-tail
make-list
split-at
has?
hasq?
Examples:
(lref '(a b c) 1)
= 'b
(lset '(a b c) 1 'x)
= '(a x c)
(ldel '(a b c) 1)
= '(a c)
(led '(1 2 3) 1 (C * 10))
= '(1 20 3)
(ledn '(1 2 3) 1 (H cons 'x))
=
'(1 x 2 3)
(lins '(a b c) 1 'x)
= '(a x b c)
(length '(a b c))
= 3
(take '(a b c) 2)
= '(a b)
(take '(a) 100)
= '(a)
(drop '(a b c) 2)
= '(c)
(drop '(a) 100)
= '()
(iota 0 1 5)
= '(0 1 2 3 4)
(iota 10 -2 0)
= '(10 8 6 4 2)
(list-tail '(a b c) 1)
= '(b c)
(make-list 3 'x)
= '(x x x)
(split-at '(a b c d) 2)
=
(values '(a b) '(c d))
##false Exported values:
repl-file
repl-port
repl-string
repl-ui
repl
print-repl-error
bind-toplevel
*owl-kernel*
Typical Scheme programs have a continuation underlying in the execution of a program. Depending on the implementation strategy, this continuation may be an actual variable and a function, as is done in Owl, or it is simulated on a more traditional stack-based system.
Owl has in fact two continuations. One is the normal one you can capture with call-with-current-continuation, but the other one is threaded even through that operation. The other continuation is that of the thread scheduler. It is also a regular program. The main difference in it is that it is expected to be called initially when the program starts and the second continuation is blank. Calling the blank continuation is what eventually terminates the whole program.
Normal progams can only access the second continuation via a primop. When this happens, the continuation of the running program is captured as usual, and the second thread scheduler continuation is called with the information provided in the call along with the continuation allowing resuming execution of the program from the corresponding state.
Wrappers for calling the thread scheduler are defined in this library.
Exported values:
syscall
error
interact
accept-mail
wait-mail
next-mail
check-mail
exit-owl
release-thread
catch-thread
set-signal-action
single-thread?
kill
link
mail
return-mails
fork-named
exit-thread
exit-owl
poll-mail-from
start-profiling
stop-profiling
running-threads
library-exit
thread
thunk->thread
This library is for converting potentially large integers to more readable and less accurate form. Mainly used for formatting output of ,time repl command.
Exported values:
format-time
format-number
format-number-base2
Examples:
(format-time 123456789)
= "123.45ms"
(format-number 4096)
= "4.09k"
(format-number-base2 4096)
= "4.00K"
Exported values:
abs
floor
sum
product
render-number
log2
log
lcm
negative?
positive?
number?
exact-integer-sqrt
isqrt
sqrt
sqrt-n
expt
expt-mod
round
ncr
npr
truncate
modulo
remainder
ceiling
!
factor
prime?
real?
complex?
rational?
primes-between
totient
phi
divisor-sum
divisor-count
dlog
dlog-simple
fib
histogram
negative?
min
max
minl
maxl
<
>
>=
<=
+
-
*
/
bisect
Examples:
(round 10000000000001/10000000000000)
=
1
(round 14999999999999/10000000000000)
=
1
(round 3/2)
= 2
(round -7/5)
= -1
(round -3/2)
= -2
(round -1/2)
= -1
(round 30864/25)
= 1235
(round 9/5)
= 2
(round -9/5)
= -2
(round -149/100)
= -1
(round 199999999999999999999999/10000000000000000000000)
= 20
(floor 199999999999999999999999/100000000000000000000000)
= 1
(ceiling 1/100000000000000000000000)
=
1
(bisect (lambda (x) (< x 0)) 0 10)
=
#false
(bisect (lambda (x) (< x 4)) 0 10)
=
3
(bisect (lambda (x) (< x 100)) 0 10)
=
9
(bisect (lambda (x) (< (* x 13) 123456789)) 0 123456789)
= (quotient 123456789 13)
(sqrt-n (expt 8 5) 5)
= 8
(expt 27 -2/3)
= 1/9
(expt 27 2/3)
= 9
(expt 1283918464548864 1/2)
=
35831808
This library defines arbitrary precision rational arithmetic operations. A rational number p/q is a typed pair of arbitrary precision integers. A valid rational number has q != 0, q != 1, and gcd(p, q) = 1.
Exported values:
mk-rational-add
mk-rational-sub
+
-
<
/
gcd
gcdl
rational
numerator
denominator
divide
negate
This library defines complex arbitrary precision math functions.
Exported values:
+
-
*
/
=
complex
negate
conjugate
Examples:
(conjugate 1+2/3i)
= 1-2/3i
(+ 1/2+1/10i 1/2+9/10i)
= 1+i
This library defines arbitrary precision integer arithmetic. Some of the functions are only defined for integers, whereas others are typically extended to handle also more complex kinds of numbers.
Operations to be extended typically export both the operation defined for integers only, and a corresponding mk-* version which calls a given function in case the types of inputs are not integers. This allows extending the definitions in other modules while checking the most common cases of integer inputs in first branches.
Owl currently has 24 bits available for encoding a value in an immediate value, meaning a value that does not have to be stored in memory. A fixnum, or a fixed size number, is a number which fits these bits. The sign of a fixnum is stored the type of the immediate object.
When a number is bigger or smaller than the maximum fixnum, it is converted to an allocated integer. In this case the representation of the number is a linked sequence of base 2²⁴ digits of the number starting from the least significant digits. Again the sign of the number is stored in the type.
A valid fixnum zero is always positive, and a valid negative bignum has the negative type only at the root node.
Exported values:
fixnum?
integer?
mk-add
mk-sub
+
-
*
=
/
<<
<
<=
=
>=
>
>>
band
bior
bxor
quotient
ediv
truncate/
nat-succ
big-bad-args
negate
ncar
ncdr
even?
odd?
zero?
negative?
positive?
rem
mod
ncons
ncar
ncdr
big-digits-equal?
fx-greatest
fx-width
to-int-
to-int+
to-fix+
to-fix-
add-big
sub-big
right-out
The digest library provides functions for computing cryptographic signatures. Currently SHA1 and SHA256 digests and corresponding message authentication codes are supported.
The hash functions also have hasher-raw
and
hasher-bytes
-variants, which return the state words and
raw signature bytes correspondingly.
(sha1 data) → hash-string (sha256 data) → hash-string (hmac-sha1 key message) → hash-string (hmac-sha256 key message) → hash-string
Exported values:
sha1
sha1-raw
sha1-bytes
sha256
sha256-raw
sha256-bytes
hmac-sha1
hmac-sha1-bytes
hmac-sha256
hmac-sha256-bytes
Examples:
(sha1 "")
=
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
(sha1 "owl")
=
"2c730e3a6d2aad6d914872e45f868d20a543570a"
(sha1 "λä.ä")
=
"726f4307d91f7433472917b27c486c9004b94179"
(sha256 "")
=
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
(sha256 "owl")
=
"10f7127b980b0b06ef157749f8a02cca1c606d22642729dcb2d24dbbf2bf30df"
(sha256 "λä.ä")
=
"e5b21ee5e8532c8f768aa9090ba71ae799016a0904b13ee855cbd58853464234"
The goal of this library is to make writing software tests and documenting functionality as simple as possible. The operation is as follows:
Tests run automatically when a program or library is loaded. Failure aborts the load via an error.
Tests can be collected from code automatically for documentation.
To add tests, import (owl proof) and write an example.
(import (owl proof)) (example (+ 1 2) = (+ 2 1) (car (cons 1 2)) = 1)
Exported values:
theorem
theorem-equal?
example
Examples:
1
= 1
(cons 1 2)
= '(1 . 2)
(values 1 2)
= (values 1 2)
Number stores (radix trees with a ff at each node)
Exported values:
iget
iput
ifold
iff->list
iempty
This library defines the thread controller. It handles activation, suspension and requested external actions of continuation-based threads. It is much like a very small kernel.
A thread is simply a function. Owl programs have two continuations, one for the regular one and one for the thread scheduler under which the function is running. Every function eventually calls its own final continuation, which results in the thread being removed from the thread scheduler. It can also call the second continuation and request operations from this thread scheduler.
The task of the thread scheduler is to run each running thread for a while, suspend and activate them, and handle message passing.
Threads have a primitive only for asynchronous message passing, but they can voluntarily wait for a specific kind of response, resulting in synchronous operation where desired.
Exported values:
start-thread-controller
thread-controller
signal-handler/repl
signal-handler/halt
signal-handler/ignore
report-failure
try-thunk
try
Values exported in this library are available when starting owl interactively. The form (exports ...) allows exporting all values exported by another library.
Exported values:
(exports (owl core))
(exports (owl list))
(exports (owl boolean))
(exports (owl rlist))
(exports (owl list-extra))
(exports (owl ff))
(exports (owl io))
(exports (owl lazy))
(exports (owl string))
(exports (owl symbol))
(exports (owl sort))
(exports (owl bytevector))
(exports (owl vector))
(exports (owl equal))
(exports (owl random))
(exports (owl render))
(exports (owl syscall))
(exports (owl function))
(exports (owl fasl))
(exports (owl port))
(exports (owl time))
(exports (owl regex))
(exports (owl math extra))
(exports (owl math))
(exports (owl tuple))
(exports (owl digest))
(exports (owl syntax-rules))
(exports (owl json))
halt
lets/cc
read
read-ll
ref
suspend
wait
(exports (scheme base))
(exports (scheme cxr))
(exports (scheme write))
This library defines macros for using functions (which are lambda expressions) as data structures.
Exported values:
define-sum-type
trod
Operations in this library depend only on primitive operations. The rest of the usual operations typically depend on numbers, which are implemented in (owl math).
Exported values:
pair?
null
null?
caar
cadr
cdar
cddr
cdddr
cddar
cdadr
cdaar
caddr
cadar
caadr
caaar
car*
cdr*
list?
zip
foldl
foldr
fold
map
for-each
mem
memq
assq
getq
last
fold-map
foldr-map
append
concatenate
reverse
filter
remove
separate
keep
all
every
any
unfold
first
find
find-tail
take-while
break
split
fold2
halve
interleave
diff
union
intersect
╯°□°╯
Examples:
(zip cons '(1 2 3) '(a b c d))
=
'((1 . a) (2 . b) (3 . c))
(foldr cons () '(a b c))
= '(a b c)
(map not '(#false #false #true))
=
'(#true #true #false)
(mem eq? '(1 2 3) 2)
= '(2 3)
(assq 'a '((a . 1) (b . 2)))
=
'(a . 1)
(assq 'c '((a . 1) (b . 2)))
= #false
(last '(1 2 3) 'a)
= 3
(last '() 'a)
= 'a
(append '(1 2 3) '(a b c))
=
'(1 2 3 a b c)
(reverse '(1 2 3))
= '(3 2 1)
(find null? '(1 2 3))
= #false
(find null? '(1 ()))
= ()
(split (lambda (x) (eq? x 'x)) '(a b x c d x e))
=
'((a b) (c d) (e))
l
= '(1 2 () 3 () 4)
(filter null? l)
= '(() ())
(remove null? l)
= '(1 2 3 4)
l
= '(#true (1 2) #false (3))
(separate l pair?)
=
(values '((1 2) (3)) '(#true #false))
(interleave 'x '(a b c))
=
'(a x b x c)
(interleave 'x '())
= ()
(halve '(a b c d))
=
(values '(a b) '(c d))
(halve '(a b c d e))
=
(values '(a b c) '(d e))
##false Exported values:
sexp-parser
sexp-stream-parser
list->number
get-sexps
get-padded-sexps
string->sexp
vector->sexps
list->sexps
byte-stream->sexp-stream
read
read-ll
This trivial library defines boolean values.
Exported values:
boolean?
boolean=?
true
false
##false Exported values:
char?
char=?
char<?
char>?
char<=?
char>=?
char->integer
integer->char
Byte vectors are vectors holding only numbers in the range 0-255. They are internally represented as chunks of memory in the garbage collected heap. Typical use cases for them is packing data for an operating system call, or receiving data from an external source.
Vectors and byte vectors differ from Scheme in that they are not disjoint types. Regular vectors can be, or can contain, byte vectors if the content happens to fit in the range representable by bytes. This makes it possible to use a more compact representation of data where possible. Representation changes to regular vectors where necessary, much like numbers are converted from fixnums to bignums where the values would no longer be representable by fixnums.
Exported values:
bytevector
bytevector?
bytevector-length
bytevector-u8-ref
bytevector-append
bytevector-concatenate
bytevector-concatenate->list
bytevector-copy
bytevector->list
list->bytevector
##false Exported values:
parse-json
parse-json-or
byte-stream->json-stream
Examples:
(parse-json "0")
= 0
(parse-json "42")
= 42
(parse-json "-42")
= -42
(parse-json "0.0")
= 0
(parse-json "-0.0")
= 0
(parse-json "-0.0e1")
= 0
(parse-json " 0.001")
= 1/1000
(parse-json "-12.34 ")
= -617/50
(parse-json " 1.23e2 ")
= 123
(parse-json "-1.23e10")
=
-12300000000
(parse-json "[]")
= (vector)
(parse-json "[1]")
= (vector 1)
(parse-json "[1, 2, 3]")
=
(vector 1 2 3)
(parse-json "{}")
= '()
(parse-json "{\"foo\": 42}")
=
'((foo . 42))
(parse-json "{\"foo\": 42, \"bar\": 43}")
=
'((foo . 42) (bar . 43))
(parse-json "\"a\\nb\"")
= "a\nb"
(parse-json "\"\\u03BB\"")
= "λ"
(parse-json "\"\\uD834\\uDD1E\"")
=
(list->string (list 119070))
(force-ll (byte-stream->json-stream (string->bytes "1")))
= (list 1)
(force-ll (byte-stream->json-stream (string->bytes " 1 ")))
= (list 1)
(force-ll (byte-stream->json-stream (string->bytes " 1 \n\t 2 \r\n")))
= (list 1 2)
Exported values:
qnull
qnull?
qlen
qcons
qsnoc
quncons
qunsnoc
qcar
qrac
list->queue
queue->list
Lazy lists (or streams) are like lists, but they are computed only as far as needed. You can for example define a lazy list of all integers below a million, and then proceed to run computations on them, without worrying whether you have enough memory to hold a million numbers. Lazy lists are for example useful in computations, where you know how something is constructed, but don't yet know how many of them will be needed, or know that you only need them one at a time and don't want to waste memory.
A lazy list is either ##null, a pair of a value and rest of the lazy list, or a function of zero arguments (a thunk) which when called will return the rest of the lazy list. Therefore, since normal lists are a subset of lazy lists, all lazy list functions can also take normal lists as arguments.
Scheme warning: recall that Owl does not have mutable data structures, so lazy lists do not cache their results.
(pair head exp) → ll, lazy equivalent of (cons head exp), but exp is not evaluated yet (force-ll ll) → list, turn a lazy list into a regular one
Exported values:
lfold
lfoldr
lmap
lappend
lfor
liota
liter
lnums
lzip
ltake
lsplit
llast
llen
lcar
lcdr
lkeep
lremove
ldrop
llref
ledit
pair
tail
uncons
force-ll
subsets
permutations
lunfold
delay
force
avg
lnull?
lpair?
Examples:
(lsplit '(1 2 3 4) 2)
=
(values '(1 2) '(3 4))
Exported values:
string?
string-length
runes->string
bytes->string
string->bytes
string->runes
list->string
string->list
string-append
string-concatenate
c-string
null-terminate
finish-string
render-string
render-quoted-string
str-iter
str-iterr
str-fold
str-foldr
str-iter-bytes
str-replace
string-map
str-rev
string
string-copy
substring
string-ref
string=?
string<?
string>?
string<=?
string>=?
string-ci=?
string-ci<?
string-ci>?
string-ci<=?
string-ci>=?
unicode-fold-char
make-string
This library is used by by the REPL to read input similarly to what the traditional readline library does.
Exported values:
readline-default-options
readline-result-stream
readline
port->readline-line-stream
port->readline-byte-stream
Lambdas can have any number of variables. The variables eventually end up representing virtual machine registers. There are only a fixed number of registers in the VM, and certain values should be in certain registers at certain points in evaluation. Therefore we need a transformation from the AST expression to something closer to bytecode operating on registers. This format uses a similar tuple structure and is called RTL, short for register transfer language.
The task of this library is mainly to assign a VM register to each variable and convert references accordingly.
This module is quite old. The register allocator could be updated at some point, after which the number of VM registers could be reduced.
Exported values:
compile
This library contains shared data structures used in the compiler. Originally all the data structures were either S-expressions or tuples with a fixed structure. They are being converted to sum types, which makes it possible to verify some aspects of use of the data structure at compile time.
Exported values:
success
ok
fail
rtl-case
ret
move
prim
cons-close
ld
refi
goto
jeqi
jeq
Evaluation happens in an environment. An environment maps variables to values. This library defines operations on the environment structure used by the compiler.
Exported values:
lookup
env-bind
empty-env
apply-env
env-fold
verbose-vm-error
prim-opcodes
opcode->wrapper
primop-of
primitive?
poll-tag
link-tag
buffer-tag
signal-tag
thread-quantum
current-library-key
env-set-macro
*tabula-rasa*
env-del
env-get
env-del
env-set
env-keep
env-get-raw
env-put-raw
env-keys
Implementing recursion becomes an interesting problem when mutation is not allowed. Scheme allows recursion in definitions and via the letrec primitive form, which under the hood require mutable data structures. We don't have them here, and the only way to construct bindings and functions is a run of the mill lambda.
Luckily, a lambda is all it takes to recurse. This library takes a version of the program in which one can use a recursive lambda, or rlambda, and converts it to a corresponding expression using only normal lambdas.
The conversion is a custom one developed for the first version of Owl. It operates by exteding each recursive lambda with the other recursive functions it may call, propagating the required values to required call sites at required places, and constructing wrapper functions which convert the originally intended arguments to ones the actual recursive operations need.
The added extra arguments are added after the original ones. This is important, because we want the wrapper functions to just consist of a few loads to free registers from their environment and a jump.
Exported values:
fix-points
Even though all bindings and operations are just lambdas, we need to differentiate a few kinds of them depending on their content and use.
A lambda occurring in the operator position does not need to be treated as a function, because it simply means assigning names to values. They are leaft intact and generally end up being implemented as register moves.
The lambdas we need to represent somehow at runtime are further split to closures and procedures. A procedure is lambda which does not depend on values known only at runtime, so the corresponding object can be constructed at compile time. In the special case where it also does not require any references to non-trivial other values, the value ends up being just the bytecode.
A closure is a lambda which requires some values known only at runtime. For these a procedure is generated at compile time, and instructions to add the remaining values at runtime are added to bytecode.
Exported values:
build-closures
uncompiled-closure?
This library converts the RTL expressions to bytecode executable by the VM.
CPS conversion usually produces a lot of small functions with equal bytecode but potentially different bindings in the environment. In order to make programs smaller and improve cache friendliness the bytecode is interned, just like we do with symbols.
Exported values:
assemble-code
simple-value?
small-value?
The continuation passing style, or CPS, transformation converts a lambda expression to another lambda expression, with some useful properties. The output is a function with one extra argument, which will be called with the result of the original function.
There are three main reasons for using the conversion. For one, the resulting lambda expression can be evaluated without the need for a stack, because nested function calls are eliminated and replaced by tail calls. What would normally be stored on a stack now ends up being stored in a closure.
The second reason is that Scheme allows capturing the added argument, which is called the continuation, to a variable. This requires no special support from the runtime, because the continuation is just a regular lambda.
Thirdly the presence of continuations makes implementing multithreading easy. Owl has pre-emptive multithreading, meaning a thread does not need to voluntarily give up control for other threads to run. This is done by jumping from the thread-local continuation to thread-scheduler continuation whenever a thread switch occurs.
Exported values:
cps
S-expressions could be used internally by the compiler at each step. Here we however construct a simple tree of tuples format out of the S-expression while checking its structure. This is done so that the subsequent compiler steps don't have to check S-expression structure.
The AST format could switch to using sum types at some point.
Exported values:
call?
var?
value-of
sexp->ast
mkcall
mklambda
mkvarlambda
mkvar
mkval
The VM has only a finite number of registers. This library attempts to reduce the number of registers needed by the RTL, and choose better registers for values to avoid unnecessary shuffling of values.
Old code. Scheduled for a rewrite.
Exported values:
allocate-registers
n-registers
This library is needed when compiling programs to C-programs. This library is also used to compile Owl itself.
The simples way to build standalone C-programs out of owl programs is to write the virtual machine code and the FASL-encoded program to run to a file. This happens when owl is invoked without the -O flags. On the plus side the resulting program has very little C-code, so the C-compiler won't take much time when compiling it. This is how the small Owl version is built.
Typically the output is optimized. This is done by translating some of the bytecode sequences of the program to be compiled to corresponding C-code fragments, adding them as new instructions to the vanilla virtual machine, and replacing them from the program with calls to the new instructions. The resulting C-code is larger and may take quite a bit of memory to compile using some C-compilers, but the resulting binary will be faster. This is how the usual bin/ol interpreter is built.
Exported values:
compile-to-c
This step renames all variables to fresh symbols. This is not strictly necessary, but it makes some compilation steps easier.
Exported values:
alpha-convert
Macros are operations on code to be performed before evaluating it. This is done by the macro expander defined in this library. The role of the macro expander is to apply macros at appropriate positions on the code and pass required information to it. The actual transformations are done by the functions stored as values of the macros.
A function to be used as a macro receives two arguments, the form to be expanded and the next free fresh symbol not occurring in the form or the parent expression of it, and returns a tuple of the expanded form and the next free symbol.
The macro expander can be tested from toplevel by using ,expand.
Exported values:
macro-expand
match
Owl is is by default a multitasking system. Multiple threads can be working on tasks and waiting for input or output at the same time, so we cannot simply define input and output (I/O) as lisp functions calling the corresponding operating system code via the VM.
There are a few ways how I/O could work in a multithreaded setting. Having a lot of I/O code in the thread scheduler is convenient to use, but results in an ugly thread scheduler. Having separate threads for each file descriptor and using only message passing for I/O is elegant, but turned out to be somewhat cumbersome to use. The current version is something in between. One thread, called iomux, handles port blocking and timers. Threads attempt to do I/O directly when calling e.g. print or read, but if the operation would require waiting for input or output, then the thread sends a synchronous message to iomux and gets a response when the task can be completed without blocking.
Notice that the I/O defined in this library cannot in general be used unless iomux is running. This may happen when working with code that has not called (start-muxer), and when trying to debug the thread scheduler.
Exported values:
open-output-file
open-input-file
open-append-file
open-socket
open-tcp-socket
open-udp-socket
open-connection
file-size
port->fd
fd->port
port?
close-port
start-io-threads
wait-write
when-readable
readable?
writeable?
blocks->port
closing-blocks->port
tcp-client
tcp-clients
tcp-send
udp-packets
udp-client-socket
wait-udp-packet
check-udp-packet
send-udp-packet
file->list
list->file
file->byte-stream
port->meta-byte-stream
port->byte-stream
port->tail-byte-stream
byte-stream->port
byte-stream->block-stream
port->block-stream
block-stream->port
copy-port
copy-file
display-to
print-to
print
print*
print*-to
write
writer-to
write-to
write-bytes
write-bytevector
read-bytevector
try-get-block
try-write-block
byte-stream->lines
lines
system-print
system-println
system-stderr
fasl-save
fasl-load
start-muxer
sleep
popen
This library defines finite functions. They are commonly used in Owl to construct efficient key-value mappings. A finite function is much like an association list, which are frequently used in Lisp. The main difference is that finite functions are represented as red-black trees internally, so the performance is much better when there are many keys.
The value empty
is an empty finite function. It contains
no mappings. You can read the value of of a mapping with
get
, which accepts a finite function, a key to be fetched
and optionally a default value to return if the key is not mapped.
put
can be used to extend a ff and del
removes
a mapping.
(define f (put (put empty 'foo 100) 'bar 42)) f ##
(get f 'foo ##f) 100 (get f 'x ##f) ##f (get (del f 'foo) 'foo ##f) ##f This data structure is made possible by the fact, that Owl has an order-preserving garbage collector. Therefore we have a total order on objects, which makes it possible to build balanced trees by comparison.
Exported values:
put
get
del
empty
empty?
fupd
ff-fold
ff-foldr
ff-union
ff-has?
list->ff
ff->list
ff-iter
ff-min
ff-max
ff-map
ff
keys
Examples:
test
=
(list->ff '((a . 1) (b . 2) (c . 3)))
(get test 'a 0)
= 1
(get test 'x 0)
= 0
(keys test)
= '(a b c)
(ff->list empty)
= ()
(ff->list (put empty 'a 42))
=
'((a . 42))
(ff->list (put test 'a 42))
=
'((a . 42) (b . 2) (c . 3))
(ff->list (put test 'd 42))
=
'((a . 1) (b . 2) (c . 3) (d . 42))
(ff->list (del test 'a))
=
'((b . 2) (c . 3))
(ff->list (del test 'x))
=
'((a . 1) (b . 2) (c . 3))
(ff-fold (λ (out k v) (cons v out)) () test)
=
'(3 2 1)
(ff-foldr (λ (out k v) (cons v out)) () test)
=
'(1 2 3)
(ff-map (lambda (k v) (null? v)) (ff 'a 1 'b '()))
=
(ff 'a #false 'b #true)
Exported values:
bytes->symbol
initialize-interner
string->uninterned-symbol
string->interned-symbol
strings->symbol-tree
set-symbol-tree!
put-symbol
empty-symbol-tree
intern-symbols
start-symbol-interner
Exported values:
utf8-encode
utf8-decode
utf8-sloppy-decode
utf8-decoder
encode-point
min-2byte
min-3byte
min-4byte
two-byte-point
three-byte-point
four-byte-point
last-code-point
valid-code-point?
There are two ways to serialize values to sequences of bytes: S-expressions and the FASL encoding. S-expressions represent trees of certain values and can thus only be used to accurately represent a subset of all possible values. FASL encoding can represent all values, apart from from their external semantics such as open network connections.
This library implements the S-expression part. There are further two flavors of rendering: the one intended to be printed out of programs and the one which results in expressions that can be read back in. The first one is usually seen as the output of print, whereas the other one is typically written with write.
Some R7RS Scheme extensions to represent shared structure in S-expressions is also implemented.
Str and str* are used to translate values to strings, whereas render and render* are typically used in a fold to construct lists of bytes to be later converted to strings or other formats.
Exported values:
make-serializer
render
render*
str
str*
Examples:
(render "foo" null)
=
(string->list "foo")
(render* "foo" null)
=
(string->list "\"foo\"")
This library implements serialization of objects to byte lists, and parsing of the byte lists to corresponding objects. The format is used internally for storing memory images to disk. Files with .fasl suffix used in booting up Owl are just fasl-encoded functions.
Exported values:
fasl-encode
fasl-encode-cooked
fasl-encode-stream
fasl-decode
decode-or
encode
objects-below
object-closure
decode-stream
sub-objects
Examples:
(fasl-decode '() 'x)
= 'x
(fasl-decode (fasl-encode 42) 'x)
=
42
(fasl-decode (fasl-encode '(1 2)) 'x)
=
'(1 2)
(fasl-encode 42)
= '(0 0 42)
(fasl-encode 1/4+i)
=
'(1 42 2 0 0 1 0 0 4 1 43 2 1 0 0 1 0)
(fasl-decode '(0 0 0 0) 'bad)
= 'bad
((fasl-decode (fasl-encode prime?) 'bad) 13337)
=
#true
This library defined various system calls and wrappers for calling them. The calls available are mainly frequently needed ones defined in the POSIX standard, or otherwise portable enough to be available in most systems these days.
Exported values:
dir-fold
dir->list
dir->list-all
errno
strerror
exec
fork
pipe
wait
waitpid
getpid
kill
getenv
setenv
unsetenv
umask
getcwd
chdir
readlink
symlink
link
rename
unlink
rmdir
mknod
mkdir
mkfifo
stat
directory?
file?
chmod
chown
lseek
seek-current
seek-set
seek-end
sighup
sigint
sigquit
sigill
sigabrt
sigfpe
sigkill
sigsegv
sigpipe
sigalrm
sigterm
O_RDONLY
O_WRONLY
O_APPEND
O_CREAT
O_TRUNC
close
fcntl
open
dupfd
dup2
read
write
port->non-blocking
port->blocking
CLOCK_REALTIME
clock_gettime
set-terminal-rawness
mem-string
mem-strings
peek-word
peek-byte
get-environment
get-heap-bytes-written
get-heap-max-live
isatty
resolve-host
catch-signals
execvp
system
##false Exported values:
time
time-ms
time-ns
this library implements a mostly complete POSIX-compatible regular expressions. at the moment lib-regex tries to just get all the features right. lots of non-constant-factor optimizations are missing.
spec: http://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html syntax ref of portable scheme regexps (Dorai Sitaram): http://evalwhen.com/pregexp/index-Z-H-3.html
Exported values:
get-sexp-regex
get-replace-regex
string->regex
string->replace-regex
string->complete-match-regex
rex-matches
##false Exported values:
port?
fd->port
port->fd
stdin
stdout
stderr
stdio-port?
##false Exported values:
command-line
emergency-exit
exit
get-environment-variable
get-environment-variables
##false Exported values:
read
read-all
##false Exported values:
*
+
-
...
/
<
<=
=
=>
>
>=
abs
and
append
apply
assoc
assq
assv
begin
binary-port?
boolean=?
boolean?
bytevector
bytevector-append
bytevector-copy
bytevector-copy!
bytevector-length
bytevector-u8-ref
bytevector-u8-set!
bytevector?
caar
cadr
call-with-current-continuation
call-with-port
call-with-values
call/cc
car
case
cdar
cddr
cdr
ceiling
char->integer
char-ready?
char<=?
char<?
char=?
char>=?
char>?
char?
close-input-port
close-output-port
close-port
complex?
cond
cons
current-error-port
current-input-port
current-output-port
define
define-library
define-record-type
define-syntax
define-values
denominator
do
dynamic-wind
else
eof-object
eof-object?
eq?
equal?
eqv?
error
error-object-irritants
error-object-message
error-object?
even?
exact
exact-integer-sqrt
exact-integer?
exact?
expt
features
file-error?
floor
floor-quotient
floor-remainder
floor/
flush-output-port
for-each
gcd
get-output-bytevector
get-output-string
guard
if
include
include-ci
inexact
inexact?
input-port-open?
input-port?
integer->char
integer?
lambda
lcm
length
let
let*
let*-values
let-syntax
let-values
letrec
letrec*
letrec-syntax
list
list->string
list->vector
list-copy
list-ref
list-set!
list-tail
list?
make-bytevector
make-list
make-parameter
make-string
make-vector
map
max
member
memq
memv
min
modulo
negative?
newline
not
null?
number->string
number?
numerator
odd?
open-input-bytevector
open-input-string
open-output-bytevector
open-output-string
or
output-port-open?
output-port?
pair?
parameterize
peek-char
peek-u8
port?
positive?
procedure?
quasiquote
quote
quotient
raise
raise-continuable
rational?
rationalize
read-bytevector
read-bytevector!
read-char
read-error?
read-line
read-string
read-u8
real?
remainder
reverse
round
set!
set-car!
set-cdr!
square
string
string->list
string->number
string->symbol
string->utf8
string->vector
string-append
string-copy
string-copy!
string-fill!
string-for-each
string-length
string-map
string-ref
string-set!
string<=?
string<?
string=?
string>=?
string>?
string?
substring
symbol->string
symbol=?
symbol?
syntax-error
textual-port?
truncate
truncate-quotient
truncate-remainder
truncate/
u8-ready?
unless
unquote
unquote-splicing
utf8->string
values
vector
vector->list
vector->string
vector-append
vector-copy
vector-copy!
vector-fill!
vector-for-each
vector-length
vector-map
vector-ref
vector-set!
vector?
when
with-exception-handler
write-bytevector
write-char
write-string
write-u8
zero?
string->integer
##false Exported values:
angle
imag-part
magnitude
make-polar
make-rectangular
real-part
##false Exported values:
caaar
caadr
cadar
caddr
cdaar
cdadr
cddar
cdddr
caaaar
caaadr
caadar
caaddr
cadaar
cadadr
caddar
cadddr
cdaaar
cdaadr
cdadar
cdaddr
cddaar
cddadr
cdddar
cddddr
##false Exported values:
case-lambda
##false Exported values:
display
write
write-shared
write-simple
##false Exported values:
current-jiffy
current-second
jiffies-per-second
Owl Lisp is being written mainly as a standalone pet project, but it has gotten many useful bug reports, suggestions and patches during the course of its development. Here is a partial list of people who have helped in the development:
Jo Henke A ton of improvements Doug Currie GC improvements, data representation optimizations, etc. John Cowan A slew of filed bugs. Pekka Pietikäinen Portability suggestions and patches. Erno Kuusela Portability suggestions and general insight.
The code similarly isn't based on any implementation or document, but many have definitely had an impact on it. A partial list of such sources follows:
Recursive Programming Techniques, William H. Burge, 1975 Essentials of Programming Languages, Friedman, Wand, Haynes, 1992 Efficient Garbage Compaction Algorithm, Johannes Martin, 1982 Scheme48, http://s48.org/ and various papers on it Structure and Interpretation of Computer Programs, http://mitpress.mit.edu/sicp/
#[https://github.com/yuriy-chumak/ol, Otus Lisp] is a related language. It adds features such as FFI to be able to write graphical programs and interface with other external libraries.
#[https://small.r7rs.org/attachment/r7rs.pdf, R7RS Scheme (pdf)]
#Q: Where can I get help?: #'You can stop by at #owl-lisp on libera.chat', file tickets to #[https://gitlab.com/owl-lisp/owl,gitlab] or send email to aki.helin@iki.fi.
#Q: How can I use third party libraries?: Grab them to
source directory and include them. (import (foo bar))
attempts to load ./foo/bar.scm
, which should have
(define-library (foo bar) ...)
#Q: The error messages suck.: True. Current best practice in Owl programs is not to make errors.
#Q: Why is it not called a Scheme?: I don't want people
filing issues about set-car!
not working.
#Q: Is this the last question?: No, that one was already asked some years ago.
#project #owl #post