Check out a free preview of the full Building Your Own Programming Language course
The "Transpiling to JavaScript" Lesson is part of the full, Building Your Own Programming Language course featured in this preview video. Here's what you'd learn in this lesson:
Steve live codes a babel visitor that transpiled code to JavaScript, and explains that generally transformation happens when entering a tree.
Transcript from the "Transpiling to JavaScript" Lesson
[00:00:00]
>> Steve Kinney: Once you have the ability to create your tree, and the ability to traverse it, and modify it, you can now write a language that compiles to JavaScript, and use it in the browser, right? There you are at that point. So if we look at we look at ad you can see it is a CallExpression.
[00:00:17]
Funny, right? So we cheated it a little bit by sticking pretty close, but like maybe we call these functions or that you couldn't like our lambda. You could call them anything you wanted, right? Because you can just write a slightly more sophisticated visitor that changes everything to what you need, right?
[00:00:35]
I'm here [LAUGH] to teach you the concepts out to bring you totally in the weeds with it. So they call it a CallExpression, but we just have a name for ours, right? You can see that they do it a little different. It's called a callee and it is It is in fact an identifier with the name add, right?
[00:00:53]
So these other ones are useful for source maps and stuff along those lines but they're not required per se. So we need to go find the name and change it to be a callee property a seven name property, with the type of identifier. And the name, of whatever the name actually was, right?
[00:01:15]
So if we do this, we can have the ability to turn all of our regular, now drop their functions into good old fashioned JavaScript functions, right? And this will now be a compiler to JS language, that we can now add it to our CLI to compile it, spit out some JavaScript and shoot it over to the web, right?
[00:01:35]
So let's try it out.
>> Steve Kinney: We can see there's a two JavaScript test. That's gonna take this nested function. And this is, again, it's the AST that comes from that syntax that we saw, effectively, at the very beginning of this course, right? A nested subtract inside of an add, using that list-like syntax, right?
[00:01:59]
This is the AST that we get. And then what we're expecting to get is what looks a lot more like good old fashion C style syntax, right? And I guess theoretically, this is simple enough that it'll work in many languages cuz this is pretty agnostic right here. But we'll have the ability to have effectively a compile to JavaScript language.
[00:02:23]
We just need to actually implement it ourselves. So we've got this toJavaScript function, right? I've called it babelVisitor because we're targeting the babel generator. That is not actually an important implementation detail. It is only for our sake, to differentiate it from the other kinds of visitors, cool. So let's go ahead and, what kind of expressions do we need to change?
[00:02:50]
I gave halfway the answer there. What kind of nodes do I need to change?
>> Speaker 2: CallExpression.
>> Steve Kinney: CallExpression, cool. So this will then take CallExpression and I'm gonna repeat it so I think it bears repeating. I don't have to change any of the rehearsal stuff, it's all just ready to go and we'll say enter.
[00:03:14]
>> Steve Kinney: I really only care about the node in this case and remember had that callee property. And so okay, we'll say hey, we're gonna add you a node.callee, and is gonna have the type of identifier.
>> Steve Kinney: The name, node.name.
>> Steve Kinney: Right? Again, there's likely in our language other things that need to happen here, all right?
[00:03:46]
We have not implemented variable definitions yet. But clearly we know that syntax will be different than the syntax for our language, right? So more than likely, this would, in fact, have to grow, all right? There are other things that gonna need to do. But for now we can start here and then to JavaScript it's gonna take the entire ast program.
[00:04:09]
And we're gonna try to traverse it, and at the very end we are going to try to generate out some JavaScript that it will make that test pass, right? All right, so what needs to happen? Shockingly little.
>> Steve Kinney: So we have that traverse function already, right? We've spent the time writing that.
[00:04:33]
>> Steve Kinney: And it starts at the top of whatever ast it's given. It takes the babelVisitor, right?
>> Steve Kinney: Now, it is affiliates slightly out of Vogue in JavaScript these days, but for a lots of important reasons. Cuz in the traversal and transformation tree, as you work through it, wherever we have that ability to enter and exit.
[00:04:59]
It is okay to mutate the tree, yeah?
>> Speaker 2: When would you choose Enter or Exit?
>> Steve Kinney: Yeah, if you have to like, if you're waiting for after the fact. It's something that happened, maybe, to a child, will affect the parent, or something along those ways. It's like predominantly, it is, I would say 90% of the time, enter, right?
[00:05:23]
It is enter unless you know you need to exit for some reason because the change you need or you're relying on to compute something on a higher up node hasn't happened yet. Yeah, it's great question. I would say like the OC implementations that we saw before, where it's just the method name, and those would only happen on an enter.
[00:05:40]
In my experience, right? I'm sure and if code base is largest babel there's probably instances of both but it is predominantly happened on enter in my experience. Yeah, I would say you almost always want enter unless you need exit because enter is not getting you something at the time that you need.
[00:06:02]
>> Steve Kinney: All right, so this will mutate the tree. And I think as I was saying earlier, like we like affiliate modern JavaScript think it's terrible to mutate trees. But because you are working kind of up and down and you might be doing multiple transformations, right? We just kinda mutate the tree in place.
[00:06:19]
That's how it's done the large majority of the time. So it's feels almost slightly awkward even as I was typing it. Not to have to store the result in a variable. But it turns out, we've actually been doing that a lot. Not talking about it like when we're popping tokens often array, the fact that, and this is like a definite thing I actually want to like re-point out when like working through strings and arrays.
[00:06:43]
Is in JavaScript the arrays are passed by reference. So you can pass that array around, and you'll have the same copy of it and different functions can mutate it. Remember how we had parent size and parse. They could pass the same array around, but you couldn't do that a string of strings are passed by value.
[00:07:04]
So here we have this tree that we can mutate and we don't actually have to store it in anything. Once that happens, right? If we've made it effectively a babel compliant tree. Then we can go ahead and just return,
>> Steve Kinney: Generate, and this generate, if you look up here is coming from Babel generator.
[00:07:27]
We just have to use the default here because it's subtle to be an Essence module, and we are in plain old node. So that's the only reason. If you're using ESSence syntax, it would just be import generate from @babel/generator. Cool, so we'll do that. And we get on our AST because it's the only one we really care about.
[00:07:49]
And this will return an object with a number of properties. The one that we care about is the actual code that comes out the other side, right?
>> Steve Kinney: All right, and again, the tricky part is as our language goes of course, making it babel compliant tree. But should we be able to do that we get, you'll see it for yourself, let's go to the to JavaScript test.
[00:08:18]
>> Steve Kinney: We will unskip it.
>> Steve Kinney: It was actually run the JavaScript runs.
>> Steve Kinney: And it passes. We're now outputting real deal JavaScript, all right? So now we have effectively three ways to interact with our budding new language, right? We have a REPL, where we can work with it in real time.
[00:08:50]
We have the ability to read files in the file system evaluate them and execute them. And we now have the ability to transpile out to JavaScript, right? So we've got kind of an interpreter and we've got a compiler at this point. We've kind of had our toes in both, right?
[00:09:06]
And kind of gotten a feel for depending on what you're doing is going to be like slightly different. And in a lot of cases there is a case for both, right? Even if you are doing a templating language there's some amount of look ups of okay, do we have that value in the template?
[00:09:23]
There might be conditionals or wire loops or something like that. There's still stuff that effectively happens in there, you might need to work through. But you might, in that case most definitely have a compile target. On the other hand, if you are doing something like we were talking about terraforming in the very beginning, right?
[00:09:36]
The ability to spin up cloud infrastructure, right? Well, that's gonna be primarily run time, right? Or even RubyGems or something like that, that is primarily pulling stuff down off of a repository, right? So the thing that your language does is usually going to be slightly different. Most of us will probably make a domain specific language or something along those lines that solves a given problem, right?
[00:10:02]
General purpose language is also cool to do, right? It obviously has a wider range when you're gonna need both in one of those cases. But depending on the problem you're gonna solve, the answer of what you need is gonna be slightly different.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops