Hello and welcome, everybody.
In the last lecture we implemented the Plaza Scope entities.
In this lecture, we are going to implement the offsets of the variable node.
So head over to compiler hedge and you're going to want to find struc node.
When you found struct nodes, scroll down into the substructure of var and just done the padding.
We're going to say in it a offset.
So a means aligned.
Okay.
So essentially this will be the offset of where the variable node can be found in memory when it's running.
Okay.
It is not an absolute offset.
It's just an offset from whichever scope it's relating to.
And our compiler will be aware of which scope it's relating to because it needs to be able to generate
appropriate runtime code.
Right.
That will ensure that it uses the same address when it's running.
So that's why we need to keep track of it during compiling.
So obviously this will be set to zero until it's set and in this lecture we will be setting this so
called once you've done your offset, let's go to parse a dot C, but before you do just say aligned
offset OC, expect positive dot C and we're going to start implementing that a offset now.
So head over to make variable node and register.
You can see down here and here we're just going to basically call parser scope offset and we're going
to pass in the VAR node and we're also going to pass in the history.
Now we need to make that function.
So just go void, parse the scope offset struct nodes, var node struct history, history.
So this function will be in charge of calculating the scope offset for the given variable node.
Okay.
So just by here, we're going to say parse the scope offset for stack.
And again, we're going to pass in the VAR node in the history.
And just by here we're going to say void parse of scope offset for stack.
You know, it's good to break the problem down.
Okay, guys.
So that's important
because we'll be dealing with structures and global variables in this scope offset soon as well.
And I just want to scroll up a minute because we need to make one other function and parse the scope
new scope entity.
We're just going to say structure, pause the scope entity, pause the scope, last entity, stop global
scope.
Okay.
So this is going to get the last entity from the scope and it's going to stop at the global scope.
Okay.
So we're just going to say return scope, last entity, stop at current process and then our stop scope
will be current process scope dot root.
So yeah, essentially let's go in here while this will do is is it will go and find us the last entity
from the current scope and it will keep going up the scopes until it finds the stop scope, which in
our case is the global scope.
So this will get us the the last entity, but it will stop looking when it gets to the global scope.
So this is useful for getting local variables and even function variables until we get to the actual
global scope.
And the global scope is anything outside of a function.
So let's head back to the pause scope offset for Stack.
We're going to say struct plaza scope entity, last entity equals parse of scope, entity stop global
scope.
And we're then going to say ball upwards stack equals history flags and we don't have a flag for this
yet.
So just scroll to the top and we're going to create a new history flag.
We're going to say history flag is upward stack and that's going to equal CO two OC So now we head back
to where we were.
We can say history flag is upward stack.
So remember I explained in the scope offsets presentation, the stack is going upwards when we're looking
for function arguments.
So here we determine if if we need to look up the stack or down the stack is this variable node something
in a function argument and we need to look up or is it a local variable and we need to look down?
Okay, so that's important to realize.
So now we're just going to say init offset equals minus variable size file node.
Now why have I got a minus?
Because this is a scope offset.
Now later on in this passive scope offset function, we will check for if it was a global variable and
if it was to be treated differently.
But this function we're making now has a scope for stack that is for the stack, and the stack goes
downwards, which is why we negate this.
So this variable size of it's four, it becomes minus four.
But don't worry, because things that are not on the actual stack will be handled differently later
on in this function scope offset function.
So now we're just going to say if there's an output stack, then hashtag warning handle output stack.
So we haven't implemented functions yet, have we?
So we can't implement the offset for function arguments right now.
Okay.
So if if we do end up here, we're just going to do a compile error not yet implemented.
Right.
And that will just ensure that we can come back to this later and, and, uh, implement that because
we can't, we just can't work with that yet because the functionality is not there.
We haven't even created a function node yet.
So that will have to wait for now for the output stack.
Let's carry on here.
So we're just going to say F last entity offset plus equals variable node.
And we haven't yet created this function.
So just head over to nodes C and we're going to write that.
So we're just going to go struct node, variable node, struct node.
We're just going to say struck no var node equals null return var node.
And now here we're just going to say switch node type.
We're just going to say case node type variable.
Break var node equals node.
So in case you haven't realized already, this variable node function will basically go through the
nodes you provide it and pull out the variable variable node from it, right?
So now we're going to say case no type struct.
And what do we get for a node type struct?
We go var node equals node struct dot var.
And Yeah.
That set of calls.
So that's the structure node.
Yeah.
So we do a variable node on a node type of struct.
It will pull out the actual variable node of the structure declaration.
So that's for when people do things like this, right?
So when they essentially create a variable for that structure, they're declaring like that, nice.
So now we're going to say case node type union
and she's going to do the same thing while node equals node.
Don't forget the colon, guys.
Yeah.
And we don't have a union yet.
That's okay.
We'll just.
We'll just write it as if we have it and we'll just put a comment there as well, guys.
Right.
So we know what it looks like.
We'll just put a compiler error.
And we don't have a compiler process yet.
So let's just do an assertion instead.
Then one equals zero.
And unions are not yet supported because we just want these error messages.
So we have no surprises later on because obviously we don't have we can't implement everything at once.
Right.
So we're obviously going to clash now and again where we have things that aren't done that we need to
come back to later.
And this is not an easy project, right?
But, you know, stick with it and it'll all come together soon.
So let's go to Compiler H and now we're going to paste that in the header file, the variable node prototype,
so that we can access it and parse a dot C back to parse dot C and now we can continue it.
We're just going to go variable node last entity and we're just going to do Node and then we're going
to say var a offset just like that.
Guys.
Okay, guys.
So now that we've implemented this variable node, let's head back to Node C, just want to make another
function here.
So we're just going to say bool variable node is primitive and it's just going to basically tell us,
is this a primitive variable node or not?
Right?
So first we assert that they provided a node type variable.
Next we say return data type is primitive.
I know we haven't yet created that function either.
So let's head today type C and I'll just type Zazi.
We're going to say ball data type is primitive
and this is going to just accept a data type and we're just going to let you return data type, instructor
union D type and then we're just going to put we're just going to negate it.
So if it's not structural union, then it has to be a primitive data type.
Right now, we're just going to copy and paste that into the header file
just like that.
So.
So now we can go back to A.S. and we can say, is the data type is a primitive.
We can just go, no, var dot type and they'll tell us if a variable no type is primitive.
Now just copy and paste this.
We're going to put it in the header file,
just like that back deposit at sea.
And now we can continue writing the scope off set for stack function.
So now here we're going to say if variable node is primitive and we're going to pass in the VAR node,
and if it's primitive, what we're going to do, we're going to add padding.
So we're going to say variable node node var dot padding equals padding upward stack.
And if that's the case, if it's an upward stack, then we're going to pass the offset.
If it isn't, then we're going to pass the offset negated and then we're just going to go no var dot
type size.
And yeah, let's just rename var node to node guys because it's just easier to work with.
Right.
So just rename it to Node just like that.
And then it's a little simpler to see what's going on.
Right.
Just name it to note here as well.
Okay.
Perfect.
So it's easier to type and stuff now.
Okay, nice.
So now I can explain this to you so we can understand this a little more.
So we go in here, we grab the last entity from the scope.
If it can't find it on the current scope, it will look up a scope.
So why do we need to get the last entity?
Well, because the last entity affects the stack position, right?
If we have two integers in some main function by here and we go and it x and y, obviously x is going
to be position minus four, p minus four and Y is going to be P minus eight.
Right?
So that's why we need to get the last entity because obviously we need to take into account all of the
stack elements before us so that we're able to figure out the correct position.
So that's that's why then we check we have an upward stack by checking of the history flag was set.
We haven't really dived into that bit yet.
So it's going to be it's not going to be true right now.
And then here we say the offset is equal to the variable size negated.
So if we imagine a integer no passed in here and struct node, our offset right now would be minus four
OC.
So that's what it will be after we finish running that instruction.
So then we go through here and we check if there's a last entity, if there's not a last entity.
So let's just imagine it's just into X.
Then we finished calculating the position because now it's just minus four, which is the correct position
for a scope variable on the stack, for instance x.
Okay, now let's imagine there is a last entity, so we're going to say ensure y, our current node
is Y and the last entity is X.
So in that case, if last entity will be true, because we've checked the scope here to see if it's
there and then we say offset plus equals the last variable nodes aligned offset.
So now what we have is, is we have minus eight because if we imagine that for integer X minus four
was computed right then here when we go in at offset equals minus four because that's the size of integer
y and we then go last entity and we do offset plus equals the last aligned offset of minus four.
Well minus four plus minus four is minus eight.
So that is our new offset for Y.
So then this is minus four, this is minus eight.
Just see how that's working.
Right?
So I hope that makes sense.
I hope I've explained that correctly for you.
If you're still confused about that, you should let me know and I'll I'll clarify that further.
Anyway, here we say variable node var padding equals padding.
So here we're calculating the padding, but only if it's a primitive node.
Because if you remember, I've said before, the structure, the structures are done a little differently
here.
We call our padding function and we check there's an upward stack.
That's the very first thing we do.
So if there's an upward stack, then we just pass the offset, okay?
If it's not an upward stack, then we negate the negated offset to bring it back to a positive number
again.
And then we are basically passing the variable size here that we are padding to OC.
So that's what we're doing there, guys.
Okay.
So that that computes our extra padding.
So if you imagine something like this upward stack will be false.
So it will run the negation on the minus four.
Let's pretend there's just one variable again in store x to keep it simple.
So I'll run the negation on minus four, given us four, and then the size of our axis four bytes because
it's an integer.
So we basically had 4 to 4, which should give us a padding of zero.
OC So that's how that's working.
Guys obviously don't judge the logic too much yet because we need to finish the upward stack before
that bit starts to make sense.
We will be coming back to here where I'll explain that again when we implement the output stack, which
won't be implemented until we have functions and function arguments.
So yeah, that is how you calculate the offset for the stack.
And as shown in the presentation on the on a on a few lectures ago, you'll see exactly visually how
it's done.
I break the whole thing down for you.
Well, this is us starting to write that code that makes that work.
So we're not done implementing the puzzle scope offsets yet, but I think is appropriate for us to have
a part two for this because we've covered quite a lot in this lecture.
So thanks for watching.
And in the next few lectures, we'll continue implementing the scope offsets for our variable nodes.
Take care.