-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Parse integer division operator #6470
Parse integer division operator #6470
Conversation
Isn't it the other way around? |
Yes @straight-shoota I mean to overflow as "raise overflow". |
This reminds me of perl. I don't like it. |
I really don't like all these new operators being proposed. I really don't think we should introduce all these operators, they're just more things for a newcomer to learn to save a few characters typing ( |
I would expect that from all the operators added in this PR The division operator Adding The wrapper api, can be implemented on top of this. But not the other way around. So operators are more powerful and expressive. For wrapper to work well with overloads you will need The discussion left (hence this PR) is not about if the language needs to add this, but if the implementation is good enough. |
Back to this issue, I'm not sure what your concern is about I'm really against adding these operators because what we have now just isn't broken, so lets not fix it. Change for the sake of change is a bad thing. |
I just want data that this is actually a problem in the real world, and not a solution to a problem that we've invented in our heads. |
Let's use an example, if The Again, the fact that we are used to C like math operators does not make it the best option. |
@bcardiff I think issues like that are fairly rare, and |
That snippet is a workaround and harder to come up when learning the language. Having different operators is cleaner. Something new to learn today, yes. But is a more consistent set of operators. Try to explain that line when coding a method that manipulate numbers vs explain that
I could understand removing 3 from the PR, if and only if |
@bcardiff it's not just something new to learn today, it's something new that every newcomer from now until forever will have to learn. At the end of the day though, I'm more concerned about the overflow stuff adding a bazillion more operators than this PR only adding two. |
|
src/compiler/crystal/syntax/to_s.cr
Outdated
when StringInterpolation | ||
visit_interpolation(exp) { |s| Regex.append_source s, @str } | ||
if (exp = node.value).is_a?(StringLiteral) && exp.value.empty? | ||
# // is not allways an empty regex, sometimes is an operator |
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.
Typo: allways -> always
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.
Generally there is no point of using case
statements for only one single condition vs if
.
There is strictly no performance difference between the two. However using a case
adds one more line to the code because the condition is splitted in two lines, which is IMO less clear than on one line with if
it "lexes #{string.inspect}" do | ||
lexer = Lexer.new string | ||
unless (v = slash_is_regex).nil? |
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.
Replace simply by if v = slash_is_regex
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 not the same. slash_is_regex
is Bool?
so you need to test for .nil?
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.
Ok 👍 - the language evaluate if the expression is true and not nil, and we just want to know if it isn't nil.
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.
That would be abusing that the default value of slash_is_regex is false.
@@ -287,8 +288,17 @@ module Crystal | |||
line = @line_number | |||
column = @column_number | |||
char = next_char | |||
if !@slash_is_regex && char == '=' | |||
if (@wants_def_or_macro_name || !@slash_is_regex) && char == '/' | |||
case next_char |
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.
Can be replaced by a smaller if
statement
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.
Using the case is more about following the pattern of the rest of the lexer.
But I don't follow which is the proposed smaller if
.
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 fine - this just makes the lexer looks a bit more complex that it is, that's it :)
Here it would be
if next_char == '/'
next_char :"//="
else
@token.type = :"//"
end
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 thought you ment the if (@wants_def_or_macro_name ...
.
There are some if
in the lexer, bust mostly case
even single when
-ed (?) case. I found them easier to read the lexer if all the structures are the same and not case by case based that we will end up having if your suggestions are applied.
At there is no real change the the actual code.
So case
are kept. But thanks.
src/compiler/crystal/syntax/lexer.cr
Outdated
when '/' | ||
case next_char | ||
when '/' | ||
case next_char |
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.
ditto
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.
With if
s it would be (start at line 648, replacing the 2 case
s):
raise "&/ is not a valid operator. Did you mean &// ?" if next_char != '/'
if next_char == '='
next_char :"&//="
else
@token.type = :"&//"
end
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.
At a note, the previous code was:
case next_char
when '/'
case next_char
when '='
next_char :"&//="
else
@token.type = :"&//"
end
else
raise "&/ is not a valid operator. Did you mean &// ?"
end
src/compiler/crystal/syntax/lexer.cr
Outdated
@@ -470,6 +489,13 @@ module Crystal | |||
else | |||
symbol "&*" | |||
end | |||
when '/' | |||
case next_char |
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.
ditto
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.
And here it would be:
if next_char == '/'
next_char_and_symbol "&//"
else
symbol "&/"
end
@@ -412,7 +426,12 @@ module Crystal | |||
symbol "*" | |||
end | |||
when '/' | |||
next_char_and_symbol "/" | |||
case next_char |
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.
ditto
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.
ditto ditto :-)
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.
It would be here
if next_char == '/'
next_char_and_symbol "//"
else
symbol "/"
end
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.
Let's merge!
I regularly fumble on this in Ruby or put to_f
in too many places because I can't trust /
to rrturn a float. This is inconsistent and I'm happy we finally fix this in Crystal, with a minimal change for floor division (just add a /
).
@RX14 points were already made/refuted in related issues.
I forgot we'd already merged all the |
I would prefer And if anyone else is thinking: how can integer division overflow? 🤔 |
def342d
to
a85334f
Compare
I fixed the typo and rebased. ! @oprypin I will still raise the card of completeness regarding the |
By the way: https://stackoverflow.com/questions/30378591/overflow-division-operator-not-recognized I would recommend not having the &/ operator in Crystal, just like in Swift. It's an extra check for something that can rarely happen. |
completeness for completeness's sake seems like a very bad idea to me. It just expands the set of operators. I'd be much happier if the only operators we were adding were |
Since division by zero would still be raised in |
Refactor specs to assert operators all together
Allow // to be used as empty regex in some context only. Use %r() for empty regex when in to_s of AST.
the position of :"[]?" was off, new line added to compare them easily
DRY logic of allowed operators names for def and macro
a85334f
to
5cb2886
Compare
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.
Great work 💯 Thank you @bcardiff 👍
In the path to implement #2968 first the operators needs to be allowed.
This PR allows
//
andoperator&//
sto be defined.The behavior will need to wait to the next release.
//
will raise on overflowand&//
won't. Similar to+
vs&+
.//
will continue to mean empty regex when possible:foo // bar
is equivalent tofoo.//(bar)
foo //
is a syntax error (from now on)foo(//)
is a call passing empty regexThe
ASTNode#to_s
will use%r()
as empty regex always since//
will not always work.There are some refactors on specs and code for allowing operators to be used as methods and macro names introducing a state ivar
@wants_def_or_macro_name
that works similar to@slash_is_regex
configuring the behavior of the lexer depending on the context.