-
Notifications
You must be signed in to change notification settings - Fork 8
1. Introduction
Lisp is one of the oldest programming languages in the world and yet despite its 60 years, it is far from retirement, especially thanks to its numerous descendants (see The Lisp family).
The version I propose here is rather rich and complete and is based on the syntax as John McCarthy had imagined it (see The Root of Lisp ).
You will find all the basic methods that made the language's heyday: cons, list, car, cdr, setq, defun, lambda, apply and so on, and some of the best (see LispE Help).
You will also find a rich collection of functions including:
- string processing
- mathematical functions (log, exp)
- system functions (ls, command, date, chrono)
- random distributions (the template random of C++11)
- sockets
- sqlite
- transducers
- xml
- json
- Python bridge
Furthermore, the interpreter is multi-threaded.
Note: LispE
is not the first programming language that we have implemented. The previous one: Tamgu
is still available as open source at: GitHub Tamgu
We have also added functions inspired by Haskell: map, filter, zip, zipwith, takewhile, dropwhile...
These functions can be freely combined with each other in order to provide a lazy evaluation implementation.
LispE also provides data structures together with pattern matching declarations.
Note that historically, Lisp had a maplist
operator, which is very similar to the map
function that we provide here.
We have added a bunch of functions to execute shell commands from a LispE script. You can even integrate it in a pipe in a very transparent way. (see Shell)
As you will discover the structure of the interpreter is quite simple and can be easily modified. You will be able to add not only new instructions but also create your own dynamic libraries. We even provide a lisp program to generate an already compilable stub that you can modify at will.
You can derive, overload or enrich all the objects of the language...
There is an important trend in designing systems that would automatically generate codes out of textual descriptions. For instance, GPT3 has been demonstrated as being able to learn how to code, thanks to thousands of examples it was feed with. However, if you compare Lisp to Python or Java or any other languages of that type, Lisp stands out with a very specific feature: Whatever the code you write, operators, functions and arguments will always fall in the same place. For instance, let's take an example in Python:
y = myfunction(10,20)
x = 10 + 20 - (y/2)
Now let's do the same in Lisp:
(setq y (myfunction 10 20))
(setq x (- (+ ( 10 20) (/ y 2))))
Hence, in Python, code mixes infix, prefix and postfix notations. For instance, operations are based on an infix notation where operators occur in the middle of the expression with the use of parentheses to explicit ambiguous cases.
In the case of Lisp, the pattern is the same whatever the instruction: always prefix.
This simplicity in patterns makes it quite interesting to automatically generate code as it is both regular and consistent across all code. Lisp code is hence much easier to learn than any other types of code.
Furthermore, we have put a lot of emphasis on usability. Our Lisp interpreter
can be run from within Python
for instance.
The interpreter has been entirely coded in C++11 and it exploits massively the notions of class inheritance as well as the STL templates proposed in C++11.
It does compile on most platforms: Windows, Mac OS and different versions of Linux (Centos, Fedora and Ubuntu).
The basic principle of our interpreter is the following:
All the elements of the language are implemented as classes derived from the class: Element
In this way, a list in Lisp can be implemented as a vector of Element .
We also used the basic C++11 types to implement our different elements:
- The strings are implemented as std::wstring, which allows the language to directly manipulate Unicode strings.
- Lists are implemented as std::vector.
- Dictionaries are implemented as std::map and std::unordered_map.
As an example, a list is implemented in the following form:
class List : public Element {
public:
std::vector<Element*> liste;
...
Since an Element can correspond to an Atom, a String or another List, it is understandable that such a representation allows recursively described lists.
Compiling a Lisp program consists in creating an initial list in which we will put all the Element that the compiler will detect in the code.
As each class derived from Element overloads its own eval method, we just have to call eval on this initial list to launch the execution...
That's it, there is nothing more complicated than that...