Open albertodemichelis opened 5 years ago
I'm not an expert on language designs, but to me this seems like c# "object initializer" syntax. It's meant for the same thing as in c#, even though it's more powerful here since it can apply to any return value from a function and not just the constructor.
It seems like it should be applicable to any object-type rvalue ONCE, call it an initialized-object-type. That is, you shouldn't be able to do x=test(){a=1}{b=1}
. You shouldn't be able to do x={a=1}{b=1}
either because the new first new object expression {a=1}
is actually returning an initialized-object-type. Can we do q=(worldpos+localpos){z=0}
? If we can return tables from operators in squirrel (I would assume so for the common vector case which must have been planned for from the beginning for a language meant for games) then I would assume so: the add operator would be returning an object-type which can then be initialized once.
Can you do q=mypos{z=0}
? It seems you should be able to, if you could do q=getmypos(){z=0}
or even more absurdly q=function(){return mypos}(){z=0}
It's possible you could relax this restriction and allow the syntax to apply to any object-type rvalue multiple times, which might make the specifications simpler, even if the code doing this would look really weird. But I don't know what that would do to the parser.
You could go further and define that adjacent tables are always combined left to right with an implied combination operator. So actually any time you see origin2d{z=5}the_w_of_1{y=1}
that means we'd end up with {x=0,y=1,z=5,w=1}. You couldn't write origin2d the_w_of_1
because that's just crazy, but the first example you'd have to throw at runtime if you tried to combine {z=5} with an origin2d that was accidentally not an object.
But of course support for such weird code isn't worth chasing. It's just to help clarify thinking.
Unfortunately doing id{ x= 10 }
creates a ton of ambiguities. I tested a number of syntax possibilities and id() { x = 10 }
seems the best choice.
So you suggest "object initializer"? In our case is more "return value initializer" but "object initializer" sounds good enough for me.
If it's noteworthy for being limited to returned objects and that comes as a surprise to anyone, maybe "return object initializer" would be better to distinguish from a more general "object initializer" which does already exist in your grammar actually even if it isnt named that (really it's just the {x=0}
table creation syntax)
Maybe it would be better to add an operator for updating tables / instances?
Let's say << (or any other, maybe new one) when applied to table writes given fields (working like dict.update() in Python)
Then we can use it both for newly initialized object and any other variable.
This would allow id << {x = 10}
and id() << {x=10}
- only 2 chars more than in current version.
I also want to note that this feature is a breaking change. Since comma is optional in squirrel, in previous versions the following code creates an array with 2 elements
local function foo() {
return 123
}
local a = [
foo()
{x=123}
]
Now it applies initialization to foo()
call result (and also fails with trying to set 'integer'
error).
It breaks all the places with optional comma, including function calls with multiple arguments.
Yes, that was expected to break some stuff, that would be for Squirrel 3.2. BTW, I'd be open to consider the obj << {} semantic but it cannot be "<<" that is already "shift left".
Yes, that was expected to break some stuff, that would be for Squirrel 3.2.
Just to notice - in current stage this seems like a dangerous change. I think it would be almost impossible to find all the code that could be potentionally broken in real application. The places with optional commas are everywhere - arrays and tables declaration, classes, function calls... We have updated projects of 3-7MB of Squirrel code from Squirrel 2 to Squirrel 3 - it was doable in a 2-3 days of one person (and several days after to find some edge cases), even for old project without active development and without help of code authors. But I think such change would be much harder to handle - at least if comma would be still optional in Squirrel, and wont fire compiler error. Also this probably will create some bugs in a future, with new code, because that is not that easy to 'parse' missing comma in code like
function merge(table1, table2){
//..here is some code
}
local a = foo(
bar()
{x=123 y=150 z=500}
)
Syntactic sugar is sweet but it sometimes can cause syntactic diabetes. May be that is the case I really hope to see Squirrel becoming more safe.
BTW, I'd be open to consider the obj << {} semantic but it cannot be "<<" that is already "shift left".
I think that would be much better way to do this. Or, may be (I'm not expert so I may be wrong) just add some global function? like
::init(Vector(), {x=1, y=2})
::init(array(), {[20]=30})
May be this is not that sweet, but straightforward, backcompatible and safe.
Or may be to add a delegate in arrays\tables\instances?
foo().__init({a=123})
again - may be not that sweet but safer
As for operator can I suggest just update (or _update to prevent ambiguity)?
foo() update {a=123}
probably would be good to add both update and merge - second one creates a copy, like syntactic sugar for clone + update.
BTW, would it be nice to allow clone work with more types?
function foo(x){
return clone x
}
foo(1) //error here, requires typechecking
assuming that you may initialize things NOT returned by the called function, I suggest <+ operator. Similar to <- it's clear it could a) override and b) create slots if not exist, plus it should be syntactically unambiguous: function foo() { return {foo=null};} local x=foo()<+{ bar=true };
---> x = { foo=null, bar=true };
And for what it's doing, you may call it "table merge operator" - and not limit it to calls but apply to all tables?
I guess you could use an existing keyword extends
if you don't want to overburden yourself with symbols or new keywords.
local x = {foo=null, bar=true} extends foo();
But if you're unaware of such feature, it becomes unclear what it extends. The returned value or the function (if there's even such a thing)
Or something like two ..
or three ...
dots. I believe Lua uses the two dots for string concatenation.
local x = foo() .. {foo=null, bar=true};
The latter should be easy to distinguish by the parser and not infringe on existing syntax.
I was actually contemplating using the .. for string concatenation(in a future version), It would solve a lot of problems with the + operator.
The only character that is not really used in squirrel at the moment is $ but I find it a bit repulsive. Maybe something like :
local x = foo() <! {foo=null, bar=true};
using .. for string concatenation can be dangerous, as one dot is used to indexing local foo = {bar = 2} local baz = foo..bar //typo. but it would work if .. is for string concatenation
tripple dots are also used in squirrel now for vairable arguments. PS We haven't found any suitable symbols or combination of symbols for string concatenation, discussed this a lot.
This change breaks existing codes in non-obvious and unexpected ways:
local a = []
local b = a.len()
// open some new block
{
local c = 0
}
complains about [expected 'IDENTIFIER'].
I suggest to use that feature only with some additional operator.
I added this syntactic sugar that allows to initialize objects returned by a call but I have no iadea how to call it. Any idea??
here a few examples