From 281ca27a6f7eb3ea519615cb99d0776530c76a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C4=81vis=20Mos=C4=81ns?= Date: Sat, 14 Mar 2026 23:33:07 +0200 Subject: [PATCH] Support `&=`, `^=`, `|=` for macros --- dstep/translator/MacroDefinition.d | 6 +++ dstep/translator/MacroParser.d | 70 ++++++++++++++++++++++++++++-- dstep/translator/TypeInference.d | 3 ++ tests/unit/MacroTranslTests.d | 34 +++++++++++++++ 4 files changed, 109 insertions(+), 4 deletions(-) diff --git a/dstep/translator/MacroDefinition.d b/dstep/translator/MacroDefinition.d index 1775c982..b943983f 100644 --- a/dstep/translator/MacroDefinition.d +++ b/dstep/translator/MacroDefinition.d @@ -343,6 +343,9 @@ string translate(Expression expression, ExpressionContext context) translateBinaryOperator!OrExpr, translateBinaryOperator!LogicalAndExpr, translateBinaryOperator!LogicalOrExpr, + translateBinaryOperator!AndAssignExpr, + translateBinaryOperator!XorAssignExpr, + translateBinaryOperator!OrAssignExpr, delegate string(CondExpr condExpr) { return format( @@ -403,6 +406,9 @@ void guessParamTypes(Expression expression, ref ExprType[string] params, ExprTyp guessBinaryOperator!OrExpr, guessBinaryOperator!LogicalAndExpr, guessBinaryOperator!LogicalOrExpr, + guessBinaryOperator!AndAssignExpr, + guessBinaryOperator!XorAssignExpr, + guessBinaryOperator!OrAssignExpr, delegate void(CondExpr condExpr) { condExpr.expr.guessParamTypes(params, type); diff --git a/dstep/translator/MacroParser.d b/dstep/translator/MacroParser.d index 8f3686e5..c4bf72a7 100644 --- a/dstep/translator/MacroParser.d +++ b/dstep/translator/MacroParser.d @@ -480,6 +480,15 @@ class LogicalAndExpr : BinaryExpr class LogicalOrExpr : BinaryExpr { } +class AndAssignExpr : BinaryExpr +{ } + +class XorAssignExpr : BinaryExpr +{ } + +class OrAssignExpr : BinaryExpr +{ } + class CondExpr { Expression expr; @@ -530,6 +539,9 @@ alias Expression = Algebraic!( OrExpr, LogicalAndExpr, LogicalOrExpr, + AndAssignExpr, + XorAssignExpr, + OrAssignExpr, CondExpr); Expression debraced(Expression expression) @@ -1032,6 +1044,56 @@ Expression parseCondExpr(ref Token[] tokens, Cursor[string] table, bool defined) return expr; } +Expression parseAssignExpr(ref Token[] tokens, Cursor[string] table, bool defined) +{ + auto local = tokens; + + Expression expr = parseCondExpr(local, table, defined); + + if (!expr.hasValue) + return Expression.init; + + string op; + + if (accept!("&=", "^=", "|=")(local, op, TokenKind.punctuation)) + { + Expression rhs = parseAssignExpr(local, table, defined); + + if (!rhs.hasValue) + return Expression.init; + + tokens = local; + + if (op == "&=") + { + auto result = new AndAssignExpr; + result.left = expr; + result.right = rhs; + result.operator = op; + return Expression(result); + } + else if (op == "^=") + { + auto result = new XorAssignExpr; + result.left = expr; + result.right = rhs; + result.operator = op; + return Expression(result); + } + else + { + auto result = new OrAssignExpr; + result.left = expr; + result.right = rhs; + result.operator = op; + return Expression(result); + } + } + + tokens = local; + return expr; +} + bool parseBasicSpecifier(ref Token[] tokens, ref string spelling, Cursor[string] table) { import std.meta : AliasSeq; @@ -1425,10 +1487,10 @@ Expression parseExpr(ref Token[] tokens, Cursor[string] table, bool defined) if (concatExpr.hasValue) return concatExpr; - auto condExpr = parseCondExpr(tokens, table, defined); + auto assignExpr = parseAssignExpr(tokens, table, defined); - if (condExpr.hasValue) - return condExpr; + if (assignExpr.hasValue) + return assignExpr; return Expression.init; } @@ -1437,7 +1499,7 @@ Expression parseExpr(ref Token[] tokens, bool defined) { Cursor[string] table; - return parseCondExpr(tokens, table, defined); + return parseAssignExpr(tokens, table, defined); } Expression parseEnumMember(Token[] tokens, Cursor[string] table) diff --git a/dstep/translator/TypeInference.d b/dstep/translator/TypeInference.d index 65934f16..e0efb559 100644 --- a/dstep/translator/TypeInference.d +++ b/dstep/translator/TypeInference.d @@ -229,6 +229,9 @@ InferredType inferExpressionType(Expression expression) inferBinaryOperator!OrExpr, inferBinaryOperator!LogicalAndExpr, inferBinaryOperator!LogicalOrExpr, + inferBinaryOperator!AndAssignExpr, + inferBinaryOperator!XorAssignExpr, + inferBinaryOperator!OrAssignExpr, delegate InferredType(CondExpr condExpr) { return commonType( diff --git a/tests/unit/MacroTranslTests.d b/tests/unit/MacroTranslTests.d index 6a7cb3c3..3026b72b 100644 --- a/tests/unit/MacroTranslTests.d +++ b/tests/unit/MacroTranslTests.d @@ -841,3 +841,37 @@ extern (D) auto fun(T)(auto ref T a) } D"); } + +// Translate compound assignment operators &=, ^=, |=. +unittest +{ + assertTME("#define FOO(a, b) a &= b", "a &= b"); + assertTME("#define FOO(a, b) a ^= b", "a ^= b"); + assertTME("#define FOO(a, b) a |= b", "a |= b"); +} + +// Compound assignment operators are right-associative. +unittest +{ + assertTME("#define FOO(a, b, c) a &= b |= c", "a &= b |= c"); + assertTME("#define FOO(a, b, c) a ^= b &= c", "a ^= b &= c"); + assertTME("#define FOO(a, b, c) a |= b ^= c", "a |= b ^= c"); + assertTME("#define FOO(a, b, c, d) a &= b |= c ^= d", "a &= b |= c ^= d"); +} + +// Compound assignment has lower precedence than conditional ?:. +unittest +{ + assertTME("#define FOO(a, b, c, d) a &= b ? c : d", "a &= b ? c : d"); + assertTME("#define FOO(a, b, c, d) a ^= b ? c : d", "a ^= b ? c : d"); + assertTME("#define FOO(a, b, c, d) a |= b ? c : d", "a |= b ? c : d"); +} + +// Compound assignment mixed with other operators. +unittest +{ + assertTME("#define FOO(a, b, c) a &= b & !c", "a &= b & !c"); + assertTME("#define FOO(a, b, c) a ^= ~b + c", "a ^= ~b + c"); + assertTME("#define FOO(a, b, c, d) d &= a |= b && c", "d &= a |= b && c"); + assertTME("#define FOO(a, b, c, d) d ^= a ? ~c % d : c * ~d ^ -(b ? c & d : d) | c", "d ^= a ? ~c % d : c * ~d ^ -(b ? c & d : d) | c"); +}