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

@@ -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 */
static void readNumber(int radix, int32_t baseValue)
@@ -1568,8 +1583,6 @@ static int yylex_NORMAL(void)
yylval.tzSym[1] = '\0';
return T_ID;
/* Handle accepted single chars */
case '[':
return T_LBRACK;
case ']':
@@ -1580,8 +1593,6 @@ static int yylex_NORMAL(void)
return T_RPAREN;
case ',':
return T_COMMA;
case ':':
return T_COLON;
/* Handle ambiguous 1- or 2-char tokens */
char secondChar;
@@ -1639,6 +1650,16 @@ static int yylex_NORMAL(void)
}
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 */
case '$':

View File

@@ -262,7 +262,9 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
%token <tzSym> T_LABEL
%token <tzSym> T_ID
%token <tzSym> T_LOCAL_ID
%token <tzSym> T_ANON
%type <tzSym> scoped_id
%type <tzSym> scoped_anon_id
%token T_POP_EQU
%token T_POP_SET
%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 */
| T_COLON {
sym_AddAnonLabel();
}
| T_LOCAL_ID {
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_OP_LOGICNOT relocexpr %prec NEG {
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_LOW T_LPAREN relocexpr T_RPAREN { rpn_LOW(&$$, &$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. */
rpn_BankSymbol(&$$, $3);
}
| T_OP_BANK T_LPAREN string T_RPAREN { rpn_BankSection(&$$, $3); }
| T_OP_DEF {
lexer_ToggleStringExpansion(false);
} T_LPAREN scoped_id T_RPAREN {
} T_LPAREN scoped_anon_id T_RPAREN {
struct Symbol const *sym = sym_FindScopedSymbol($4);
rpn_Number(&$$, !!sym);

View File

@@ -800,6 +800,33 @@ must be the actual current scope.
.Pp
Local labels may have whitespace before their declaration as the only exception to the rule.
.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.
However, if the section in which the label is declared has a fixed base address, its value is known at assembly time.
.Pp

View File

@@ -515,11 +515,60 @@ struct Symbol *sym_AddLabel(char const *name)
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
*/
void sym_Export(char const *symName)
{
if (symName[0] == '!') {
error("Anonymous labels cannot be exported\n");
return;
}
struct Symbol *sym = sym_FindScopedSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */
@@ -671,6 +720,7 @@ void sym_Init(void)
#undef addString
labelScope = NULL;
anonLabelID = 0;
math_DefinePI();
}