Slisp:简易 Lisp 编译器(Linux/amd64)
Slisp: Simple Lisp compiler (Linux/amd64)

原始链接: https://github.com/skx/slisp

**slisp** 是一个将 Lisp 程序转换为独立 Linux/AMD64 汇编的编译器。作为一项学习项目,它为传统的交互式 Lisp 环境提供了一种编译型替代方案,并支持整数、字符串、列表、带有闭包的 lambda 以及用 slisp 自身编写的标准库等基本功能。 主要功能包括用于堆内存的指针碰撞分配器(bump-allocator),以及多种数学和逻辑原语。值得注意的是,该项目排除了垃圾回收、宏和 `quote` 等复杂功能,旨在打造一个简洁且实用的基础。 代码仓库包含一套强大的测试套件,其中包括将输出与预期结果进行对比的功能测试,以及针对编译器内部组件的 Go 语言单元测试和模糊测试。该项目的开发源于作者此前参与更复杂语言项目的经验;通过选择 Lisp 语法,作者旨在利用标准系统 ABI 实现一个更易于维护且具备“实际应用价值”的编译器。用户可以通过内置的构建流程或提供的 Makefile 轻松编译和链接代码。

```Hacker News 最新 | 往期 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Slisp: 简单的 Lisp 编译器 (Linux/amd64) (github.com/skx) 33 分,stevekemp 发布于 2 小时前 | 隐藏 | 往期 | 收藏 | 1 条评论 stevekemp 2 小时前 [–] Hacker News 一向喜欢 Lisp 相关的内容,虽然这是一个非常简单的编译器,但我编写它的过程非常愉快。 我原本在写另一个编译器,但因类型编码和缺乏明确的计划而陷入了困境。我决定退一步,尝试写一个 Lisp,因为它的语法是已知的,而且非常精简。 最终的成果支持列表、整数、字符串、字符、lambda(带有闭包)以及一个相当的标准库——足以编写一个简单的 Brainfuck 解释器,以及标准的斐波那契、阶乘和 FizzBuzz 玩具程序。 所以它只是个玩具,但它是属于我的玩具,也许对某些人来说是有趣的! 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 加入 YC | 联系 搜索: ```
相关文章

原文

This repository contains slisp a compiler which will read lisp programs as input, and generate standalone assembly representations for Linux/AMD64.

The project is either named for "Simple Lisp" or "Steve's lisp", take your pick.

Lisp is traditionally interactive, and provides a REPL, but having a compiled version is still useful, and still allows most common lisp-programs to execute.

Quick links:

    ;; factorial.  woo.
    (defun fact (n)
      (if (<= n 1) 1 (* n (fact (- n 1)))))

    ;; entry-point
    (defun main ()
      (println "Showing some factorials:")
      (println (fact 4))
      (println (fact 5))
      (println (fact 9))
      (println (fact 10))

      ;; exit code - use "(exit 3)" if you prefer
      0)

There are several examples beneath our test/ directory, including:

example.lisp has other misc. snippets, and finally brainfuck.lisp contains a useful/working brainfuck interpreter.

It should be noted that we prepend a standard library of functions to all user programs unless -stdlib=false is added to the command line. That library itself is a useful reference/demonstration of functionality:

  • stdlib.slisp - Our standard library, written in slisp itself.
    • Has a good print definition which handles known types appropriately.
    • Has map, length and similar general-purpose functions.
  • Support for bindings, functions, integers, strings, lambdas, lists, etc.
    • The lambdas have support for closures.
    • Run-time type detection via functions such as int?, and cons?.
  • A rough and ready bump-allocator used for heap-allocated cons-cells.
  • Mathematical operations:
  • Comparision operations:
    • =, <, <=, >=, >, and ! to invert a result.
  • Special forms
    • (cond ..)
    • (defun ..)
    • (do ..)
    • (if ..)
    • (lambda ..)
    • (let ..)
    • (list ..)
    • (set! ..)

You can see a complete list of our primitives, and their details in PRIMITIVES.md - documenting both the built-in special-forms, and the parts of the standard library which are implemented in assembly, or slisp itself.

Anti-features:

  • No garbage collection.
  • No macros.
    • It wouldn't be impossible to add them, but without quote, quasiquote, etc, it's a lot of work.
  • No quote
    • Only really useful if you can call eval and as a compiler? That's not going to happen easily.

Build the compiler:

Use it to compile and link a program:

./slisp example.lisp  > example.s
nasm -f elf64 example.s
ld -o example example.o

Finally execute your program:

ProTip Any *.lisp file in the current directory will be compiled if you run:

This avoids the need to manually redirect, asssemble, or link. It will also run the example.lisp file - though just "make clean example" will do that too, for neatness.

There are some functional test programs beneath test/, which compile fixed programs and compare their output to known-good results. You can run these tests by executing:

Running make clean at the top-level will remove the test artifacts, and compiled programs.

In addition to the functional tests there are also golang tests of the internal implementation packages, these can be executed in the standard fashion:

$ go test ./...
ok      github.com/skx/slisp	0.004s
ok      github.com/skx/slisp/compiler	0.009s
ok      github.com/skx/slisp/env	(cached)
ok      github.com/skx/slisp/lexer	0.008s
ok      github.com/skx/slisp/parser	0.006s

There is also support for the fuzz-testing that golang provides, you can run five minutes of fuzz-testing by executing the following (remove the -fuzztime=300s to run forever, and remove -parallel=1 to run more than a single instance at a time):

$ go test -fuzztime=300s -parallel=1 -fuzz=FuzzProject -v

I've spent a few weeks writing a compiler for a home-made language, s-lang. Initially that language only used integers, but later I added floats/strings/pointers with appropriate type-markers in the lower bits of the values.

I found the overhead of dealing with typing and syntax a bit complex, and kinda backed myself into a corner with it - I wrote a reasonably complete standard-library with File I/O, getenv, and other things.

However adding more types, and dynamic things felt like it would be too complex as it would involve ripping out so much of what I'd done. The compiler, the standard library, and the interface between the two.

So this repository was born:

  • Implement a compiler.
  • With proper typing from the ground-up. Using macros for readability and to minimize the chances of making mistakes.
  • Use the well-known SysV ABI, rather than my home-grown alternative.
  • Use lisp because the syntax is trivial to parse.
    • And I've written interpreters for it in the past so there are dragons, but somewhat friendly ones.

Already this compiler is more "real" and "usable", although it lacks the quality, standard-library, test-cases, and creativity of s-lang. I guess at the end of the day both are toys, and both are here for my own personal learning.

联系我们 contact @ memedata.com