---
title: Crystal's interpreter – A very special holiday present
summary: A little F.A.Q. about this new feature
thumbnail: +
author: beta-ziliani
comment_href: https://disqus.com/home/discussion/crystal-lang/crystals_interpreter_a_very_special_holiday_present_98/
categories: technical
tags: feature
---

The awaited Crystal interpreter has been [merged](https://github.com/crystal-lang/crystal/pull/11159). To use it, you need to compile Crystal with a special flag and, at the time of writing, the official releases (.deb, .rpm, docker images, etc.) are not being compiled with it.

This post doubles as a F.A.Q. for this special feature. Let's start from the very beginning:


## Why Crystal needs an interpreter

While many will find this obvious, it's useful to point out two specific features that an interpreter might enable:

1. In principle an interpreter should start executing code faster, since the codegen phase is skipped (see below), allowing to quickly test the result of some code without needing to recompile. It should be noted that the interpreter is quick for short programs that need to run only once, but compiled mode provides much more performance and should be preferred for most production use cases. Also, while the behavior of interpreted and compiled programs might differ in some aspects (due to the system), we intend to keep the differences down to a minimum, and most programs should behave exactly the same in interpreted and compiled mode.
2. An interpreter improves the experience of debugging, being an ad-hoc tool to the language (unlike the generic `lldb`).


## What is the current status?

The interpreter is currently on an experimental phase, with lots of [missing bits](https://github.com/crystal-lang/crystal/issues/11555). The reason to merge it at this early stage is to enable a proper discussion of interpreter-related PRs and speed up its development a bit. For those willing to try it out, we welcome interpreter-related issues taking into consideration the already known issues aforelinked.


## How does it work?

The [original PR](https://github.com/crystal-lang/crystal/pull/11159) answers it:

> When running in interpreted mode, semantic analysis is done as usual, but instead of then using LLVM to generate code, we compile code to bytecode (custom bytecode defined in this PR, totally unrelated to LLVM). Then there's an interpreter that understands this bytecode.


## How do I invoke it?

⚠️ This is not set in stone!

Assuming you've compiled Crystal passing the flag `interpreter=1` to `make`, you can invoke the interpreter using two modes right now:

* `crystal i file.cr`

This commands runs a file in interpreted mode, so if `write_hello.cr` contains the following:

```crystal
File.write("/tmp/hello", "Hello from the interpreter!")
puts "done"
```

Invoking `crystal i write_hello.cr` will produce the file `/tmp/hello` and print `"done"` to the `stdout`.

* `crystal i`

This command opens an interactive crystal session (REPL) similar to `irb` from Ruby. In this session we can write a command and get its result:

```crystal
icr:1:0> File.read_lines("/tmp/hello")
=> ["Hello from the interpreter!"]
```

(Note: the highlighting is not yet part of the interpreter.)

## Debugging a program

In any of these two modes, you can use `debugger` in your code to debug it at that point. This is similar to Ruby's `pry`. There you can use these commands (similar to `pry` also):

 * `step`: go to the next line/instruction, possibly going inside a method.
 * `next`: go to the next line/instruction, doesn't enter into methods.
 * `finish`: exit the current method.
 * `continue`: resume execution.
 * `whereami`: show where the debugger is.

For instance, if we add `debugger` between the `write` and the `puts` in `write_hello.cr`, we get the following after interpreting the file:

```crystal
From: /tmp/write_hello.cr:3:6 <Program>#/tmp/write_hello.cr:

    1: File.write("/tmp/hello", "Hello from the interpreter!")
    2: debugger
 => 3: puts "done"

pry>
```

And if we `step`, we can see the method form the standard library being called:

```crystal
pry> step
From: /Users/beta/projects/crystal/crystal/src/kernel.cr:385:1 <Program>#puts:

    380:
    381: # Prints *objects* to `STDOUT`, each followed by a newline character unless
    382: # the object is a `String` and already ends with a newline.
    383: #
    384: # See also: `IO#puts`.
 => 385: def puts(*objects) : Nil
    386:   STDOUT.puts *objects
    387: end
    388:
    389: # Inspects *object* to `STDOUT` followed
    390: # by a newline. Returns *object*.

pry>
```

At this point, we might be curious: what are the `objects` that we passed to this method? We `step` once again to have the variable in scope, and we issue:

```crystal
pry> objects
{"done"}
```

## How much faster does the interpreter load a program?

We're definitely missing benchmarks, more so given that not many shards can be successfully interpreted. Testing on a few random files from the standard library and [Crystal Patterns](https://github.com/crystal-community/crystal-patterns), it loads them (and _executes_ them, see below) between 50 and 75% faster than it takes when compiling them (comparing `time crystal <file>` vs. `time crystal i <file>`). This depends significantly on how much time Crystal takes on the common steps to the compiler and the interpreter, like parsing and semantic analysis.


## How fast (or slow) does it run?

As Ary, its creator, [showed](https://www.youtube.com/watch?v=een_W1YEICw) in [Crystal Conf 1.0](https://www.crystal-lang.org/conference/), it runs sufficiently fast—for an interpreter that is. Of course, it's not nearly as efficient as a mature interpreter implementation like Ruby's, yet in our preliminary tests it runs fast enough for the expected use cases. For instance, it can handle millions of integer additions within the second. This said, using the interpreter for processing intensive tasks is definitively discouraged, as that's the task for a compiled program.

---
To conclude, merging the PR is the first step to get a working interpreter in Crystal and, more importantly, it's a testament of the will of the Crystal team to improve the developer experience.
