[Feature request] Named macro parameters
- Dominant language
- C++
- Stars
- 1.6k
- Forks
- 193
- Avg merge
- 20h 32m
- Merged PRs (30d)
- 29
Description
This is a feature of various other assemblers that would be convenient for rgbasm. Using parameter names instead of index numbers is more self-documenting, and could allow for default argument values.
### Status quo
Macro parameters have to be manually documented with comments. If they're defined as named symbols, it's tidy to manually purge them at the end, and they can't conflict with outside symbols.
```
MACRO mac ; one, two, ...
DEF one EQUS "\1"
DEF two EQUS "\2"
SHIFT 2
DEF rest EQUS "\#"
println "{one} and {two}, then {rest}"
PURGE one, two, rest
ENDM
mac A, B, C, D, E
; A and B, then C,D,E
DEF one EQU 1
mac X, Y
; ERROR: 'one' already defined
```
### Core feature
```
MACRO mac one, two
println "{one} is the same as \1"
println "{two} is the same as \2"
ENDM
mac arg1, arg2
; arg1 is the same as arg1
; arg2 is the same as arg2
```
This is basically the same as manually doing `DEF one EQUS "\1"` and `DEF two EQUS "\2"`, so it ought to be a fatal error to provide insufficient arguments for the parameters.
### Local scope
It would be convenient if the symbols defined for macro arguments were scoped to within the macro body. That would mean (a) they get purged at the end of the macro, and (b) they shadow any identical symbols outside the macro.
```
MACRO mac arg
println "arg = {arg}"
ENDM
DEF arg EQUS "outer"
mac inner
; arg = inner
println "{arg}"
; arg = outer
```
("Locally scoped symbols" have been brought up before, where any symbol starting with an `@` would be similarly locally scoped, not just to macro bodies but to if/else/rept/for blocks or even files, but that's a separate issue.)
### Default arguments
```
MACRO mac one, two = (a,b)\,(c,d), three = "hello"
println "one: [{one}]"
println "two: [{two}]"
println "three: [{three}]"
ENDM
mac x
; one: [x]
; two: [(a,b),(c,d)]
; three: ["hello"]
mac first,,last
; one: [first]
; two: []
; three: [last]
```
Their values would be lexed in raw mode (so `,` `;` `\n` would end them). It would at least be a warning, or even an error, to declare non-default arguments after default ones, since the default value of a middle argument would never be available.
```
MACRO mac x1, x2=second, x3
println "{x1} and {x2} is {x3}"
ENDM
mac A, B, C
; A and B is C
mac A
; ERROR: no argument for 'mac' parameter 'x3'
```
### ~~Variable-length arguments~~
One way to do this would be an array of extra arguments, like C's `__VA_ARGS__`, but it shouldn't be necessary to add lists/arrays just for this: `\#` already provides access to all the arguments.
```
MACRO mac one, two = blue, ...
println "one = {one}"
shift 2
println "rest = (\#)"
println "two = {two}"
ENDM
mac A, B, C, D, E, F
; one = A
; rest = (C,D,E,F)
; two = B
mac red
; one = red
; rest = ()
; two = blue
```
No parameters would be allowed after a `...`.
```
MACRO mac first, ..., last
println "from {first} to {last}"
ENDM
; ERROR: 'mac; parameter 'last' after '...'
```
Contributor guide
Research direction
Start by tracing rgbasm's existing macro parameter handling and the current behavior of positional arguments, SHIFT, and \#. Use the examples and error cases in this issue as acceptance criteria; done means named, locally scoped, default, and variable-length parameters behave as specified, including validation errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100