-
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
12c Passing by reference #24
Conversation
Using a local frame for CALL executionTo use a local frame, we are going to need to create one when we execute the call: https://github.com/nyjc-computing/pseudo/blob/c2319a7f5477a4d8eadbe40e504d8334be51b6a2/interpreter.py#L69-L73 https://github.com/nyjc-computing/pseudo/blob/c2319a7f5477a4d8eadbe40e504d8334be51b6a2/interpreter.py#L76-L79 https://github.com/nyjc-computing/pseudo/blob/c2319a7f5477a4d8eadbe40e504d8334be51b6a2/interpreter.py#L81 |
Designating pass-by-value or pass-by-referenceWhile 9608 pseudocode assumes pass-by-value as the default mode of passing arguments, it also supports passing-by-reference:
|
The use of BYVALUE and BYREF is now recognised by the parser when used in a procedure declaration.
Parsing BYVALUE and BYREFWe start with recognising these two new keywords in the parser. We initialise Our |
Add a declare() parser for parameter declarations. declareStmt() is also refactored to use this expression parser.
Implementing pass-by-valueOur current resolver code certainly isn't using pass-by-value; it's still Should we create a Each time the procedure is called, we will need:
The resolver feels like a more appropriate place to set up the Handling parameter declarationsFor starters, notice how similar parameter declarations are to variable declarations. We have a name, followed by Let's set up a Since declarations need to be type-checked, we might as well do it in |
BREAKING CHANGE: procedureStmt() now stores params as a list instead of a dict. This will require changes in resolver and interpreter.
Let's refactor Here we fix an early |
BREAKING CHANGE: procedure resolving uses a local frame instead of the global frame. This will affect interpreter execution of procedure statements.
Resolving BYREF parametersFirst we declare our params as local vars. This is now made easier with our If the procedure is declared with BYREF, reassignments of the parameters cause the global variable to change as well. So we need to type-check BYREF parameters: Not forgetting to verify the procedure's own statements. But we pass the |
Update verifyCall() to follow the new format for procedure statements, which uses list instead of dict for params
Our CALL resolver needs updating as well, to handle |
Since the frame is already evaluated to return proc, there is no more need to retrieve proc values through the 'value' key
And now fixing our CALL executor: |
RewindOops ... here we realise we won't be able to update BYREF variables; if they are changed in local, how would we know, and how would we change them back in global? Time to rethink our approach from #24 (comment). Recall that
That suggests a different approach to local variable declaration. Recall that when we declare variables, we add a dict to the frame: For lack of a better name, let's call this dict a slot. So the frame is a dict with the name as key, and a slot as value. When we initialise variables in the procedure's
Let's see what that looks like. |
Instead of declaring new variables in local all the time, BYREF variables will refer to the global variable.
Resolving BYREF parameters, reduxWe still have a Whether for BYVALUE or BYREF variables, we have to loop through For BYVALUE variables, we just declare local variables with new slots like we did in the previous version. We do the usual type-checking (param type should match global var type) ... Before we reference the global frame slot in the And the rest of the code is the same. With one small embarrassing fix: we were checking the passby token instead of the word: |
TestingCode:
Result:
That's because of these two lines:
Result:
That's because each Result:
|
Interpreting BYREF callsWith our This way, BYREF variables will be accessed directly from the global variable slot, while BYVALUE variables will be ... stored in the same local frame?! That sounds dangerous ... shouldn't each call use a fresh frame for BYVALUE variables? Well yes ... we don't want to complicate our code more than it needs to be though. The next step is to assign arguments into the And the rest of the code remains the same: Elegantly, |
TestingCode:
Result:
Our BYREF works 😎 |
9608 pseudocode recognises the notion of passing-by-value vs passing-by-reference.
Procedures are able to access all variables in the global frame. But reassigning variables is another matter.