New users coming fresh to Elements are often wondering how the compiler is able to mix different programming languages, or how it ends up that you can use the same language on different platforms ("doesn't C# only work on .NET?"). Here's how it works.
Elements is not made up of several compilers. We don't have, say, one compiler for Oxygene, one for C#, one for Swift and so on. No, Elements is a single compiler that can understand all these languages.
You can, very roughly speaking, think of a compiler as two halves — a front-end and a back-end.
The front-end is what takes the source code you write and processes it into something more abstract. Usually a project is made up of one or more source files, and the front-end goes through them, parses them according to the language syntax, and generates a more abstract description of what the code you wrote means — called an Abstract Syntax Tree. You might have written "type X = public class
" or "public class X
". The front-end understands this and makes a note "ok, she's declaring a class, and its called X. It does this with all the source files, in one phase, so every part of the code in your project can "see" all the rest of the code that's out there.
Most compiler front-ends understand a single language (say, C#, or Delphi, or C++). Because the Elements compiler understands all five languages, it can apply different parsers to each file – based on its file extension, .pas
, .cs
, .swift
, .java
or .go
— but it still parses all files in your project within the same phase and into the same single syntax tree.
Once this phase is done, the compiler has a full tree of all the code you wrote – all the classes or structs, all the members, and all the actual logic you wrote in your methods or funcs. It's all stored in the abstract syntax tree but, and that's the crucial part, the compiler at this point no longer knows (or rather, no longer cares) what language any given portion was written in. It just knows what your code does.
In fact, not only is the Abstract Syntax Tree representation independent from the language(s) you used to write it – it's also (largely) independent of what target platform(s) you've been intending to write it for. That's where the compiler back-end comes in:
Just as with the languages, Elements does not have "separate compilers" for the different platforms we support. There's no ".NET Compiler" and no "Linux Compiler". It's all one Elements compiler that can use different back-ends to convert the abstract syntax tree into actual binary code (which might be CPU-native or an intermediate byte code such as .NET's IL or Java's bytecode) that can run on a given target platform, and interact with the platform's API interface.
The syntax tree still says "there's a class, and it's called X". The compiler back-end knows what IL code to emit to make this an actual .NET class that the Common Language Runtime will recognize, or what machine code to generate to have the class for the Objective-C Runtime, and so on.
Of course the above is just a high-level description, and a bit of an oversimplification. For example, not all but most code relies on external references and platform APIs, which will make the abstract syntax tree be platform-independent in concept, but not in practice (because external types or APIs it references aren't available on all platforms). Similarly, not all abstract syntax ideas can be expressed in all languages — for example, Go cannot declare classes, and Future Types are specific to Oxygene. But that should not distract from the overall picture.
Summary
I hope this post gave a good, high-level overview of how the Elements compiler handles the different languages. Mixing languages is not for everyone, but it can be a very powerful tool in your tool belt. It lets you reuse code you find online directly in your project without the need to translate it (or make it a separate project dependency). More importantly, it lets you use different languages for their respective strengths. You might be writing the bulk of your app in, say, C#, but find that a certain algorithm is just easier to express in Go, for example.
This blog post is part of a series that looks behind the screens of the Technologies that make up the Elements compiler.