This is a compiler implementation for the Ribbon programming language. This project is still in the very early development stages. For now, issues are turned off and pull requests without prior discussion are discouraged.
Ribbon aims to strike a new balance between high level programmer experience and low level systems access. It is an algebraic effects language in the lineage of Koka and Effekt, but with some adapted influences from the imperative world inspired by a history in game development, particularly in its focus on performance and allocation, and the capability to be embedded into other applications. Semantically, Ribbon has a focus on (fully inferred) strong and static data types, structural data polymorphism, and allocator strategies. The design is also motivated by a pursuit of deep extensibility in the style of Lisp and Terra.
The compiler created here will target bytecode for RibbonI, an effect-aware low level stack machine interpreter (which is in a very early stage of development) with JIT capabilities. Eventually, the two will be merged into a single unified system, and AOT native compilation will be supported as well.
- âś… Lisp interpreter (95%)
- âś… Lisp environment (95%)
- âś… REPL (95%)
- âś… CLI (95%)
- 🟥 Type inference (0% here, but well prototyped)
- 🟥 Bytecode generation (0%)
- 🟥 ML-like syntax via lisp reader macros (0%)
The compiler is undergoing heavy development, but there will be test releases available for most platforms soon. The release will consist of a c header file, a static library, and a REPL/CLI application. Features implemented thus far constitute a lisp interpreter with an effects system and a fairly complete standard library environment.
- Finish integrating existing global environments with the new module system
- Comment syntax
- Support reader macros
- Ensure a minimum REPL functionality in Windows terminals
- Wrap more API with C bindings
- More testing & clean up
Eventually I will create some places for public discourse about the language, for now you can reach me via:
- Email: noxabellus@gmail.com
- Discord DM, or on various dev servers: my username is
noxabellus
You will need zig
; likely, the nightly build.
The latest version known to work is 0.14.0-dev.1583+812557bfd
.
You can either:
- Get it through ZVM or Zigup (Recommended)
- Download it directly
- Get the nightly build through a script like night.zig
There are several commands available for zig build
that can be run in usual fashion (i.e. zig build run
):
Command | Description |
---|---|
run |
Build and run a quick debug test version of ribbonc only (No headers, readme, lib ...) |
quick |
Build a quick debug test version of ribbonc only (No headers, readme, lib ...) |
full |
Runs the following commands: test, readme, header |
verify |
Runs the following commands: verify-readme, verify-header, verify-tests |
release |
Build the release versions of RibbonC for all targets |
unit-tests |
Run unit tests |
cli-tests |
Run cli tests |
c-tests |
Run C tests |
test |
Runs the following commands: unit-tests, cli-tests, c-tests |
readme |
Generate ./README.md |
header |
Generate ./include/ribbonc.h |
verify-readme |
Verify that ./README.md is up to date |
verify-header |
Verify that ./include/ribbonc.h is up to date |
verify-tests |
Verify that all tests pass (this is an alias for test ) |
Running zig build
alone will build with the designated or default target and optimization levels.
See zig build --help
for more information.
In addition to typical zig build options, the build script supports the following options (though not all apply to every step):
Option | Description | Default |
---|---|---|
-DlogLevel=<log.Level> |
Logging output level to display | .err |
-DlogScopes=<string> |
Logging scopes to display | ribbonc,repl |
-DuseEmoji=<bool> |
Use emoji in the output | true |
-DuseAnsiStyles=<bool> |
Use ANSI styles in the output | true |
-DreplDumpStdIn=<bool> |
(REPL) Default setting for dumping stdin to a file | false |
-DreplHistoryPath=<string> |
(REPL) Default path to the history file | .ribbonc-repl-history |
-DmaxComptimeDepth=<usize> |
(Compiler Eval) Default maximum call depth | 1024 |
-DforceNewSnapshot=<bool> |
(Tests) Force a new snapshot to be created instead of referring to an existing one | false |
-DstripDebugInfo=<?bool> |
Override for optimization-specific settings for stripping debug info from the binary | { 110, 117, 108, 108 } |
See zig build --help
for more information.
The ribbonc
executable is a work in progress, but offers a functional command line interface for Ribbon.
ribbonc [-i] [--dump-stdin <bool>] [--history <path>] [--disable-raw-mode] [--use-emoji <bool>] [--use-ansi-styles <bool>] [--max-comptime-depth <uint>] <path>...
ribbonc --help
ribbonc --version
Option | Description |
---|---|
--help |
Display options help message, and exit |
--version |
Display SemVer2 version number for RibbonC, and exit |
-i , --interactive |
Run the compiler in REPL mode |
--dump-stdin <bool> |
(REPL) Dump stdin to a file [Default: false] |
--history <path> |
(REPL) Path to the REPL history file [Default: .ribbonc-repl-history] |
--disable-raw-mode |
(REPL) Disable raw line editing mode |
--use-emoji <bool> |
Use emoji in the output [Default: true] |
--use-ansi-styles <bool> |
Use ANSI styles in the output [Default: true] |
--max-comptime-depth <uint> |
Maximum call stack depth for the compile time evaluator [Default: 1024; Note: going higher may cause segfaults due to native stack overflow; Minimum: 8] |
<path>... |
Root files to include in the compilation |
The ribbonc
executable is a work in progress, but offers a functional REPL interface for Ribbon.
See CLI for information on how to access the REPL mode.
Command | Description |
---|---|
:help |
Display a help message |
:quit |
Exit the REPL |
:clear-screen |
Clear the screen |
:clear-history |
Clear the history |
Ctrl +D |
Exit the REPL |
Ctrl +C |
Cancel current line input |
- Include ribbon in your
build.zig.zon
in the.dependencies
section, either by linking the tar,zig fetch
, or provide a local path to the source. - Add ribbon to your module imports like this:
const ribbon_c = b.dependency("ribbon-c", .{
// these should always be passed to ensure ribbon is built correctly
.target = target,
.optimize = optimize,
// additional options can be passed here, these are the same as the build options
// i.e.
// .logLevel = .info,
});
module.addImport("RibbonC", ribbon_c.module("Core"));
- See
src/bin/ribbonc.zig
for usage
Should be straight forward, though the API is limited as of now.
Use the included header file, then link your program with the .lib
/.a
file.
Example of binding from C can be found at tests/test.c
,
and at tests/test.sh
If your host language has C FFI, it should be fairly straight forward. If you make a binding for another language, please let me know and I will link it here.
Mostly the same as other lisps, major differences as of now include:
- No comments yet
- Only decimal integer literals are supported as of now
- Character literals are c-style (i.e.
'c'
,'\x00'
etc) - Quasiquotes do not traverse through regular quotes; this is used to allow nested quasi logic
- Some naming convention differences (e.g.
out<-in
conversion function names) - Minimal environment
- Nil
()
- Symbols
foo
,foo'
,/
,+inf
- Integers
1001
,+9
,-32
- Characters
'x'
,'\x00'
,'\esc'
,'\t'
,'\''
- Floats
1.0
,1.
,.0
,+1.0e-3
- Strings
"foo"
,"\tfoo\x00"
,"\""
- Pairs
(1 . 2)
,(1 2 3 . 4)
- Lists
(1 2 3)
,(+ 1 2 3)
- Quotes
'foo
,'(1 2 3)
- Quasiquotes
`foo `(1 2 3)
- Unquote
`(1 ,foo 3)
- Unquote splicing
`(1 2 ,@foo)
This module contains functions for creating and manipulating association lists.
Symbol | Description |
---|---|
alist-pair |
lookup a key symbol in an association list, returning the pair it binds; prompts fail if the key is not found |
alist-lookup |
lookup a key symbol in an association list, returning its associated value; prompts fail if the key is not found |
alist-member? |
check if a key symbol is present in an association list |
alist-put |
append a key-value pair to an association list |
alist-set! |
set the value of an existing key-value pair in an association list, returning the old value; prompts fail if the key is not found |
alist-each |
calls a function with each key-value pair in an association list |
alist-keys |
get the keys of a given association list |
This module provides basic arithmetic functions, constants, and predicates.
Symbol | Description |
---|---|
add , + |
integer/floating point addition on any number of values |
sub , - |
integer/floating point subtraction on any number of values |
mul , * |
integer/floating point multiplication on any number of values |
div , / |
integer/floating point division on any number of values |
mod , % |
integer/floating point remainder division on any number of values |
pow , ^ |
integer/floating point exponentiation on any number of values |
nan? |
check if input is not a number |
inf? |
check if input is a floating point infinity |
-inf? |
check if input is a negative floating point infinity |
+inf? |
check if input is a positive floating point infinity |
inf |
floating point infinity constant |
nan |
floating point not a number constant |
max-int |
the maximum possible integer constant |
min-int |
the minimum possible integer constant |
epsilon |
the minimum difference between two floating point numbers constant |
floor |
round a floating point number down |
ceil |
round a floating point number up |
round |
round a floating point number |
frac |
take the fractional part of a floating point number |
This module provides special forms for defining and manipulating bindings in the current environment.
let
creates a new environment frame with the given bindings,
and evaluates the body in that frame.
(let ((var x (+ 1 1)) (fun f (x) (+ x 1)) (macro m (x) `(f ,x))) (action1 x) (action2 (f x)) (action3 (m x)))
def-var
, def-fun
, and def-macro
forms mirror the syntax of let
,
but bind individual symbols in the current environment.
(def-var x (+ 1 1)) (action x)
bound?
and set!
can be used to query and manipulate
bindings created with any of the above forms.
Symbol | Description |
---|---|
def-var |
define a new variable |
def-fun |
define a new function |
def-macro |
define a new macro function |
let |
create local value bindings |
bound? |
check if a given symbol is bound in the current env |
set! |
set the value of a symbol in the current env. symbol must already be bound. returns old value |
This module provides special forms for controlling the flow of execution.
if
is a one or two option conditional branch.
(if x (conditional-action)) (if y (conditional-action) (else-action))
cond
is a multi-option conditional branch.
(cond (x (conditional-action1)) (y (conditional-action2)) (else (default-action)))
match
uses lambda lists to perform structural pattern matching on an input.
(match x ((0) (conditional-action0)) ((x) (conditional-action1 x)) ((x y) (conditional-action2 x y)) ((x y . z) (conditional-action3 x y z)) (else (default-action)))
For more information on the syntax of lambda lists, see the LambdaList
module.
begin
allows for sequencing expressions.
(begin (action1) (action2))
Symbol | Description |
---|---|
if |
two-option conditional branch |
when |
single-option conditional branch, taken if the condition is true |
unless |
single-option conditional branch, taken if the condition is false |
cond |
multi-option conditional branch |
match |
lambda list based matching on any inputt |
begin |
allows sequencing expressions |
panic |
runs format on the values provided and then triggers a panic with the resulting string |
panic-at |
uses the first argument as the source attribution for the panic; runs format on subsequent values provided and then triggers a panic with the resulting string |
throw |
prompts exception with the value provided; this is a shortcut for (prompt exception arg) |
stop |
prompts fail ; this is a shortcut for (prompt fail) |
assert |
asserts that a condition is true; if it is not, triggers a panic with the subsequent arguments, or with the condition itself if none were provided |
assert-eq |
asserts that the first two values provided are equal, using structural equality on objects; if they are not, triggers a panic with any subsequent values provided, or with the condition if none were |
assert-eq-addr |
asserts that the first two values provided are equal, using address equality on objects; if they are not, triggers a panic with any subsequent values provided, or with the condition if none were |
assert-at |
asserts that a condition is true; if it is not, triggers a panic with the subsequent arguments, or with the condition itself if none were provided |
assert-eq-at |
asserts that the first two values provided are equal, using structural equality on objects; if they are not, triggers a panic with any subsequent values provided, or with the equality inputs if none were |
assert-eq-addr-at |
asserts, using the location provided as the first argument, that the next two values provided are equal, using address equality on objects; if they are not, triggers a panic with any subsequent values provided, or with the equality inputs if none were |
e-assert |
asserts that a condition is true; if it is not, prompts exception with the second value provided or with the symbol AssertionFailed if one is not |
e-assert-eq |
asserts that the first two values provided are equal, using structural equality on objects; if they are not, prompts exception with any subsequent values provided, or with the condition if none were |
e-assert-eq-addr |
asserts that the first two values provided are equal, using address equality on objects; if they are not, prompts exception with any subsequent values provided, or with the condition if none were |
f-assert |
asserts that a condition is true; if it is not, prompts fail |
f-assert-eq |
asserts that the two values provided are equal, using structural equality on objects; if they are not, prompts fail |
f-assert-eq-addr |
asserts that the two values provided are equal, using address equality on objects; if they are not, prompts fail |
This module provides functions for converting between specific types, as well as converting between arbitrary values and strings.
Symbol | Description |
---|---|
bool<-int |
convert an integer to a boolean |
int<-bool |
convert a boolean to an integer |
int<-char |
convert a character to an integer |
char<-int |
convert an integer to a character |
int<-float |
convert a float to an integer |
float<-int |
convert an integer to a float |
string<-symbol |
convert a symbol to a string |
symbol<-string |
convert a string to a symbol |
stringify |
convert any value to a string representation; optionally accepts two parameters where the first may be a symbol of the set Display , Attr , Dotted , or Source , indicating the way in which to format the second value |
unstringify |
convert a string representation to a value |
This module provides an api to trigger and handle
arbitrary user-defined side effects at compile time.
The special forms with
and with-global
provide a let
-like syntax for binding
functions, macros, and variables to symbols in a special dynamic environment,
which can be accessed with the fetch
and prompt
special forms.
The three kinds of bindings are discriminated by a keyword at the head of the binding:
(with ((kind name def)...) body...) (with-global (kind name def)...)Where
kind
is one of:
fun
for lambda-like effect handlersmacro
for macro-like effect handlersvar
for simple variable bindings
Values created via with
and with-global
are provided a special binding, terminate
,
which can be called to cancel the inner computation of
the with
they are bound to, and return a value in its place.
Caution
In the case of terminate being called from a with-global
binding,
the computation being terminated is the entire compilation.
Caution
Macros calling terminate
will need to manually evaluate
their termination value if it requires it;
all termination values are passed as-is
(with ((fun abort (x) (terminate x)) (macro error (x) (prompt abort (eval x))) (var abort2 terminate)) (action1) (action2))
Some builtin effects can also be handled via with
/with-global
;
for example exception
and fail
, which are triggered by some builtins.
Items bound this way can be accessed with prompt
and fetch
:
fetch
simply retrieves the valueprompt
retrieves and then invokes the value with a given list of arguments
Symbol | Description |
---|---|
with-global |
provide one or more named top-level effect handlers to serve as a last resort; note that the terminate which is provided to handlers bound this way will terminate compilation |
with |
provide one or more named effect handlers, and an expression to execute under them |
fetch |
get a dynamically bound variable or effect handler from its binding symbol |
prompt |
defer execution to a named effect handler; (prompt sym args...) is equivalent to ((fetch sym) args...) |
This module provides functions for interacting with the file system. Many of these will likely be converted to prompts in the future.
Also available here are the std-io constants std-in
, std-out
, and std-err
,
which refer to file descriptors usable by read-file
, write-file
and their related functions.
Symbol | Description |
---|---|
open-file |
open a file at the provided path; accepts mode symbol 'r , 'w , or 'rw ; prompts an exception if it fails |
read-file |
read the content of a text file to a string; if a second parameter is given it is expected to be the length in bytes to read, otherwise reads the whole file; prompts an exception if it fails |
read-ln |
read a single line of a text file to a string; prompts an exception if it fails |
write-file |
write a string to a file; prompts an exception if it fails |
write-ln |
write an optional string to a file, then write a new line; prompts an exception if it fails |
print |
stringify all* arguments with 'Display , concatenate, then write-file with the resulting string; if the first parameter is a file, prints to that file instead of std-out; prompts an exception if it fails |
print-ln |
stringify all* arguments with 'Display , concatenate, then write-ln with the resulting string; if the first parameter is a file, prints to that file instead of std-out; prompts an exception if it fails |
file-end |
get the cursor position that marks the end of a file; prompts an exception if it fails |
file-cursor |
get the current cursor position in a file; prompts an exception if it fails |
file-cursor! |
set the current cursor position in a file; prompts an exception if it fails |
std-in |
the input file constant |
std-out |
the output file constant |
std-err |
the error file constant |
optional
=(? var)
if there are arguments remaining, apply
var
to the next argument; otherwise any bindings withinvar
arenil
rest
=(... symbol)
if there are arguments remaining, bind the rest of the arguments to
symbol
; otherwise bindnil
cons
=. var
if there are arguments remaining, bind the rest of the arguments to
var
; otherwise bindnil
var
=(var* optional* rest? cons?)
expect a list of
var
s, or the empty list,expr
same as quasiquote's unquote, evaluate
expr
and apply it to the next argument(@ symbol var)
match the given
var
, then bind it to the givensymbol
(ie(@ foo 1)
matches the number1
and binds it tofoo
)(: predicate)
expect a value that satisfies the given
predicate
(-> translator-function var*)
apply the given
translator-function
to the argument, and bind the results to the givenvar
s if any are present; translator function can(prompt fail)
to reject the matchsymbol
bind any value to the given
symbol
'symbol
expect a literal
symbol
- expect an exact match to the given atom:
int
(ie1
,9900
, etc)float
(ie1.
,3.14e-2
, etc)char
(ie'a'
,'\n'
, etc)string
(ie"hello"
,"\x00\""
, etc)bool
(ietrue
,false
)
Additionally, repeated binding symbols (ie (a a)
) are allowed, and will have their bound values checked for equality with eq?
(assert-eq (run-lambda-list (a b) '(1 2)) '((b . 2) (a . 1))) (assert-eq (with ((fun fail () (terminate ()))) (f-lambda-list (a b) '(1))) ()) (assert-eq (run-lambda-list (@ a 1) 1) '((a . 1))) (assert-eq (run-lambda-list 1 1) ()) (assert-eq '((x . (2 3))) (run-lambda-list (-> (lambda (x) (f-assert-eq x 2) (list x 3)) . x) 2)) (assert-eq 'okay (with ((fun fail () (terminate 'okay))) (f-lambda-list (-> (lambda (x) (f-assert-eq x 1) (list x)) a) 2))) (assert-eq '((x . (1 . 2))) (run-lambda-list (@ x (: pair?)) '(1 . 2))) (assert-eq 'okay (with ((fun fail () (terminate 'okay))) (f-lambda-list (@ x (: pair?)) 1)))
Symbol | Description |
---|---|
validate-lambda-list |
given a lambda list, returns a boolean indicating whether it is valid |
lambda-list-binders |
given a lambda list, returns a list of the symbols that will be bound if it is successfully run on an input |
f-lambda-list |
given a lambda list and an input, returns an env frame binding the symbols of the list to the values of the input, or prompts fail |
e-lambda-list |
given a lambda list and an input, returns an env frame binding the symbols of the list to the values of the input, or prompts an exception on failure |
run-lambda-list |
given a lambda list and an input, returns an env frame binding the symbols of the list to the values of the input, or causes a compile time error on failure |
This module contains functions for creating and manipulating lists and pairs.
Symbol | Description |
---|---|
nil |
the empty list constant |
cons |
join a head and tail into a pair |
car |
get the head of a pair |
set-car! |
set the head of a pair; returns the old value |
cdr |
get the tail of a pair |
set-cdr! |
set the tail of a pair; returns the old value |
list |
create a new list, with any number of values |
length |
get the length of a list |
map |
apply a function to each element of a list, returning a new list of the results |
each |
apply a function to each element of a list |
element |
determine if a given value is contained in a list |
This module contains functions and primitives for logical operations, such as comparison and boolean algebra.
Additionally, there is the truthy?
function, which converts
any value into a boolean, as well as the constants true
and false.
Symbol | Description |
---|---|
eq? , == |
determine if any number of values are equal; uses structural comparison |
not-eq? , /= |
determine if any number of values are not equal; uses structural comparison |
less? , < |
determine if any number of values are in order of least to greatest; uses structural comparison |
greater? , > |
determine if any number of values are in order of greatest to least; uses structural comparison |
less-or-equal? , <= |
determine if any number of values are in order from least to greatest, allowing adjacent values to be equal; uses structural comparison |
greater-or-equal? , >= |
determine if any number of values are in order from greatest to least, allowing adjacent values to be equal; uses structural comparison |
eq-addr? , ==* |
determine if any number of values are equal; uses address comparisons for object types |
not-eq-addr? , /=* |
determine if any number of values are not equal; uses address comparisons for object types |
less-addr? , <* |
determine if any number of values are in order of least to greatest; uses address comparisons for object types |
greater-addr? , >* |
determine if any number of values are in order of greatest to least; uses address comparisons for object types |
less-or-equal-addr? , <=* |
determine if any number of values are in order from least to greatest, allowing adjacent values to be equal; uses address comparisons for object types |
greater-or-equal-addr? , >=* |
determine if any number of values are in order from greatest to least, allowing adjacent values to be equal; uses address comparisons for object types |
not , ! |
logical not, performs truthy conversion |
and , && |
logical and accepting any number of values, short circuiting. performs truthy conversion for tests and returns the first failing value |
or , || |
logical or accepting any number of values, short circuiting. performs truthy conversion for tests and returns the first succeeding value |
truthy? |
performs truthy conversion |
true |
boolean constant |
false |
boolean constant |
This module contains functions for converting data to syntax, and for direct access and manipulation of the execution environments.
See Syntax for more information on the syntax of quote
and quasiquote
.
Symbol | Description |
---|---|
quasiquote |
a quote accepting unquote and unquote-splicing in its body |
quote |
makes a given input into its literal equivalent by skipping an evaluation step |
eval |
evaluate a given expression in the current env or an optional provided env |
gensym |
generate a unique symbol |
env-keys |
get the names of all bindings in the given env |
env-lookup |
lookup a key symbol in an environment, returning the value it binds; prompts fail if the key is not found |
env-pair |
lookup a key symbol in an environment, returning the pair it binds; prompts fail if the key is not found |
env-set! |
set the value associated with a name in an environment, returning the old value; prompts fail if the name is not bound |
env-put! |
append a key-value pair to the top frame of an environment |
env-copy |
copy a given environment |
env-new |
make a new environment from a simple a-list |
env-get-frame |
get the environment frame at the given offset depth; prompts fail if the depth is out of bounds |
env-push |
push a given environment frame into the current environment, returning the modified enviroment |
env-pop |
pop an environment frame off the current environment, returning it and the modified environment as a pair (frame . env) ; prompts fail if the environment is empty |
swap-env |
replace the current environment with the given one, returning the old one; optionally accepts 'self 'caller or 'evidence symbols indicating which environment to effect |
take-env |
take the current environment, leaving it empty; optionally accepts 'self 'caller or 'evidence symbols indicating which environment to effect |
get-env |
take a copy of the current environment, leaving it in place; optionally accepts 'self 'caller or 'evidence symbols indicating which environment to effect |
replace-env |
replace the current environment with the given one; optionally accepts 'self 'caller or 'evidence symbols indicating which environment to effect |
get-global-evidence |
get the global evidence environment frame |
set-global-evidence |
set the global evidence environment frame |
This module provides access to Ribbon's parser from within the language.
(def-var p (parser-new)) (def-var first-ln "(print-ln \"hello world\")") (def-var line-len (string-length first-ln)) (parser-filename! p "foo") (parser-input! p (string-intercalate "\n" first-ln "(print-ln \"goodbye world\")" "(+ 1 2)") '((2 . 1) . 0)) (def-var res1 (parse-sexpr! p)) (assert-eq (cdr (attr-range (attr-of res1))) (cons (cons 2 (+ 1 line-len)) line-len)) (eval res1) (eval (parse-sexpr! p)) (assert-eq (eval (parse-sexpr! p)) 3) (assert (parser-eof? p))
Symbol | Description |
---|---|
parser-new |
create a parser object |
parser-filename! |
set the file name of the given parser; returns old value |
parser-filename |
get the file name of the given parser |
parser-input! |
set the input of the given parser, optionally providing a position offset; returns old values as a pair |
parser-input |
get the input of the given parser |
parse-sexpr! |
parse an S-expression from the given parser's input; prompts exception with an error symbol if an error is encountered; prompts fail if there was nothing to parse |
parser-eof? |
determine whether a parser is at the end of its input |
This module provides the primitive lambda
and macro
special forms,
which can be used anywhere to create closure-binding procedural abstractions.
The only difference between lambda
and macro
is the timing of evaluation:
lambda
evaluates its arguments at the time of invocation, and evaluates its return value in its own environment.macro
does not evaluate its arguments, and evaluates its return value in the environment of the caller.
(lambda (x y) (+ x y))(macro (x y) `(+ ,x ,y))
Symbol | Description |
---|---|
lambda |
inline function definition |
macro |
inline macro definition |
apply |
apply a function to a list of arguments |
This module provides functions for the creation, access and transformation of source-attribution primitives.
Symbol | Description |
---|---|
attr-here |
create a source attribution referencing the call location |
attr-filename |
get the filename stored in an Attr |
attr-range |
get the range stored in an Attr |
attr-new |
create a new Attr from a filename string and a range object |
attr-of |
extract the Attr from a value |
attr-set! |
set the Attr of a value; returns the old Attr |
This module provides functions for working with strings, as well as conversions between strings and lists of chars.
The functions in this module are designed to be utf8-safe, and will generally cause a compilation error if used improperly.
Most functions come in a codepoint-indexed and byte-index variant.
Caution
Special care must be take in particular with the byte-indexed functions to avoid causing errors, as they validate that their operation is boundary-aligned.
Symbol | Description |
---|---|
empty-string? |
check if a value is the empty string |
string-length |
get the number of characters in a string |
list<-string |
convert a string to a list of characters |
string<-list |
convert a list of characters to a string |
string-find |
within a given string, find the character index of another string, or a character; returns nil if not found |
string-find-byte-offset |
within a given string, find the byte index of another string; returns nil if not found |
nth-char |
get the character at the given character index; returns nil if out of range |
index<-byte-offset |
given a string, convert a byte index within it to a character index; returns nil if out of range |
byte-offset<-index |
given a string, convert a character index within it to a byte index; returns nil if out of range or mis-aligned |
string-concat |
given any number of strings or characters, returns a new string with all of them concatenated in order |
string-intercalate |
given a string or a char, and any number of subsequent strings or chars, returns a new string with all of the subsequent values concatenated in order with the first value in between concatenations |
substring |
given a string and two character indices, returns a new string containing the designated section; returns nil if out of range |
substring-byte-offset |
given a string and two byte indices, returns a new string containing the designated section; ; returns nil if out of range or mis-aligned |
format |
stringify all arguments with 'Display , then concatenate |
This module provides functions for working with symbols, paralleling the String
module.
Symbol | Description |
---|---|
empty-symbol? |
check if a value is the empty symbol |
symbol-length |
get the number of characters in a symbol |
symbol-find |
within a given symbol, find the character index of another symbol, or a character; returns nil if not found |
symbol-concat |
given any number of symbols or characters, returns a new symbol with all of them concatenated in order |
symbol-intercalate |
given a symbol or a char, and any number of subsequent symbols or chars, returns a new symbol with all of the subsequent values concatenated in order with the first value in between concatenations |
subsymbol |
given a symbol and two character indices, returns a new symbol containing the designated section; returns nil if out of range |
This module provides predicate and conversion functions, to enable working with utf8 text and utf32 codepoints.
All functions here are overloaded to work both with single characters, as well as strings.
Symbol | Description |
---|---|
utf-category |
given a char, gives a symbol representing the unicode general category |
utf-describe-category |
given a unicode character category symbol, returns a string explaining the value |
utf-control? |
given a string or char, checks if all characters are control characters |
utf-letter? |
given a string or char, checks if all characters are letter characters |
utf-mark? |
given a string or char, checks if all characters are mark characters |
utf-number? |
given a string or char, checks if all characters are number characters |
utf-punctuation? |
given a string or char, checks if all characters are punctuation characters |
utf-separator? |
given a string or char, checks if all characters are separator characters |
utf-symbol? |
given a string or char, checks if all characters are symbol characters |
utf-math? |
given a string or char, checks if all characters are math characters |
utf-alphabetic? |
given a string or char, checks if all characters are alphabetic characters |
utf-id-start? |
given a string or char, checks if all characters are id-start characters char |
utf-id-continue? |
given a string or char, checks if all characters are id-continue characters char |
utf-xid-start? |
given a string or char, checks if all characters are xid-start characters char |
utf-xid-continue? |
given a string or char, checks if all characters are xid-continue characters char |
utf-space? |
given a string or char, checks if all characters are space characters |
utf-hex-digit? |
given a string or char, checks if all characters are hexadecimal digit characters char |
utf-diacritic? |
given a string or char, checks if all characters are diacritic characters |
utf-numeric? |
given a string or char, checks if all characters are numeric characters |
utf-digit? |
given a string or char, checks if all characters are digit characters |
utf-decimal? |
given a string or char, checks if all characters are decimal digit characters char |
utf-hex? |
given a string or char, checks if all characters are hexadecimal digit characters char |
utf-lowercase? |
given a string or char, checks if all characters are lowercase |
utf-uppercase? |
given a string or char, checks if all characters are uppercase |
utf-lowercase |
given a string or char, returns a new copy with all of the characters converted to lowercase |
utf-uppercase |
given a string or char, returns a new copy with all of the characters converted to uppercase |
utf-casefold |
given a string or a char, returns a new copy with all characters converted with unicode case folding; note that is may require converting chars to strings |
utf-byte-count |
given a string or a char, returns the number of bytes required to represent it as utf-8 |
utf-display-width |
given a string or a char, returns the width of the value in visual columns (approximate) |
utf-case-insensitive-eq? |
compare two strings or chars using unicode case folding to ignore case |
This module provides facilities for the inspection of value types.
Symbol | Description |
---|---|
type-of |
get a symbol representing the type of a value |
nil? |
determine if a value is the empty list |
pair? |
determine if a value is a cons pair |
bool? |
determine if a value is a boolean |
int? |
determine if a value is an integer |
char? |
determine if a value is a character |
float? |
determine if a value is a floating point number |
string? |
determine if a value is a string |
symbol? |
determine if a value is a symbol |
function? |
determine if a value is a function |
lambda? |
determine if a value is a lambda |
macro? |
determine if a value is a macro |
extern-data? |
determine if a value is external data such as a file |
extern-function? |
determine if a value is an external function |
builtin? |
determine if a value is a builtin function |
callable? |
determine if a value is callable |
We truly stand on the shoulders of giants and I'm afraid listing everything I've referenced in the design of this language would be impossible, so the following are some highlights I found particularly well-written, informative, or otherwise inspiring
- Abstracting Extensible Data Types or Rows By Any Other Name
- A Polymorphic Record Calculus and Its Compilation
- Generalized Evidence Passing for Effect Handlers
- Do Be Do Be Do
- Zero-cost Effect Handlers by Staging
- Typed Memory Management in a Calculus of Capabilities
- A Lightweight Formalism for Reference Lifetimes and Borrowing in Rust
- Alias Types for Recursive Data Structures
- System F-omega with Equirecursive Types for Datatype-Generic Programming
- Numbering Matters: First-Order Canonical Forms for Second-Order Recursive
- The Simple Essence of Algebraic Subtyping
- Basically every publication from the fabulous Daan Leijen
- How to compile pattern matching and various other works by Jules Jacobs
- Lisp in Small Pieces
- Let Over Lambda
- Fear of Macros
- Thunderseethe's blog
- Ryan Brewer's blog
- Colin James' blog
- Max Bernstein also has a great collection of resources