Closed michael-buschbeck closed 2 years ago
Access to the main script's variables: yes or no?
default
value).script
– e.g. script.cOwnID
to access the cOwnID
variable defined on the main script's level.caller
. I struggle to imagine what the use case for that could be though.All of this is very much like Christmas in MMMland, quite appropriately timed :)
- Maybe: Function code gets read-only access to the script's variables through a special variable called
script
– e.g.script.cOwnID
to access thecOwnID
variable defined on the main script's level.
This would be extremely useful. In my very pedestrian experiments with "library" code, I found quite a few instances where I need to use a lot of variables global to each script, particularly as long as I won't be able to handle struct
variables. This would make for loooooong list of parameters and providing easy read-only access to all of this would be super helpful, including the read-only part in terms of avoiding mistakes.
- Maybe: Function code gets read-only access to the parent function call's variables through another special variable called
caller
. I struggle to imagine what the use case for that could be though.
I struggle to see the need for this, as well. If we had the script.var
feature, anything else I can think of can easily be handled using explicit function parameters.
Very good!
script.foo
to read the main script's foo
is definitely supported.
If you call a function with fewer arguments than it has parameters, those extra parameters are initialized with the special default
value so you can write function code to apply custom defaults that are able to distinguish "no argument given" from "argument given as undef
".
Global functions – those defined outside of a script
block – are supported. They're lost as soon as the API sandbox is stopped or restarted; we may have to think of some way to work with that fact.
Global functions are per-player. There's no way to see which global functions are currently defined, but you can check the existence of any given (global or script-local) function with if funcname
(note absence of parentheses: this isn't syntactically a call of funcname
).
Customizations of the main script done via preceding !mmm customize
blocks extend into functions, including global functions – so you can use chat [label]:
and set customizable
in functions, too.
- Global functions – those defined outside of a
script
block – are supported. They're lost as soon as the API sandbox is stopped or restarted; we may have to think of some way to work with that fact.
Would it be one way of working with that fact to simply make sure that every script "loads" all required functions upon execution? I think that would not be difficult to do, in particular if MMM had no trouble with the same function being loaded several times (and thereby overwritten). If that would be fine, MMM scripts could simply include all required functions using the Roll20 include syntax (%{Sheet|Ability}
or #Macro
).
It'd work for sure.
It just seems unnecessarily wasteful to not take advantage of only having to parse function
s once. :)
O-kay. Right. So what do we do? Could MMM simply skip any duplicate global function
blocks? I don't see a way from my end to only include the code if it hasn't been included before. I guess you could call some predefined autorun
macro the first time any particular player calls MMM...?
In other news, I am happy to have implemented my first MMM functions using the currently installed 1.24.0-pre :)
Something like "autorun" macros was on my mind, too. Or, well, have players manually run the "library macro" at session start.
For now, simply redefining all functions on each run will be fine. Performance issues are non-issues until observed.
Glad you like 1.24.0-pre – it's still got some issues with accessing outside data from within a global function especially when calls are nested. Working on it!
One of the things that are yet to make to work is being able to call functions that were defined on script level from a global function called by that script:
!mmm function foo()
!mmm do script.exclaim("It is I!")
!mmm do script.exclaim("Who dares to challenge me?")
!mmm end function
!mmm script
!mmm function exclaim(msg)
!mmm chat: Hark!, ${msg}
!mmm end function
!mmm do foo()
!mmm end script
!mmm script
!mmm function exclaim(msg)
!mmm chat: ${msg}, I say!
!mmm end function
!mmm do foo()
!mmm end script
For that matter, you can also pass functions as parameters or assign them to variables.
I didn't have a use case for this problem, yet. What I did encounter is the inability so far to call one global function from another, if I'm not doing anything wrong here.
This...
!mmm function bar()
!mmm return "Hello!"
!mmm end function
!mmm function foo()
!mmm return bar()
!mmm end function
!mmm script
!mmm chat: ${foo()}
!mmm end script
... leads to this error:
(From Mych's Macro Magic): During execute, During execute, unknown function in expression in script: return bar❌() in expression in template in script: !mmm chat: ${❌foo()}
And as you mentioned above, access to script.var
from a global function does not work yet:
!mmm function foo()
!mmm return "The answer is " & script.testVar
!mmm end function
!mmm script
!mmm set testVar = 42
!mmm chat: ${foo()}
!mmm end script
... results in ...
The answer is
Both issues are connected – fix upcoming.
The latest prerelease seems to fix all known issues.
Things left to do:
Better diagnostics for malformed parameter lists in the !mmm function
command. Currently, if anything is wrong inside the parentheses, the error message will point to the start of the parameter list.
Better diagnostics for runtime errors in (nested) function calls. Currently, such errors produce a correspondingly nested message that's hard to parse for humans. I'd rather like the message to look something like a call stack spread out over several lines.
Aside: It looks like some MMM error messages do and some don't include the leading !mmm
. Normalize this to always show the entire command including !mmm
.
Documentation.
The latest prerelease fixes the !mmm
diagnostics inconsistency and improves nested-call error messages, which now look something like this:
During execute, unknown function in expression in script: !mmm do runtimeError❌()
Called from: !mmm do ❌test3()
Called from: !mmm do ❌test2()
Called from: !mmm do ❌test1()
The "diagnostics for errors in parameter lists" amazingly turns out to be a non-issue because the parser consumes as much of the parameter list as can be interpreted as a valid one, and then points to the character afterwards for the error – close enough! (It's really just a matter of taste whether the error in test(foo,)
is the presence of the comma or the absence of a following parameter name.)
Speaking of error diagnostics and the location of an error – I've been wondering what kind of information might make it easier to identify which line the error occurred in?
I'm already quoting the entire source line containing the error, but if it's just some variation of !mmm do func()
that may not be particularly unambiguous.
Would ~spiri~ support for end-of-line comments help to mitigate the ambiguity?
Line numbers are tricky: Do non-!mmm
lines count or not? If they do, what about non-!mmm
lines leading the !mmm script
line? If not, how useful is a line number if you can't use your text editor to address it?
Speaking of error diagnostics and the location of an error – I've been wondering what kind of information might make it easier to identify which line the error occurred in? ...
- Would spiri support for end-of-line comments help to mitigate the ambiguity?
What does "spiri support" mean? "Boost Spirit" was my closest match on Auntie G...
- Line numbers are tricky: Do non-
!mmm
lines count or not? If they do, what about non-!mmm
lines leading the!mmm script
line? If not, how useful is a line number if you can't use your text editor to address it?
Agree. With various %{...}
includes, in particular, it's quite unrealistic for anyone to match MMM's line numbers as received from Roll20's auto expansion to their scripts in a way that would be helpful for debugging.
How about thinking in a slightly different direction? Could MMM add more info to identify the active block(s)? Function names as part of error messages would help, and functions should not be so complicated as to contain lots of identical lines, should they? Maybe error messages could even contain other open blocks ("in if foo==bar
, in combine chat
, in for item in list
, in m3mgdMyCrazyLoopingFunction()
")? And script
blocks could get (optional) names, too (!mmm script rangedAttack
)?
"spiri support" was Siri channeling… dunno, the spirit of Christmas maybe? I really just meant plain "support" (for end-of-line comments). You could disambiguate several identical do func()
lines by adding a comment like do func() // first try
.
Identifying a call stack of blocks sounds interesting. I'll think about that.
(Copied from this comment in phylll#32)
I've felt inspired to think about MMM support for custom functions. Shouldn't be too difficult to implement. Syntax is more interesting to ponder.
Define function – scoped to the script that contains the definition, like variables:
Call function – like any other:
That's the baseline. What else?