Skip to content
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

07 Resolving #14

Merged
merged 10 commits into from
Apr 16, 2022
Merged

07 Resolving #14

merged 10 commits into from
Apr 16, 2022

Conversation

ngjunsiang
Copy link
Contributor

In later statements we will encounter not just names, but possibly subnames, in code like:

FUNCTION GetId(Array : ARRAY) RETURNS ARRAY
    DECLARE i : INTEGER
    DECLARE AttrArray[1:Array.Length] OF STRING
    ...
    AttrArray[i] <- Array[i].Id

Here, i is a name within the frame of the GetId function, and does not exist in the global frame. Id is a name within an element of Array, and does not exist in GetId's frame, nor in the global frame.

Determining which frame to get names from feels like a responsibility beyond the scope of interpreter.py. Who should be responsible for determining the frame to get a name from?

This process is called resolving, and we will build a resolver in this pull request.

resolve (v.)

late 14c., resolven, "melt, dissolve, reduce to liquid; separate into component parts; alter, alter in form or nature by application of physical process," " intransitive sense from c. 1400; from Old French resolver or directly from Latin resolvere "to loosen, loose, unyoke, undo; explain; relax; set free; make void, dispel."

re-
word-forming element meaning "back, back from, back to the original place;" also "again, anew, once more," also conveying the notion of "undoing" or "backward," etc. (see sense evolution below), c. 1200, from Old French re- and directly from Latin re- an inseparable prefix meaning "again; back; anew, against."

@ngjunsiang
Copy link
Contributor Author

Let's take a look at the statements returned by the parser:

[{'rule': 'declare',
  'name': {'type': 'name', 'word': 'Index', 'value': None},
  'type': {'type': 'name', 'word': 'INTEGER', 'value': None}},
 {'rule': 'assign',
  'name': {'type': 'name', 'word': 'Index', 'value': None},
  'expr': {'type': 'integer', 'word': '1', 'value': 1}},
 {'rule': 'output',
  'exprs': [
   {'left': None,
    'oper': {'type': 'symbol',
             'word': '', 'value': <function get at 0x7f91d9171940>},
    'right': {'type': 'name', 'word': 'Index', 'value': None}}
  ]
}]

While our interpreter interpret()s, our resolver inspect()s:
https://github.com/nyjc-computing/pseudo/blob/d3ba4bf0e368d4ce32bc3606d7cd531f81b2dced/resolver.py#L9-L17

As it visits each statement, it verify()s it.

To do so, it will also need a frame to hold declared variables and their types, and later insert it into get statements. And we can pass this frame to the interpreter later to continue interpreting the program.

@ngjunsiang
Copy link
Contributor Author

https://github.com/nyjc-computing/pseudo/blob/0f3580e04df88564b02d370e2c5ad7dda9a64b7d/resolver.py#L6-L23

Like the interpreter, we use multiple dispatch within verify() to call a separate statement verifier (verify*()) for each statement.

So far, only output and assign statements require getting variables, so we call resolve() within those verifiers to resolve the frame for those get expressions.

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 16, 2022

The resolve() function only handles get exprs, ignoring all others. When it finds one, it inserts the frame into the expr's 'left':

https://github.com/nyjc-computing/pseudo/blob/5e1034da3ecffd2e7007e5ff0965f9a3f9bfe584/resolver.py#L6-L14

Inspecting the frame and statements returned by the resolver:

> main.py(28)main()
-> try:
(Pdb) frame
{}
(Pdb) statements
[{'rule': 'declare',
  'name': {'type': 'name', 'word': 'Index', 'value': None},
  'type': {'type': 'name', 'word': 'INTEGER', 'value': None}},
 {'rule': 'assign',
  'name': {'type': 'name', 'word': 'Index', 'value': None},
  'expr': {'type': 'integer', 'word': '1', 'value': 1}},
 {'rule': 'output',
  'exprs': [
    {'left': {},
     'oper': {'type': 'symbol', 'word': '', 'value': <function get at 0x7f2360e09940>},
     'right': {'type': 'name', 'word': 'Index', 'value': None}}]
}]

The frame has been created, and inserted into the expr's 'left'.

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 16, 2022

Reverting the interpreter

Now we can undo whatever we had to do to the interpreter to make it insert frames:
[25576e5]

Bugtracing

  File "interpreter.py", line 13, in evaluate
    oper = expr['oper']['value']
KeyError: 'oper'

Inspecting the expr that caused this:

{'left': {}, 'oper': {'type': 'symbol', 'word': '', 'value': <function get at 0x7fbf54602940>}, 'right': {'type': 'name', 'word': 'Index', 'value': None}}

Of course ... evaluate() can't handle a frame where it was expecting a token or expr. Let's add more code to have it return a frame if it guesses one. For now we assume that if expr is a dict but does not have an 'oper' key, it is a frame.
https://github.com/nyjc-computing/pseudo/blob/25576e5e253d72f74818c102cc6240685eded9dc/interpreter.py#L11-L13

Very fragile! We will come back to fix later.

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 16, 2022

Frame passing

Bug:

  File "builtin.py", line 44, in get
    return frame[name]['value']
KeyError: 'Index'

We forgot to pass the frame returned by the resolver down to the interpreter. We have to do so since this is the frame that was inserted into the get exprs. We'll figure out a more elegant way later.

[405e885]

Oops, forgot an empty dict is considered False in an if statement. We have to explicitly check for None:
[f6e8005]

@ngjunsiang
Copy link
Contributor Author

Testing

Result:

5
{'Index': {'type': 'INTEGER', 'value': 5}}

We have a working resolver. Much more refactoring ahead, but before we optimise prematurely we also want to consider static typing.

@ngjunsiang
Copy link
Contributor Author

Bug

Our main program can't catch RuntimeErrors if they are being caught in interpret(). Let's take that out:
[910de22]

@ngjunsiang ngjunsiang merged commit 28e6763 into main Apr 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant