-
Notifications
You must be signed in to change notification settings - Fork 43
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
feat(stdlib): Add lookupCI for assocarray and Node #639
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.
I think the lookupCI
code looks great, but let's pull out the case-sensitive createObject
code into its own PR 🙂
This reverts commit e8ea91d.
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 looks great, thanks for the updates!
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 looks good, a few comments below
…h creating fields on Node by []. Fixed logic of using/saving keys in AA
src/brsTypes/components/RoSGNode.ts
Outdated
field = new Field(value, fieldType, alwaysNotify); | ||
this.fields.set(mapKey, field); | ||
} | ||
// TODO: change to using interpreter.stderr |
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 having this creates issues int the current PR (which I think it's breaking unit tests right now) I'm ok with opening a github issue and tackling this work in a separate PR
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.
yep, did it
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.
A few last comments below
* @returns the element at `index` if one exists, otherwise throws an Error. | ||
*/ | ||
get(index: BrsType): BrsType; | ||
get(index: BrsType, isCaseSensitiveGet?: boolean): BrsType; |
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 you can remove Get
and Set
from the end of this variables as this can be inferred from the name of the function
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.
done
@@ -77,7 +76,7 @@ export class RoAssociativeArray extends BrsComponent implements BrsValue, BrsIte | |||
.map((value: BrsType) => value); | |||
} | |||
|
|||
get(index: BrsType) { | |||
get(index: BrsType, isCaseSensitiveGet = 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.
same suggestion for the name of the variables here and in line 102
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.
done
let indexValue = this.modeCaseSensitive ? index.value : index.value.toLowerCase(); | ||
return this.elements.get(indexValue) || this.getMethod(index.value) || BrsInvalid.Instance; | ||
return ( | ||
this.findElement(index.value, isCaseSensitiveGet) || |
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 you can get rid of findElement
function and just do the following:
this.elements.get(this.findElementKey(index.value), isCaseSensitiveGet)
because if the key is undefined
the map should return that as well
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.
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.
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 good start, @Vasya-M! I'm a bit worried by the O(n)
lookups this introduces in case-insensitive mode, so let's see what we can do to keep that from being an exhaustive search 😃
for (let key of this.elements.keys()) { | ||
if ((useCaseSensitiveSearch ? key : key.toLowerCase()) === elementKey) { | ||
realElementKey = key; | ||
break; | ||
} | ||
} |
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 seems less than ideal, as it'll cause an O(n)
lookup for every access once setCaseInsensitive
is called. I'm curious if there's an alternative solution we can take that won't require us to iterate across every element every time. Could we maintain a case-insensitive Map
next to the case-sensitive one, and write to both for every set()
? It'd of course require twice as much memory, but would maintain constant time lookups.
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.
Hey, I even forget about it, a great catch.
So I've taken your idea of using a second map and used it for mapping lower case keys to case sensitive keys. so it will not consume so much memory, will not introduce additional computation and almost saves previous performance.
but there is still be O(n)
when we delete keys in case sensitive mode, cuz we can delete key that was used as alias in KeysMap
returns: ValueKind.Dynamic, | ||
}, | ||
impl: (interpreter: Interpreter, key: BrsString) => { | ||
let lKey = key.value; |
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.
Perhaps I've missed something — why's this variable lKey
while the rest are key
?
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.
idk why, but it was as is before my changes
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.
fixed
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, looks great! Thanks for all the hard work 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.
Other than the already given feedback this looks good to me
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're really close! I've got one last idea to avoid an O(n)
operation for every .delete()
call, but otherwise this is looking really good :) Thanks for sticking with it, @Vasya-M!
// if we have {"DD": 0, "dD": 0}, keyMap["dd"] is pointed to "DD", | ||
// and we delete it, then we should find another value for case insensitive accessing ("dD") |
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 calling this out! This is an interesting case, and feels like keyMap
should be Map<string, Set<string>>
: a map of lowercase keys to all encountered cases of that key. In your example with { "DD": 0, "dD": 1}
, keyMap.get("dd")
would be the set ("DD", "dD")
. When someAA.delete("DD")
is called from BrightScript, this function removes the first element from that set (something like let newSet = new Set([ ...keyMap.get(lKey) ].slice(1));
) in case-sensitive mode, and removes the entire entry (this.keyMap.delete(lKey)
) in case-insensitive mode.
Lookups become pretty similar, using the element at index zero of this Set<string>
.
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.
done
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.
Looks great! Thanks for the changes @Vasya-M 😃
lookupCI
method
solution $629 issue
what were done:
CreateObject("roSGNode", "node")
lookupCI
for AA and Nodesresolves #629
UPD:
[]
, or lowercase key if accessed by.
)