gbdev/rgbds

[Feature request] A predeclared EQUS symbol for "the current global label"

Closed this issue · 20 comments

For instance, _GLOBAL or _SCOPE. This would be undefined if there is no current globally scoped label.

This would be useful to avoid repeating names. I've had multiple use cases for code like this:

Foobar:: mac Foobar

In this case mac might be defining more labels that use Foobar as a prefix or suffix. I like to avoid having mac do \1:: to declare the main label itself, since that makes Foobar's definition harder to find.

Or, mac could be mentioning Foobar in some error message. This is useful for macros that don't declare any code/data/labels, but just check assertions about what's already there, like data table lengths or verifying that certain related symbols are defined.

I'm not a fan of this, tbh. The redundancy is fine, imo, and adding this would mean creating/deleting/updating an extra symbol on every label creation. Plus, given that RGBASM strings are managed so poorly, either defining a label within an EQUS would UAF, or we'd leak crazy amount of memory.

Closing due to the latter reason. May be worth re-opening later, if there's still a need for it.

I was expecting this wouldn't need to be updated eagerly, but could reuse whatever access local labels have to their current global scope, only when _GLOBAL/_SCOPE/whatever is evaluated.

I might open a PR if I can get it working without UAF or excessive memory allocation. If not, that's a good reason to close this.

If it's not updated eagerly, how would it handle the lack of scope?

I had in mind using strCallback like __FILE__.

There's a need for this (see duplicate #1156) unless #1157 is implemented. How do I go about requesting a reopen?

EDIT: I guess I could work around this by writing a preprocessor that redefs __SCOPE__ whenever the global label changes.

Note the previous discussion in PR #776. Given that we don't have some expanded multi-level scope system actually worked out, and there'd still be the concept of a top-level current scope even if we did, I'd still like to see __SCOPE__ added.

ISSOtm commented

As soon as multi-level scopes are implemented, what constitutes "the current scope" becomes murky: is it

  • the top level,
  • the last label that was defined,
  • or the parent thereof?

Even the __LABEL__ suggested in #1158 would be unclear--at a glance, I'd expect it to be option 2.

I recognise the interest in this feature (see my own macros), but I do not want it implemented in a way that will block multi-level scopes, which is another feature that has garnered interest.

I'd say that "the last label" and "the last top-level label" are separate concepts and could warrant separate definitions. And those are very well defined.

ISSOtm commented

Alternative syntax proposed in #506 (comment): . refers to the topmost label. By continuity:

Mult: ; hl = h * e
    ld d, 0
    sla h
    sbc a, a
    and e
    ld l, a
    ld c, 7
.loop
    add hl, hl
    jr nc, ..bitNotSet
    add hl, de
..bitNotSet
    dec c
    jr nz, .loop
    ; At this point:
    ;  - `.` refers to `Mult`
    ;  - `..` refers to `.loop`
    ;  - `...` refers to `..bitNotSet`
    ;  - `....` is undefined
    ret

I like that it meshes well with nested scopes, also that it's consistent with the declaration syntax, but less that it's kind of cryptic. Thoughts?

I'd say that's valid for nested scopes, or for when you need to extract a label at a specific level (something that is currently a valid concern already, as we have two levels as it stands). But this doesn't solve the issue of "last label defined", since you'd have to know the level of that label, so that should be separate.

ISSOtm commented

What's the use case for "last label defined"? All I can think of is "offset from a label in the same section", and . is sufficient for that.

What's the use case for "last label defined"? All I can think of is "offset from a label in the same section", and . is sufficient for that.

Any macro that shows a message to the user, for instance. (Also, if we're talking about labels being declarable outside their parents' scopes, . might not be in the same section.)

ISSOtm commented

A macro showing a message to the user seems more likely to want to print the top-level label than any local, imo.

. should be in the same section (and defined earlier) for code, which is where I'd see the use case. (Alternatively, STARTOF(SECTION(@))?)

The case brought up in #1157 relates to local RAM labels associated to a function (in ROM, naturally), so those would obviously be in separate sections.

ISSOtm commented

Yes, but I don't see why you'd want the offset between those and something in the ROM section.

You wouldn't. You'd want the offset between your current address and something, and if your current address is in RAM and . is not, that offset is nonconstant.

ISSOtm commented

Between the current address and something in the same section should be covered by @ - STARTOF(SECTION(@))?

Alternative syntax proposed in #506 (comment): . refers to the topmost label. By continuity:

Mult: ; hl = h * e
    ld d, 0
    sla h
    sbc a, a
    and e
    ld l, a
    ld c, 7
.loop
    add hl, hl
    jr nc, ..bitNotSet
    add hl, de
..bitNotSet
    dec c
    jr nz, .loop
    ; At this point:
    ;  - `.` refers to `Mult`
    ;  - `..` refers to `.loop`
    ;  - `...` refers to `..bitNotSet`
    ;  - `....` is undefined
    ret

I like that it meshes well with nested scopes, also that it's consistent with the declaration syntax, but less that it's kind of cryptic. Thoughts?

I like it too! We already lex a . token, we just don't do anything with it in the parser.

fasm does it this way too:

If an identifier is just a single dot, by the above rules it refers to the most recent label that did not start with a dot.

Note that this means we can't reserve ... as a keyword for e.g. variadic functions (#201). We can still implement this by checking for the symbol's name explicitly, but we'll have to keep that in mind.