Bug fixes, tests improved

This commit is contained in:
Matthias Koefferlein 2026-02-27 23:35:15 +01:00
parent 6e161d938f
commit a0d7af3d70
11 changed files with 149 additions and 102 deletions

View File

@ -1928,9 +1928,9 @@ TEST(205_extended_props)
TempPropertiesRepository temp_pr;
db::SaveLayoutOptions options;
EXPECT_EQ (options.get_option_by_name ("extended_features").to_bool (), true);
options.set_option_by_name ("extended_features", false);
EXPECT_EQ (options.get_option_by_name ("extended_features").to_bool (), false);
EXPECT_EQ (options.get_option_by_name ("gds2_extended_features").to_bool (), true);
options.set_option_by_name ("gds2_extended_features", false);
EXPECT_EQ (options.get_option_by_name ("gds2_extended_features").to_bool (), false);
db::PropertiesSet ps1;
ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5));

View File

@ -3722,12 +3722,96 @@ get_variable_name (tl::Extractor &ex, std::string &name)
}
}
bool
Eval::read_number (ExpressionParserContext &ex, tl::Variant &t)
{
ExpressionParserContext exl = ex, exd = ex, ex1 = ex;
long long l = 0;
double d = 0.0;
bool is_long = exl.try_read (l);
bool is_double = exd.try_read (d);
if (! is_long && ! is_double) {
return false;
} else if ((is_long && ! is_double) || (is_long && is_double && exl.get () >= exd.get ())) {
ex = exl;
if (l <= (long long) std::numeric_limits<long>::max () && l >= (long long) std::numeric_limits<long>::min ()) {
t = tl::Variant (long (l));
} else {
t = tl::Variant (l);
}
} else {
ex = exd;
t = tl::Variant (d);
}
double f = 0.0;
double dbu = ctx_handler () ? ctx_handler ()->dbu () : 1.0;
if (ex.test ("um2") || ex.test("micron2") || ex.test ("mic2")) {
f = 1.0 / (dbu * dbu);
} else if (ex.test ("nm2")) {
f = 1e-6 / (dbu * dbu);
} else if (ex.test ("mm2")) {
f = 1e6 / (dbu * dbu);
} else if (ex.test ("m2")) {
f = 1e12 / (dbu * dbu);
} else if (ex.test ("bs")) {
f = 0.005 / dbu;
} else if (ex.test ("nm")) {
f = 1e-3 / dbu;
} else if (ex.test ("um") || ex.test("micron") || ex.test ("mic")) {
f = 1.0 / dbu;
} else if (ex.test ("mm")) {
f = 1e3 / dbu;
} else if (ex.test ("m")) {
f = 1e6 / dbu;
}
// DBU conversion
if (f != 0.0) {
if (! ctx_handler ()) {
if (m_sloppy) {
t = tl::Variant ();
} else {
throw EvalError (tl::to_string (tr ("Length or area value with unit requires a layout context")), ex1);
}
} else {
if (m_sloppy) {
t = tl::Variant (t.to_double () * f);
} else {
double gg = t.to_double ();
double g = floor (0.5 + t.to_double ());
if (fabs (g) < 1e12 && fabs (g - gg) > 1e-3) {
throw EvalError (tl::to_string (tr ("Value is not a multiple of the database unit")), ex1);
}
}
}
}
return true;
}
void
Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr<ExpressionNode> &n, int am)
{
double g = 0.0;
int match_group = 0;
std::string t;
tl::Variant g;
ExpressionParserContext ex1 = ex;
if (ex.test ("(")) {
@ -3881,97 +3965,9 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr<ExpressionNode>
n.reset (new ConstantExpressionNode (ex1, tl::Variant (x)));
} else if (ex.try_read (g)) {
} else if (read_number (ex, g)) {
bool dbu_units = false;
if (ex.test ("um2") || ex.test("micron2") || ex.test ("mic2")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1.0 / (ctx_handler ()->dbu () * ctx_handler ()->dbu ());
}
} else if (ex.test ("nm2")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e-6 / (ctx_handler ()->dbu () * ctx_handler ()->dbu ());
}
} else if (ex.test ("mm2")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e6 / (ctx_handler ()->dbu () * ctx_handler ()->dbu ());
}
} else if (ex.test ("m2")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e12 / (ctx_handler ()->dbu () * ctx_handler ()->dbu ());
}
} else if (ex.test ("bs")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 0.005 / ctx_handler ()->dbu ();
}
} else if (ex.test ("nm")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e-3 / ctx_handler ()->dbu ();
}
} else if (ex.test ("um") || ex.test("micron") || ex.test ("mic")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1.0 / ctx_handler ()->dbu ();
}
} else if (ex.test ("mm")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e3 / ctx_handler ()->dbu ();
}
} else if (ex.test ("m")) {
dbu_units = true;
if (ctx_handler ()) {
g *= 1e6 / ctx_handler ()->dbu ();
}
}
if (m_sloppy) {
if (dbu_units && ! ctx_handler ()) {
n.reset (new ConstantExpressionNode (ex1, tl::Variant ()));
} else {
n.reset (new ConstantExpressionNode (ex1, tl::Variant (g)));
}
} else {
if (dbu_units && ! ctx_handler ()) {
throw EvalError (tl::to_string (tr ("Length or area value with unit requires a layout context")), ex1);
}
if (dbu_units) {
// round to integers and check whether that is possible
double gg = g;
g = floor (0.5 + g);
if (fabs (g) < 1e12 && fabs (g - gg) > 1e-3) {
throw EvalError (tl::to_string (tr ("Value is not a multiple of the database unit")), ex1);
}
n.reset (new ConstantExpressionNode (ex1, tl::Variant (g)));
} else {
tl::Variant v (g);
// prefer integers
if (g - floor (g) == 0.0 && v.can_convert_to_long ()) {
n.reset (new ConstantExpressionNode (ex1, tl::Variant (v.to_long ())));
} else {
n.reset (new ConstantExpressionNode (ex1, v));
}
}
}
n.reset (new ConstantExpressionNode (ex1, g));
} else if (ex.try_read_quoted (t)) {

View File

@ -616,6 +616,7 @@ private:
void eval_unary (ExpressionParserContext &context, std::unique_ptr<ExpressionNode> &v);
void eval_atomic (ExpressionParserContext &context, std::unique_ptr<ExpressionNode> &v, int am);
void eval_suffix (ExpressionParserContext &context, std::unique_ptr<ExpressionNode> &v);
bool read_number (ExpressionParserContext &ex, tl::Variant &t);
static Eval m_global;
};

