Remove pos #145
Remove pos #145
Conversation
|
androidmk patch is https://android-review.googlesource.com/c/333894/ |
b94f665
to
e64127a
| @@ -639,23 +639,276 @@ func TestParseValidInput(t *testing.T) { | |||
| t.Errorf("test case: %s", testCase.input) | |||
| t.Errorf("length mismatch, expected %d definitions, got %d", | |||
| len(testCase.defs), len(file.Defs)) | |||
|
|
|||
colincross
Mar 16, 2017
Contributor
I suggest moving this to compare.go, and it seems worth including some tests for it on its own. Code-wise, it is very similar to clone and extend in the blueprint/proptools package, so maybe move it there, and try to make them structured similarly?
I suggest moving this to compare.go, and it seems worth including some tests for it on its own. Code-wise, it is very similar to clone and extend in the blueprint/proptools package, so maybe move it there, and try to make them structured similarly?
mathjeff
Mar 17, 2017
Author
Contributor
good idea
good idea
| return found | ||
| } | ||
|
|
||
| // checks if two objects are the same class, returns True if they match |
colincross
Mar 16, 2017
Contributor
Go doesn't have objects or classes.
s/class/type/
s/object/value/
Go doesn't have objects or classes.
s/class/type/
s/object/value/
mathjeff
Mar 17, 2017
Author
Contributor
ok
ok
| if actual.item.Kind() != expected.item.Kind() { | ||
| return false, fmt.Sprintf("%s is of type %s whereas %s is of type %s", actual.name, actual.item.Kind(), expected.name, expected.item.Kind()) | ||
| } | ||
| switch actual.item.Kind() { |
colincross
Mar 16, 2017
Contributor
This conversion is unnecessary, use
reflect.DeepEqual(actual.item.Interface(), expected.item.Interface())
This conversion is unnecessary, use
reflect.DeepEqual(actual.item.Interface(), expected.item.Interface())
mathjeff
Mar 17, 2017
Author
Contributor
cool
cool
mathjeff
Mar 21, 2017
Author
Contributor
Well I investigated a little further but it seems that unfortunately, x.Interface() fails if there were any private fields traversed while accessing x. My thought now is that it's preferable to have support for diffs of private fields if it requires reimplementing this method like this.
Well I investigated a little further but it seems that unfortunately, x.Interface() fails if there were any private fields traversed while accessing x. My thought now is that it's preferable to have support for diffs of private fields if it requires reimplementing this method like this.
| aKeys := a.MapKeys() | ||
| bKeys := b.MapKeys() | ||
| for _, aKey := range aKeys { | ||
| if !(isIn(aKey, bKeys)) { |
colincross
Mar 16, 2017
Contributor
This is unnecessary, use:
if !b.MapIndex(aKey).IsValid()
This is unnecessary, use:
if !b.MapIndex(aKey).IsValid()
mathjeff
Mar 17, 2017
Author
Contributor
See below about keys being pointers
See below about keys being pointers
colincross
Mar 17, 2017
Contributor
What happens if two deeply equal pointers are keys in the same map? Only the pointer value will be hashed, so they will still be two different keys.
What happens if two deeply equal pointers are keys in the same map? Only the pointer value will be hashed, so they will still be two different keys.
mathjeff
Mar 17, 2017
Author
Contributor
Yeah exactly; the test doesn't confirm that the pointers themselves are correct, only that they have a corresponding value in the other map.
Do you mean that we want to fix deepCompare for the following case?
////
var a and b, deeply equal to each other
var c and d, deeply equal to each other
map1{a:true, b:true, c:true}
map2{a:true, c:true, d:true}
?
for which deepCompare would return true when really we want false
Yeah exactly; the test doesn't confirm that the pointers themselves are correct, only that they have a corresponding value in the other map.
Do you mean that we want to fix deepCompare for the following case?
////
var a and b, deeply equal to each other
var c and d, deeply equal to each other
map1{a:true, b:true, c:true}
map2{a:true, c:true, d:true}
?
for which deepCompare would return true when really we want false
mathjeff
Mar 17, 2017
Author
Contributor
I guess it could also be worth fixing this case too:
////
var a and b, deeply equal to each other
map1{a:true, b:false}
map2{a:true, b:false}
for which we want an answer of true but theoretically get a random answer (though I expect in practice it's always false).
Is that what you're referring to?
I guess it could also be worth fixing this case too:
////
var a and b, deeply equal to each other
map1{a:true, b:false}
map2{a:true, b:false}
for which we want an answer of true but theoretically get a random answer (though I expect in practice it's always false).
Is that what you're referring to?
colincross
Mar 17, 2017
Contributor
We would need to define what the semantics are for comparing two maps whose keys are deepequal but not equal. Even for two maps that were deep-cloned off of each other, there is no guarantee that we can determine a 1-1 mapping between keys in the two maps.
The alternative is to sidestep this problem and refuse to deepcompare pointers or anything that might contain pointers (structs, arrays) as the keys in maps. This should be fine for the parser/printer, since they only support strings as the keys to maps.
We would need to define what the semantics are for comparing two maps whose keys are deepequal but not equal. Even for two maps that were deep-cloned off of each other, there is no guarantee that we can determine a 1-1 mapping between keys in the two maps.
The alternative is to sidestep this problem and refuse to deepcompare pointers or anything that might contain pointers (structs, arrays) as the keys in maps. This should be fine for the parser/printer, since they only support strings as the keys to maps.
mathjeff
Mar 17, 2017
•
Author
Contributor
Unfortunately the test needs to be able to compare SyntaxTree.comments, whose type is map[ParseNode](*CommentPair) , which uses an interface type as its key
I think that my inclination, if we want to spend the effort, would be two changes. 1. we fix the case in deepCompare involving maps having recursively equal keys such that deepCompare deems them equal, and 2. we require the individual tests to explicitly invoke deepCompare, so if the tests require certain relationships between the pointers themselves (currently the checks specific to the pointers are in the Builder class itself), then the tests can validate those requirements separately, and can still use deepCompare while doing it.
The changes to deepCompare I imagine would be to have it assume that if there exists a 1-1 mapping for which we cannot find a relevant difference, then we deem the data structures equivalent. In other words: 1. For each combination of entries in map1 and map2, check whether those two entries are equal. 2. For each entry A in map1 that has K matches in map2, confirm that there are exactly K such entries in map1 that have the same set of matches in map2 3. If every condition in 2 yielded true, then return true.
As for sidestepping the problem, my favorite option is to invoke the printer (or perhaps VerbosePrinter) in printer_test.go and check the text output.
Alternatively we could adjust the code being tested attach an integer id to each ParseNode and to use that as the map key rather than the ParseNode reference itself.
Unfortunately the test needs to be able to compare SyntaxTree.comments, whose type is map[ParseNode](*CommentPair) , which uses an interface type as its key
I think that my inclination, if we want to spend the effort, would be two changes. 1. we fix the case in deepCompare involving maps having recursively equal keys such that deepCompare deems them equal, and 2. we require the individual tests to explicitly invoke deepCompare, so if the tests require certain relationships between the pointers themselves (currently the checks specific to the pointers are in the Builder class itself), then the tests can validate those requirements separately, and can still use deepCompare while doing it.
The changes to deepCompare I imagine would be to have it assume that if there exists a 1-1 mapping for which we cannot find a relevant difference, then we deem the data structures equivalent. In other words: 1. For each combination of entries in map1 and map2, check whether those two entries are equal. 2. For each entry A in map1 that has K matches in map2, confirm that there are exactly K such entries in map1 that have the same set of matches in map2 3. If every condition in 2 yielded true, then return true.
As for sidestepping the problem, my favorite option is to invoke the printer (or perhaps VerbosePrinter) in printer_test.go and check the text output.
Alternatively we could adjust the code being tested attach an integer id to each ParseNode and to use that as the map key rather than the ParseNode reference itself.
| return item, false | ||
| } | ||
|
|
||
| func isIn(item reflect.Value, list []reflect.Value) (contained bool) { |
colincross
Mar 16, 2017
Contributor
I think this function (and findMatching and compareValues) are unnecessary due to comments below
I think this function (and findMatching and compareValues) are unnecessary due to comments below
mathjeff
Mar 17, 2017
Author
Contributor
See below about keys being pointers
See below about keys being pointers
| b := expected.item | ||
| aLen := a.Len() | ||
| bLen := b.Len() | ||
| sharedLen := int(math.Min(float64(aLen), float64(bLen))) |
colincross
Mar 16, 2017
Contributor
Don't cast to float, either implement a trivial min function or open code it
Don't cast to float, either implement a trivial min function or open code it
mathjeff
Mar 17, 2017
Author
Contributor
ok
ok
| aLen := a.Len() | ||
| bLen := b.Len() | ||
| sharedLen := int(math.Min(float64(aLen), float64(bLen))) | ||
| for i := 0; i < sharedLen; i++ { |
colincross
Mar 16, 2017
Contributor
Or:
for i := 0; i < a.Len() && i < b.Len(); i++ {
Or:
for i := 0; i < a.Len() && i < b.Len(); i++ {
mathjeff
Mar 17, 2017
Author
Contributor
I'll re-implement the 'min' function as you mentioned above.
I extracted the 'min' operation to slightly decrease the number of values being recomputed during each loop iteration (I don't see anything in https://github.com/golang/go/wiki/CompilerOptimizations specifying that the Go compiler can optimize 'i < a.Len() && i < b.Len()').
I'll re-implement the 'min' function as you mentioned above.
I extracted the 'min' operation to slightly decrease the number of values being recomputed during each loop iteration (I don't see anything in https://github.com/golang/go/wiki/CompilerOptimizations specifying that the Go compiler can optimize 'i < a.Len() && i < b.Len()').
| b := expected.item | ||
| aCount := a.NumField() | ||
| bCount := b.NumField() | ||
| if aCount != bCount { |
colincross
Mar 16, 2017
Contributor
It would be more reliable to compare the types here, or delete the check entirely
It would be more reliable to compare the types here, or delete the check entirely
mathjeff
Mar 17, 2017
Author
Contributor
ok
ok
| return true, "" | ||
| } | ||
|
|
||
| // deepCompare is much like reflect.DeepEqual, but it does a deep comparison of map keys too (which is slower than a map lookup) |
colincross
Mar 16, 2017
Contributor
Deep comparing map keys seems unnecessary to me. If they hash to the same value they must be == equal, and if they are == equal then they must be DeepEqual. If that is not true, can you construct a test that returns different values for reflect.DeepEqual and deepCompare?
Deep comparing map keys seems unnecessary to me. If they hash to the same value they must be == equal, and if they are == equal then they must be DeepEqual. If that is not true, can you construct a test that returns different values for reflect.DeepEqual and deepCompare?
mathjeff
Mar 17, 2017
Author
Contributor
Yeah, I was surprised too. I'll add a test involving a map having keys of pointer type.
Yeah, I was surprised too. I'll add a test involving a map having keys of pointer type.
| return deepCompareObjects(namedItem{"actual", reflect.ValueOf(actual)}, namedItem{"expected", reflect.ValueOf(expected)}, 0) | ||
| } | ||
|
|
||
| // deprecated |
colincross
Mar 16, 2017
Contributor
Why check in a deprecated function?
Why check in a deprecated function?
mathjeff
Mar 17, 2017
Author
Contributor
I'll remove it
I'll remove it
| Comments: comments, | ||
| }, errs | ||
|
|
||
| type parser struct { |
colincross
Mar 17, 2017
Contributor
reordering these makes this really hard to review
reordering these makes this really hard to review
mathjeff
Mar 22, 2017
Author
Contributor
done
done
| Name string | ||
| Defs []Definition | ||
| Comments []*CommentGroup | ||
| type ParseTree struct { |
colincross
Mar 17, 2017
Contributor
The distinction between ParseTree and SyntaxTree is not clear to me. Leaving this as File and File.Name would be much easier to review, and then you can use a separate patch to do renames. The gorename command line tool is useful for doing programmatic renames, and you can include the gorename command in the commit message.
The distinction between ParseTree and SyntaxTree is not clear to me. Leaving this as File and File.Name would be much easier to review, and then you can use a separate patch to do renames. The gorename command line tool is useful for doing programmatic renames, and you can include the gorename command in the commit message.
mathjeff
Mar 22, 2017
Author
Contributor
done
done
5f96b9c
to
d263470
|
Partial review of the first 2 commits |
| @@ -16,7 +16,7 @@ package parser | |||
|
|
|||
| import ( | |||
| "bytes" | |||
| "reflect" | |||
| "github.com/google/blueprint/proptools" | |||
colincross
Apr 11, 2017
Contributor
Imports for blueprint modules should go after all the imports from the standard library, with a blank line between the groups.
Imports for blueprint modules should go after all the imports from the standard library, with a blank line between the groups.
mathjeff
May 5, 2017
Author
Contributor
Have you made any customizations to Gogland that automate things like line lengths and import ordering? If so, I wonder if we can check them in somewhere.
Have you made any customizations to Gogland that automate things like line lengths and import ordering? If so, I wonder if we can check them in somewhere.
colincross
May 17, 2017
Contributor
No, and it keeps screwing them up for me too.
No, and it keeps screwing them up for me too.
| expectedNodes := expected.ListOfAllNodes() | ||
| remappings = make(map[ParseNode]ParseNode, 0) | ||
| for i, actual := range actualNodes { | ||
| remappings[actual] = expectedNodes[i] |
colincross
Apr 11, 2017
Contributor
Might be worth at least adding sanity checks here. If len(expectedNodes) < len(actualNodes) this will panic
Might be worth at least adding sanity checks here. If len(expectedNodes) < len(actualNodes) this will panic
mathjeff
May 5, 2017
Author
Contributor
ok
ok
| proptools.NamedItem{"actual.SyntaxTree.Comments", analogousActualComments}) | ||
| } | ||
|
|
||
| func compareSourcePositions(expected *ParseTree, actual *ParseTree, actualNode_remappings map[ParseNode]ParseNode) proptools.Comparison { |
colincross
Apr 11, 2017
Contributor
Go doesn't enforce a hard line length limit, but try to keep lines under 100 characters.
Also Go doesn't use underscores in variable names.
Go doesn't enforce a hard line length limit, but try to keep lines under 100 characters.
Also Go doesn't use underscores in variable names.
mathjeff
May 5, 2017
Author
Contributor
Yeah I don't like the underscore either; I just use it to clarify that this is the remappings of the actualNodes as opposed to being the actual nodeRemappings. What name would you recommend that conveys as much information?
Yeah I don't like the underscore either; I just use it to clarify that this is the remappings of the actualNodes as opposed to being the actual nodeRemappings. What name would you recommend that conveys as much information?
colincross
May 6, 2017
Contributor
Sometimes its better to use a short, readable name and put the extra information in a comment, rather than carrying all the information everywhere you use the name. See https://github.com/golang/go/wiki/CodeReviewComments#variable-names
Sometimes its better to use a short, readable name and put the extra information in a comment, rather than carrying all the information everywhere you use the name. See https://github.com/golang/go/wiki/CodeReviewComments#variable-names
mathjeff
May 9, 2017
Author
Contributor
Hmm it seems to me that the trouble in this case is more that I forgot to add a comment than that I used a long variable name.
In any case, I've now redone it in the next version of this commit for you to take a look at.
Hmm it seems to me that the trouble in this case is more that I forgot to add a comment than that I used a long variable name.
In any case, I've now redone it in the next version of this commit for you to take a look at.
|
|
||
| // now compare the comments | ||
| return proptools.DeepCompare( | ||
| proptools.NamedItem{"expected.SyntaxTree.Comments", expected.SyntaxTree.comments}, |
colincross
Apr 11, 2017
Contributor
NamedItem isn't making the API any clearer, maybe just pass the string and tree as adjacent parameters
NamedItem isn't making the API any clearer, maybe just pass the string and tree as adjacent parameters
mathjeff
May 5, 2017
Author
Contributor
ok
ok
| // The reason we replace the keys is so proptools.DeepCompare can know which keys we want to compare against which others | ||
|
|
||
| analogousSourcePositions := make(map[ParseNode]scanner.Position) | ||
| for actualKey, actualValue := range expected.SourcePositions { |
colincross
Apr 11, 2017
Contributor
Should this be actual.SourcePositions?
Should this be actual.SourcePositions?
mathjeff
May 5, 2017
Author
Contributor
true
true
| if in[0] == '\n' { | ||
| in = in[1:] | ||
| } else { | ||
| panic(fmt.Sprintf("A newline at the beginning of testcase input is required, to make the test code easily readable. Offending test case '%s' starts with character: '%s'", description, string(in[0]))) |
colincross
Apr 11, 2017
Contributor
This doesn't seem worth enforcing, just strip it if it is there.
This doesn't seem worth enforcing, just strip it if it is there.
mathjeff
May 5, 2017
Author
Contributor
ok
ok
| Item interface{} | ||
| } | ||
|
|
||
| type Comparison struct { |
colincross
Apr 11, 2017
Contributor
a two-value return is more idiomatic than returning a struct
a two-value return is more idiomatic than returning a struct
mathjeff
May 5, 2017
Author
Contributor
When is that (two values preferable to a struct) the case? Is it when the two values have fundamentally different meanings? It seems to me that a Comparison is itself a concept. If we wanted to instead return an entire list of differences, it'd be convenient to not have to update the signatures of each method based on the implementation of a Comparison.
When is that (two values preferable to a struct) the case? Is it when the two values have fundamentally different meanings? It seems to me that a Comparison is itself a concept. If we wanted to instead return an entire list of differences, it'd be convenient to not have to update the signatures of each method based on the implementation of a Comparison.
colincross
May 6, 2017
Contributor
I'd say that a struct should be used if you are going to pass that encapsulated value around (more than just returning it). If you are going to immediately unpack and operate on it, a multiple-value return is better.
I'd say that a struct should be used if you are going to pass that encapsulated value around (more than just returning it). If you are going to immediately unpack and operate on it, a multiple-value return is better.
mathjeff
May 9, 2017
Author
Contributor
That logic sounds like a decent rule of thumb to me: to use a multi-valued return if the caller is likely to immediately read all of the contained fields. Does it apply here? None of the functions in compare.go read the field Comparison.Difference .
If for example we wanted to add another field to Comparison that perhaps specified the property path of the difference, then those fields probably wouldn't be read by anything in compare.go either.
That logic sounds like a decent rule of thumb to me: to use a multi-valued return if the caller is likely to immediately read all of the contained fields. Does it apply here? None of the functions in compare.go read the field Comparison.Difference .
If for example we wanted to add another field to Comparison that perhaps specified the property path of the difference, then those fields probably wouldn't be read by anything in compare.go either.
mathjeff
May 10, 2017
Author
Contributor
I talked to Dan and he thinks that if the number of fields is small then the thing to do is to make the fields be separate return values, and if the number of fields grows then to consider making the return be a struct at that time. I suppose that that's enough of a rule of thumb for me to be able to follow anyway, so I'll do that.
I talked to Dan and he thinks that if the number of fields is small then the thing to do is to make the fields be separate return values, and if the number of fields grows then to consider making the return be a struct at that time. I suppose that that's enough of a rule of thumb for me to be able to follow anyway, so I'll do that.
| } | ||
| } | ||
|
|
||
| // compares two maps |
colincross
Apr 11, 2017
Contributor
document the semantics of this comparison, especially as compared to reflect.DeepEqual
document the semantics of this comparison, especially as compared to reflect.DeepEqual
mathjeff
May 5, 2017
Author
Contributor
done
done
| return equal | ||
| } | ||
|
|
||
| // compares two arrays |
colincross
Apr 11, 2017
Contributor
compares two arrays or slices
document that this compares [0,len()), and not [0,cap())
compares two arrays or slices
document that this compares [0,len()), and not [0,cap())
mathjeff
May 5, 2017
Author
Contributor
ok
ok
| } | ||
| } | ||
|
|
||
| // compares two pointers in the same manner as reflect.deepEqual |
colincross
Apr 11, 2017
Contributor
compares two pointers or interfaces
compares two pointers or interfaces
mathjeff
May 5, 2017
Author
Contributor
done
done
455e669
to
551628b
| return false, explanation | ||
| } | ||
|
|
||
| // end of public members |
colincross
May 17, 2017
Contributor
This seems both unnecessary and misleading, since same() and different() are not public
This seems both unnecessary and misleading, since same() and different() are not public
mathjeff
May 24, 2017
Author
Contributor
oops
oops
| } | ||
| } | ||
| if len(differences) > 0 { | ||
| return firstDifference(differences) |
colincross
May 17, 2017
Contributor
return false, sort.Strings(differences)[0]
return false, sort.Strings(differences)[0]
mathjeff
May 24, 2017
Author
Contributor
thanks
thanks
| return same() | ||
| } | ||
|
|
||
| // compares two arrays (this ignores cap(), and only compares elements from indices 0 to len() ) |
colincross
May 17, 2017
Contributor
arrays or slices
arrays or slices
mathjeff
May 24, 2017
Author
Contributor
ok
ok
| // a map with several differences, for confirming determinism | ||
| map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, | ||
| map[int]int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 0}, | ||
| `a[0(%!t(int=0))] = 0 whereas b[0(%!t(int=0))] = 1`, |
colincross
May 17, 2017
Contributor
This seems like a bug?
This seems like a bug?
mathjeff
May 24, 2017
Author
Contributor
What would you prefer instead?
What would you prefer instead?
colincross
May 26, 2017
Contributor
These results contain %!t, which is an error inserted into the result of fmt.Sprintf. %t expects a bool, did you mean %T?
These results contain %!t, which is an error inserted into the result of fmt.Sprintf. %t expects a bool, did you mean %T?
da366ce
to
1e20165
| p.requestNewlinesForPos(pos) | ||
| func (p *printer) beforePrint(node ParseNode) { | ||
| var comments = p.syntaxTree.GetCommentsIfPresent(node) | ||
| if comments != nil { |
colincross
May 26, 2017
Contributor
Nil check is unnecessary, range on a nil slice is not an error
Nil check is unnecessary, range on a nil slice is not an error
mathjeff
May 26, 2017
Author
Contributor
Hmm, when I remove the check, I get a panic trying to access comments.preComments
Hmm, when I remove the check, I get a panic trying to access comments.preComments
| // a map with several differences, for confirming determinism | ||
| map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, | ||
| map[int]int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 0}, | ||
| `a[0(%!t(int=0))] = 0 whereas b[0(%!t(int=0))] = 1`, |
colincross
May 26, 2017
Contributor
These results contain %!t, which is an error inserted into the result of fmt.Sprintf. %t expects a bool, did you mean %T?
These results contain %!t, which is an error inserted into the result of fmt.Sprintf. %t expects a bool, did you mean %T?
this should help when making more test cases Bug: b/34671474 Test: make blueprint_tools Change-Id: Iff6197ac64053fde33119bbf59885c714b27c91f
A ParseTree represents a tree that was parsed from text, and also includes information about where its nodes came from. Currently the source information is attached to the nodes themselves, but that's changing in a subsequent patch, at which point the ParseTree will directly contain the source information. At that point, it will be possible for a programmatic builder class to generate a syntax tree without having to think about or pass in empty values for text positions. Really the reason that this patch is separate is to make the code review more convenient. gorename -from 'github.com/google/blueprint/parser.File.Name' -to 'FileName' gorename -from 'github.com/google/blueprint/parser.File' -to 'ParseTree' Bug: b/34671474 Test: make blueprint_tools Change-Id: I7d591ac123ccd1697a3d739308596655afc39197
Now the final position of each node is now decided separately by the printer during printing, based on the node's place in the SyntaxTree The parser now generates a ParseTree, which has slightly more information than a SyntaxTree - it contains both the syntax tree and the original source positions of the tokens from which the syntax tree was parsed Bug: b/34671474 Test: make blueprint_tools Change-Id: I55466114917cc88ac9e4e60b56bda2d4318c231a
…d by compare_test.go Bug: b/34671474 Test: upload a patch for Travis to build Change-Id: I1bb7f94104e904c09c4175c5c85164080c6756fc

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

No description provided.