5 ms·
Writing a simple Lisp interpreter in Rust
- eldenring 4y agoIt seems dirty to use parser combinators and a tokenizer generator for lisp instead of just... writing the parser. That's part of the beauty of lisp. Maybe a different syntax would have fit this sort of post better.
- shakow 4y agoIMHO, for having written a Lisp-like DSL that is heavily used in our product, the beauty of Lisp is not in spending a week writing a bug-free parser. It actually resides in the regularity and the leanness of the generated AST, that makes it very easy to add syntax, forms, builtins, etc. and thus iterate reliably and fast according to emerging use cases and requirements, virtually without ever having to worry about breaking the grammar. Also, it's pretty easy to learn for end-users, even those not overly familiar with programming.
- alpaca128 4y agoWhy? It's a simple tree-shaped algorithm for a tree-shaped problem. Doesn't get cleaner than that.
- eldenring 4y agoThe benefit of parser combinators is that they allow you to write "tree shaped code" to parse a syntax that doesn't map cleanly to a tree, ex: a c-style syntax. My argument was that if the article wanted to show off the rust parser combinators, another syntax would have done the job better.
- ludston 4y agoEspecially because it closes the door on things like "reader macros" which are one of my favourite Lisp features.
- linkdd 4y agoWell, the article is called "a simple Lisp", not "a fully featured Lisp". It's a demo, not a compliant implementation. You may be interested by https://github.com/brundonsmith/rust_lisp https://github.com/brundonsmith/rust_lisp though
- ludston 4y agoAnd to be fair, reader macros are a pretty contentious feature in themselves, as they can be used for great evil. I was mainly making the comment to be educational. Your work here is impressive regardless.
- linkdd 4y agoAuthor here, the article is more about how Rust and its ecosystem are nice tools for language designers rather than the beauty of Lisp. The crates listed in that article are the ones I use for my compiler: https://letlang.dev https://letlang.dev Lisp was only chosen as a way to demonstrate the power of those crates and Rust features. A kind of way of justifying my choices for Letlang. It's not "you should do it like this" but "you can do it like this".
- nerpderp82 4y agoYour language is the first that I know of that compiles to Rust. Can you talk about that? Does the generated code have to pass the borrow checker or did you design around it?
- linkdd 4y agoAfter the translation of the source code to AST, I have walk through the AST to validate the code as much as possible. The generated Rust code should be valid Rust code that compiles with no error. I only have 2 exceptions to that rule: --- Function calls: I haven't found a way to verify that the identifier used resolve to a function with the correct signature (it would need some sort of partial evaluation and static type checking I don't have). Rust FFI: Letlang will have the ability to call native Rust functions (allowing me to rely on the Rust ecosystem to implement the stdlib), but it expects functions to have a specific signature. --- Those are the 2 cases where `cargo` can throw an error. So I (plan to) include source mapping comments in the generated Rust code. After getting the output of `cargo` as JSON, I can identify the error type and location, then using the source mapping info, I can pinpoint what AST node generated the wrong code. On a semi-unrelated note: Every Letlang module is a single Rust crate. The compiler generates a cargo workspace, and a cargo project in that workspace for every module. It makes dependency management (and my life) easier. It's funny how people are intrigued by my choice of targeting Rust, while other compilers targets C, Javascript, or other higher-level languages :P But I guess it's because I use the term "compiler" and not "transpiler". Hint: I hate the word "transpiler", which is just a synonym to "compiler" IMHO.
- ameliaquining 4y agoWorth noting that this implementation doesn't incorporate a tracing garbage collector. Any reference cycles in the interpreted Lisp code will result in memory leaks.
- samsquire 4y agoThis is really interesting, thank you. I am interested in a Lisp language called Parlanse, http://www.semdesigns.com/Products/Parlanse/examples.html http://www.semdesigns.com/Products/Parlanse/examples.html It has built in parallelisation. I find LISP is really useful as an AST but I am not familiar enough with LISP to read it fluently as well as C or Python.
- dgb23 4y agoFamiliarity can certainly be a stumbling block initially but I found it isn’t always the main problem. It’s very easy to write code that’s hard to read, because everything nests and composes neatly together. But a Lisp usually also gives you a lot of tools to structure code very nicely. Such as: - local bindings to structure code in a LTR fashion - macros to provide syntactic sugar - functions are very easily extracted, because everything is an expression - closures provide context and can be composed and specialized Points 1 and 3 can be provided as automatic refactorings by a good editor. So code can be literally an AST dump versus very clear and expressive with a Lisp.
- montmorency88 4y agorustacean lisp should be called clam-bda.