Multiple implementations (JS, Wasm, C) of a Lisp.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

14 KiB

Types:

integer

double

rational

character

string

cons

fn

macro

array

hash-table

stream

Symbols:

nil

t

NaN

undefined

Functions:

number?

Is the argument a number?

Takes anything and return T if it's type is an integer, double or rational.

integer?

Is the argument an integer?

Takes anything and return T if it's type is an integer.

double?

Is the argument a double floating-point number?

Takes anything and return T if it's type is a double.

rational?

Is the argument a rational number?

Takes anything and return T if it's type is a rational.

NaN?

Is the argument not a number?

Takes anything and return T if it's type is a double and the value is NaN.

undefined?

Is the argument undefined?

Takes anything and return T if it's pointer is the same as undefined.

char?

Is the argument a character?

Takes anything and return T if it's type is a character.

string?

Is the argument a string?

Takes anything and return T if it's type is a string.

cons?

Is the argument a cons pair?

Takes anything and return T if it's type is a cons.

fn?

Is the argument a function?

Takes anything and return T if it's type is a function.

macro?

Is the argument a macro?

Takes anything and return T if it's type is a macro.

array?

Is the argument an array?

Takes anything and return T if it's type is an array.

hash-table?

Is the argument an hash-table?

Takes anything and return T if it's type is an hash-table.

stream?

Is the argument a stream?

Takes anything and return T if it's type is a stream.

double

Convert a number to double floating-point?

Takes a number and convert it into a double floating-point number.

ceil

Round a number upward?

Takes a number and round it upward.

floor

Round a number downward?

Takes a number and round it downward.

round

Round a number

Takes a number and round it to mathematically closest and if it's in between two integers i.e 0.5, round to closest even (divisible by two) integer

+

Add n numbers

Takes integers, rationals and doubles. If a number is double return a double. Else if a number is rational and the result's denominator is not 1 then return a rational. Else return an integer.

0 number => 0
1 number => n
> 1 numbers => n0 + n1 + n2 …

-

Substract n numbers

Takes integers, rationals and doubles. If a number is double return a double. Else if a number is rational and the result's denominator is not 1 then return a rational. Else return an integer.

0 number => Error
1 number => 0 - n
> 1 numbers => n0 - n1 - n2 …

*

Multiply n numbers

Takes integers, rationals and doubles. If a number is double return a double. Else if a number is rational and the result's denominator is not 1 then return a rational. Else return an integer.

0 number => 1
1 number => n
> 1 numbers => n0 * n1 * n2 …

/

Divide n numbers

Takes integers, rationals and doubles. If a number is double return a double. Else if a number is rational and the result's denominator is not 1 then return a rational. Else return an integer.

0 number => Error
1 number => 1 / n
> 1 numbers => n0 / n1 / n2 …

**

Power 2 numbers

Takes integers, rationals and doubles. If a number is double return a double. Else if a number is rational and the result's denominator is not 1 then return a rational. Else return an integer.

< 2 number => Error
2 numbers => n0 ** n1
> 2 numbers => Error

&

Bitwise AND numbers

Takes integers and return an integer.

0 number => -1
1 number => n
> 1 numbers => n0 & n1 & n2 …

|

Bitwise OR numbers

Takes integers and return an integer.

0 number => 0
1 number => n
> 1 numbers => n0 | n1 | n2 …

^

Bitwise XOR numbers

Takes integers and return an integer.

0 number => 0
1 number => n
> 1 numbers => n0 ^ n1 ^ n2 …

~

Bitwise NOT numbers

Takes an integer and return an integer.

0 number => ERROR
1 number => ~n
> 1 numbers => ERROR

&&

Branching AND expresions.

Takes anything, evaluate left to right and return the first item that eval to NIL or the result of the last eval.

0 item => T
> 0 items => first item to eval to nil or the last item.

||

Branching OR expresions.

Takes anything, evaluate left to right and return the first item that eval to not NIL or the result of the last eval.

0 item => T
> 0 items => first item to eval to not nil or the last item eval.

!

Return T if NIL else NIL.

Takes anything.

if

Branching tree.

Evaluate the condition and then evaluate the true or false branch based on the result. I.e: if the condition evaluate to nil, evaluate the false branch.

=

Compare 2 arguments for equality.

Takes 2 argument and compare them for equality. If the types are different, return false, unless they're different number types. Compare numbers by value: Integer value to integer value if both integer. If integer and rational than always return false. If one is a double, the other argument is to be converted to double and then compared. Rationals are to be compared numerator to numerator and denominator to denominator.

<

Is n0 less than n1?

Compare the first number with the second and return true if it's less than.

>

Is n0 greater than n1?

Compare the first number with the second and return true if it's greater than.

<=

Is n0 less than or equal to n1?

Compare the first number with the second and return true if it's less than or equal.

>=

Is n0 greater than or equal to n1?

Compare the first number with the second and return true if it's greater than or equal.

!=

Compare 2 arguments for inequality.

Takes 2 argument and compare them for inequality. Compare

list

Return a list from the arguments

Return nil if no arguments.

cons

Return a cons pair from 2 arguments

Return an error if more than 2 arguments.

car

Return first element of a pair/list

Return an error if not a cons.

cdr

Return second element of a pair / rest of the list

Return an error if not a cons.

peek-char

Return first char in the specified stream.

Takes nothing, NIL, T or a stream, anything else return an error. If nothing, NIL or T use the standard-input stream as the stream. Return the first character without increasing the reading pointer in the stream.

read-char

Return first char in the specified stream.

Takes nothing, NIL, T or a stream, anything else return an error. If nothing, NIL or T use the standard-input stream as the stream. Return the first character while increasing the reading pointer in the stream.

