squirrel icon indicating copy to clipboard operation
squirrel copied to clipboard

feature idea: object initialization

Open zeromus opened this issue 8 years ago • 9 comments

This is a speculative idea. I haven't thought it through completely.

I would like to see a functionality like struct or object initialization (I think that's what it's called.) Essentially an instance would have a {} operator which would let you set values on it as in table creation.

For example:

    local myinst = MyClass() { X=1,Y=2 };
    //as a possibly unfortunate side effect, this is oddity might be allowed:
    myinst { X=3 };

Here, you could view a { at this point in the grammar as a .setvalues( { method which takes a table, but naturally we'd want to avoid the overhead of creating a new table just to immediately stuff the values in another instance.

All of the above could be applied to tables as well, sensibly, but you would probably want to support { X=1,Foo<-2 } in that case, and you can no longer conceptualize it as a .setvalues( {. Moreover, it's just plain weird to do this on tables. But I could see someone defending it as useful.

zeromus avatar Apr 17 '16 00:04 zeromus

I put some thought to something like this in the past. I got stuck by the fact that {} already has several semantics in Squirrel. I was playing with "name { x = 1 , y = 2 }" and I remember it was causing some ambiguities. Maybe "name() { x = 1, y = 2 }" would be simpler. However, it's not completely clear to me how that would compile(probably a series of "set"). Or if the values are set before or after the constructor is invoked, that also implies it only works on classes.

albertodemichelis avatar Apr 20 '16 15:04 albertodemichelis

I don't think I have any doubt that the values should be set after the constructor is invoked--that's how it would work in c#. That is communicated clearly by seeing it written as name() {} such that the constructor parens are first, then the curly braces. I think a series of "set" operations are precisely what most people will expect it to be--not just a matter of convenience or simplicity in the compiler, but exactly what's wanted in fact. So I suppose I should write it differently than I did before: local myinst = MyClass() {X=1,Y=2}; -> local myinst = MyClass(); myinst.x = 1; myinst.y = 1;.

I hadn't thought of making it an actual part of the construction grammar. That would be simpler and cleaner than what I originally pictured.

zeromus avatar Apr 20 '16 16:04 zeromus

So I implemented something, it literally took 15 mins :) so I don't guarantee perfection but it kind of does what we talked about. For a quick test just replace FunctionCallArgs() with the follwing code. Let's see if we like it

void FunctionCallArgs()
{
    SQInteger nargs = 1;//this
     while(_token != _SC(')')) {
         Expression();
         MoveIfCurrentTargetIsLocal();
         nargs++;
         if(_token == _SC(',')){
             Lex();
             if(_token == ')') Error(_SC("expression expected, found ')'"));
         }
     }
     Lex();
     for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget();
     SQInteger stackbase = _fs->PopTarget();
     SQInteger closure = _fs->PopTarget();
     _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs);
     SQInteger retval = _fs->TopTarget();
     if (_token == '{')
     {
         SQInteger nkeys = 0;
         Lex();
         while (_token != '}') {
             switch (_token) {
             case _SC('['):
                 Lex(); CommaExpr(); Expect(_SC(']'));
                 Expect(_SC('=')); Expression();
                 break;
             default:
                 _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER)));
                 Expect(_SC('=')); Expression();
                 break;
             }
             if (_token == ',') Lex();
             nkeys++;
             SQInteger val = _fs->PopTarget();
             SQInteger key = _fs->PopTarget();
             _fs->AddInstruction(_OP_SET, 0xFF, retval, key, val);
         }
         Lex();
     }
}

albertodemichelis avatar Apr 21 '16 17:04 albertodemichelis

thanks! first test: local sprite2 = Sprite() { Image = smile, X = 160, Y = 10+yadd++ }; I'll start using it and kick it occasionally and see if I can smoke out any problems. I'm not scripting too much yet, still working on my engine.

zeromus avatar Apr 22 '16 03:04 zeromus

It seems commas aren't required

class Foo { x=0;y=0; }
print(Foo() { x=1 y=1 }.y); //compiles OK and prints 1

I think commas should be required.

zeromus avatar May 10 '16 15:05 zeromus

I agree that commas should be required. 😄 There's less room for typos to cause bugs that way. 🐞

aab29 avatar May 10 '16 16:05 aab29

Well, this might be a change for a major release(4.0) I'm quite sure this would break a lot of stuff in our codebase. Often ppl omit the comma when declaring tables with a newline between fields. I don't have strong feelings about it. It never seemed to cause problems in the last 13 years. FYI, also arrays and function calls have optional commas :scream_cat: .

albertodemichelis avatar May 18 '16 16:05 albertodemichelis

Since it's so consistent otherwise in older features, I surely want this new feature to follow the standard. Forget I mentioned it.

zeromus avatar May 18 '16 17:05 zeromus

Oh, I see! 👓 Right, I'd still like commas to be required (not a huge problem, but it has tripped me up before). However, if they're required in one place, they should be required everywhere. 🌐 And yeah, it's a good idea to wait until a major release to change anything so we don't break existing applications. 🔨 👍

aab29 avatar May 19 '16 01:05 aab29