About a month ago I posted my intent to build a new projectional editor for Cedalion. As promised, this post is here to update on my progress.
The thing that makes Cedalion special is the fact that it uses projectional editing. For those not familiar with this concept, the idea is that unlike most programming languages, where developers write their code using a text editor and then the language implementation (compiler or interpreter) parses the code they wrote, in Cedalion developers need to use a special editor to edit the code. This special editor does not show the code as it appears in the source file on disk, but rather shows a projection of this code. What is this projection you ask? well, whenever a developer defines something new in Cedalion, they have the opportunity to define a projection for this thing. In other words, they can define how this thing will be displayed inside the editor. When the editor edits a Cedalion program it asks that program how the different “things” that appear in the program should look like. Then it displays them accordingly. Cedalion also has a “default projection”, which is used if a developer did not specify a projection. Projectional editing is what makes Cedalion’s syntax extensible. You can define a factorial function to look like a factorial, or even a sum expression look like a sum expression (an example taken from Cedalion’s submission to the Language Workbench Challenge of 2016):
The project I announced a month ago and I now refer to as Cedalion2 (for lack of a better name) is intended to build a projectional editor for Cedalion. It is natural that the first major step towards such an editor should be implementing its projection logic. This phase was completed today.
Cedalion’s projection logic is a translation of a Cedalion program or part of a program to a Cedalion DSL named visualization. As the name suggests the visualization DSL consists of language constructs that represent, well, visualization. There is, for example, a construct named “label” which represents static text, layout constructs for laying stuff horizontally or vertically, constructs for changing font color, background color, or font style of whatever is inside them, and more.
The translation from Cedalion to visualization is done in three phases:
- Checkers are applied to the AST. Checkers are pieces of Cedalion code that take a part of an AST and emit markers with information on this code. These markers can report errors (e.g., when types do not match) or any other information that may be useful. The AST is then decorated with these markers.
- The decorated AST goes through the projection process. Both “normal” AST elements (the original code in the program) and the markers are being translated to visuals.
- This third part is new to Cedalion2: We add a post-processing pass over the visuals we got in the previous phase to remove some hierarchy from the visualization, and make it better suitable for displaying in a browser.
The logic that performs these three phases is implemented. Note that this is pure logic at this point. There is no way to execute this logic from an actual editor at this point. So how do I know it works? I use BDD!
Stability with Cedalion Containers
A major drawback of “Cedalion1” — Cedalion’s existing, Eclipse-based projectional editor is its instability. It is easy to cause the editor to get stuck, or to silently fail and show you a blank page. These things happen because the projection logic I described above consults the program being edited. And if the program being edited is buggy (because, after all, we are still editing it…) then you get buggy behavior.
The fact that we need to consult the program being edited did not change. And that program can still be buggy even in Cedalion2. But there are still things we can do to make sure a local failure remains local. For example we can catch this kind of failures and report them instead of crashing.
In “Cedalion1”, the program being edited also implemented the editor. In Cedalion2, however, the editor is a separate program from the one being edited. How do we manage having two separate programs running on the same Cedalion interpreter? We use Cedalion containers.
Cedalion containers, like Docker containers, are a virtualization technique that allows a program to run in isolation from its host. As in Docker, this is done by placing that program under a namespace (Docker uses a feature of the Linux kernel named cgroups). We simply add a certain prefix to all the names the “containerized” program defines or uses, with the exception of built-in predicates which are treated separately. Given Cedalion’s purity, this addition of a prefix is enough to create isolation in which a Cedalion program can work from within a container without even knowing it is containerized, and without having the ability of affecting anything outside the container. This is very important if we wish to allow user code to run on a shared server. But even before we get there, containers are useful for separating between the editor and the program it edits.
Cedalion2 is therefore designed in a way that separates between the two. The editor implements the three phases described above, but every time it needs something from the program being edited it calls it from within the container. We also use the timeout() built-in predicate to limit the time we allow each of these operations to take (to guard against non-termination) and these calls are placed in a try/catch block to guard against exceptions.
Plans Going Forward
After mastering projection, the obvious next step is to tackle editing. Editing is not pure logic anymore. It requires state to be maintained (the content of the file being edited) and imperative mutations of this state (the very essence of editing). Cedalion has a DSL for doing such imperative work while remaining close to its logical core, a DSL named impred, which stands for impure or imperative predicates.
I’m thinking about an abstraction called an editing command, which can be applied to an AST and returns a modified AST and an undo-command — an editing command that reverts this one. This allows an undo/redo mechanism to be implemented by the client without knowledge of the actual editing logic.
After editing logic is in place it will be time to wrap the editor in a HTTP server, and start working on a client.
Time (or lack of)
As I expected, my time is a limiting factor in this effort. I’m working part-time in the industry and doing a PhD, which requires me to write papers and prepare and presentations (e.g., for SPLASH ’16). All these take time. As result, it is hard to predict when a first “shippable” editor will be ready.
IDE (or lack of)
“Cedalion1” was implemented as an Eclipse plug-in. While this is one of its limiting factors today (e.g., you need to install Eclipse to have Cedalion), it also had some benefits. For example, I didn’t have to implement a file browser. Eclipse already had one, and double-clicking a Cedalion file there opened the Cedalion editor. I didn’t have to implement git support either. Eclipse comes with git support out of the box.
Cedalion2 is intended to be a web application. This means I cannot integrate it as part of a desktop IDE such as Eclipse. There are a few interesting looking open-source cloud IDEs out there, but none of them looks like it is robust enough, general enough or simple enough to be an obvious choice.
With lack of an IDE a lot of things that such an implementation could get for free now requires implementation. I would appreciate comments and ideas on what I can use to reduce this kind of redundant work to a minimum.