mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement compound assignment operators for mutable constants
Fixes #943
This commit is contained in:
121
src/asm/lexer.c
121
src/asm/lexer.c
@@ -284,8 +284,6 @@ static struct KeywordMapping {
|
|||||||
{"EQU", T_POP_EQU},
|
{"EQU", T_POP_EQU},
|
||||||
{"EQUS", T_POP_EQUS},
|
{"EQUS", T_POP_EQUS},
|
||||||
{"REDEF", T_POP_REDEF},
|
{"REDEF", T_POP_REDEF},
|
||||||
/* Handled before as T_Z80_SET */
|
|
||||||
/* {"SET", T_POP_SET}, */
|
|
||||||
|
|
||||||
{"PUSHS", T_POP_PUSHS},
|
{"PUSHS", T_POP_PUSHS},
|
||||||
{"POPS", T_POP_POPS},
|
{"POPS", T_POP_POPS},
|
||||||
@@ -1805,12 +1803,6 @@ static int yylex_NORMAL(void)
|
|||||||
|
|
||||||
/* Handle unambiguous single-char tokens */
|
/* Handle unambiguous single-char tokens */
|
||||||
|
|
||||||
case '^':
|
|
||||||
return T_OP_XOR;
|
|
||||||
case '+':
|
|
||||||
return T_OP_ADD;
|
|
||||||
case '-':
|
|
||||||
return T_OP_SUB;
|
|
||||||
case '~':
|
case '~':
|
||||||
return T_OP_NOT;
|
return T_OP_NOT;
|
||||||
|
|
||||||
@@ -1832,66 +1824,113 @@ static int yylex_NORMAL(void)
|
|||||||
|
|
||||||
/* Handle ambiguous 1- or 2-char tokens */
|
/* Handle ambiguous 1- or 2-char tokens */
|
||||||
|
|
||||||
case '*': /* Either MUL or EXP */
|
case '+': /* Either += or ADD */
|
||||||
if (peek() == '*') {
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_ADDEQ;
|
||||||
|
}
|
||||||
|
return T_OP_ADD;
|
||||||
|
|
||||||
|
case '-': /* Either -= or SUB */
|
||||||
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_SUBEQ;
|
||||||
|
}
|
||||||
|
return T_OP_SUB;
|
||||||
|
|
||||||
|
case '*': /* Either *=, MUL, or EXP */
|
||||||
|
switch (peek()) {
|
||||||
|
case '=':
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_MULEQ;
|
||||||
|
case '*':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_EXP;
|
return T_OP_EXP;
|
||||||
|
default:
|
||||||
|
return T_OP_MUL;
|
||||||
}
|
}
|
||||||
return T_OP_MUL;
|
|
||||||
|
|
||||||
case '/': /* Either division or a block comment */
|
case '/': /* Either /=, DIV, or a block comment */
|
||||||
if (peek() == '*') {
|
switch (peek()) {
|
||||||
|
case '=':
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_DIVEQ;
|
||||||
|
case '*':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
discardBlockComment();
|
discardBlockComment();
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return T_OP_DIV;
|
||||||
}
|
}
|
||||||
return T_OP_DIV;
|
break;
|
||||||
|
|
||||||
case '|': /* Either binary or logical OR */
|
case '|': /* Either |=, binary OR, or logical OR */
|
||||||
if (peek() == '|') {
|
switch (peek()) {
|
||||||
|
case '=':
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_OREQ;
|
||||||
|
case '|':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICOR;
|
return T_OP_LOGICOR;
|
||||||
|
default:
|
||||||
|
return T_OP_OR;
|
||||||
}
|
}
|
||||||
return T_OP_OR;
|
|
||||||
|
|
||||||
case '=': /* Either SET alias, or EQ */
|
case '^': /* Either ^= or XOR */
|
||||||
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_XOREQ;
|
||||||
|
}
|
||||||
|
return T_OP_XOR;
|
||||||
|
|
||||||
|
case '=': /* Either assignment or EQ */
|
||||||
if (peek() == '=') {
|
if (peek() == '=') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICEQU;
|
return T_OP_LOGICEQU;
|
||||||
}
|
}
|
||||||
return T_POP_EQUAL;
|
return T_POP_EQUAL;
|
||||||
|
|
||||||
case '<': /* Either a LT, LTE, or left shift */
|
case '!': /* Either a NEQ or negation */
|
||||||
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_OP_LOGICNE;
|
||||||
|
}
|
||||||
|
return T_OP_LOGICNOT;
|
||||||
|
|
||||||
|
/* Handle ambiguous 1-, 2-, or 3-char tokens */
|
||||||
|
|
||||||
|
case '<': /* Either <<=, LT, LTE, or left shift */
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case '=':
|
case '=':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICLE;
|
return T_OP_LOGICLE;
|
||||||
case '<':
|
case '<':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_SHLEQ;
|
||||||
|
}
|
||||||
return T_OP_SHL;
|
return T_OP_SHL;
|
||||||
default:
|
default:
|
||||||
return T_OP_LOGICLT;
|
return T_OP_LOGICLT;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '>': /* Either a GT, GTE, or right shift */
|
case '>': /* Either >>=, GT, GTE, or right shift */
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case '=':
|
case '=':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICGE;
|
return T_OP_LOGICGE;
|
||||||
case '>':
|
case '>':
|
||||||
shiftChar();
|
shiftChar();
|
||||||
|
if (peek() == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_SHREQ;
|
||||||
|
}
|
||||||
return T_OP_SHR;
|
return T_OP_SHR;
|
||||||
default:
|
default:
|
||||||
return T_OP_LOGICGT;
|
return T_OP_LOGICGT;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '!': /* Either a NEQ, or negation */
|
|
||||||
if (peek() == '=') {
|
|
||||||
shiftChar();
|
|
||||||
return T_OP_LOGICNE;
|
|
||||||
}
|
|
||||||
return T_OP_LOGICNOT;
|
|
||||||
|
|
||||||
/* Handle colon, which may begin an anonymous label ref */
|
/* Handle colon, which may begin an anonymous label ref */
|
||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
@@ -1904,11 +1943,7 @@ static int yylex_NORMAL(void)
|
|||||||
|
|
||||||
/* Handle numbers */
|
/* Handle numbers */
|
||||||
|
|
||||||
case '$':
|
case '0': /* Decimal or fixed-point number */
|
||||||
yylval.constValue = readHexNumber();
|
|
||||||
return T_NUMBER;
|
|
||||||
|
|
||||||
case '0': /* Decimal number */
|
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
case '3':
|
case '3':
|
||||||
@@ -1925,9 +1960,12 @@ static int yylex_NORMAL(void)
|
|||||||
}
|
}
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
|
|
||||||
case '&':
|
case '&': /* Either &=, binary AND, logical AND, or an octal constant */
|
||||||
secondChar = peek();
|
secondChar = peek();
|
||||||
if (secondChar == '&') {
|
if (secondChar == '=') {
|
||||||
|
shiftChar();
|
||||||
|
return T_POP_ANDEQ;
|
||||||
|
} else if (secondChar == '&') {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
return T_OP_LOGICAND;
|
return T_OP_LOGICAND;
|
||||||
} else if (secondChar >= '0' && secondChar <= '7') {
|
} else if (secondChar >= '0' && secondChar <= '7') {
|
||||||
@@ -1936,12 +1974,19 @@ static int yylex_NORMAL(void)
|
|||||||
}
|
}
|
||||||
return T_OP_AND;
|
return T_OP_AND;
|
||||||
|
|
||||||
case '%': /* Either a modulo, or a binary constant */
|
case '%': /* Either %=, MOD, or a binary constant */
|
||||||
secondChar = peek();
|
secondChar = peek();
|
||||||
if (secondChar != binDigits[0] && secondChar != binDigits[1])
|
if (secondChar == '=') {
|
||||||
return T_OP_MOD;
|
shiftChar();
|
||||||
|
return T_POP_MODEQ;
|
||||||
|
} else if (secondChar == binDigits[0] || secondChar == binDigits[1]) {
|
||||||
|
yylval.constValue = readBinaryNumber();
|
||||||
|
return T_NUMBER;
|
||||||
|
}
|
||||||
|
return T_OP_MOD;
|
||||||
|
|
||||||
yylval.constValue = readBinaryNumber();
|
case '$': /* Hex constant */
|
||||||
|
yylval.constValue = readHexNumber();
|
||||||
return T_NUMBER;
|
return T_NUMBER;
|
||||||
|
|
||||||
case '`': /* Gfx constant */
|
case '`': /* Gfx constant */
|
||||||
|
|||||||
@@ -364,6 +364,17 @@ static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, s
|
|||||||
dest[i] = '\0';
|
dest[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void compoundAssignment(const char *symName, enum RPNCommand op, int32_t constValue) {
|
||||||
|
struct Expression oldExpr, constExpr, newExpr;
|
||||||
|
int32_t newValue;
|
||||||
|
|
||||||
|
rpn_Symbol(&oldExpr, symName);
|
||||||
|
rpn_Number(&constExpr, constValue);
|
||||||
|
rpn_BinaryOp(op, &newExpr, &oldExpr, &constExpr);
|
||||||
|
newValue = rpn_GetConstVal(&newExpr);
|
||||||
|
sym_AddSet(symName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
static void initDsArgList(struct DsArgList *args)
|
static void initDsArgList(struct DsArgList *args)
|
||||||
{
|
{
|
||||||
args->nbArgs = 0;
|
args->nbArgs = 0;
|
||||||
@@ -468,6 +479,7 @@ enum {
|
|||||||
char string[MAXSTRLEN + 1];
|
char string[MAXSTRLEN + 1];
|
||||||
struct Expression expr;
|
struct Expression expr;
|
||||||
int32_t constValue;
|
int32_t constValue;
|
||||||
|
enum RPNCommand compoundEqual;
|
||||||
enum SectionModifier sectMod;
|
enum SectionModifier sectMod;
|
||||||
struct SectionSpec sectSpec;
|
struct SectionSpec sectSpec;
|
||||||
struct MacroArgs *macroArg;
|
struct MacroArgs *macroArg;
|
||||||
@@ -579,6 +591,12 @@ enum {
|
|||||||
%token T_POP_EQUAL "="
|
%token T_POP_EQUAL "="
|
||||||
%token T_POP_EQUS "EQUS"
|
%token T_POP_EQUS "EQUS"
|
||||||
|
|
||||||
|
%token T_POP_ADDEQ "+=" T_POP_SUBEQ "-="
|
||||||
|
%token T_POP_MULEQ "*=" T_POP_DIVEQ "/=" T_POP_MODEQ "%="
|
||||||
|
%token T_POP_OREQ "|=" T_POP_XOREQ "^=" T_POP_ANDEQ "&="
|
||||||
|
%token T_POP_SHLEQ "<<=" T_POP_SHREQ ">>="
|
||||||
|
%type <compoundEqual> compoundeq
|
||||||
|
|
||||||
%token T_POP_INCLUDE "INCLUDE"
|
%token T_POP_INCLUDE "INCLUDE"
|
||||||
%token T_POP_PRINT "PRINT" T_POP_PRINTLN "PRINTLN"
|
%token T_POP_PRINT "PRINT" T_POP_PRINTLN "PRINTLN"
|
||||||
%token T_POP_PRINTF "PRINTF" T_POP_PRINTT "PRINTT" T_POP_PRINTV "PRINTV" T_POP_PRINTI "PRINTI"
|
%token T_POP_PRINTF "PRINTF" T_POP_PRINTT "PRINTT" T_POP_PRINTV "PRINTV" T_POP_PRINTI "PRINTI"
|
||||||
@@ -894,10 +912,23 @@ directive : endc
|
|||||||
trailing_comma : %empty | T_COMMA
|
trailing_comma : %empty | T_COMMA
|
||||||
;
|
;
|
||||||
|
|
||||||
|
compoundeq : T_POP_ADDEQ { $$ = RPN_ADD; }
|
||||||
|
| T_POP_SUBEQ { $$ = RPN_SUB; }
|
||||||
|
| T_POP_MULEQ { $$ = RPN_MUL; }
|
||||||
|
| T_POP_DIVEQ { $$ = RPN_DIV; }
|
||||||
|
| T_POP_MODEQ { $$ = RPN_MOD; }
|
||||||
|
| T_POP_XOREQ { $$ = RPN_XOR; }
|
||||||
|
| T_POP_OREQ { $$ = RPN_OR; }
|
||||||
|
| T_POP_ANDEQ { $$ = RPN_AND; }
|
||||||
|
| T_POP_SHLEQ { $$ = RPN_SHL; }
|
||||||
|
| T_POP_SHREQ { $$ = RPN_SHR; }
|
||||||
|
;
|
||||||
|
|
||||||
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
|
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
set : T_LABEL T_POP_EQUAL const { sym_AddSet($1, $3); }
|
set : T_LABEL T_POP_EQUAL const { sym_AddSet($1, $3); }
|
||||||
|
| T_LABEL compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
| T_LABEL T_POP_SET const {
|
| T_LABEL T_POP_SET const {
|
||||||
warning(WARNING_OBSOLETE, "`SET` is deprecated; use `=`\n");
|
warning(WARNING_OBSOLETE, "`SET` is deprecated; use `=`\n");
|
||||||
sym_AddSet($1, $3);
|
sym_AddSet($1, $3);
|
||||||
@@ -1145,6 +1176,8 @@ redef_equ : redef_id T_POP_EQU const { sym_RedefEqu($1, $3); }
|
|||||||
|
|
||||||
def_set : def_id T_POP_EQUAL const { sym_AddSet($1, $3); }
|
def_set : def_id T_POP_EQUAL const { sym_AddSet($1, $3); }
|
||||||
| redef_id T_POP_EQUAL const { sym_AddSet($1, $3); }
|
| redef_id T_POP_EQUAL const { sym_AddSet($1, $3); }
|
||||||
|
| def_id compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
|
| redef_id compoundeq const { compoundAssignment($1, $2, $3); }
|
||||||
| def_id T_POP_SET const {
|
| def_id T_POP_SET const {
|
||||||
warning(WARNING_OBSOLETE, "`SET` is deprecated; use `=`\n");
|
warning(WARNING_OBSOLETE, "`SET` is deprecated; use `=`\n");
|
||||||
sym_AddSet($1, $3);
|
sym_AddSet($1, $3);
|
||||||
|
|||||||
@@ -1036,6 +1036,25 @@ COUNT = COUNT*2
|
|||||||
Note that colons
|
Note that colons
|
||||||
.Ql \&:
|
.Ql \&:
|
||||||
following the name are not allowed.
|
following the name are not allowed.
|
||||||
|
.Pp
|
||||||
|
Mutable constants can be conveniently redefined by compound assignment operators like in C:
|
||||||
|
.Bl -column -offset indent "*= /= %="
|
||||||
|
.It Sy Operator Ta Sy Meaning
|
||||||
|
.It Li += -= Ta Compound plus/minus
|
||||||
|
.It Li *= /= %= Ta Compound multiply/divide/modulo
|
||||||
|
.It Li <<= >>= Ta Compound shift left/right
|
||||||
|
.It Li &= \&|= ^= Ta Compound and/or/xor
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Examples:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
DEF x = 10
|
||||||
|
DEF x += 1 ; x == 11
|
||||||
|
DEF y = x - 1 ; y == 10
|
||||||
|
DEF y *= 2 ; y == 20
|
||||||
|
DEF y >>= 1 ; y == 10
|
||||||
|
DEF x ^= y ; x == 1
|
||||||
|
.Ed
|
||||||
.Ss Offset constants
|
.Ss Offset constants
|
||||||
The RS group of commands is a handy way of defining structure offsets:
|
The RS group of commands is a handy way of defining structure offsets:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alter a SET symbol's value
|
* Alter a mutable symbol's value
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
||||||
{
|
{
|
||||||
|
|||||||
40
test/asm/compound-assignment.asm
Normal file
40
test/asm/compound-assignment.asm
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
macro try
|
||||||
|
println \1, "\2:"
|
||||||
|
def prefix equs \1
|
||||||
|
{prefix}\2 = 10
|
||||||
|
println \2 ; 10
|
||||||
|
{prefix}\2 += 5
|
||||||
|
println \2 ; 15
|
||||||
|
{prefix}\2 -= 1
|
||||||
|
println \2 ; 14
|
||||||
|
{prefix}\2 *= 2
|
||||||
|
println \2 ; 28
|
||||||
|
{prefix}\2 /= 4
|
||||||
|
println \2 ; 7
|
||||||
|
{prefix}\2 %= 3
|
||||||
|
println \2 ; 1
|
||||||
|
{prefix}\2 |= 11
|
||||||
|
println \2 ; 11
|
||||||
|
{prefix}\2 ^= 12
|
||||||
|
println \2 ; 7
|
||||||
|
{prefix}\2 &= 21
|
||||||
|
println \2 ; 5
|
||||||
|
{prefix}\2 <<= 2
|
||||||
|
println \2 ; 20
|
||||||
|
{prefix}\2 >>= 1
|
||||||
|
println \2 ; 10
|
||||||
|
purge prefix
|
||||||
|
endm
|
||||||
|
|
||||||
|
try "", p
|
||||||
|
try "def ", q
|
||||||
|
try "redef ", r
|
||||||
|
|
||||||
|
_RS += 100
|
||||||
|
println _RS
|
||||||
|
|
||||||
|
__LINE__ *= 200
|
||||||
|
println __LINE__
|
||||||
|
|
||||||
|
UnDeFiNeD ^= 300
|
||||||
|
println UnDeFiNeD
|
||||||
5
test/asm/compound-assignment.err
Normal file
5
test/asm/compound-assignment.err
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ERROR: compound-assignment.asm(36):
|
||||||
|
'__LINE__' already defined as constant at <builtin>
|
||||||
|
ERROR: compound-assignment.asm(39):
|
||||||
|
Expected constant expression: 'UnDeFiNeD' is not constant at assembly time
|
||||||
|
error: Assembly aborted (2 errors)!
|
||||||
39
test/asm/compound-assignment.out
Normal file
39
test/asm/compound-assignment.out
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
p:
|
||||||
|
$A
|
||||||
|
$F
|
||||||
|
$E
|
||||||
|
$1C
|
||||||
|
$7
|
||||||
|
$1
|
||||||
|
$B
|
||||||
|
$7
|
||||||
|
$5
|
||||||
|
$14
|
||||||
|
$A
|
||||||
|
def q:
|
||||||
|
$A
|
||||||
|
$F
|
||||||
|
$E
|
||||||
|
$1C
|
||||||
|
$7
|
||||||
|
$1
|
||||||
|
$B
|
||||||
|
$7
|
||||||
|
$5
|
||||||
|
$14
|
||||||
|
$A
|
||||||
|
redef r:
|
||||||
|
$A
|
||||||
|
$F
|
||||||
|
$E
|
||||||
|
$1C
|
||||||
|
$7
|
||||||
|
$1
|
||||||
|
$B
|
||||||
|
$7
|
||||||
|
$5
|
||||||
|
$14
|
||||||
|
$A
|
||||||
|
$64
|
||||||
|
$25
|
||||||
|
$0
|
||||||
Reference in New Issue
Block a user