From 27f38770d4860305ef90922743f399eedf52de63 Mon Sep 17 00:00:00 2001 From: Rangi Date: Tue, 20 Apr 2021 19:50:46 -0400 Subject: [PATCH] Parentheses in macro args prevent commas from starting new arguments This is similar to C's behavior, and convenient for passing function calls as single values, like `MUL(3.0, 4.0)` or `STRSUB("str", 2, 1)`. Fixes #704 --- src/asm/lexer.c | 24 +++++++++++++++++++++--- src/asm/rgbasm.5 | 27 ++++++++++++++++++++------- test/asm/macro-arg-parentheses.asm | 19 +++++++++++++++++++ test/asm/macro-arg-parentheses.err | 0 test/asm/macro-arg-parentheses.out | 11 +++++++++++ test/asm/raw-macro-args.asm | 4 ++-- 6 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 test/asm/macro-arg-parentheses.asm create mode 100644 test/asm/macro-arg-parentheses.err create mode 100644 test/asm/macro-arg-parentheses.out diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 483d8eed..9c02d1bd 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -2056,6 +2056,7 @@ static int yylex_RAW(void) lexer_GetLineNo(), lexer_GetColNo()); /* This is essentially a modified `appendStringLiteral` */ + unsigned int parenDepth = 0; size_t i = 0; int c; @@ -2076,8 +2077,7 @@ static int yylex_RAW(void) discardComment(); c = peek(); /* fallthrough */ - case ',': /* End of macro arg */ - case '\r': + case '\r': /* End of line */ case '\n': case EOF: goto finish; @@ -2092,12 +2092,29 @@ static int yylex_RAW(void) append_yylval_string(c); /* Append the slash */ break; + case ',': /* End of macro arg */ + if (parenDepth == 0) + goto finish; + goto append; + + case '(': /* Open parentheses inside macro args */ + if (parenDepth < UINT_MAX) + parenDepth++; + goto append; + + case ')': /* Close parentheses inside macro args */ + if (parenDepth > 0) + parenDepth--; + goto append; + case '\\': /* Character escape */ shiftChar(); c = peek(); switch (c) { - case ',': /* Escape `\,` only inside a macro arg */ + case ',': /* Escapes only valid inside a macro arg */ + case '(': + case ')': case '\\': /* Escapes shared with string literals */ case '"': case '{': @@ -2137,6 +2154,7 @@ static int yylex_RAW(void) /* fallthrough */ default: /* Regular characters will just get copied */ +append: append_yylval_string(c); shiftChar(); break; diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index e9f5d9a3..8c57f385 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -1184,7 +1184,11 @@ ENDM .Pp Macro arguments support all the escape sequences of strings, as well as .Ql \[rs], -to escape commas, since those otherwise separate arguments. +to escape commas, as well as +.Ql \[rs]( +and +.Ql \[rs]) +to escape parentheses, since those otherwise separate and enclose arguments, respectively. .Ss Exporting and importing symbols Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file. .Pp @@ -1553,18 +1557,27 @@ which will print 5 and not 6 as you might have expected. Line continuations work as usual inside macros or lists of macro arguments. However, some characters need to be escaped, as in the following example: .Bd -literal -offset indent -MACRO PrintMacro +MACRO PrintMacro1 + PRINTLN STRCAT(\[rs]1) +ENDM + PrintMacro1 "Hello "\[rs], \[rs] + "world" +MACRO PrintMacro2 PRINT \[rs]1 ENDM - - PrintMacro STRCAT("Hello "\[rs], \[rs] - "world\[rs]n") + PrintMacro2 STRCAT("Hello ", \[rs] + "world\[rs]n") .Ed .Pp -The comma needs to be escaped to avoid it being treated as separating the macro's arguments. +The comma in +.Ql PrintMacro1 +needs to be escaped to prevent it from starting another macro argument. +The comma in +.Ql PrintMacro2 +does not need escaping because it is inside parentheses, similar to macro arguments in C. The backslash in .Ql \[rs]n -does not need to be escaped because string literals also work as usual inside macro arguments. +also does not need escaping because string literals work as usual inside macro arguments. .Pp Since there are only nine digits, you can only access the first nine macro arguments like this. To use the rest, you need to put the multi-digit argument number in angle brackets, like diff --git a/test/asm/macro-arg-parentheses.asm b/test/asm/macro-arg-parentheses.asm new file mode 100644 index 00000000..8efaf4a9 --- /dev/null +++ b/test/asm/macro-arg-parentheses.asm @@ -0,0 +1,19 @@ +MACRO printargs + REPT _NARG + PRINTLN \1 + SHIFT + ENDR +ENDM + + printargs mul(3.0, 4.0) + +MACRO printlit + REPT _NARG + PRINTLN "\1" + SHIFT + ENDR +ENDM + + printlit a(b,c\,d), ((e,f),g), ))h, i\,j, + printlit \(k, l), (m:\)n,o(p)q), (r,s)t + printlit "))u,v(", ("w,x","y,z"), diff --git a/test/asm/macro-arg-parentheses.err b/test/asm/macro-arg-parentheses.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/macro-arg-parentheses.out b/test/asm/macro-arg-parentheses.out new file mode 100644 index 00000000..c6030a79 --- /dev/null +++ b/test/asm/macro-arg-parentheses.out @@ -0,0 +1,11 @@ +$C0000 +a(b,c,d) +((e,f),g) +))h +i,j +(k +l) +(m:)n,o(p)q) +(r,s)t +"))u,v(" +("w,x","y,z") diff --git a/test/asm/raw-macro-args.asm b/test/asm/raw-macro-args.asm index a7ca23a3..16c302c7 100644 --- a/test/asm/raw-macro-args.asm +++ b/test/asm/raw-macro-args.asm @@ -22,7 +22,7 @@ STR EQUS "str\"ing" printargs "literal \"\\\"", \ ; comment 2 """multi-"line" ""string"" arg""" - printargs MUL(2.0\, 3.0) + printargs MUL(2.0, 3.0) printargs "unclosed printlit NUM @@ -32,7 +32,7 @@ STR EQUS "str\"ing" printlit "literal \"\\\"", \ ; comment 4 """multi-"line" ""string"" arg""" - printlit MUL(2.0\, 3.0) + printlit MUL(2.0, 3.0) printlit this\n is\, \{not\} a\\n syntax\" error printlit "unclosed printlit """EOF \ No newline at end of file