gbdev / gbdev/rgbds

[Feature request] User-defined functions

Open
#201 26 comments 1 reaction 0 assignees View on GitHub
enhancement rgbasm
Dominant language
C++
Stars
1.6k
Forks
193
Avg merge
22h 17m
Merged PRs (30d)
26

Description

I'm sure that this has been mentioned before, but I've been coming up with a full feature proposal and I'd like to know how feasible it is. What follows is a proposed way of implementing functions, at least in a way I'd consider usable.

The idea is to allow the user to define functions, that would be evaluated in-line when called, similar to the various built-ins. Functions can have one or more arguments, which define variables that are local to the function; they are substituted in the expression. An example of the syntax I have in mind would be:

```
;definition
addnums(first, second) = ({first}) + ({second})

;usage
ld hl, wTwoPlusTwo
ld [hl], addnums(2, 2)
```

Functions can be defined with the same name as long as they have a different number of arguments; the proper version of the function would be used when called. For simplicity, all arguments are required.

```
average(one) = {one}
average(one, two) = (({one}) + ({two})) / 2
average(one, two, three) = (({one}) + ({two}) + ({three})) / 3
```

Declaring functions with the same name and number of arguments is not allowed; such declarations would either replace previous ones or cause an error (either works).

Calling built-in functions should obviously be allowed from user-defined functions:

```
linearaddress(label) = (BANK({label}) - 1) * $4000 + ({label})
```

In order to simplify functions like the average one above, varargs functions could be defined. Such functions would be used when there is no suitable non-varargs function (i.e., with the correct number of arguments) to call. For instance:

```
countargs() = 0
countargs(arg, ...) = 1 + countargs({...})

sum() = 0
sum(arg, ...) = ({arg}) + sum({...})

average(...) = sum({...}) / countargs({...})
roundedavg(...) = (2 * sum({...}) + 1) / (2 * countargs({...}))
```

The special symbol `{...}` is replaced by the list of variable arguments. Defining multiple varargs functions with the same name is not allowed; however, they can coexist with non-varargs functions.

As a silly example just to show how they would coexist:

```
fn(a) = {a}
fn(a, b, c) = {c}
fn(a, b, ...) = {b}

db fn(4) ;4
db fn(5, 9) ;9
db fn(2, 6, 3) ;3
db fn(1, 8, 0, 7) ;8
;db fn() is an error because there's no version that takes 0 arguments
```

I hope that this is doable, as it would certainly reduce some repetition and extremely macro-heavy code in some cases. As a simple realistic example, take this macro from Prism (might be slightly different because I'm typing it from memory):

```
;definition
coord: MACRO
if _NARG > 3
ld \1, (\4) + (\2) + ((\3) * SCREEN_WIDTH)
else
ld \1, wTilemap + (\2) + ((\3) * SCREEN_WIDTH)
endc
ENDM

hlcoord EQUS "coord hl, "

;usage
hlcoord 2, 5 ;sets hl to the corresponding tilemap position
```

Which using functions would be a lot cleaner:

```
;definition
coord(x, y, base) = ({base}) + ({x}) + ({y}) * SCREEN_WIDTH
coord(x, y) = coord({x}, {y}, wTilemap)

;usage
ld hl, coord(2, 5)
```

I await your responses; thanks for reading.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.