Write a Blog >>

People keep developing new general purpose and domain specific languages, and we need ways to help them produce efficient implementations of those languages. Two popular ways to implement a language are writing an interpreter or compiler by hand. As the language grows, though, there’s typically an increased demand for tools which need to agree with the semantics encoded in these hand-written implementations, such as type checkers and debuggers.

Keeping these artifacts consistent is tricky, and so we want to support another way of implementing a language: generating the implementation and tools directly from common styles of operational semantics. Existing metalanguages support exactly this: they can generate verifiers, define languages with IDE support, randomly test and debug a semantics , and more.

To be practical, we believe a generated implementation must compete with mature, compiled implementations; to our knowledge no metalanguage is there yet. Researchers working on popular metalanguages continue to improve performance, but the literature on this is sparse.

Our metalanguage, Jam, takes a deterministic, sequential abstract machine and generates an interpreter written in RPython. Key to our approach is optimizing the interpreter with a just-in-time compiler (JIT), which RPython gives us. Our work is inspired by Pycket, an interpreter for Racket hand-written in RPython, that on average beat mature Scheme compilers.

In this work, we present challenges to getting good performance, plausible solutions, and preliminary performance results: on average, we are 21 times slower than Pycket.