View File

@ -60,6 +60,8 @@ TEST(1)
v = e.parse ("7%4").execute ();
EXPECT_EQ (v.to_string (), std::string ("3"));
v = e.parse ("2+3/2").execute ();
EXPECT_EQ (v.to_string (), std::string ("3"));
v = e.parse ("2+3/2.0").execute ();
EXPECT_EQ (v.to_string (), std::string ("3.5"));
v = e.parse ("to_i(1)*to_i(2)").execute ();
@ -650,13 +652,57 @@ TEST(2)
EXPECT_EQ (v.to_string (), std::string ("2"));
}
// to_double
// numerical constants
TEST(3)
{
tl::Eval e;
tl::Variant v;
v = e.parse ("[1,2,3]/2").execute ();
v = e.parse ("2.5+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##3.5"));
v = e.parse ("-2.5+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##-1.5"));
v = e.parse ("2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("#3"));
v = e.parse ("-2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("#-1"));
v = e.parse ("2.0+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##3"));
v = e.parse ("-2.0+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##-1"));
v = e.parse ("2.+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##3"));
v = e.parse ("-2.+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##-1"));
v = e.parse ("2.e1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##21"));
v = e.parse ("-2.e1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##-19"));
v = e.parse ("2e-1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##1.2"));
v = e.parse ("-2e-1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##0.8"));
v = e.parse (".2e+1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##3"));
v = e.parse ("-.2e+1+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##-1"));
v = e.parse (".2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##1.2"));
v = e.parse ("-.2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##0.8"));
v = e.parse ("0.2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##1.2"));
v = e.parse ("-0.2+1 ").execute ();
EXPECT_EQ (v.to_parsable_string (), std::string ("##0.8"));
}
// to_double
TEST(4)
{
tl::Eval e;
tl::Variant v;
v = e.parse ("[1,2,3]/2.0").execute ();
EXPECT_EQ (v.to_string (), std::string ("1.5"));
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1146,8 +1146,8 @@ class DBLayoutTest(unittest.TestCase):
self.assertEqual(shape.property("2"), None)
self.assertEqual(shape.property(2.0), None)
self.assertEqual(shape.property(22), None)
self.assertEqual(shape.property(42), "the answer")
self.assertEqual(shape.property("42"), None)
self.assertEqual(shape.property("42"), "the answer")
self.assertEqual(shape.property(42), None)
self.assertEqual(shape.property(42.0), None)
ly2 = pya.Layout()

View File

@ -40,12 +40,12 @@ class TLTest(unittest.TestCase):
self.assertEqual(str(type(res)).replace("class", "type"), "<type 'NoneType'>")
self.assertEqual(repr(res), "None")
expr = pya.Expression.eval("1+2")
expr = pya.Expression.eval("1+2.0")
self.assertEqual(str(type(expr)).replace("class", "type"), "<type 'float'>")
self.assertEqual(repr(expr), "3.0")
expr = pya.Expression()
expr.text = "1+2"
expr.text = "1+2.0"
res = expr.eval()
self.assertEqual(str(type(res)).replace("class", "type"), "<type 'float'>")
self.assertEqual(str(res), "3.0")

View File

@ -40,14 +40,18 @@ class Tl_TestClass < TestBase
assert_equal(res.to_s, "")
expr = RBA::Expression.eval("1+2")
assert_equal(expr.class.to_s, "Integer")
assert_equal(expr.to_s, "3")
expr = RBA::Expression.eval("1+2.0")
assert_equal(expr.class.to_s, "Float")
assert_equal(expr.to_s, "3.0")
expr = RBA::Expression::new
expr.text = "1+2"
res = expr.eval
assert_equal(res.class.to_s, "Float")
assert_equal(res.to_s, "3.0")
assert_equal(res.class.to_s, "Integer")
assert_equal(res.to_s, "3")
expr = RBA::Expression::new
expr.var("a", 5)