I have a minimal amount of experience with Lisp and have read the blog posts on how to implement the basic special forms which are needed to define a Lisp. I’m considering trying to write my own Lisp as a scripting language for a platform whose primary language is something that I don’t like. Thing is, I don’t really want to write code using a minimal set of primitives. I want to write in a full Lisp.

Are there any “bring your own bootstrap” Lisp implementations, which provide a more full-featured language once the basic primitives have been provided? Something that would let me go more quickly from a couple hundred lines of bootstrap code into a more full fledged language, even if the runtime is slow.

  • aartaka@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    There’s Mal, which goes from the most basic REPL setup to more or less complete Lisp. I haven’t tried it (because the only implementation I want to make is a Brainfuck one, and that’s quite non-trivial), but it seems to be quite hackable and incremental.

  • neonscribe@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    Take a look at GNU Guile. It’s a Scheme implementation that’s designed to be embeddable easily in a C/C++ environment.

  • Frere_de_la_Quote@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    Hello,

    I have implemented a full Lisp, which my company has accepted to put in open source. The documentation is here: https://github.com/naver/lispe/wiki.

    The list of all available functions are here:

    https://github.com/naver/lispe/wiki/5.-Description-of-Functions,-Operators-and-Libraries

    And you can find the code over here: https://github.com/naver/lispe

    The engine is implemented in C++11 and can compile on any platforms: Windows, Mac and Linux. I also provide installers for Windows and Mac OS, and a Web Assembly version.

    You can use it as an external library in a C++ program or in a Python program.

    In the documentation, I have documented how the internal engine is built.

    You can very easily add external libraries and extend the instruction set as you wish.

    The engine also proposes many methods to handle Unix commands and retrieve their content as a list of strings.

    • jacobb11@alien.topB
      link
      fedilink
      English
      arrow-up
      1
      ·
      1 year ago

      I find your implementation of lists with shared vectors rather than cons cells very interesting. However, I am surprised that function cons (prepending an element the front of the list) is relatively inefficient, since its performance is assumed by so much code to be constant time. Have you considered changing the storage underlying the shared vectors from arrays to rings? It’s not a fully baked idea, but I think it would permit prepending an element to be as efficient as appending one.

    • ralfD-@alien.topB
      link
      fedilink
      English
      arrow-up
      1
      ·
      1 year ago

      But doesn’t that leave out the jucy bits? This neither shows how to call into LispE from the C/C++ side nor how to call a C/C++ programs functions from LispE code (i.e. neither embedding nor extending). Or did I miss something?

      • Frere_de_la_Quote@alien.topB
        link
        fedilink
        English
        arrow-up
        1
        ·
        1 year ago

        Actually, you have two different ways to execute your program: as a string with “execute” or with a file with “load”. In both cases, when you execute your code, it returns an Element object that can be anything that your program may return. You can then test if it is a list with isList(), or a string or a number. You have then many way to translate this object into what you want. The method “toString” is just one way to transform your output in a string to display it on screen:

        Element* e = lisp->execute(“(numbers 1 2 3 4 5)”) returns a vector of doubles. You can then iterate on it to extract your values:

        vector res; if (e->label() == t_numbers) { ((Numbers*)e)->liste.to_vector(res); }

        Another way, which is much more generic:

        if (e->isList()) { for (long i = 0; i < e->size(); i++) { res.push_back(e->index(i)->asNumber()); } }

      • Frere_de_la_Quote@alien.topB
        link
        fedilink
        English
        arrow-up
        1
        ·
        1 year ago

        In the case of calling C++ from LispE, there is a directory template, which you can use to create a Makefile and a stub to compile your oan library

  • raevnos@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    Getting a macro system up and running is the logical next step; then you can write macros that build on the very basic forms and provide other syntax.

  • ventuspilot@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    I think the “Tools for going from the basic special forms into a full Lisp” are usually called “standard library”. Most if not all Lisps have them, and usually the standard libraries are written in Lisp, i.e. in the (simpler) Lisp that is implemented by the core.

    I don’t think there are any “bring your own bootstrap” Lisp implementations that you will be able to use 100% unchanged. Since different Lisps will have different sets of basic special forms/ promitives the various standard libraries are not drop-in interchangeable, but there’s a lot of overlap. E.g. the function list-length of my own Lisp is almost identical to the code from CLtL2.

    I’d say write your own standard library for your Lisp, taking inspiration from others such as Lisp500 or maybe even my own Lisp’s default library.

    Or skip the “write your own Lisp” part and integrate one of the embeddable Lisps into your platform.

  • jng@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    I’m interested in this too. Functions, lambdas, macros come to mind. But I’d like to here the take from lisp experts.