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

Block scoping #601

Merged
merged 6 commits into from
Jan 3, 2018
Merged

Block scoping #601

merged 6 commits into from
Jan 3, 2018

Conversation

jacqueswww
Copy link
Contributor

- What I did

Partial PR for #545. I chose to separate this part for easier review.

The a:num =1 annotate assign will probably be a big onslaught of changes which I would rather keep separate from block scoping.

- How I did it

Add a blockscopes list to context. Using the start_blockscope and end_blockscopemethod to set the start and end of a blockscope for for and if blocks.

- How to verify it

Check the tests ;) (test_blockscope.py).

- Description for the changelog

- Cute Animal Picture

69f2c95efe348b7cefa11314c50da19b--cute-baby-bunnies-cutest-bunnies

@jacqueswww jacqueswww changed the title 545 block scoping Block scoping Dec 22, 2017
Copy link
Member

@fubuloubu fubuloubu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, a few issues though.


def end_blockscope(self, blockscope_id):
if blockscope_id not in self.blockscopes:
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this raise an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could probably remove that check completely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, I mean this is an internal thing you added to keep track of blockscopes, which is something Python already does with the AST module to prevent uses from changing it arbitrarily.

Wait, does AST provide any information for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I am aware no, since python doesn't use block scoping like we want to. I'll remove it - just a bit of 'defensive coding' ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think AST will help here as it only parses with python scoping. I think this check can be removed given that we're only calling it internally and inputting blockscope_id's that will be in self.blockscopes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python does scoping, at least behind the scenes. All languages do some aspect of scoping. Python specifically tracks indenting to figure out block scoping. This might be handled in the lexer and not made present in the AST, but it's definitely in the Python front-end parser somewhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to pop scopes from the list to prevent this from compiling:

def foo(b: bool):
    if b:
        a: num = 1
    a += 2

Copy link
Contributor

@DavidKnott DavidKnott Dec 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacqueswww I don't think the above example would compile even with the aforementioned check removed. Does it currently?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did for me, will be reworking this PR today ;)

poplist.append(name)
for name in poplist: self.vars.pop(name)
# Remove blockscopes
self.blockscopes = self.blockscopes[self.blockscopes.index(blockscope_id):]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify to self.blockscopes.pop(blockscope_id)

Copy link
Contributor Author

@jacqueswww jacqueswww Dec 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pop would probably give the desired effect. My idea when I coded was: If you have nested scopes, you want to slice awat all nested scopes as well.

Copy link
Member

@fubuloubu fubuloubu Dec 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, that's an interesting thought. but the blockscopes should still both appear in order so I don't think this would happen e.g.

for i in range(5): # "for" block started
    # "for" body
    if i == 3: # "if" block started
         # "if" body
         return i
    # "if" block ended
# "for" block ended

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at it now pop would be 100%, was just my thought process at that moment... hahaha 😸

block_scope_id = id(self.stmt)
self.context.start_blockscope(block_scope_id)
o = LLLnode.from_list(
['if', Expr.parse_value_expr(self.stmt.test, self.context), parse_body(self.stmt.body, self.context)] + add_on,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does self.stmt.test do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidKnott I believe it's basically the comparison expression or statement part: if (<test>)


def end_blockscope(self, blockscope_id):
if blockscope_id not in self.blockscopes:
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think AST will help here as it only parses with python scoping. I think this check can be removed given that we're only calling it internally and inputting blockscope_id's that will be in self.blockscopes

@DavidKnott
Copy link
Contributor

@jacqueswww Tests look good!


def set_in_for_loop(self, name_of_list):
self.in_for_loop.add(name_of_list)

def remove_in_for_loop(self, name_of_list):
self.in_for_loop.remove(name_of_list)

def start_blockscope(self, blockscope_id):
if blockscope_id not in self.blockscopes:
Copy link

@professoroakz professoroakz Dec 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will blockscopes contain only distinct elements? If so, then consider changing it (self.blockscopes) into a set for hashmap complexity, O(log(n)) lookup time for the "not in" lookups (this expression degrades into a O(n) operation). More on this:
https://juliank.wordpress.com/2008/04/29/python-speed-x-in-list-vs-x-in-set/

Copy link
Contributor Author

@jacqueswww jacqueswww Dec 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 yeah will do so, used the same for in-for-loop ;) as stated in the other comment I thought I required a sliceabke.

@jacqueswww
Copy link
Contributor Author

@DavidKnott I believe this one is ready now too :)

@DavidKnott DavidKnott merged commit cb2c916 into vyperlang:master Jan 3, 2018
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.

4 participants