-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
23b Object: Attributes #59
Conversation
Getting an attributeOkay, we can do this in a number of ways. Our object is very similar to a Frame, except without Or we could make a new Expr, let's call it Attr. We would need a new resolver and evaluator for it. Hmm, without trying to plan further than I can imagine, I think I'll go with reusing a Get Expr. Resolving is gonna be a bit tricky, since we can't just insert the frame; we'll have to retrieve the object and insert that instead, and the resolver can't do that (since the resolver doesn't carry out any instantiation of objects). It will have to be the interpreter's job. Let's clean up some things before we start parsing attribute gets: |
Parsing attribute getsLet's start with thinking through how to proceed: pseudo-9608/pseudocode/parser.py Lines 120 to 130 in bacf9a4
pseudo-9608/pseudocode/parser.py Lines 120 to 129 in c845787
This represents an expression for Getting the attribute from the object. |
This code doesn't handle nested attributes yet (e.g. |
Now comes some major refactoring: up to this point, we've been assuming that Assign Exprs only ever need to store a name and an expr. We assume that this name is declared in the frame. But if we are assigning values to an attribute, that's a name declared in an object and not in the frame. We'll have to refactor Assign to accept left-hand Gets. Let's prep the stage: I didn't do a complete job of refactoring the parser to use the |
Assigning to attributesWe'll need to replace pseudo-9608/pseudocode/lang.py Lines 328 to 334 in f0eae5c
And update pseudo-9608/pseudocode/parser.py Lines 25 to 39 in f0eae5c
Not every |
Now we turn our attention to pseudo-9608/pseudocode/parser.py Lines 228 to 232 in f2766db
Let's make it create and pass an assignee: pseudo-9608/pseudocode/parser.py Lines 228 to 241 in 0bff815
|
We haven't added any new functionality yet; pseudo-9608/pseudocode/parser.py Lines 104 to 134 in 2b7fe41
Unfortunately, Let's reorganise it this way:
In pseudo-9608/pseudocode/parser.py Lines 149 to 159 in 7b50c6f
We've just extended the functionality a bit: this will recognise chains of |
Finally, our pseudo-9608/pseudocode/parser.py Lines 104 to 131 in 0cb05d8
|
And back to pseudo-9608/pseudocode/parser.py Lines 233 to 245 in 070c6c4
No more Note that |
We can't remove Resolving attribute assignmentsTwo things we need to handle:
First, nested Gets: pseudo-9608/pseudocode/resolver.py Lines 127 to 141 in ce8a61c
We do a check for a Get Expr, and have the nested frame accept() resolveGet() recursively. When we finally hit the innermost Get Expr (which would be the first one parsed by name() ), we carry out a frame insertion by checking for frame is NULL .
And we lean on the frame's typesystem to help us determine the value's type. And now the assignee: pseudo-9608/pseudocode/resolver.py Lines 119 to 125 in ce8a61c
The assignee is going to be a Get Expr, so let's resolve it; thankfully resolveGet() now handles nested Gets (although we haven't tested this yet). Then we can do a type-check for the assignee's type and the expr's type.
|
I'm not quite happy with how I handled verifyFunction and verifyProcedure in the resolver; it looks unwieldly and too detailed, and it is difficult to see the common parts between them. In particular I don't like the return type checking being in verifyFunction; it looks like it could easily be handled in verifyStmts even if procedures won't use it. Let's upgrade verifyStmts to do that: [b9eb1d6] and then use it in verifyFunction: [db6ec08] together with this change, we also need another way to check for the existence of a Return statement. I do this by just checking for a statement with a |
Found a bug which didn't come up in testing: verifying BYREF parameters was not being done properly. I was setting the value in the Frame does not have a |
It's still not quite how I would like it. Within the resolver, it doesn't make sense to use the Oops, accidentally took out some code I needed. Undoing it: [13e91aa] Declaring parameters shares some code with declaring variables, but can't use So let's upgrade |
Parameter checking and replacement is the same across both verify* functions. Let's make a helper for that: [23c4667] |
We are going to be adding a new object type in the next chapter. That means more Then refactor the parser to use it in place of |
I also use Since this doesn't need to consume tokens, I set Then a small fix for chaining |
Did a more thorough pass for loops in the parser to catch EOFs: [4f1a240] Next, some really long lines stand out in the parser:
This needs some reorganising ... Let's do this: |
About This feels like it might be a constant source of bugs; it relies on human consistency in using this check whenever there's a loop. I just had to add a whole bunch of them earlier. Since absolute blistering performance is not the goal here, I'd rather have safety. I'll throw in an pseudo-9608/pseudocode/parser.py Lines 18 to 20 in d06205d
And use it in the Refactoring |
Finally we can take out the Not only do we get more readable loops, we also get more safety knowing that our match and expect helpers help us to check and raise errors. This will need testing too, to ensure it's not raising |
Almsot got myself into infinite recursion there: [9260d50] Can't have |
BREAKING CHANGE Hlpers return the matched token, or None. This may break boolean matching in rare cases.
matchElseError uses msg keyword argument
It's very clean to have match and expect helpers return a boolean, but it's quite limiting. In many places we While compressing code, I accidentally also added a whole bunch of other formatting changes. Including adding a
|
At the end of the day, we have cleaner code, and still mostly working. I think we can move on to arrays. |
Before starting this chapter, a very minor delayed update in our parser for parsing precedence: [dccbb97]
After we register our custom type and enable variables to be declared with this type, how do we ... you know, retrieve its attributes or update them?
Most languages use a dot(
.
) notation for accessing object attributes, and 9608 pseudocode is no different.Student.Name
refers to theName
attribute ofStudent
. Let's start by getting our scanner to recognise them.Scanning attributes
Oh, looks like our scanner already recognises the
.
symbol and can tokenise them just fine. Moving on to the parser.