Skip to content

1. Introduction

Claude Roux edited this page Jun 26, 2023 · 42 revisions

Lisp

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:

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

Haskell Inspiration

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. However, if LispE provides its own version of maplist, note that this operator works as a standalone function. The operators à la Haskell, on the other hand, work as macros that can combine with its other to form one single loop.

Data Structure and Pattern Programming

LispE also provides data structures together with pattern matching declarations.

Check minizorg for a nice example of the kind of programs you can implement.

Moreover: you can use LispE as a real Shell scripting language.

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)

Internal Editor

LispE provides its own terminal mode editor with mouse support. You can edit and debug your own program in the same environment. (see editor)

Why a Lisp interpreter?

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...

Implementation

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 .

Basic types

We also used the basic C++11 types to implement our different elements:

  1. The strings are implemented as std::wstring, which allows the language to directly manipulate Unicode strings.
  2. Dictionaries are implemented as std::map and std::unordered_map.

As an example, a dictionary is implemented in the following form:

class Dictionary : public Element {
public:
    std::map<std::wstring, Element*> dictionary;
...

Since an Element can correspond to an Atom, a String or another List, it is understandable that such a representation allows this dictionary to handle any elements.

Above all, it can be modified at will.

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...
Clone this wiki locally