commit 09299304297f9244d866289eafc912422be489e7
parent dca693c3073799b8d46cc9f6479839ea4c8d913d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 23 Apr 2026 17:05:40 -0700
m1pp: allow paren-less invocation of 0-arg macros
Diffstat:
5 files changed, 135 insertions(+), 11 deletions(-)
diff --git a/m1pp/m1pp.c b/m1pp/m1pp.c
@@ -675,16 +675,20 @@ static int expand_macro_tokens(struct Token *call_tok, struct Token *limit,
struct Token *end_pos;
int mark;
- if (call_tok + 1 >= limit || (call_tok + 1)->kind != TOK_LPAREN) {
+ if (call_tok + 1 < limit && (call_tok + 1)->kind == TOK_LPAREN) {
+ if (!parse_args(call_tok + 1, limit)) {
+ return 0;
+ }
+ if (arg_count != m->param_count) {
+ return fail("wrong arg count");
+ }
+ end_pos = call_end_pos;
+ } else if (m->param_count == 0) {
+ arg_count = 0;
+ end_pos = call_tok + 1;
+ } else {
return fail("bad macro call");
}
- if (!parse_args(call_tok + 1, limit)) {
- return 0;
- }
- if (arg_count != m->param_count) {
- return fail("wrong arg count");
- }
- end_pos = call_end_pos;
mark = pool_used;
for (body_tok = m->body_start; body_tok < m->body_end; body_tok++) {
@@ -949,7 +953,9 @@ static int eval_expr_atom(struct Token *tok, struct Token *limit,
int mark;
macro = find_macro(tok);
- if (macro != NULL && tok + 1 < limit && (tok + 1)->kind == TOK_LPAREN) {
+ if (macro != NULL &&
+ ((tok + 1 < limit && (tok + 1)->kind == TOK_LPAREN) ||
+ macro->param_count == 0)) {
if (!expand_macro_tokens(tok, limit, macro, &after, &mark)) {
return 0;
}
@@ -1229,8 +1235,8 @@ static int process_tokens(void)
macro = find_macro(tok);
if (macro != NULL &&
- tok + 1 < s->end &&
- (tok + 1)->kind == TOK_LPAREN) {
+ ((tok + 1 < s->end && (tok + 1)->kind == TOK_LPAREN) ||
+ macro->param_count == 0)) {
if (!expand_call(s, macro)) {
return 0;
}
diff --git a/tests/m1pp/13-parenless-control.M1pp b/tests/m1pp/13-parenless-control.M1pp
@@ -0,0 +1,30 @@
+# Control fixture for 13-parenless: same macros and test points, but every
+# 0-arg call uses the explicit () form. The output from the two test points
+# where 13-parenless uses paren-less calls must be byte-identical here,
+# proving paren-less is semantically equivalent to paren-ful for 0-arg macros.
+
+%macro FRAME_BASE()
+16
+%endm
+
+%macro add1(x)
+%((+ x 1))
+%endm
+
+# Top-level: both forms yield `16`.
+%FRAME_BASE()
+%FRAME_BASE()
+
+# Expression atom: both yield 4-byte LE hex for 24 (16 + 8).
+%((+ %FRAME_BASE() 8))
+%((+ %FRAME_BASE() 8))
+
+# Undefined %foo passes through unchanged.
+%foo
+
+# Param-ful macro without parens does NOT match โ literal text flows through.
+%add1
+
+# Param-ful macro with proper call still works (sanity).
+%add1(41)
+END
diff --git a/tests/m1pp/13-parenless-control.expected b/tests/m1pp/13-parenless-control.expected
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+16
+
+16
+
+
+
+'18000000'
+'18000000'
+
+
+%foo
+
+
+%add1
+
+
+'2A000000'
+
+END
diff --git a/tests/m1pp/13-parenless.M1pp b/tests/m1pp/13-parenless.M1pp
@@ -0,0 +1,32 @@
+# Paren-less 0-arg macro calls (M1PP-EXT ยง4):
+# - a zero-param macro invoked without trailing () expands the same as with ()
+# - applies at top level and as an atom inside %(...) expressions
+# - non-zero-param macros still require their (arg, ...) syntax โ %add1
+# without parens must pass through unchanged
+# - a %foo where foo is not a macro still passes through unchanged
+
+%macro FRAME_BASE()
+16
+%endm
+
+%macro add1(x)
+%((+ x 1))
+%endm
+
+# Top-level: both forms yield `16`.
+%FRAME_BASE
+%FRAME_BASE()
+
+# Expression atom: both yield 4-byte LE hex for 24 (16 + 8).
+%((+ %FRAME_BASE 8))
+%((+ %FRAME_BASE() 8))
+
+# Undefined %foo passes through unchanged.
+%foo
+
+# Param-ful macro without parens does NOT match โ literal text flows through.
+%add1
+
+# Param-ful macro with proper call still works (sanity).
+%add1(41)
+END
diff --git a/tests/m1pp/13-parenless.expected b/tests/m1pp/13-parenless.expected
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+16
+
+16
+
+
+
+'18000000'
+'18000000'
+
+
+%foo
+
+
+%add1
+
+
+'2A000000'
+
+END