From 12071e7300b929ac8695854d250f904753a26532 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Jun 2026 17:33:25 +0200 Subject: [PATCH] Change in expression parser: nil is now propagated through expressions as 'x+nil'=x, 'nil+x'=nil. This is useful for evaluating device parameter expressions. --- src/tl/tl/tlExpression.cc | 44 +++++++++++++++++++------- src/tl/unit_tests/tlExpressionTests.cc | 38 +++++++++++++++++++++- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 5328616eb..f7fb174e5 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -1075,7 +1075,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1131,7 +1133,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1187,7 +1191,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1249,7 +1255,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1309,7 +1317,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1399,7 +1409,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1555,7 +1567,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1611,7 +1625,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1667,7 +1683,9 @@ public: m_c[0]->execute (v); m_c[1]->execute (b); - if (v->is_user ()) { + if (v->is_nil () || b->is_nil ()) { + // don't change + } else if (v->is_user ()) { const EvalClass *c = v->user_cls () ? v->user_cls ()->eval_cls () : 0; if (! c) { @@ -1802,7 +1820,9 @@ public: { m_c[0]->execute (v); - if (v->is_user ()) { + if (v->is_nil ()) { + // don't change + } else if (v->is_user ()) { throw EvalError (tl::to_string (tr ("Unary minus not implemented for objects")), m_context); /* @@ -1859,7 +1879,9 @@ public: { m_c[0]->execute (v); - if (v->is_user ()) { + if (v->is_nil ()) { + // don't change + } else if (v->is_user ()) { throw EvalError (tl::to_string (tr ("Unary tilde not implemented for objects")), m_context); /* diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 98e64a69c..29f50b11f 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -39,6 +39,14 @@ TEST(1) EXPECT_EQ (v.to_string (), std::string ("1")); v = e.parse ("1+2").execute (); EXPECT_EQ (v.to_string (), std::string ("3")); + v = e.parse ("nil+2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("2+nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("2")); + v = e.parse ("nil-2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("2-nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("2")); v = e.parse ("1.2e3").execute (); EXPECT_EQ (v.to_string (), std::string ("1200")); v = e.parse ("-0.25e-2").execute (); @@ -49,6 +57,14 @@ TEST(1) EXPECT_EQ (v.to_string (), std::string ("4097")); v = e.parse ("0x1").execute (); EXPECT_EQ (v.to_string (), std::string ("1")); + v = e.parse ("nil*2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("2*nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("2")); + v = e.parse ("nil/2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("2/nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("2")); v = e.parse ("1-2+3").execute (); EXPECT_EQ (v.to_string (), std::string ("2")); v = e.parse ("1-4*2+3").execute (); @@ -972,8 +988,16 @@ TEST(9) v = e.parse ("1<<2+3").execute (); EXPECT_EQ (v.to_string (), std::string ("32")); + v = e.parse ("nil<<2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("1<>2+3").execute (); EXPECT_EQ (v.to_string (), std::string ("2")); + v = e.parse ("nil>>2").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("1>>nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("1")); } // bitwise ops @@ -986,8 +1010,16 @@ TEST(10) EXPECT_EQ (v.to_string (), std::string ("20")); v = e.parse ("31&63").execute (); EXPECT_EQ (v.to_string (), std::string ("31")); + v = e.parse ("nil&63").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("31&nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("31")); v = e.parse ("31^63").execute (); EXPECT_EQ (v.to_string (), std::string ("32")); + v = e.parse ("nil^63").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); + v = e.parse ("31^nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("31")); } // unary ops @@ -1000,8 +1032,12 @@ TEST(11) EXPECT_EQ (v.to_string (), std::string ("2")); v = e.parse ("~1").execute (); EXPECT_EQ (v.to_string (), std::string ("-2")); + v = e.parse ("~nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); v = e.parse ("-1").execute (); EXPECT_EQ (v.to_string (), std::string ("-1")); + v = e.parse ("-nil").execute (); + EXPECT_EQ (v.to_string (), std::string ("nil")); v = e.parse ("--1").execute (); EXPECT_EQ (v.to_string (), std::string ("1")); } @@ -1159,7 +1195,7 @@ TEST(16) v = e.parse ("'abc' ~ '(*)a(*)'").execute (); EXPECT_EQ (v.to_string (), std::string ("true")); v = e.parse ("$1+'.'+$2+'.'+$3").execute (); - EXPECT_EQ (v.to_string (), std::string (".bc.nil")); + EXPECT_EQ (v.to_string (), std::string (".bc.")); v = e.parse ("'abc' ~ 'b*'").execute (); EXPECT_EQ (v.to_string (), std::string ("false")); v = e.parse ("'abc' !~ '*a*'").execute ();