Add anonymous labels

Fix #497
This commit is contained in:
ISSOtm
2020-12-12 11:52:06 +01:00
committed by Eldred Habert
parent 0e40543757
commit 8f2a894b88
12 changed files with 151 additions and 8 deletions

View File

@@ -115,6 +115,8 @@ int32_t sym_GetValue(struct Symbol const *sym);
void sym_SetExportAll(bool set); void sym_SetExportAll(bool set);
struct Symbol *sym_AddLocalLabel(char const *symName); struct Symbol *sym_AddLocalLabel(char const *symName);
struct Symbol *sym_AddLabel(char const *symName); struct Symbol *sym_AddLabel(char const *symName);
struct Symbol *sym_AddAnonLabel(void);
void sym_WriteAnonLabelName(char name[static MAXSYMLEN + 1], uint32_t ofs, bool neg);
void sym_Export(char const *symName); void sym_Export(char const *symName);
struct Symbol *sym_AddEqu(char const *symName, int32_t value); struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct Symbol *sym_AddSet(char const *symName, int32_t value); struct Symbol *sym_AddSet(char const *symName, int32_t value);

View File

@@ -1045,6 +1045,21 @@ static void readLineContinuation(void)
} }
} }
/* Function to read an anonymous label ref */
static void readAnonLabelRef(char c)
{
uint32_t n = 0;
// We come here having already peeked at one char, so no need to do it again
do {
shiftChars(1);
n++;
} while (peek(0) == c);
sym_WriteAnonLabelName(yylval.tzSym, n, c == '-');
}
/* Functions to lex numbers of various radixes */ /* Functions to lex numbers of various radixes */
static void readNumber(int radix, int32_t baseValue) static void readNumber(int radix, int32_t baseValue)
@@ -1568,8 +1583,6 @@ static int yylex_NORMAL(void)
yylval.tzSym[1] = '\0'; yylval.tzSym[1] = '\0';
return T_ID; return T_ID;
/* Handle accepted single chars */
case '[': case '[':
return T_LBRACK; return T_LBRACK;
case ']': case ']':
@@ -1580,8 +1593,6 @@ static int yylex_NORMAL(void)
return T_RPAREN; return T_RPAREN;
case ',': case ',':
return T_COMMA; return T_COMMA;
case ':':
return T_COLON;
/* Handle ambiguous 1- or 2-char tokens */ /* Handle ambiguous 1- or 2-char tokens */
char secondChar; char secondChar;
@@ -1639,6 +1650,16 @@ static int yylex_NORMAL(void)
} }
return T_OP_LOGICNOT; return T_OP_LOGICNOT;
/* Handle colon, which may begin an anonymous label ref */
case ':':
c = peek(0);
if (c != '+' && c != '-')
return T_COLON;
readAnonLabelRef(c);
return T_ANON;
/* Handle numbers */ /* Handle numbers */
case '$': case '$':

View File