set-reader-macro

Bind a function on a character that will be triggered during reading.

Takes a character and a function. The later takes in a character and a stream and return a value.

read

Return an expression read in the specified stream.

Takes nothing, NIL, T or a stream, anything else return an error. If nothing, NIL or T use the standard-input stream as the stream. Return the first character while increasing the reading pointer in the stream.

eval

Return the result of the evaluation of an expression.

Takes 1 argument of anything and evaluate it and return the result.

quote

Return the argument received as is.

Therefore preventing any evaluation of it.

def

Bound a value to a symbol in the global environment.

Takes a symbol and anything else. More than 2 arguments result in an error.

set

Set the value of the symbol bound in the environment.

It goes from the local environmnet outward trying to find a binding on the symbol and if it doesn't then it bind it in the global env.

macro

Create a macro function from a list of arguments and the rest is the body.

fn

Create a function from a list of arguments and the rest is the body.

string

Create a string from different value.

If it's a string return it. If it's a symbol, create string from it's text value. If it's a character, create string from it. If it's a number, create the string representation of it. If it's a list or array, concactenate it's content string representation. i.e (string '(\h \e \l \l \o)) => "hello" Return an error on anything else. I.e hash-table, function…

array

Create an array from the arguments.

Takes anything or nothing and create an array with the arguments in it. (array 1 2 3 4) => [1 2 3 4]

hash-table

Create an hash-table from the arguments.

Takes anything or nothing and create an hash-table with the arguments splitted in pairs. The first element of a pair is the key and the second is the value. Return an error if odd number of arguments.

push

Push an element at the end of a collection.

If the collection is a string, convert the element to string and append it. If the collection is a list, append the element to the end of it. If the collection is an array, append the element to the end of it. Else return an error.

pop

Pop an element from the end of a collection.

If the collection is a string, reduce it's length by 1 and return the last character. Handle UTF-8 character so that if the last byte of the string is part of an UTF-8 character, reduce the length until you get the header, and return the UTF-8 character. If the collection is a list, set the cdr of second to last pair to nil and return the last element. If the collection is an array, reduce the length by 1 and return the last element. Else return an error.

insert

Insert an element at a position in a collection.

Let you insert into list, string, array and hash-table. Takes a number position for a list, string, array or anything as key for an hash-table. Takes anything as value to be inserted.

get

Get an element at a position in a collection.

Let you retrieve an element into list, string, array and hash-table. Takes a number position for a list, string, array or anything as key for an hash-table. Return undefined if nothing is found.

write

Write to a stream in a format that could be read back.

Write in a way that the reader could make sense of the result. Useful for writing to a file to be read from later on, or just debugging.

print

Print to a stream in a human friendly way.

Useful for user messages.

do

Block of expresions to be evaluated.

The last evaluation is to be returned.

length

Return the length of string, list or array.

If anything but a string, list or array return an error. Accepts only 1 argument.

let

Create a local environment

Takes a list of key value list and a body. Creates a new environment table and insert for the key, the evaluated value. Reuse this new environment table for the next key, value pair. Making it that the key, value of the previous pair can be used to eval the current one. Then eval the body, one expression after the other using the new local environment and return the result of the last expression.

quasiquote

Acts like quote but with a twist.

Don't eval the expression but navigate it and eval only the expression with 'unquote' or 'unquote-splicing' as their car.

unquote

Evaluate the expression in place

unquote-splicing

Evaluate the expression and inline the list result.

Inline in a list by changing the cdr of the previous pair to result and the cdr of the last cons in result to be the cdr of the previous cons before the change. i.e (quasiquote 1 2 3 (unquote-splicing (list 4 5)) 6) => (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 (cons 6 nil))))))

Reader macros:

\(

Create a list by reading elements after elements until a \) is encountered.

\)

Return a error because there shouldn't be a closing bracket without a begining.

\'

Quote the following read. I.e 'hello => (quote hello)

\[

Create an array by reading elements after elements until a \] is encountered.

\]

Return a error because there shouldn't be a closing bracket without a begining.

\{

Create an hash-table by reading elements after elements until a \} is encountered.

\}

Return a error because there shouldn't be a closing bracket without a begining.

\`

Quasiquote the following read. I.e `hello => (quasiquote hello). But also create the \, reader-macro and remove it after the following read.

\,

Is created and removed by \`. Unquote or unquote-splicing the following read. I.e ,hello => (unquote hello) ,@'(1 2) => (unquote-splicing '(1 2))

Requirements:

Garbage collection

The idea of the garbage collector is to iterate through the blocks in memory and marking them as free. Then navigate the global environment to mark everything in it as used (not free). And finally reiterate through the blocks again to then actually free them. It's probably a naive implementation, but it's better than nothing. It will run when something is to be allocated and according to a certain interval. Maintain a counter that decrease by an amount relative to the percent of memory used. If the counter goes below zero, run the garbage collector and reset the counter.

Tail call optimisation

This optimisation will be done in the evaluator. The goal is to detect when a function is calling it self in tail position. And to achieve this, on function evaluation on the last item of the body navigate through those constructs if existant: LET, IF, AND, OR and DO. And when we navigate through those construct we're looking for the tail call of them. I.e LET, AND, OR and DO is the last expression, IF is the true or false path. And when after that navigation we end up on call to the function currently being evaluated then we can optimise. The optimisation will consist in updating the environment with the new argument values to be passed to the function and instead of calling eval on it, just execute a jump to the start of the current function evaluation. In the eye of the user nothing will happen, but it will prevent that the stack based host pulls out a stack overflow. There might be a boost in performance, but it's probably minimal.