Implement compound assignment operators for mutable constants

Fixes #943
This commit is contained in:
Rangi
2021-11-18 19:40:23 -05:00
committed by Eldred Habert
parent b76819792d
commit 8e2a164a32
7 changed files with 220 additions and 39 deletions

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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

View File

@@ -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)
{ {

View 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

View 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)!

View 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