TelluIoT / ThingML

The ThingML modelling language
https://github.com/TelluIoT/ThingML
Apache License 2.0
103 stars 32 forks source link

Problem calling functions to initialize properties #168

Open ffleurey opened 7 years ago

ffleurey commented 7 years ago

At least when using the C/C++ code generator, calling functions in property initialization results in generated code which do not compile. The generated code does not make a correct reference to the Thing Instance.

brice-morin commented 7 years ago

@vassik if you have some time, could you write a test on that and see what is the behaviour for all compilers? If you do not have time, try to push @ffleurey to write this test :-)

brice-morin commented 7 years ago

Well, it is not possible in Java neither, and not sure if it will ever by possible... at least not before we refactor the Java main generator to use empty constructors and setters rather than "big" constructor where we pass all params (and in which we cannot call a method on the intance being created...)

brice-morin commented 7 years ago

In Java, it will still be a problem with readonly properties, which are (and probably should) be generated as final attributes in a Java class. Final attributes have to be set either when declared, or at latest in the constructor. Not possible to set them after constructor has been called.

@ffleurey should we forbid the initialization from a function call for readonly properties?

brice-morin commented 6 years ago

@jakhog @ffleurey should we forbid properties to be initialized by a function call? This is impractical to implement in the compilers, can potentially produce "strange" results (e.g. if the function produces a result based on other properties, which might or might not be initialized, yet).

If we want that behavior, we can still set properties in the on entry of the state machine (but it cannot be read-only), that is only executed once after the rest is properly initialized.

jakhog commented 6 years ago

@brice-morin I don't think this is constrained to just functions, with regards to other properties that might not be initialised, you will have the same problem with e.g.:

thing T {
    property A : Int = B
    property B : Int = 1
    ...
}

and even more complicated

thing T {
    property A : Int = B
    property B : Int = 1
    ...
}
configuration C {
    instance t : T
    set t.B = 2
}

And even if we disallow functions, you can still do strange things with simpler expressions, e.g.

thing T {
   property A : Int = 0
   property B : Int = A++ * 2
   property C : Int = A++ * 3

   set A = 0
}

I think, that rather than forbidding special expressions in intialisers, we should enforce a strict ordering of how they are evaluated by the compilers, so that at least you get the same behaviour when you compile the code.

As far as I can tell, there are three ways of initialising a property:

  1. Directly in the declaration (var A : Int = ...)
  2. With a set statement in the thing
  3. With a set statement in the configuration

I propose that we set the values of properties like this:

  1. Properties are just declared, an in that way get the default value of the language
  2. Initialisations from the declaration (var ... = ...) are evaluated in the order of appearance
  3. set statements from the configuration is evaluated in the order of appearance
  4. set statements from the thing is evaluated in the order of appearance

It's a bit strange to evaluate the configuration sets before the thing sets, but that would be the only way I can think of to set read-only properties based on properties that are changed from the configuration.

This doesn't stop strange things from happening, but at least it would be possible to write predictable ThingML code.

jakhog commented 6 years ago

I kind of forgot about arrays when I wrote the previous comment, which complicates things even further.

IMO we should also re-think the static-size arrays in ThingML, it makes things very complicated when the size is set by properties, even when they are read-only (e.g. if they are set in the configuration).

As far as I know, it's really only a problem for C (on Arduino and the like), where there is no malloc. I think a better solution is to allow dynamic array size for all languages, but for C (or other languages where it might be a problem), a max size of the array can be set using annotations.

What do you guys think?