-
Notifications
You must be signed in to change notification settings - Fork 26
Add dict to stdlib #172
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
Add dict to stdlib #172
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you a lot for this PR, I love it.
I especially appreciate that you highlighted all the various "pain point": I agree we should do better, and I opened an issue for each of them (some are easy to fix/implement, others are harder).
@antocuni I left a couple more |
stdlib/dict.spy
Outdated
# return nt(intfunc(int(self), int(other))) | ||
# ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ | ||
# ZeroDivisionError: integer modulo by zero | ||
# I guess this is saying that capacity() might return 0? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it says that someone tried to do x % 0
(not that it might do modulo 0... someone actually DID that).
You get the same tb with this toy program:
def main() -> void:
print(2 % 0)
We should detect the case and raise a proper exception/panic, see #183.
That said, I cannot really understand where it comes from, because I don't see any modulo operator in your source code, and I cannot reproduce it.
Can you got this error with a different/previous version of the code?
Or, can you show me how to reproduce?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! If you substitute &
→ %
on line 74 you should see that error
@dpdani here are my updated comments |
@dpdani it seems I need to manually approve the workflow to run the tests every time you do a new push. I'll try to invite you as a maintainer, hopefully it will help. |
joined 👍 |
The test passes 🎉 |
@dpdani I have some news which should be relevant to you:
|
Hi @antocuni 👋 Nice!! That's a lot of good improvements! I have been focused on other things for a while and I'll probably get back to spy dict in a week or so 👍 |
And maybe someday we will roll our own |
hey there @antocuni 👋 big merge from main coming through 🚂 I'm seeing this error in all doppler and C tests, but not in the interpreter tests. How do I deal with it?
|
@dpdani In the method
So, the error you get it's actually correct. Why the However, when we do redshift we check ALL functions for TypeErrors, and that's why compilation fails. This is called "eager error mode", and it's the default (because it's what you expect in a statically typed language, after all). There is also the "lazy error mode", in which E.g., consider this program: def main() -> None:
print('hello')
if 0:
1 + 'xxx'
print('world')
In tests, you can turn on lazy errors by using |
oh yeah, big brain fart yesterday 😆 |
actually, it took a while also for me to understand what was happening. |
Agreed 👍 I think I want to tackle deletes and the iterator in this PR, and the rest can be separated. |
iterators are possible, but ATM I don't have any language support for |
I'm happy with the state of this PR. @antocuni want to do a review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @dpdani.
I didn't review deeply the actual hashtable strategy but I assume it's correct thanks to tests.
I'm curious to know whether the basic idea is similar to CPython's dict
or this is a different strategy.
Maybe if you feel like you could add some short comment at the beginning of dict.spy
to explain it?
Also, I'd suggest to rename it to _dict.spy
, for consistency with _range.spy
and _list.spy
.
Eventually I will need a way to automatically make these names available in the builtins
module, but currently I cannot do it easily because of bootstrapping issues.
stdlib/dict.spy
Outdated
entries: ptr[Entry] | ||
|
||
def new_index(log_size: i32) -> ptr[i32]: | ||
# assert MIN_LOG_SIZE <= log_size <= MAX_LOG_SIZE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks to #240 we now have assert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but the chained comparison doesn't work, and using and
doesn't work either:
self = <spy.parser.Parser object at 0x106fbcf50>
primary = 'not implemented yet: BoolOp', secondary = 'this is not supported'
loc = <Loc: '/Users/dp/repos/spy/stdlib/_dict.spy 37:15 37:68'>
def error(self, primary: str, secondary: str, loc: Loc) -> NoReturn:
> raise SPyError.simple("W_ParseError", primary, secondary, loc)
E spy.errors.SPyError: ParseError: not implemented yet: BoolOp
E --> /Users/dp/repos/spy/stdlib/_dict.spy:37:16
E 37 | assert MIN_LOG_SIZE <= log_size and log_size <= MAX_LOG_SIZE
E | |___________________________________________________| this is not supported
I'll split it into two asserts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stdlib/dict.spy
Outdated
# NameError: name `KeyError` is not defined | ||
# --> /Users/dp/repos/spy/stdlib/dict.spy:184:23 | ||
# 184 | raise KeyError(key) | ||
# | |______| not found in this scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can add KeyError
here:
Lines 127 to 174 in ead6d95
@BUILTINS.builtin_type('TypeError') | |
class W_TypeError(W_StaticError): | |
""" | |
Note that TypeError is a subclass of StaticError | |
""" | |
pass | |
@BUILTINS.builtin_type('ValueError') | |
class W_ValueError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('IndexError') | |
class W_IndexError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('ParseError') | |
class W_ParseError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('ImportError') | |
class W_ImportError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('ScopeError') | |
class W_ScopeError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('NameError') | |
class W_NameError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('PanicError') | |
class W_PanicError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('ZeroDivisionError') | |
class W_ZeroDivisionError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('AssertionError') | |
class W_AssertionError(W_Exception): | |
pass | |
@BUILTINS.builtin_type('WIP') | |
class W_WIP(W_Exception): | |
""" | |
Raised when something is supposed to work but has not been implemented yet | |
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite see a way around this error 🤔
I guess, I'll just leave it as raise KeyError()
without an argument?
E spy.errors.SPyError: TypeError: cannot call blue function with red arguments
E --> /Users/dp/repos/spy/stdlib/_dict.spy:161:28
E 161 | raise KeyError(str(key))
E | |______| this is red
E
E --> /Users/dp/repos/spy/spy/vm/exc.py:67:1
E 67 | def w_new(vm: 'SPyVM', w_cls: W_Type, w_message: W_Str) -> T:
E | |___________________________________________________________________| function defined here
E
E --> /Users/dp/repos/spy/stdlib/_dict.spy:161:19
E 161 | raise KeyError(str(key))
E | |________________| operator::CALL called here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By which I mean raise KeyError("")
with an empty string because an argument is required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, current support for exceptions is VERY limited:
raise
always causes a panic and exceptions cannot be caught- the error message must be
blue
(this is the error that you are seeing).
Note that you don't need raise KeyError("")
. You can just do raise KeyError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because an argument is required
why required?
The following example works for me:
def main() -> None:
raise KeyError
❯ spy /tmp/x.spy
KeyError:
--> /tmp/x.spy:2:2
2 | raise KeyError
| |_______________|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mm right, but if I write raise KeyError()
with the parens then it doesn't work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, opened #250 for it
return self.data.entries[self.i].key | ||
|
||
def __continue_iteration__(self: dict_iterator) -> bool: | ||
return self.i <= self.data.length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is good enough for now. However, eventually we probably want to introduce the same check as in CPython and detect whether the dict has changed size during the iteration.
So maybe we can add a # TODO
comment?
stdlib/hash.spy
Outdated
@@ -0,0 +1,24 @@ | |||
@blue.generic | |||
def hash(T): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have a better way to deal with hash
nowadays 🎉.
Look how we implement len
:
spy/spy/vm/modules/builtins.py
Lines 112 to 125 in ead6d95
@BUILTINS.builtin_func(color='blue', kind='metafunc') | |
def w_len(vm: 'SPyVM', wam_obj: W_MetaArg) -> W_OpSpec: | |
w_T = wam_obj.w_static_T | |
if w_fn := w_T.lookup_func('__len__'): | |
w_opspec = vm.fast_metacall(w_fn, [wam_obj]) | |
return w_opspec | |
t = w_T.fqn.human_name | |
raise SPyError.simple( | |
'W_TypeError', | |
f'cannot call len(`{t}`)', | |
f'this is `{t}`', | |
wam_obj.loc | |
) |
We should do the same for hash
.
We can do it in this PR, but I'm also happy to do it in a follow-up PR if you prefer.
I'll leave the |
# Conflicts: # spy/vm/modules/builtins.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, merging it.
We finally have a dict type 🎉.
Thank you, this is a big step for SPy!
) | ||
|
||
@BUILTINS.builtin_func | ||
def w_hash_i8(vm: 'SPyVM', w_x: W_I8) -> W_I32: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is good for now, but ideally I'd like these functions to be in W_I8.w_hash
etc, similarly to what we do for __str__
, __repr__
and __len__
.
It's not a merge blocker though, we can do it in a follow up PR
No description provided.