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

06c Retrieving variables #13

Merged
merged 11 commits into from
Apr 15, 2022
Merged

06c Retrieving variables #13

merged 11 commits into from
Apr 15, 2022

Conversation

ngjunsiang
Copy link
Contributor

Testing

Let's see how our program handles variables being used in place of values:
https://github.com/nyjc-computing/pseudo/blob/ecc694bbfabc210fd9c85bb336278320f9061780/main.py#L10-L14

Result:

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

This is because evaluate() handles name tokens by returning their word value:
https://github.com/nyjc-computing/pseudo/blob/ecc694bbfabc210fd9c85bb336278320f9061780/interpreter.py#L5-L9

get() takes in a frame and a name.
It returns the value mapped to name in the frame.
@ngjunsiang
Copy link
Contributor Author

Get expressions

We need a new kind of expr: one that says "please look up this word in a frame, and get its assigned value". Let's call it a 'get' expr.

Handcrafting a get expr

So far we have been treating exprs as binary expressions, which involve a left expression/token, an oper operator, and a right expression/token. Let's see if we can continue this pattern so we don't have to invent a new kind of expr.

A get involves:

  • getting a frame (which contains mappings of names to values),
  • getting a name,
  • getting the value associated with the name from the frame

That's promising; get lends itself well to being a binary operator. We can try to define it like get(frame, name):
https://github.com/nyjc-computing/pseudo/blob/99b4d015362d2de1463f0ad04cc8650f0e0ca873/builtin.py#L62-L63

@ngjunsiang
Copy link
Contributor Author

Parsing names

We add a new parser for names, which we call identifier() because we are using name as a variable name:
https://github.com/nyjc-computing/pseudo/blob/e15fb417c475fe94bc1debeb0bdbbe35fb373177/parser.py#L31-L36

DECLARE and Assign statements call identifier() instead of value() to get a name.
@ngjunsiang
Copy link
Contributor Author

Our declare and assignment statements also need updating so they get a name token and not a get expr for the first token:
[737a3e4]

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 15, 2022

Get parsing precedence

Where should we slot this 'get' expr in recursive descent parsing?

We are likely to be writing code like i <- i + 1, and want i to be treated as its assigned value rather than a string or keyword, so that means it should have the same precedence as value() (where we capture integers, reals, and strings too).
[26a13ac]

This is tricky: our scanner just recognises words, and doesn't know what frame to get the name from. So we'll set the frame as None for now, and add it in ... some other time?

Also we need to wrap our get operator in a token which isn't actually in the source code. This feels incredibly hacky (because scanner isn't supposed to know how tokens work), but it's already checking for token keys and we'll have to refactor that sometime. For now, we'll wrap get in a symbol-type token, with the name as the word key and the get operator itself as the value key.

@ngjunsiang
Copy link
Contributor Author

Frame insertion

For now we will insert the frame in the interpreter, when we evaluate() a get expr:
https://github.com/nyjc-computing/pseudo/blob/7835fc47de7d1ee83a6326a8d515bcdcf03778b3/interpreter.py#L11-L18

@ngjunsiang
Copy link
Contributor Author

Don't forget to update the evaluate() calls to add the frame argument:
[bf07c08]

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 15, 2022

Bug

It's not working:

  File "main.py", line 26, in main
    frame = interpreter.interpret(statements)
  File "interpreter.py", line 57, in interpret
    execute(frame, stmt)
  File "interpreter.py", line 47, in execute
    execOutput(frame, stmt)
  File "interpreter.py", line 23, in execOutput
    print(str(evaluate(expr)), end='')
  File "interpreter.py", line 19, in evaluate
    return oper(left, right)
  File "builtin.py", line 63, in get
    return frame[name]
TypeError: 'NoneType' object is not subscriptable

Suspect the line in evaluate() that handles get operator:
https://github.com/nyjc-computing/pseudo/blob/bf07c087c1d2fa59222128c21dc54559f096819e/interpreter.py#L13

With a breakpoint there:

> interpreter.py(15)evaluate()
-> left = frame  # <-- expr['left'] is None
(Pdb) expr
{'left': None, 'oper': {'type': 'symbol', 'word': '', 'value': <function get at 0x7f66262c99d0>}, 'right': {'type': 'name', 'word': 'Index', 'value': None}}
(Pdb) 

'left' didn't receive a frame for a get expr. Stepping through the debugger, evalute() is being called from execOutput():
https://github.com/nyjc-computing/pseudo/blob/bf07c087c1d2fa59222128c21dc54559f096819e/interpreter.py#L22

@ngjunsiang
Copy link
Contributor Author

ngjunsiang commented Apr 15, 2022

Fix

Add frame into the evaluate() call in execOutput():
[f79b555]

Result:

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

Instead of printing out only the value of Index, OUTPUT printed the entire stored dict. Let's fix that. Since our operators are supposed to return values and not objects, get should also return a value and not a dict:
[f609d3a]

@ngjunsiang
Copy link
Contributor Author

Result:

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

Now our program can declare variables, assign values to variables, and subsequently retrieve (evaluate) them.

@ngjunsiang
Copy link
Contributor Author

Refactor

We have an OPERATORS constant containing a list of operator symbols:
https://github.com/nyjc-computing/pseudo/blob/f609d3ab0dc29628c060820baf4776ac26bad510/builtin.py#L21-L24

And we have an operators dict mapping operator symbols to operator functions:
https://github.com/nyjc-computing/pseudo/blob/f609d3ab0dc29628c060820baf4776ac26bad510/builtin.py#L69-L80

We don't need both: [58e5ded]

@ngjunsiang ngjunsiang merged commit 1cbb022 into main Apr 15, 2022
@ngjunsiang ngjunsiang deleted the variables branch April 15, 2022 23:15
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