-
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.
Note that historically, Lisp had a maplist
operator, which is very similar to the map
function that we provide here.
LispE also provides data structures together with pattern matching declarations.
Check minizorg for a nice example of the kind of programs you can implement.
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)
LispE provides its own terminal mode editor with mouse control. You can edit and debug your own program in the same environment.
(see editor)
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 fed 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)
s = " qdkqld "
s = s.strip()
Now let's do the same in Lisp:
(setq y (myfunction 10 20))
(setq x (- (+ ( 10 20) (/ y 2))))
(setq s (trim " qdkqld "))
Hence, in Python, code mixes infix, prefix and postfix notations:
- Prefix: Function calls is a prefix notation, where you provide the function name first
- Infix: Mathematical expressions are usually infix, with the operators in the middle of the expression with parentheses to explicit ambiguous cases.
- Postfix: Methods associated with objects such as:
s.strip()
.
In the case of Lisp, the pattern is the same whatever the instructions are: always prefix, without any ambiguity of interpretation.
This simplicity in patterns makes it quite interesting to automatically generate programs as Lisp syntax is systematically consistent across all code. Lisp code is hence much easier to learn than any other types of code. In addition, the tree representation of all Lisp expressions makes the combination of different codes extremely easy. Indeed, each expression or argument is a node in a tree that can be easily replaced or enriched.
Furthermore, we have put a lot of emphasis on usability. Our Lisp interpreter
can be run from within Python
for instance.
Finally, this interpreter despite its numerous features is very lightweight and its C++ code compiles in less than a minute on most platforms.
Lightness and Python compatibility makes it the perfect candidate to code generation...
The interpreter has been entirely coded in C++11 and it massively exploits 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.
As you can see, 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...