Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

[[ Feature ]] Array Literals #5824

Open
wants to merge 7 commits into
base: develop
Choose a base branch
Loading
from

Conversation

runrevmark
Copy link
Contributor

@runrevmark runrevmark commented Aug 21, 2017

This patch adds support for 'array literals' to LiveCode Script:

  put [ 1, 2, [ 3, 4, { "size" : the length of tOtherSeq } ] ] into tSeqVar
  put { "foo" : "bar", "baz" : "foo" , "bar": [ 1, 2, 3 ] } into tArrVar

Literals can be either completely constant, or completely dynamic, or a mixture of both.

In order to be able to detect constancy, a new (general) 'getattrs()' virtual method has been added to the MCExpression AST base class. This allows a node to be asked what attributes it has (the first being 'constant', but there are others which would be useful - like 'pure', or 'waitable'). The constant attribute is used to determine whether compile-time evaluation should be tried on the node - if the latter succeeds it is truly constant, if eval throws an error it is considered dynamic so the error will occur at runtime.

If a literal is constant, then the constant value is uniqued and is directly returned on eval. As the value is uniqued, the same literal can appear repeatedly, but only actually have one value in memory representing it.

If a literal is not constant, then it is stored as a (uniqued) base value with the constant key/values, along with a list of dynamic values. When a non-constant literal is evaluated, a new array value is built based on a copy of the base value; and the dynamic elements evaluated and added to it.

Note: This is an stg stack, hence the lack of commit messages as yet.

Note: Only seq literals can be constant at the moment - to do array literals needs some work to ensure that duplicate keys are handled appropriately. i.e. A duplicate dynamic key which appears before a constant identical key should be ignored, but used instead of a constant identical key if it appears after. This helps preserve a strict left-to-right evaluation order. There is an argument to be had for throwing an error with duplicate keys - although the dynamicity of caseSensitivity of keys suggests that for now, left-to-right evaluation and replacement if it occurs is probably the best path.

@montegoulding
Copy link
Member

@runrevmark considering you can't set the case sensitivity in script scope I'd say throw an error with exact same case key but treat it as case sensitive otherwise. Probably non-constant array literals should throw a runtime error in the same case and additionally with the if the same key with different casing and the case sensitive is currently false.

One thing that would be really cool given this would be referencing previously declared constants. I guess that's going to need some parser support.

constant kFoo = 1
constant kBar = 2
constant kBaz = { "foo" : kFoo, \
                               "bar" : kBar }

@runrevmark
Copy link
Contributor Author

runrevmark commented Aug 21, 2017

@montegoulding Yes - having proper expressions in constants would be useful. There's an anomaly there though in that the value of a constant can only be a literal (which means constant kFoo = colon is different from using colon in normal expressions :( ) - http://quality.livecode.com/show_bug.cgi?id=19413. Certainly we can make it so that anything more than a literal is evaluated as an expression, but that would then leave you with:

constant kFoo = ( empty )

being different from

constant kFoo = empty

However, again, it might be that this is a case where breaking backwards-compatibility is completely justified for the benefit it would bring. I'm wondering lately whether we should start marking stacks with the last engine version they were saved in, and then adding a warning mechanism for subtle changes to things like this. However, the infrastructure needed here isn't insignificant - as we need to make the warnings easily grokable in the IDE. Perhaps this is just another thing we need a 'scriptVersion' type thing for...

In terms of the overlapping keys thing then I wonder if that is actually a 'strictness' concern. It needs a bit more thought - however, the underlying implementation to handle the case means we can either make it an error, or make it do 'the right thing' when considered from a strict LTR eval order. i.e. Internally it looks much the same.

It very much comes down to whether we want the direct equivalence between:

  put { tKey1 : tValue1, tKey2: tValue2 } into tArray

and

  put empty into tArray
  put tValue1 into tArray[tKey1]
  put tValue2 into tArray[tKey2]

Or not. The advantage of this equivalence, is that existing code can be rewritten to use array literals; rather than lots of puts which has significant performance advantages.

@montegoulding
Copy link
Member

@runrevmark I assume put { tKey1 : tValue1, tKey2, tValue2 } into tArray was a typo but it does leave me wondering if the parser will cope with setting a key or a value to something with multiple items. I guess we can use parentheses.

