 Alright. Good. Let's get started. Plus you. Let's kick it off with questions. Any type of questions? No questions? Yes. For the homework, can we basically just provide pseudocode for the closer? Yes. The parser should be coded similarly to how we did in class. So is it fine if we use, like, platform-style things, like you just draw in? Uh, yes. That would be okay. She probably doesn't have a comment at the top that says what you're doing. That way, whoever reads it knows what's going on. Cool. Any other questions? Yes. Hmm. So, let's think about it. So let's say we have A goes to little A, C, A, something like that. Cool. So walking through what I'm doing. Is that okay? Yeah. So why are we checking this? Cool. Okay. Checking to make sure we're doing the right rule. We're sort of testing the first of A, C, A, right? The first of this rule. Perfect. Okay. So then what? Cool. So we call part C. So assuming this returns correctly, what do we know? Yeah. So think about the input string, right? So we first read in an A. So it had to have been an A to get there. Yeah. And then, I don't know, then what happened? Part C. Yeah. Part C. Parts whatever this is as a C. Right? So what do we know must come after this C? Small. Got to be a little A. Exactly. So how are we going to check? And then when we return, when part A returns, what do we want to be true? Right? So we parse it up to here. And then we call part C. So we parse it up to here. So after A returns, where do we want to parse? Yeah. Yeah. But where do I want to have been parsed? Do I want to return it like this? No. I want to return it. It has parsed all this thing, right? So whoever called us says, hey, great. They just called parse A. All this stuff is an A. And then they can start parsing. So like I think, as both of you said, right, I'd want to get a token. And then what do I want to check? If it is A, then what? Print A C A. Then print the rule A goes to rule A. Same text. Yeah. So why do I need the else? Yeah. If it's not an A here, then you know for a fact, because you've already successfully parsed a C. So next thing, better be an A, right? That's what the rule says. Parse an A, then parse a C, then parse an A. Right? So we've parsed a C, and the next thing is not an A. And this is a syntax error. And this string did not come from this grammar. So print the rule aside from bottom of bottom. And it's kind of a print. So could we print? Let's say it's here. It's kind of a theoretical question. It's more of a philosophical question. So could we print here? Yeah. So we've decided between all the different rules of A, right? By looking at that first token, we've decided is it A goes to ACA, or is it A goes to beta, or gamma, or delta, whatever, right? We've decided it's A goes to little A, C, A. But what do we actually know that we successfully have parsed an ACA? Right. It'll come in par C. But we said the rule A goes to little C, little A, big C, A, when really that's not true, because it's an invalid part. So it's really just a matter of... It's more preference to me to make sense of the end, because here, right here, is when I know that this is a valid rule. Up until this point, all along here, it could blow up, right? It could not be a C, there could not be a little A. Anything can happen. But I know for a fact that right here, it's a valid rule. Whatever else happens, yes? First of all, the follow of A contains B. Yep. So the follow of X contains B. Does it, or actually does it, or would it? Let's say it's something also that's on. So what do I know? So what do you guys think? So by writing the parser for X, so we'll first check whether the delay contains B. You're checking for, right? So you have a rule in the form of X goes to alpha, right? So you're checking the first of alpha. You're checking the first of A B, which in this case is going to be whatever this dot dot dot is, and also B. So after that, do we have to check for B again? Does more of it in the loop? Yeah. So how do we write this? Let's do one parse X. So T type, get token. So I'm checking if T type is this, right? I'm going to do super and go like this, because I can. So T type is one of those things? Parse. Do I just want to call parse A, though? Did this generate one of those terminals? Right, you first want to call unget token, right? You really want to call parse A. So we think about the input string that's generated here, right? So we were parsing something. The first thing is going to come from an A. We don't know it's going to be a sequence of terminals. But the parse A function is going to handle that, right? So when we first parse here, we read the first token. Maybe it came from here. Maybe it came afterwards. We don't know, but we know it's included of first A B, right? So we call parse A, and when parse A returns, the parsing is now here. So now what do we know must have to be the next token here? B. It's got to be a B. So I do the same check I did above here, right? And I have to check and consume that token because I called unget token here. It could be the case, I don't know if 100 is that true, but it could be the case that parse B, that A could start with a B. Well, technically it can't, but whatever. It will. It'll consume whatever it needs to consume, right? If A goes to epsilon, then it can consume the B. But if you choose the rule A goes to epsilon, you don't consume any tokens. You would call unget token, right? So parse A is responsible for whatever it's doing. If it turns out that A went to epsilon, then the A part is nothing, right? And it would just be B. And so how would A know that it should go to epsilon? Yes, look in the follow set of A. So it would parse A would check for a token, and it would say if I got a B which is in the follow of A, then I know it must be the rule A goes to epsilon. And so it would call unget token because that B did not come from A goes to epsilon. And then it would return to us. We would then get token and check. And it's a little B, which you can't do. But just for clarification, right? That's what's going to happen here, which is the same sequence of steps that happens in the game. We do the same thing. Any other questions? For recursive descent parsers, they're only going to ever get, and then maybe unget one token at a time? Yes. Just one. Will they ever have to get two tokens in a row? Never. Because then we're extra predictive, I guess. Predictive only by looking at one token at a time. Which feeds back into, if you want to figure out why, well, we've been only talking about first sets. It's only talking about the first token. Could you write something that was the first two set that calculated the first two characters? Yes, you could do that. It would be a lot more complicated, though. Is there a follow set aspect there? Is it still descent parsers use follow sets? How do you predict if recursive descent parsers use follow sets? You do it in epsilon. When there's an epsilon. Okay, that's the key. When there's an epsilon in the first set. So if there was an epsilon in the first of ACA, there's not. But if there's an epsilon in the first of ACA, we would also have to check for follow A to know if we chose this rule. So in the case of A goes to epsilon. Or in the case of really A goes to anything where epsilon exists in the first of that thing. How do we know if we choose A goes to alpha or A goes to beta? So we know because it's a predictive recursive descent parser that alpha and beta must be distinct. They're first sets. That's rule one. They have to be distinct. So if we're saying epsilon exists in the first of alpha, we know epsilon cannot exist in the first of beta. As defined by the graph, the production rules can't violate that rule. Correct, correct. If assuming I've done my first and follow sets correctly and I've proven that it's a predictive recursive descent parser, which is why I'm trying to write a predictive recursive descent parser, then I know this for a fact. So now I can say then if alpha went to nothing, this token I'm reading came from after. If the first token that came after that follows A and so I would check, so you're checking for alpha, so I'm checking the rule like A goes to alpha, I would check if T type in first of alpha or T type in follow of A. That's what I know this rule is chosen. Alpha itself could just be the symbol epsilon, in which case we have the rule A goes to epsilon, but even if we know that the T type is in follow of A, we still need to call, like let's say it's A goes to B and C, we still need to call part B and then part C, so that they can deal with the fact that they, B and C, are going to epsilon. Because remember, we don't know exactly what this parser is going to do for each of these leads in each of these cases, so we need to make sure we're calling the correct parsing functions to build this tree out. Like we don't want to short-circuit it because the tree, there does not exist a rule where A goes to epsilon, right? There's a rule that A has children B and C, and each of these maybe goes to epsilon, right? We want to figure out exactly what it is and what this tree looks like, because this is not about a parser in this language. Yes? So then like you see we want to figure out where the epsilon position goes, we use the follow, we use the follow set of B. We would use the follow of B, exactly. Yeah. These would each individually do it on their own. Call getToken and figure out, oh, this is B goes to epsilon and choose that production. And then we follow a C up so we do the same thing to the C. And you've got to think, right, they're all reading the same token, but that's fine. Right, they're reading it, and then they're putting it back to let somebody else deal with it. Right, because A only cares about A goes to B, C. It doesn't care what B and C do. It just knows that it has to choose this rule A goes to B, C. Yes? So for my grammar, if I cannot write predictive recursion of this kind of parser, then what often do I have to do? Do it differently. So you either change your grammar to support a predictive recursive parser, which you can do, or you can essentially write a brute force parser that would basically... So in our parser, we stop any time we get a syntax error. In a brute force parser, you would just try... You'd essentially first... If you had a rule like A goes to alpha and A goes to beta, right, you'd first basically say, try parse alpha, whatever, this is parsing all that stuff. And then if that fails, then try parsing a beta. And then if that fails, then there's actually a syntax error. Exactly. Yes. Because you have to try every possible... You're essentially trying all possible trees. I'm not sure how to answer that. So you mean where it's like running forever or actually... No, no, no. We have to implement this in a project file or whatever you said we were going to have to build one of these. You will be building one of these. So you're going to be reading the memos, the fact that the recording stops and the error happens, but it still saves the actual recording. That's a good feature. Of course, we didn't crash in the first place. That would be even better if you could read it. So, yes. No, for project 5, we're going to be building... So there's a back end that takes in this intermediate language, which is in this graph, and runs and interprets the program. And you are doing the front part of that, which does the lexing and parsing and generates this intermediate representation graph. It's very similar to a parse tree if you're doing parsing, but instead of building an explicit parse tree, you're building this back end graph. Yes, we can cross that break. I'll give you some project 3. Cool. All right. Let's get back to semantics. Cool. All right. So we are now at... We've been looking at semantics and we've been talking about... We've been talking about names. So we've been talking about when I declare either a variable or a function of my program, how long is that declaration valid for? And we've seen that there's different types of these. There's different scopes. So C is basically... These would be global scope. X, bar, and foo are all names. They are decorations of names. And X is a variable with the type int. Bar is a function which returns nothing and takes in no parameters, zero parameters. Foo is a function with the same signature. So it returns nothing and it accepts no parameters, but it does a body define. And inside here we have variable C. So we know that because C uses block level scoping that this variable character C is going to be valid just within the block that it's defined, that it's declared. So it's going to be defined here to the end of this block. Whereas X, bar, and foo are all technically global. So their scope will exist from their declaration all the way to the end of the file and any other files that include them. Cool. Okay, so we kind of went through here. So we have... So what's the big... What's the issue with this X here? So if we want to resolve this usage of the name X, right, we have an identifier X. How do you map this back to a declaration? How many X's are defined at this point? From this scope, how many X's could be accessible? Three. Two. Three. Two. Just keep saying that. So walk me through. I can see how many X's... How many declarations of X's do I see in the program? Three. Three. So that's right. We're all clear on that. Three declarations in the entire file. The first one's up at the top. Global declarations. So we know this X is valid from here all the way to the end of the file. So could this be accessed here? Yes. That's one. The second one would be this X. Second declaration. What's the scope of this declaration? Just until the end of bar, right, we cannot refer to this X outside of this scope. So can we access that X from here? No. No. So then we have our third X declaration here which is a character pointer called X which is only accessible inside this inner block inside main. So is that accessible from here? Yes. So two, we could only possibly access two. So which one should be accessed? The closest one. Closest one, how? One you're really close with. That's inside the same block. So we're going to look at how this actually happens but we're going to look first at our local block where X is, is there an X declared inside this local block? If not, we look out into the block that contains that block. In this case, it's going to be here in the main. This main block doesn't X exist there? A declaration of X exists here? No. No. And then we check global. We check the global scope and we see that there is an X here. But our algorithm terminates on the first instance it finds an X. So it finds an X here in the global scope and stops looking. So this is what language feature that you've learned about. What was that? Shaddling. Variable shadowing, right? So this, when I call this variable here X I now, inside this block and all children blocks I cannot refer to this global X at all. It shadows that global X. There's no way for me to say I actually want the X that is the global X not this local X. Because we don't have any language features to talk about scope. It's just implicit there. It would still, it would, what did it do? It would print out, because X is a character pointer so inside X is a pointer so there's an address inside X and that address is where the string lives. So it would print out that address as an integer. So it would still probably work, unfortunately. This is why programming is hard. Yes, it would print out this X as an integer but not the global X. Yeah, sorry. No. Because this, so, okay, that's, I think that's, we talked about that. You're supposed to think about that over the weekend, right? Yeah, why doesn't it give you an error? X has already been declared. Yeah, so the idea is, it really does, they're all kind of related. So coming back to shadowing, so because of the way the language is defined this X declaration shadows the global X declaration. But if we tried to declare here in between int main, like another character pointer X, a global X, it would say no, you can't do that. You've already declared a variable called X in the same scope. So in one scope, we can only have one declaration of the same name. Because the idea is, anytime we use an X, we have to know, or a name, we need to know exactly what declaration we're going to. So here, because of the way we resolve it, there's no ambiguity. We check our local scope, there's one X done. But if there were two global Xs, I would not know which one necessarily I was talking about. Cool, yes? You're saying we can't put inside where, here? Yeah. You, no, no, you couldn't. So this is not a declaration. This is using the variable X. Okay. Right, declarations are declaring X. Like here, I'm declaring there is a variable called X, which has the type name. So languages are different in how they do this. Specifically in C, you have to explicitly declare a variable. And for those of you who've experimented with just coding C, you actually have to declare them at the beginning of every block. You can't declare it later on. The question was that because the char pointer is shadowing the global, if you put another X equals 10 below your last printout statement, would it set the char pointer to 10? Possibly puke because it's a null pointer because the 10 memory address isn't accessible, or is it going to, is that what would happen? Yes. So if you move this X equals 10 line here, it would still work and compile. It would probably give you maybe a type warning. But yeah, you can set a character pointer to an explicit value, and that's, you're saying instead of my character string starting at whatever memory address is inside the variable X, it's actually in at memory location 10. Which when you try to access that you probably fail because memory address 10 is not allocated to your program. That's, sorry, just a second. That's a segmentation fault. Because that segment of memory which contains memory address 10 is not allocated to your program, so it's a segmentation fault. That's where segmentation fault ends. Yes, that's exactly what it means. Sorry, there's over here first. You can see the variable's capital scope, right? Yes. Is that true for C99 as well? I don't know. Because I know that you can do it, but I'm wondering now if it does like JavaScript where that's poisoning or something like that. It may, I... Because I'm wondering, what if you move the X equals 10 down two lines from already, so just above the chart, sorry. Like here? No, no, down at the main. Oh, oh, oh, this? And what if you move it just inside of the scope but at the top? Now we get to experiment. I didn't plan on doing this, but this is fun. Temp. What are we doing? So we have a program here. The first thing we should do is make sure this... Make it a little bigger for you. The first thing we should do is make sure this compiles, right? Before we actually have to actually write out your file. There we go. Okay, so it's giving me one warning that this integer X is unused, right? Which is kind of nice. Okay, so we have... So just to make it super clear, if we had a character pointer X, it should give me an error. So I'm trying to redefine X with a different type. Ooh, interesting. Could I actually call it an int? It's because it's not completely defined when it's up there into X. It's been declared, but it's like a weird form declaration. Whoa, crazy. It's like pseudo... Oh, yeah, okay. So it's the same thing as the function, right? So we're declaring that there is a variable X. Just like here, we're declaring there is a function bar, but we don't have a body for bar yet. That is super weird. I did not know you could do that. Learn something new. It's good that we experimented. Okay. So the other thing we wanted to try is what we did with this. But this is also not the GCC compiler. This is a Clang. So it's different. Did you do it with GCC? I did GCC, but on the Mac, GCC is linked to Clang. There's no GCC because they switched everything over to Clang. So now the interesting thing is, what happens if I print out X? Let's verify that this works really quickly. Okay. So this actually does, the warning tells you it still compiles, but it does Clang is doing some advance, like looking at this string that you're using for the format string and trying to determine. And if I ran it, yeah, instead of testing, I see the super large value, right? Okay. So that's cool. This actually works in GCC. Fortunately, I'm going to use, what do we call this file? By that, so if I GCC is weird. What if there's a way I can make it be the old standard? I'm shocked that C doesn't prevent you from shooting yourself in the foot. Is that it? Sorry, guys. What's basically all C does is help you shoot yourself in the foot. And C plus plus is just more advanced ways of shooting yourself in the foot. Oh, just see if this is silly. This is not standard. All right. Yeah, you're not supposed to do it. It's crazy. I was doing this with somebody. Wasn't we playing this and it didn't work? Wasn't it just getting warnings and it compiled it? Yeah, it did give you warnings and it did compile. It was a thing. Maybe it was an older standard. Yeah. Maybe it's saying something about non-point function maybe is a good amount in terms of new thing. Could be. Well, that would be one thing. I'm sorry. That's an unused variable. My ANCC? Yeah, I thought. Huh. Let's get up on this, but... Okay. Maybe it's because we have this global X. Let's see this error. Mainly to know I'm not crazy. Oh, because I'm printing out X here. I see. Any other... But I want... How do I make it still only warnings? All right. Well, turns out I don't know anything. Cool. Computers, how do they work? Remove what? Remove braces? Yeah. That's going to completely break it. This? Before character start. What? Yeah, I shouldn't be able to use this X here. I don't know. Maybe it's just in for loop. I know you can't declare variables inside of a for loop. Is that what we looked at? Yeah. We definitely can't do... Okay. But I thought that was because you're supposed to declare it. There we got an error. Read declaration of X with the linkage. For loop, initial declarations are only allowed in C99. Okay. That's definitely true. Cool. Any other experiments we want to run? Join. Okay. So part of what we want to do here is we always want to understand what we're looking at here. We're specifically looking at static scoping, right? We're saying that no matter how this program executes, this instance of X here is going to refer to this declaration of X. And we know that because for every declaration we can calculate based on the scoping rules of our language, we can say that X is valid here, all the way to the end of the program. Just like bar is valid from there all the way to the end of the program. And that's why we can call bar here in foo without there being a body for bar. Right? Because the compiler knows, okay, you want to call this function called bar, you told me about it. And you know that later you're going to tell me exactly what bar is, but for now I can know that you just want to call the function bar. Okay. Same with foo. Then we have a character C that's only valid in this block. And think about it. Think about how weird it would be if in another function we can refer to this value C. Like in that, if we could say, hey, print out the value of C from foo. Right? That doesn't even make sense, right? Because what if C's changed, what if foo's never executed? Right? Yes? Well, it kind of does if you think abstractly enough that all methods and functions are classes and all classes are just methods and functions. And so that C is a member of a class called foo, which is really a method. And so you're accessing a member. So you just restructure your brain if you want to do stuff like that. The key problem is that C depends on changes on each exit. Or it's a new instance of C for every invocation of the function foo. So it'd be like you create a new object with a new method foo, which had a new class C, or a member of C. So you have to be like which one are you referring to because you've already executed foo twice. So which C you actually want to access. Bar calls Baz going here. So what is this X here when we're looking at it statically? The global X. Yes. So for static scoping, which is the way we're used to, this X is only going to refer to this X here. Every single execution, no matter what functions we call before or after. We'll see. Dynamic scoping does exactly what you're talking about. It actually looks up the call chain to see where was the last X declared, and it uses that X. If I did what? If you passed the X in. Yes. If you passed in as a parameter to Baz, the value X, it would be 100. Because inside the scope here, X is exactly 100. This X. But there are no uses of X, which is why I complained about it with a warning. And so I didn't derive any of the functions, but they're the same. Oh no, bar is still the variable. I don't know if I hear it. Cool. So we have inX, so inX is valid only within this scope, this inX. And similarly, this character pointer, X is valid only within this block, because this is the scoping rules of C. Okay. So we're doing this statically, right? We do it once. We map the variable usages to the declarations, and that way we can see what happens when variables change. So I want to get, okay, yeah, cool. So as the program goes, we're mapping, right? We map this, we map the bar to this declaration of bar, which is here, right? We map this X to this global X, right? Because we said, is there an X defined in this local scope? No. So then we need to access the global X. And the same with C. We say, is there a C in the local scope? Yes. Yes. So that is the C that this usage of C refers to. Cool. The printF, where'd it come from? Include. Yeah, it came from the include, right? It's not magic. If you go look at standardIO.h, it's going to have a function that looks exactly like very similar to this bar declaration that says there's a function called printF. Here are the parameters, and here's the return value. Cool. This X refers to the global X, right? It looks in its local scope. There's no variable named X to play right there, so it checks the global scope. Cool. And so on and so forth, right? This X, does this refer to this X over here? No, it doesn't make sense, right? A, we talked about it. And B, it checks its local scope. Doesn't X exist in this local scope? No. It doesn't. We can't check this child's scope, right? This scope hasn't happened yet. Right? We can't check that, so it has to be the global X that this X maps to. And this X, as we saw, is a local X. So, we actually saw this. If we compile this, it'll compile it will, depending on the compiler. It'll give a warning about not having a return in main. And then when we run it, it's going to output testing. First, right? So that's another thing we need to be able to do. The skill that you're going to have throughout this class is be able to look at C code and tell me what the output is going to be when you execute this code. Right? So this means you need to be able to step through and think about exactly what's going to happen, right? So main's going to be executed first. So what's the first thing that's going to happen? The assignment. So which X is going to be set to 10? Global X. Global X. So global X is going to get to value 10. Then we get into this block. We initialize a local X in this block to be the character string testing. And so when we print out, what are we going to print out? Testing. Testing as a string. So we just print out testing. So now we're going to call foo. Right? So we're going to call foo. We're going to set this character C to be the character C. And then we call bar. And bar sets which X to be 100. A local X. A local X. And then we call baz. And baz prints out X. 10. So it should be what? The global X which has the value? 10. And then which X is this assignment happening to? The global X. Right. It's going to be 1333. And then it's going to come back from bar. So baz returns. Which comes here. Bar returns. Which comes here. And then it's printing out X space C. So this X is the global X which has the value of 1333. 1337. And the character C. So it's going to print out 1337 space C. Foo returns. And then we're done. That's it. So this is static scoping. This is something that we are very familiar with because this is how we write most programs in our programming language. An alternative design decision from the language designers perspective. They're the ones who decide the semantics of this language so they choose which type of scoping semantics do I want to use. So instead of static scoping you can actually have dynamic scoping. And the idea is the symbol table where the symbol table maps declarations names to declarations it's created and updated as the program executes at runtime. So as the program is executing as new blocks and new scopes are being entered the symbol table is being dynamically created. And so when we want to resolve a name called X we look it up in the table for the last encounter of declaration X. Whereas on static scoping we can do that beforehand, right? We look first in the local scope and then we look in the parent scope and then the parent scope and then finally the global scope and if we don't find it then we say that you're using a variable that hasn't been declared, right? But that's statically. We're looking at the program itself and the code in the program this, the lookup tree and the lookup that we're going to perform depends on the actual execution of the program. So we'll see how that affects things. So X could actually completely change it could be sometimes an integer it could be sometimes a string it could be a function depending on which path we take through the program. So just though you realize this isn't just some crazy esoteric feature common list as a language allows actually allows both so it does static scoping but you can specifically declare variables to say I want these variables done dynamically and we'll talk about once you understand how it works we'll talk about kind of why why it does it that way. Okay, so here's our fancy program that we already looked at and so what we're going to go through is we're basically going to step through the program as if we're executing it and we're going to build the symbol table for all the scopes as we execute it. So the first scope is wish scope the global scope, right? You can think about we're not executing foo foo is not the first function that's executed but we are looking at all the decorations so we look and we see aha in the global scope there's a name called X and it happens to be an integer right? You can't really see it now that I see it later but this is a table with two columns the first one's the name the second one is just the type information the metadata and the right one is the value the actual value that that variable holds so then here we look at bar and we say okay, bar is a function it takes in nothing and returns nothing but it doesn't have a body we don't know where the body of bar is yet it's just a declaration of bar we see foo and we see okay, we see foo we see that it's the same it's the same function signature it returns nothing and takes in nothing but it's actually body is declared on line 4 we see badge we make a similar thing then when we see bar do we create a new name in our single table called bar? no now we look it up do we see bar? yes we've seen bar this is the body of bar so we update the body of bar to say bar is actually defined on line 13 and then I do the same thing main is just a function just like every other function and so main we're saying main is a function that takes in nothing and returns in it and you wish we didn't show here and it's defined on line 17 does that have anything in my global scope? cool so now that I go through build up this global scope which function gets executed first main main right, that's the semantics of C main is the function that gets executed first so now here when we're executing this line of code we say okay what do we need we need to resolve X to some decoration so we look it up in our symbol tree we'll see we start at the bottom with the least most symbol you can see we're gonna push things on and we're gonna pop things off but we look up and we see do we see an X in the symbol table? yes yes so let's set the value of that X to be 10 right happens to be the global scope cool we enter a new scope here and now we enter a new scope we're going to I'm gonna kind of draw it with like an arrow and then a new box right because we've entered this new scope dynamically and then as we see decorations we're gonna say hey there's a new variable named X and it's tied as a character pointer and it has a string testing which technically is a true point into the string testing but we can we'll get to submitting some pointers later so now when I see this X do I say oh this X is the same thing as this global X no right I look up in my symbol table basically starting from the bottom looking up and saying is there an X in this symbol table yes there's an X character pointer so this is what this X refers to so when I print out I'm gonna print out the value testing has anything changed so far? no it's been the same now what happens once I leave this scope I want to remove all variables that have declared that scope so for my symbol table I'm gonna pop off all of these variables and get rid of everything here so let's get rid of it it's done now I call the function foo so I'm going to jump into foo and did I enter a new scope? yeah so I need to add another arrow and another scope here and now I see here I'm declaring a new character C with the value C then I call bar and I'm in bar did I enter a new scope? yes yes did I get rid of foo's scope? no right and can you see that this the symbol table is mimicking the current call chain right we have the global variables which called main right the main doesn't declare anything so we don't have main in our symbol table and then main called foo and foo declared C and then C is going to include C then called bar and bar now includes a new integer X that's the current bar when it looks up this trace does it check the typing so if you set X to a string would it look up and say well there's an X in the scope but it's not a character pointer so we're going to keep going up the scope until we find an X that matches the type of what we're trying to do short answer not usually we'll actually see how normally no so it would it will do type checking so it's expecting a string but you actually gave it something else like an integer it would usually it'll do type checking right then like Python does type checking then otherwise it's not going to do it and you'll have to kind of manually erge it will either throw an exception at that specific point but it won't try to find the correct type because it doesn't know that X here's just sees an instance of X and is trying to look it up it doesn't know what type X is it may be able to infer that from the context but we'll look at ways that you can actually do that but without that you can't know exactly which X you're trying to talk about cool, that's our time oh, we're over okay, cool