maestro
maestro copied to clipboard
Load and Unload Statement
The load
statement is for loading interpreter plugins, and unload is for unloading them.
For example:
FMI2 tankcontroller = load("FMI2", "{8c4e810f-3df3-4a00-8276-176fa3c9f000}", "src/test/resources/watertankcontroller-c.fmu");```
unload(tankcontroller);
where the interpreter plugin FMI2
is loaded with two construction(?) parameters: "{8c4e810f-3df3-4a00-8276-176fa3c9f000}"
and `"src/test/resources/watertankcontroller-c.fmu".
Loading and Unloading is currently treated as a special case in the parser, lexer and interpreter.
Interpreter Plugins provide MaBL modules with their functions, i.e.:
module FMI2 {
FMI2Component instantiate(string name, bool logging);
void freeInstance(FMI2Component comp);
}
For the following discussion points, please keep the future typechecker in mind as well.
Discussion 1: Should we turn loading and unloading into regular method calls?
I am actually leaning towards no, as these are simply special cases, similar to constructor and destructor in C++, java etc.
TBD: Copy nicks input in here
Discussion 2: No way of expressing the load
parameters (constructor parameters) and unload
functions.
We are closing in on functions such as constructor and destructor. But how should we express these?
1. Constructor as keyword, implicit destructor
module FMI2 {
constructor(string guid, string path)
FMI2Component instantiate(string name, bool logging);
void freeInstance(FMI2Component comp);
}
2. As part of the module, implicit destructor
module FMI2(string guide, string path) {
FMI2Component instantiate(string name, bool logging);
void freeInstance(FMI2Component comp);
}
3. As seperate construction module, implicit destructor
module InterpreterPluginConstructors {
FMI2 createFMI2(string guid, string path);
}
**4. Suggestions?**
**My favourite**
I am in favor of 2, as module is already a special case and we do not need to add an extra `constructor` keyword.
Next in line is 3, but I like the constructor being with the module
**Tagging for input**
@nickbattle @lausdahl
So as a general principle, I would say that special cases are best avoided but they sometimes can't be avoided! I would just point out that you can have a common syntax (ie. not a special case for the parser) with special case handling in the subsequent type checking and interpretation. But if something is genuinely special, it might be better to invent a syntax for it that specifically doesn't look like anything else. Hence the "new name(args)" for OO languages.
The "usual" style is to have constructor(s) declared with the same name as the thing being constructed, without a return type, so:
module FMI2
{
FMI2(string guid, string path); -- Note, the return type is implicit
Option 2 does have some appeal, but it limits you to a single constructor, whereas the constructor-in-body style allows for overloading, potentially. Please don't go for option 3 - that is separating the knowledge across multiple locations? That can't be good.
I see the same reasons as you for 2. Wonder if the syntax should be changed as well. Perhaps load(FMI2,...)
instead of load(“FMI2”,...)
.
However, the load
statement might stick around.
A comment about the the actual load
call. It seems like its being using in two versions currently i.e. the interpreter seems to use the type given as a string to a little more than just determining the type.
FMI2 a = load("FMI2","fmu.fmu",....)
// and
CSV c = load("CSV") // or load("CSV", csv.so")
Here it looks like the argument with type string indicating the type could be better handled by actually having a type in the language that could only represent known types like Javas Class<?>
. At least then the load call would only be called with a known type (the type will be used to know what symbols to symlink).
Load
Currently we have two types of load. The basic one which we seems to desire (load(TYPE, library)
) which is able to without knowing about the library load it using the TYPE
info and potentially forwarding stuff to a constructer for the additional arguments. Secondly, we have an FMI case where we load an FMU but where we have additional custom code to handle this. Currently we have custom code in the interpreter to load FMUs. However, we should handle this better by providing a standard load that looked like
// load(TYPE, <library name>, constructor args)
FMI2 a = load(FMI2, "Fmu2Loader", "path/to/fmu.fmu",extra args)
Then we could push the custom code out of the interpreter and into an external library and make it work for FMI similar to other libraries. With the hope that loading could then be the same both in the interpreter and in the native implementation. The idea is here that the Fmu2Loader would have the same interface as an FMU but the constructor would be used to do the custom tasks and then forward the remaining work to the FMU and return that.
I am now beginning on the typechecker and obviously encountering this issue.
@lausdahl I guess the purpose of having an explicit library name is to provide different backends for providing an FMI2 module, so in some sense it is polymorphism.
However, in some cases it seems strange to have this load(DataWriter,"dataWriter")
Are you thinking of encoding i.e. distributed FMU here?
So we discussed this today and here follows some comments:
The load call is related to the runtime. Thus, load(FMI2,"FMI2Loader", varargs)
will result in the runtime looking for a function called FMI2Loader
. This is a way to implement i.e. distributed FMU, by having a different loader but the same interface load(FMI2,"FMI2DistributedLoader", varargs)