Browse Source

commit

master
Gabriel Pariat 2 years ago
parent
commit
0146b36d25
  1. 3
      implementations/c/configure.ac
  2. 4940
      implementations/c/src/lisp.js
  3. 13
      implementations/c/tests/check_ptlisp.log
  4. 4
      implementations/c/tests/check_ptlisp.trs
  5. 14
      implementations/c/tests/test-suite.log
  6. 5
      implementations/wasm/lisp.js
  7. 375
      spec.org

3
implementations/c/configure.ac

@ -4,7 +4,10 @@ AM_INIT_AUTOMAKE @@ -4,7 +4,10 @@ AM_INIT_AUTOMAKE
LT_INIT
AC_PROG_CC
AS_IF([test "x$CC" = "xgcc"], [
PKG_CHECK_MODULES([CHECK], [check >= 0.9.6])
])
AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile])

4940
implementations/c/src/lisp.js

File diff suppressed because it is too large Load Diff

13
implementations/c/tests/check_ptlisp.log

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
Running suite(s): Memory
Array
Table
Symbol
String
Cons
Environment
Evaluator
Lisp
Reader
Printer
100%: Checks: 85, Failures: 0, Errors: 0
PASS check_ptlisp (exit status: 0)

4
implementations/c/tests/check_ptlisp.trs

@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
:test-result: PASS
:global-test-result: PASS
:recheck: no
:copy-in-global-log: no

14
implementations/c/tests/test-suite.log

@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
======================================
ptlisp 0.1: tests/test-suite.log
======================================
# TOTAL: 1
# PASS: 1
# SKIP: 0
# XFAIL: 0
# FAIL: 0
# XPASS: 0
# ERROR: 0
.. contents:: :depth: 2

5
implementations/wasm/lisp.js

@ -111,6 +111,11 @@ async function main() { @@ -111,6 +111,11 @@ async function main() {
(loop (set count (+ count 1))
(print count)
(if (>= count 5) break nil)))
(def d 5)
(defn addd (a b) (+ a b d))
(addd 1 2)
(let ((d 10)) (addd 1 2))
\0`);
// const str = new TextEncoder().encode('+');
const index = instance.exports.alloc(str.length);

375
spec.org

@ -0,0 +1,375 @@ @@ -0,0 +1,375 @@
* 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.
Loading…
Cancel
Save