-
Notifications
You must be signed in to change notification settings - Fork 4
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
infix-operator-precedence atom #10
infix-operator-precedence atom #10
Conversation
…nt-as-value classifier
It would also be nice to add similar tests to other atoms that include assignment, but if you're feeling lazy I can do that, let me know.
|
@@ -7,8 +7,8 @@ | |||
"Is this node an assignment expression" | |||
[node] | |||
(and (instance? IASTBinaryExpression node) | |||
(contains? #{IASTBinaryExpression/op_assign IASTBinaryExpression/op_binaryAndAssign IASTBinaryExpression/op_binaryXorAssign | |||
IASTBinaryExpression/op_divideAssign IASTBinaryExpression/op_minusAssign | |||
(contains? #{IASTBinaryExpression/op_assign IASTBinaryExpression/op_binaryAndAssign IASTBinaryExpression/op_binaryOrAssign |
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.
Excellent catch! Can you add a test for this case in assignment as value?
IASTBinaryExpression/op_assign IASTBinaryExpression/op_binaryAndAssign IASTBinaryExpression/op_binaryOrAssign | ||
IASTBinaryExpression/op_binaryXorAssign IASTBinaryExpression/op_divideAssign IASTBinaryExpression/op_minusAssign | ||
IASTBinaryExpression/op_moduloAssign IASTBinaryExpression/op_multiplyAssign IASTBinaryExpression/op_plusAssign | ||
IASTBinaryExpression/op_shiftLeftAssign IASTBinaryExpression/op_shiftRightAssign}] |
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'm pretty sure I've written this same list before?
It can be argued that the function returning operator precedence level isn't all that useful because of the fact that the conditional operator and assigment operator are on the same precedence level. I just end up using it to check whether the type or operator of the node is contained in the precedence list. |
I made a lot of other minor remarks, but I think this is the crux of the review. If you can only fix one thing, it should be this: I agree, as it's implemented/used now I think the way you should use |
src/atom_finder/util.clj
Outdated
@@ -496,6 +496,36 @@ | |||
|
|||
(println "==================================================="))))) | |||
|
|||
(defn get-precedence-level |
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.
clojure functions don't use the "get" prefix convention, this probably belongs in classifier-util as well.
src/atom_finder/util.clj
Outdated
IASTBinaryExpression/op_binaryXorAssign 15 IASTBinaryExpression/op_divideAssign 15 IASTBinaryExpression/op_minusAssign 15 | ||
IASTBinaryExpression/op_moduloAssign 15 IASTBinaryExpression/op_multiplyAssign 15 IASTBinaryExpression/op_plusAssign 15 | ||
IASTBinaryExpression/op_shiftLeftAssign 15 IASTBinaryExpression/op_shiftRightAssign 15 CPPASTConditionalExpression 15 | ||
CPPASTExpressionList 16}] |
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 list is fine, but if you wanted to get crafty you could use lists and a macro to reduce redundancy dramatically.
src/atom_finder/util.clj
Outdated
CPPASTExpressionList 16}] | ||
(if (instance? IASTBinaryExpression node) (precedence-list (.getOperator node)) (precedence-list (type node))))) | ||
|
||
(defn assignment-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.
Technically only one of the operators involved is the "assignment operator", yet they all perform assignment. I think probably assignment?
is the right name. Also, I think this belongs in classifier-util, not util (although, heads up, I'm in the midst of refactoring util right now actually...)
src/atom_finder/util.clj
Outdated
(let [assignment-list | ||
#{IASTBinaryExpression/op_assign IASTBinaryExpression/op_binaryAndAssign IASTBinaryExpression/op_binaryOrAssign IASTBinaryExpression/op_binaryXorAssign IASTBinaryExpression/op_divideAssign IASTBinaryExpression/op_minusAssign IASTBinaryExpression/op_moduloAssign IASTBinaryExpression/op_multiplyAssign IASTBinaryExpression/op_plusAssign IASTBinaryExpression/op_shiftLeftAssign IASTBinaryExpression/op_shiftRightAssign}] | ||
|
||
(if (instance? IASTBinaryExpression node) (contains? assignment-list (.getOperator node)) false))) |
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.
Any time you have a boolean literal in an if
you can probably do the same thing better with a logical function (i.e. and
or or
)
|
||
if (0 && 1 || 2 && 3) ; // <true> | ||
if ((0 && 1) || (2 && 3)); | ||
if (0 && (1 || 2) && 3); |
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 you add to the lines which intentionally shouldn't have an atom. helps to show intentionality instead of forgetfulness.
IASTBinaryExpression/op_minus IASTBinaryExpression/op_divide IASTBinaryExpression/op_modulo | ||
IASTBinaryExpression/op_shiftLeft IASTBinaryExpression/op_shiftRight IASTBinaryExpression/op_greaterThan | ||
IASTBinaryExpression/op_greaterEqual IASTBinaryExpression/op_lessThan IASTBinaryExpression/op_lessEqual CPPASTConditionalExpression}] | ||
(if (instance? IASTBinaryExpression node) (contains? non-associative-list (.getOperator node)) (contains? non-associative-list (type node))))) |
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.
The lispy way to do this:
(contains? non-associative-list
((if (instance? IASTBinaryExpression node)
.getOperator
type)
node))```
|
||
(defn node-operator-equal? | ||
"compare types two nodes, if both binary expression then compare their operators" | ||
[node1 node2] |
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.
node-operator-equal?
is fine, but I might consider calling it just operator-equal?
and making it variadic. Makes it more useful and the code smaller.
(some #(and (not (nil? (get-precedence-level %))) | ||
(not (node-operator-equal? node %))) (children node))) | ||
|
||
false)) |
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.
Again, don't put false
the return of an if
.
(some #(not (nil? (get-precedence-level %))) (children node)) | ||
;;Current node is an associative operator | ||
(some #(and (not (nil? (get-precedence-level %))) | ||
(not (node-operator-equal? node %))) (children node))) |
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 use (not (nil?
several times here, but the way clojure handles truthiness means you almost never need (not (nil?
unless you care about the difference between nil
and false
.
src/atom_finder/util.clj
Outdated
[node] | ||
(let [precedence-list | ||
{;"currently supports binary operators(not including sizeof), comma operator and conditional operator" | ||
IASTBinaryExpression/op_modulo 5 IASTBinaryExpression/op_multiply 5 IASTBinaryExpression/op_divide 5 |
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.
Why aren't all the precedence levels defined in this function? This should definitely have everything from 1-15 or else it won't really be useful outside your code.
"Is this node a infix-operator-precedence-atom?" | ||
[node] | ||
(and | ||
(and (precedence-level node) |
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 shouldn't need to nest ands, just add more operands to the first one
(and (and x y) z)
is the same as (and x y z)
src/atom_finder/util.clj
Outdated
(let [assignment-list | ||
#{IASTBinaryExpression/op_assign IASTBinaryExpression/op_binaryAndAssign IASTBinaryExpression/op_binaryOrAssign IASTBinaryExpression/op_binaryXorAssign IASTBinaryExpression/op_divideAssign IASTBinaryExpression/op_minusAssign IASTBinaryExpression/op_moduloAssign IASTBinaryExpression/op_multiplyAssign IASTBinaryExpression/op_plusAssign IASTBinaryExpression/op_shiftLeftAssign IASTBinaryExpression/op_shiftRightAssign}] | ||
|
||
(and (instance? IASTBinaryExpression node) (contains? assignment-list (.getOperator node))))) |
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.
All the functions in util look great, but can you put them in cdt_util instead?
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.
maybe classifier_util is better actually. what do you think?
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.
looking through cdt_util and classifier_util, I get the general sense that classifier_util contains generally higher level functions(if that makes sense to you). So I think it's more fitting to put it in classifier_util
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's a good way to put it. I think you're right about both points.
|
||
(if (non-associative? node) | ||
;;Current node is a non-associative operator | ||
(some #(precedence-level %) (children node)) |
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.
Eventually I'm going to add a few more entries to the set of operators in precedence-level
. Will that break this code?
What are you actually testing when you're using precedence-level
as a predicate? I recommend you make a function that describes what you're testing. It should probably end with a ?
and return a boolean.
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'm not sure what you're going to add and it really depends on how eclipse parses and what we consider confusing or not confusing. But I think it'll maintain a good accuracy.
(precedence-level node) is basically testing whether or not this node represents an operator. If it returns a number then yes and testing can go on, if nil then this node can be ignored.
With unary operators taken into account I think the code needs to be reconsidered.
! a && b
: we agreed that this code is confusing, and currently my code takes care of it
*p && b
: assuming p is pointing to a boolean, I don't think this code is as confusing, but in terms of precedence level it is basically the same as the one above
++a + b
: again, basically the same thing, but I don't think it's as confusing as the first one.
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 sounds like we'd be better of with a function called operator?
or something. But, to be honest, that sounds the same as expression?
no?
These are really great questions re !
, *
, and ++
. Could you type them up into an email and send to Justin/the group?
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 operators in this case is a subset of expressions. a
as a variable is also parsed as an expression, namely CPPASTIdExpression.
And yes I'll try to type up an email and send it to the group
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.
Overall, I think this PR is great. I made a bunch of comments. Fix the ones you want to, leave the ones you don't. There's only one mandatory comment, which is that you should remove the log-files you committed. But otherwise this is really good. I'll be glad to have it in the codebase!
children | ||
(map operator-group) | ||
(remove nil?) | ||
(map (fn [child-group] (sort [(operator-group node) child-group]))))) |
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 a very common pattern of have 2 function arities, both of which do the same thing, one using a predefined value. I usually define the unary version in terms of the binary:
([node] (group-pair node (children node)))
(let [node-group (operator-group node)] | ||
(or (= :arith_unary node-group) | ||
(= :not node-group) | ||
(= :pointer node-group)))) |
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.
If you wanted, this function could be a single line
(def rvalue-unary-operator? (comp #{:arith_unary :not :pointer} operator-group))
But in general, I'm more a fan of naming your data than naming your behaviors, so might go:
(def rvalue-unary-ops #{:arith_unary :not :pointer})
(defn specific-confusing-operator-combination? | ||
"if the combination of groups of operators exists in this set, then the combination is confusing" | ||
[combination] | ||
(some #(= % combination) (map sort #{[:de_incr :pointer] [:multiply :add] [:arith_unary :add] [:arith_unary :multiply] [:and :add] [:and :multiply] [:and :arith_unary] [:or :add] [:or :multiply] [:or :arith_unary] [:or :and] [:not :add] [:not :multiply] [:not :arith_unary] [:not :and] [:not :or] |
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.
- please use shorter lines, this is hard to read
- when you map over a set
#{}
, you get back a seq'()
, so I wouldn't even bother using set notation to begin with, because its misleading. - you don't sort the combination, which means that if you pass in [:pointer :de_incr] it won't match even when it should
In total I'd probably do something like:
(def confusing-op-combinations (->> [[:de_incr :pointer] [:multiply :add] ...] (map sort) (into #{})))
(defn specific-confusing-operator-combination? [combination] (confusing-op-combinations (sort combination)))
|
||
|
||
;; | ||
;;The following 3 functions are for the binary operator special case |
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.
s/binary/unary/
(= :not node-group) | ||
(= :pointer node-group)))) | ||
|
||
(defn unbracktted-unary-in-children |
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.
What is unbracktted
?
(defn unbracktted-unary-in-children | ||
"Is this node not a bracket and constains unary operator in its children?" | ||
[node] | ||
(if (not= "()" (write-node node)) |
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'd love to see a predicate parens?
, but I'd probably implement it (instance? IASTParenthesisExpression)
or whatever.
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.
Just confirm that not sorting combination
isn't a bug for me...
;; | ||
(defn rvalue-unary-operator? | ||
;;==================================== | ||
;;For binary operator special case |
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.
do you really mean binary, not unary here?? if so, please explain more, I don't understand
src/conf/henry.edn
Outdated
:gcc-path "~/opt/src/gcc", | ||
:ag-path "~/opt/src/the_silver_searcher", | ||
:github-top-c "~/opt/src/github-top-c" | ||
} |
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.
Did you remove this on purpose?
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.
JK, saw you added it back.
(some #(= % combination) (map sort #{[:de_incr :pointer] [:multiply :add] [:arith_unary :add] [:arith_unary :multiply] [:and :add] [:and :multiply] [:and :arith_unary] [:or :add] [:or :multiply] [:or :arith_unary] [:or :and] [:not :add] [:not :multiply] [:not :arith_unary] [:not :and] [:not :or] | ||
;;[:compare :and] [:compare :or] // Underclassify | ||
[:compare :not] [:compare :compare] [:pointer :add] [:cond :arith_unary] [:cond :and] [:cond :or] [:cond :not] [:cond :compare] [:cond :cond] [:non-asso :add] [:non-asso :multiply] [:non-asso :arith_unary] [:non-asso :and] [:non-asso :or] [:non-asso :not] [:non-asso :non-asso] [:field_ref :pointer]}))) | ||
(some #(= % combination) (map sort [[:de_incr :pointer] [:multiply :add] [:arith_unary :add] [:arith_unary :multiply] |
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.
Are you sure it's not wrong to not sort combination
I feel like it'll actually get the wrong answer 50% of the time?
Also, this function has to do 60 comparisons or something. But if you put all the operator combinations into a set and store that using def
, then use a set lookup, it's only a single comparison. 60x speedup.
(def confusing-combinations (into #{} (map sort [[:de_incr :pointer] [:multiply :add] ...])))
(defn specific-confusing-operator-combination? [combination] (confusing-combinations (sort combination)))
[:cond :cond] [:non-asso :add] [:non-asso :multiply] [:non-asso :arith_unary] | ||
[:non-asso :and] [:non-asso :or] [:non-asso :not] [:non-asso :non-asso] | ||
[:field_ref :pointer]]))) | ||
(some #(= % combination) (into #{} (map sort [[:de_incr :pointer] [:multiply :add] [:arith_unary :add] [:arith_unary :multiply] |
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 for doing this, but it actually misses the main benefit of what I was proposing. the construction of the set (into #{}
happens every time specific-confusing-operator-combination?
is run, which is lame, additionally, (some #(= % combination)
has to loop over every element of the set, where as if you did a set-lookup it's constant-time. So, if you wanted to make this better you could:
- put
(into #{} (map ...
into adef
variable outside the function, so it's only created one time ever - don't use
(some XXX set)
on a set, instead just do(set XXX)
That said, if you're sick of this, we can commit the code now, up to you, just let me know.
Sorry about all the BS, but I really am very happy with how this turned out. Thanks! |
also added a missing case in assignment-as-value classifier