Closed TelosTelos closed 3 years ago
Regarding issue (1), a good syntax for referring to @variables
in arbitrary contexts, I can see four fairly tempting options:
(1a) Write these just like they appear in mindustry (with hyphens morphed to underscored) , e.g., as @copper
. However, I doubt AST will let us use decorator-@
s in all (or even any?) of the places where we'd need them, so this might make linters complain a lot, and would require us to do a pre-processing pass to turn @foo
into something more AST-friendly before parsing with AST.
(1b) Define capitalized constants for all of these, much as I did for Unit
and This
already, e.g., making Copper
always mean @copper
. This option may be the most linter-friendly, as it will make PyCharm autocomplete and spelling-correct attribute names. However, this clutters the namespace with dozens more constants, and won't work automatically if new sensable attributes like @snowballs
get added, via patches or mods.
(1c) Use string-literals like "@copper"
for these. I'm not sure whether this would collide with other mindustry uses of strings, which I guess is mostly just for printing. If you try to print @copper
, or any variable that had been set equal to @copper
what prints is simply copper
, so our using "@copper"
as an abbreviation for @copper
would slightly alter the result of print("@copper") but maybe not in a way that we care about? This approach is nicely flexible, including allowing underscores and hyphens, but it won't make linters be of any help at all, though I suppose we could print a compiler warning for any
"@expression"that we don't recognize as familiar. (1d) Introduce a little pseudo-function like
At(copper)which compiles to
@copper`. This is fairly clean, and will automatically work for newly patched/modded attributes, but linters won't help, and worse will likely complain about use of unknown variables.
My current inclination is (1c) to use string-literals like "@copper"
as this seems clean and a good analog to getattr / dictionary lookup, and I don't think the collision issues with other strings will be that bad. Or do you foresee some other use for "@expression"
strings?
I feel like Res.copper
or Res['copper']
is the way to go. @
is used for matrix multiplication, but it needs a left operand (foo @= copper
would work, but it's kinda weird).
Android uses R
as the class with all the generated resource IDs by name. We could go with that shorter name, or the longer Resource
, but I think Res
is a good compromise.
Regarding issue (2), I can brainstorm three decent options and a fourth that is probably lousy. These could be combined with any of the (1x) options above for referring to @attributes
-- I'll stick with (1c) string literals like "@copper"
.
(2a) Use dictionary-lookup notation, e.g., vault1[ "@copper" ]. This is simple and familiar.
(2b) Use getattr()
notations, e.g., getattr( vault1, "@copper" ). To Python coders who are experienced with getattr( )
, this does exactly what you'd expect, and this is probably the default way you'd think of to go about accessing a dynamically specified attribute. To less experienced coders, this'll probably be impenetrable.
(2c) Use Sensor(object, "@copper")
notation, since Sensor
is basically the mlog equivalent of getattr
. This'll be more familiar to people who started using mlog in-game.
(2d) The perhaps-lousy option was to continue using vault1.attribute
notation, mapping known attributes like copper
to @copper
and known variable attributes like res
to themselves in the compiled sensor
command. This isn't very Pythonic, it risks some collisions/ambiguity (e.g. coders often will define a variable x
, after which vault1.x
would be ambiguous between asking for vault1's @x
attribute, and asking for whatever attribute the variable x
refers to).
I guess I'm leaning towards offering (1a), (1b) and (1c). The Zen of Python says there should be exactly one obviously best way to do things, but I've never really bought that. As long as each way that seems like a way of doing things will work, and as long as each way that works is easy to understand, who cares whether there's just one?
One concern about using Res('copper')
is that many sensable attributes are not really resources, e.g., @shooting
, @health
, @configure
, etc... For that reason, I'd lean a bit more towards using At('copper')
both because it sounds just like @copper
when spoken, which will be helpful to people who started out using the in-game interface, and because it seems sort of like an abbreviation for Attribute
which is helpful.
To be fair though, I can't easily imagine good uses for dynamically referencing any other @attributes
besides resources, and if this syntax will get used mostly just for dynamic references (since the most common use should still be non-dynamic object.attribute
), then maybe Res()
wouldn't be that misleading.
Regarding the third issue, iterating over static lists, this could actually be a good use for an mlog trick I'd been kicking around in the back of my head for awhile, using ops to dynamically calculate a line to jump to by setting the @counter
to it. The following is equivalent to the alloy-smelter-feeder I described above, and would be a plausible way of compiling for candidate in [At(copper), At(lead), At(graphite), At(titanium), At(silicon)]:
for index in range(5):
Counter += 2*index # skip down to the appropriate indexed element below
candidate = At(copper)
Jump_to( "loop_body" )
candidate = At(lead)
Jump_to( "loop_body" )
candidate = At(graphite)
Jump_to( "loop_body" )
candidate = At(titanium)
Jump_to( "loop_body" )
candidate = At(silicon)
Label( "loop_body" )
# ...
Your third issue seems to deviate quite a bit from the topic of this issue ("acessing attributes"). Maybe that should be a separate issue?
In any case we probably want to detect for x in [...]
and just expand all the elements (like inlining a function). Jumping adds a fair bit of overhead but could be worth it if the loops body is too long.
Another option is to have for x in inline@[...]
which would actually be valid syntax.
for x in [a, b]:
...
...should be tracked elsewhere. Please open an issue for that.
Mlog allows you to set a variable to some
@resource
like@copper
and then use asensor
to retrieve that resource, referenced via that variable. E.g. the following mlog program successfully reads the amount of copper in shard1.In Python terms,
sensor x y z
is approximately the same thing asx = getattr(y, z)
except z needs to be a member of whatever mindustry class@resource
special variables refer to, not just a string (unfortunately).It would be good to support this in pyndustric. To do so, we'll probably need (1) some way to refer to
@attribute
names in a variety of contexts, so e.g. we can do something akin tores = At(copper)
, and (2) some syntax for generating sensor readings with a variable likeres
in the attribute slot, e.g., perhapsshard1[ res ]
.It would also be good to be able to iterate over predefined lists, even if we won't allow dynamically created lists. E.g., it'd be nice if you could use the following program to use a single unloader and conveyer belt to feed an alloy smelter out of a vault containing plenty of all the relevant resources (or perhaps to supplement a series of alloy smelters that have been being fed melted scrap and silicon, by delivering a supplement containing whatever resource they're currently most short on).