I've moved this back to feature requests for the time being.

Using the . or : operators, or (), directly after a list or proc operation is not currently supported by the DM syntax. I'm not sure I'd necessarily call it a bug (I'm on the fence) as this is a long-known limitation of the language; to me this is more of an enhancement request. But I'm also not sure that changing it won't interfere with any existing code. Mainly my concern there is that where : overlaps with the ternary operator, you can introduce ambiguous statements. If it were to be classified as a bug, I'm not sure about its fixability. Take this example:

// compiler has to be aware of ternary operator
a?b:c
// ambiguous without spacing: is this b:c or c:d?
a?b:c:d

I don't know if any code currently uses the ambiguous case; I would be unsurprised if it turned out to be true of some 4K projects, though I'm not overly worried about preserving their compilation integrity.

Mainly, I'd like to discuss this more before committing to reclassifying it as a bug. Changing this in any event is likely to prove difficult, as the parser is rather blackboxy.
Isn't that where order of operators comes in handy? : as a path operator is higher than ?/:

This compiles, no errors:
mob/verb/test()
var/test = src?ckey :key
world << test


Does not compile, "expected ':'" error:
    var/test = src?ckey:key


Compiles:
    var/test = src?"[ckey]":"[key]"


Expected ':' error:
    var/test = src?src:ckey:key


Compiles fine, outputs src:ckey properly.
    var/test = src?src:ckey :key


From what I see, : as a path operator takes precedence before anything else, mostly due to the fact that you HAVE to put a space before or after the ternary operator's colon, where the b and c are variables/identifiers (without parenthesis - parenthesis and no space works fine all the time). otherwise, it spits out a compile-time error.

In a case with the ternary operator, I think it'd work fine if they manage to compile it.
I don't think order of operators solves it. It's clear that the membership operator should be higher-precedence than the ternary, but it doesn't solve whether a?b:c:d should be a?(b:c):d or a?b:(c:d). Both would be considered a case of : having the stronger binding, or else the expression would be interpreted as (a?b:c):d. So it's a question of the order in which the operator is encountered, and whether there is any whitespace.

I'm not sure how the current ternary parsing works regarding telling the membership operator from the start of the third expression. It's something I'd have to study.
Bump , pls.
Would also very much appreciate a fix for this.
I'm fairly sure this is quite impossible, due to how the compiled byte codes work. There's simply no way to pass the result of a proc (or list indexing) as the context of another proc call without first storing it in a variable, or using call().

I suppose it could rewrite the code for you to use `call(obj, str)` instead, but that's quite a bit slower IIRC.
I don't think this is such a good idea. Without the ability to also specify the type of items in a list anything you're calling or any variable you request won't be typecast to the correct type.

The last thing we should want is to use the : operator. It leads to broken code because if any object in the game shares a variable or proc name the removal of a proc or variable won't result in a compile-time error anymore.
Lummox JR wrote:
I don't think order of operators solves it. It's clear that the membership operator should be higher-precedence than the ternary, but it doesn't solve whether a?b:c:d should be a?(b:c):d or a?b:(c:d). Both would be considered a case of : having the stronger binding, or else the expression would be interpreted as (a?b:c):d. So it's a question of the order in which the operator is encountered, and whether there is any whitespace.

Personally I'd say the membership ops should be ultra-high-priority regardless of how bad that screws up the ternary op syntax, at least in principle: OOP functionality should always come first. But just having it evaluated first the naive way would probably yield something like a?b:c:d --> a ? b.c.d (ie having the ternary operator evaluate at the same level as in/to, very low with surrounding whitespace required). And that'd probably break a ton of existing code.

Might actually be able to get away with just treating the first ":" after a "?" as the ternary separator, and assuming it's not the ternary sep anywhere else. Probably would break some code somewhere (eg 4K projects as you suggested) but imho trying to maintain backwards-compatibility for code like that is a lot of work for very little reward.

Yota wrote:
I'm fairly sure this is quite impossible, due to how the compiled byte codes work. There's simply no way to pass the result of a proc (or list indexing) as the context of another proc call without first storing it in a variable, or using call().

The compiler can definitely produce the desired output. After all, that's what it does given this: "var/thing = list[5] ; thing:name = "example" ; return thing:someProc()". Judging from Lummox's responses "(list[5]):name = "example" just doesn't work because of how the ":" is getting interpreted.

Sir Lazarus wrote:
I don't think this is such a good idea. Without the ability to also specify the type of items in a list anything you're calling or any variable you request won't be typecast to the correct type.
The last thing we should want is to use the : operator.

Yeah, that's 100% true. Messing with something straight from a list or return is only really useful for low-level optimizations, mainly in tight foreach loops where the indirection adds enough overhead to warrant it.

Still, it's always nice to cross things off "known BYOND issues" :).

In response to Wild Bill Bartok
Wild Bill Bartok wrote:
Yota wrote:
I'm fairly sure this is quite impossible, due to how the compiled byte codes work. There's simply no way to pass the result of a proc (or list indexing) as the context of another proc call without first storing it in a variable, or using call().
The compiler can definitely produce the desired output. After all, that's what it does given this: "var/thing = list[5] ; thing:name = "example" ; return thing:someProc()". Judging from Lummox's responses "(list[5]):name = "example" just doesn't work because of how the ":" is getting interpreted.

Yota wrote:
without first storing it in a variable

I'm not sure how to word this. The op code that calls procs (normal calls, not call()) can only use a context from a reference. (Unless there's a code I'm not aware of, which is certainly possible.) However, proc calls and list indexing push the results to a stack. The only way to use that stack value as a context is to write it to a variable, or use call(). I'd be totally find with it rewriting it as call(), however all forms of proc calls needs some serious TLC on the VM side. They're hideously slow.

As for the : thing, I think he's pretty much saying that "colon immediately following an identifier char" is an access (or path) operator, and "colon immediately following anything else" is part of the ternary operator. Due to how the parser was written, changing it would pretty much warrant a complete rewrite of the parser.

I'm curious how much of what I just said is actually accurate. lol
Altering the priority of the operators effectively requires unraveling a lot of deep compiler code. I've looked into this (a while back) and the best I can say is that the prospects look difficult. Not impossible, but difficult.

Yota brings up an excellent point about the bytecode. However, on reading the code I believe that it'd be plenty feasible to generate the bytecode needed to do this without a change to the .dmb format.
In response to Lummox JR
pls
Yota wrote:
I'm not sure how to word this. The op code that calls procs (normal calls, not call()) can only use a context from a reference.
However, proc calls and list indexing push the results to a stack.
Yeah, that would be a problem. I assumed the compiler could treat both "thing:proc()" and "list[7]:proc()" similarly for the same reason it can handle "list[7] * 5", or "thing.biglist[5][list[7]] + thing2.biglist2[(8+list[2])]"): the bytecode wouldn't operate on ((6**(bar[baz/4]+4))/2) directly. It would evaluate that mess and store it somewhere, then operate on that.