Anyway, I think I'd be pleased if there were a parser error on multiple case sensitive equal literal keys. That's clearly a user error. As in put { "foo" : "bar", "foo" : "baz" } really should tell me I'm an idiot.

As far as dynamic or literal keys with different case but in a context we don't know what the case sensitivity might be then I see your point. LTR clobbering of values depending on the current value of the caseSensitive does make a sense although again it does seem unlikely that if it's happening in my code I wouldn't want to fix something. If I know that multiple keys may have the same value then I could use the current syntax or union or something.

@runrevmark
Copy link
Contributor Author

@montegoulding Yes - that was a typo - now corrected.

The keys and values are parsed as expressions which cannot use the ',' operator - just as expressions within a parameter list cannot. So:

put { tKey1: tValue1, tValue2, tKey2 } into tArray

Will be a syntax error (missing ':' after tValue2). Whilst we could make it work, you loose the ability to check for missing ':', which is probably more important - as you can just use ( ) to group expressions containing ','.

In terms of duplicate keys... How about this:

  • case-sensitively equal constant keys cause a compile-time error
  • case-insensitive equal constant keys mark the node as 'requiring case-sensitivity' at compile-time
  • at runtime if caseSensitive is false and the node has the require case-sensitive mark, it is a runtime error
  • if a dynamic key already exists in the array whilst building at runtime (taking into account case-sensitive) it is a runtime error

This does give consistent behavior regardless of case-sensitivity, so it is perhaps the best option - it is also a little easier to implement (as you don't need to keep the order of the constant key/values around). We also now have 'into' forms of union; so you can write 'overlay' type operations in a single line:

  union { overlay array } with { base array } into tMyArray

@montegoulding
Copy link
Member

@runrevmark so do I understand this right:

constant kFoo = { "foo" : "bar", "Foo" : "bar" }

on Handler
    set the caseSensitive to true
    local tArray
    put kFoo into tArray --> no error

    set the caseSensitive to false
    put kFoo into tArray --> error
end Handler

@runrevmark
Copy link
Contributor Author

@montegoulding Hmmm - okay - put like that I'm not sure my suggested rules necessarily make sense.

How about changing it so that array literals are always built in a case-sensitive fashion? This is closer 'to the truth' - although it is (obviously) opening up a route for coding faults which are hard to spot.

Guess this needs a little more thought!

@montegoulding
Copy link
Member

Hmm... I'll ask this here because I have no idea how long it would take me to work it out from code but is there any level of consistency when you have set an array in a case sensitive context and then access it in a case insensitive one? Any precedence or is it first come first served?

What I mean is:

set the caseSensitive to true
put 1 into tArray["foo"]
put 2 into tArray["Foo"]
set the caseSensitive to false
--> what is tArray["Foo"] here?

Why I ask is if we have a rule like return the lowercase one or something then we can use that rule to clobber when we setting the array. If there's no rule then perhaps it really should be an execution error to use a case sensitive array in a case insensitive context...

@runrevmark
Copy link
Contributor Author

@montegoulding The caseSensitive property affects the lookup that MCArrayStore/Fetch do - but it all happens with regard hash-order. So, what you get when you lookup a key which has multiple entries (all case-sensitively different), will depend on construction order, array size and what has happened to the array.

We perhaps need to define caseSensitive with regards arrays slightly better - if an array has been built case-sensitively, then from a case-insensitive context it isn't a 1-1 mapping any more, but a 1-many mapping - which would suggest it should be an error. Put like this, the error is when you ask for a key case-insensitively which has multiple case-sensitive entries (i.e. quite localized).

Of course, this isn't how things have ever worked - so if this is to be considered an error, it is an anomaly. It also suggests the problem comes with set/fetch of arrays; rather than construction - i.e. array literals should always be case-sensitively built; but then consider making it an error for access in the above case at some point.

@montegoulding
Copy link
Member

If there's no way to know which element you are getting and indeed if the element you get may change if you mutate the array I suggest an execution error would be beneficial to people. But I agree that case sensitive is the only logical way to go for the literals and it's accessing the element that should have the error.

@runrevmark runrevmark force-pushed the feature-array_literals branch from c9f18b1 to 6ac4983 Compare August 29, 2017 08:05
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.