@@ -262,7 +262,9 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
%token <tzSym> T_LABEL %token <tzSym> T_LABEL
%token <tzSym> T_ID %token <tzSym> T_ID
%token <tzSym> T_LOCAL_ID %token <tzSym> T_LOCAL_ID
%token <tzSym> T_ANON
%type <tzSym> scoped_id %type <tzSym> scoped_id
%type <tzSym> scoped_anon_id
%token T_POP_EQU %token T_POP_EQU
%token T_POP_SET %token T_POP_SET
%token T_POP_EQUAL %token T_POP_EQUAL
@@ -423,9 +425,13 @@ endc : T_POP_ENDC T_NEWLINE {
} }
; ;
scoped_id : T_ID | T_LOCAL_ID ; scoped_id : T_ID | T_LOCAL_ID;
scoped_anon_id : scoped_id | T_ANON;
label : /* empty */ label : /* empty */
| T_COLON {
sym_AddAnonLabel();
}
| T_LOCAL_ID { | T_LOCAL_ID {
sym_AddLocalLabel($1); sym_AddLocalLabel($1);
} }
@@ -914,7 +920,7 @@ relocexpr : relocexpr_no_str
} }
; ;
relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); } relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_NUMBER { rpn_Number(&$$, $1); } | T_NUMBER { rpn_Number(&$$, $1); }
| T_OP_LOGICNOT relocexpr %prec NEG { | T_OP_LOGICNOT relocexpr %prec NEG {
rpn_LOGNOT(&$$, &$2); rpn_LOGNOT(&$$, &$2);
@@ -979,14 +985,14 @@ relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
| T_OP_HIGH T_LPAREN relocexpr T_RPAREN { rpn_HIGH(&$$, &$3); } | T_OP_HIGH T_LPAREN relocexpr T_RPAREN { rpn_HIGH(&$$, &$3); }
| T_OP_LOW T_LPAREN relocexpr T_RPAREN { rpn_LOW(&$$, &$3); } | T_OP_LOW T_LPAREN relocexpr T_RPAREN { rpn_LOW(&$$, &$3); }
| T_OP_ISCONST T_LPAREN relocexpr T_RPAREN{ rpn_ISCONST(&$$, &$3); } | T_OP_ISCONST T_LPAREN relocexpr T_RPAREN{ rpn_ISCONST(&$$, &$3); }
| T_OP_BANK T_LPAREN scoped_id T_RPAREN { | T_OP_BANK T_LPAREN scoped_anon_id T_RPAREN {
/* '@' is also a T_ID, it is handled here. */ /* '@' is also a T_ID, it is handled here. */
rpn_BankSymbol(&$$, $3); rpn_BankSymbol(&$$, $3);
} }
| T_OP_BANK T_LPAREN string T_RPAREN { rpn_BankSection(&$$, $3); } | T_OP_BANK T_LPAREN string T_RPAREN { rpn_BankSection(&$$, $3); }
| T_OP_DEF { | T_OP_DEF {
lexer_ToggleStringExpansion(false); lexer_ToggleStringExpansion(false);
} T_LPAREN scoped_id T_RPAREN { } T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol const *sym = sym_FindScopedSymbol($4); struct Symbol const *sym = sym_FindScopedSymbol($4);
rpn_Number(&$$, !!sym); rpn_Number(&$$, !!sym);

View File

@@ -800,6 +800,33 @@ must be the actual current scope.
.Pp .Pp
Local labels may have whitespace before their declaration as the only exception to the rule. Local labels may have whitespace before their declaration as the only exception to the rule.
.Pp .Pp
.Sy Anonymous labels
are useful for short blocks of code.
They are defined like normal labels, but without a name before the colon.
Defining one does not change the label scope (unlike global labels).
Referencing one is done using a colon
.Ql \&:
followed by pluses
.Ql +
or minuses
.Ql - .
.Ic :+
references the next one after the expression,
.Ic :++
the one after it, and so on.
The logic is similar for -, just backwards.
.Bd -literal -offset indent
ld hl, :++
: ld a, [hli] ; Jumps to here
ldh [c], a
dec c
jr nz, :-
ret
: ; This address referenced by "ld hl"
dw $7FFF, $1061, $03E0, $58A5
.Ed
.Pp
A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants. A label's location (and thus value) is usually not determined until the linking stage, so labels usually cannot be used as constants.
However, if the section in which the label is declared has a fixed base address, its value is known at assembly time. However, if the section in which the label is declared has a fixed base address, its value is known at assembly time.
.Pp .Pp

View File

@@ -515,11 +515,60 @@ struct Symbol *sym_AddLabel(char const *name)
return sym; return sym;
} }
static uint32_t anonLabelID;
/*
* Add an anonymous label
*/
struct Symbol *sym_AddAnonLabel(void)
{
if (anonLabelID == UINT32_MAX) {
error("Only %" PRIu32 " anonymous labels can be created!");
return NULL;
}
char name[MAXSYMLEN + 1];
sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
anonLabelID++;
return addLabel(name);
}
/*
* Write an anonymous label's name to a buffer
*/
void sym_WriteAnonLabelName(char buf[static MAXSYMLEN + 1], uint32_t ofs, bool neg)
{
uint32_t id = 0;
if (neg) {
if (ofs > anonLabelID)
error("Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
" ha%s been created so far\n",
ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
else
id = anonLabelID - ofs;
} else {
ofs--; // We're referencing symbols that haven't been created yet...
if (ofs > UINT32_MAX - anonLabelID)
error("Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
" may still be created\n", ofs + 1, UINT32_MAX - anonLabelID);
else
id = anonLabelID + ofs;
}
sprintf(buf, "!%u", id);
}
/* /*
* Export a symbol * Export a symbol
*/ */
void sym_Export(char const *symName) void sym_Export(char const *symName)
{ {
if (symName[0] == '!') {
error("Anonymous labels cannot be exported\n");
return;
}
struct Symbol *sym = sym_FindScopedSymbol(symName); struct Symbol *sym = sym_FindScopedSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */ /* If the symbol doesn't exist, create a ref that can be purged */
@@ -671,6 +720,7 @@ void sym_Init(void)
#undef addString #undef addString
labelScope = NULL; labelScope = NULL;
anonLabelID = 0;
math_DefinePI(); math_DefinePI();
} }

View File

@@ -0,0 +1,18 @@
: ; Outside of section
SECTION "Anonymous label errors test", ROM0
db :-- ; Reference goes too far back
; Uncomment this if you're a badass with a *lot* of RAM
; REPT 2147483647
; :
; ENDR
; REPT 2147483647
; :
; ENDR
; db :+ ; OK
; db :++ ; Reference goes too far
:: ; Syntax error, can't export this

View File

@@ -0,0 +1,7 @@
ERROR: anon-label-bad.asm(2):
Label "!0" created outside of a SECTION
ERROR: anon-label-bad.asm(6):
Reference to anonymous label 2 before, when only 1 has been created so far
ERROR: anon-label-bad.asm(18):
syntax error
error: Assembly aborted (3 errors)!

View File

12
test/asm/anon-label.asm Normal file
View File

@@ -0,0 +1,12 @@
SECTION "Anonymous label test", ROM0[0]
ld hl, :++
: ld a, [hli]
ldh [c], a
dec c
jr nz, :-
ret
:
dw $7FFF, $1061, $03E0, $58A5

0
test/asm/anon-label.err Normal file
View File

0
test/asm/anon-label.out Normal file
View File

BIN
test/asm/anon-label.out.bin Normal file

Binary file not shown.