If the compiler doesn't currently see (X):(Y)/(X).(Y) as part of the general case (X)(operator)(Y) then yeah, it'd probably be much difficult to add this than i'd thought.

To clarify, the compiler doesn't really have a "general case binary operation" concept. Basically, when the parser encounters a new token it goes through a series of functions (which, nastily, call each other recursively) to figure out what it's dealing with. The items it recognizes first can be thought of as high-priority. The ternary operator doesn't set any kind of state variable; it's handled through that same recursive process. Hence why modifying all that is such a mess. And the code that handles path operators--which is essentially what has to be modified for something like this to work--is an even worse ball of worms.

Also this is to say nothing of the fact that type checking with the . operator would basically be impossible except for certain hard-coded procs, and even then I'm not sure how I'd do it.

Now in terms of bytecode, accessing a var or proc from a proc call or list lookup result ought to be absolutely trivial. Internally, the .dmb format is capable of doing this. It's when it comes to syntax--where I'd want to avoid breaking old code if feasible--and the compiler itself--which is hard to modify at that level--that I'd have trouble.
Bump
This is still not on my radar. Disturbing the compiler at this level is not something I'm prepared to do, and the issue of how to deal with ambiguous syntax--and how to handle it in legacy code--has still not been resolved.
In response to Lummox JR
At risk of making the language just a bit more inconsistent, how about a new symbol/operator just for non-type-checked access from a list, such as @ (or -> or => etc...):
var mobs[] = newlist(/mob/a, /mob/b, /mob/c)
src << mobs[2]@name // "b"
In response to Kaiochao
Well : would be the only logical choice; the problem (well, one of them) is that it potentially conflicts with some existing code.
In response to Lummox JR
Lummox JR wrote:
Well : would be the only logical choice; the problem (well, one of them) is that it potentially conflicts with some existing code.

So making it semi-logical, why not ::
at /tg/ we already type all of our lists.

var/list/turf/simulated/active_turfs

This is ignored by the compiler, but the syntax already works and doesn't break anything.

so getting . to work with lists should be bloody trivial
In response to MrStonedOne
It also works with square bracket list notation:
var mob/players[] // typed as /list
Page: 1 2 3 4