diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc index 8d9ccf24f6..4c894d8f11 100644 --- a/src/buddies/src/bd/bdWriterOptions.cc +++ b/src/buddies/src/bd/bdWriterOptions.cc @@ -66,6 +66,7 @@ GenericWriterOptions::init_from_options (const db::SaveLayoutOptions &save_optio m_gds2_write_file_properties = save_options.get_option_by_name ("gds2_write_file_properties").to_bool (); tl::Variant def_text_size = save_options.get_option_by_name ("gds2_default_text_size"); m_gds2_default_text_size = def_text_size.is_nil () ? -1.0 : def_text_size.to_double (); + m_gds2_extended_features = save_options.get_option_by_name ("gds2_extended_features").to_bool (); m_oasis_compression_level = save_options.get_option_by_name ("oasis_compression_level").to_int (); m_oasis_write_cblocks = save_options.get_option_by_name ("oasis_write_cblocks").to_bool (); @@ -245,6 +246,14 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin "This option enables a GDS2 extension that allows writing of file properties to GDS2 files. " "Consumers that don't support this feature, may not be able to read such a GDS2 files." ) + << tl::arg (group + + "!#--no-extended-features", &m_gds2_extended_features, "Disables extended GDS2 features", + "This option disables extended GDS2 features. Extended GDS features allow writing file and cell level " + "properties without 'write-cell-properties' or 'write-file-properties', store layer names and allow " + "string names for properties and complex property values such as very long strings or lists.\n" + "\n" + "Extended features rely on the context, so they are not available with 'no-context-info'." + ) << tl::arg (group + "#--default-text-size", &m_gds2_default_text_size, "Default text size", "This text size (given in micrometers) is applied to text objects not coming with their " @@ -474,6 +483,7 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db:: save_options.set_option_by_name ("gds2_write_timestamps", m_gds2_write_timestamps); save_options.set_option_by_name ("gds2_write_cell_properties", m_gds2_write_cell_properties); save_options.set_option_by_name ("gds2_write_file_properties", m_gds2_write_file_properties); + save_options.set_option_by_name ("gds2_extended_features", m_gds2_extended_features); save_options.set_option_by_name ("gds2_default_text_size", m_gds2_default_text_size < 0.0 ? tl::Variant () : tl::Variant (m_gds2_default_text_size)); save_options.set_option_by_name ("oasis_compression_level", m_oasis_compression_level); diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h index b0831044a3..25bd6d9223 100644 --- a/src/buddies/src/bd/bdWriterOptions.h +++ b/src/buddies/src/bd/bdWriterOptions.h @@ -139,6 +139,7 @@ class BD_PUBLIC GenericWriterOptions bool m_gds2_write_cell_properties; bool m_gds2_write_file_properties; double m_gds2_default_text_size; + bool m_gds2_extended_features; int m_oasis_compression_level; bool m_oasis_write_cblocks; diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index d6023d1598..28dea23dbd 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -1353,7 +1353,7 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti enough (tl::info) << "" << tl::noendl; } if (ci.properties_id () != 0) { - enough (tl::info) << " [" << ci.properties_id () << "]"; + enough (tl::info) << " " << db::properties (ci.properties_id ()).to_dict_var ().to_string (); } else { enough (tl::info) << ""; } @@ -1374,7 +1374,7 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti enough (tl::info) << "" << tl::noendl; } if (ci.properties_id () != 0) { - enough (tl::info) << " [" << ci.properties_id () << "]"; + enough (tl::info) << " " << db::properties (ci.properties_id ()).to_dict_var ().to_string (); } else { enough (tl::info) << ""; } diff --git a/src/db/db/dbReader.h b/src/db/db/dbReader.h index 374aecdf39..f01189ee87 100644 --- a/src/db/db/dbReader.h +++ b/src/db/db/dbReader.h @@ -91,6 +91,30 @@ class DB_PUBLIC ReaderUnknownFormatException DB_PUBLIC void join_layer_names (std::string &s, const std::string &n); +/** + * @brief A helper class to join two datatype layer name map members + */ +struct LNameJoinOp1 +{ + void operator() (std::string &a, const std::string &b) + { + join_layer_names (a, b); + } +}; + +/** + * @brief A helper class to join two layer map members + * This implementation basically merged the datatype maps. + */ +struct LNameJoinOp2 +{ + void operator() (tl::interval_map &a, const tl::interval_map &b) + { + LNameJoinOp1 op1; + a.add (b.begin (), b.end (), op1); + } +}; + /** * @brief The generic reader base class */ diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index 61d16c46c1..09d7908eb4 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1470,8 +1470,9 @@ normalize (const QString &s) tl::Variant v; - if (*c == '#' || *c == '\"' || *c == '\'') { - tl::Extractor ex (c); + tl::Extractor ex (c); + ex.skip (); + if (*ex == '#' || *ex == '\"' || *ex == '\'' || *ex == '(' || *ex == '{' || *ex == '[') { ex.read (v); ex.expect_end (); } else { diff --git a/src/layui/layui/syntax/ur_text.xml b/src/layui/layui/syntax/ur_text.xml index 5858a407f1..ecd4e6a4b1 100644 --- a/src/layui/layui/syntax/ur_text.xml +++ b/src/layui/layui/syntax/ur_text.xml @@ -7,67 +7,58 @@ - + - - + + + + + + - - + + - + - - - - - - - - - - - - - - - - + + + + - - + + + + + - + + + + + + - + - + - + - - - - - - - + - - - - - + + diff --git a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc index 4cb19a4039..7f7273f6d1 100644 --- a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc +++ b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc @@ -238,22 +238,22 @@ static void run_test2 (tl::TestBase *_this, const std::string &base, db::Layout TEST(1a) { - run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1a_au.gds.gz"); + run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1a_au2.gds.gz"); } TEST(1b) { - run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01); + run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au2.gds.gz", 0, 0.01); } TEST(1c) { - run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, true); + run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au2.gds.gz", 0, 0.01, true); } TEST(1d) { - run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au.gds.gz", 0, 0.01, false, true); + run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1b_au2.gds.gz", 0, 0.01, false, true); } TEST(2) @@ -263,17 +263,17 @@ TEST(2) TEST(3a) { - run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3a_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26"); + run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3a_au2.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26"); } TEST(3b) { - run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3b_au.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26", 0.00012); + run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3b_au2.gds.gz", "CAA:43,CCA:48,CCP:47,CMF:49,CMS:51,CPG:46,CSN:45,CSP:44,CVA:50,CWN:42,XP:26", 0.00012); } TEST(3c) { - run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3c_au.gds.gz", "(CPG:1/0) +(CPG:1000/0) (CCP:1/0) (CMF:2/0) +(CMF:1000/0) (CVA:3/0)", 0.00012); + run_test (_this, tl::testdata_private (), "t3.cif.gz", "t3c_au2.gds.gz", "(CPG:1/0) +(CPG:1000/0) (CCP:1/0) (CMF:2/0) +(CMF:1000/0) (CVA:3/0)", 0.00012); } TEST(4) diff --git a/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc b/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc index 0ecfa28272..c7aea4f610 100644 --- a/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc +++ b/src/plugins/streamers/dxf/unit_tests/dbDXFReaderTests.cc @@ -117,7 +117,7 @@ TEST(4) db::DXFReaderOptions opt; opt.layer_map = string2lm ("Metal:1,Metal2:5"); opt.create_other_layers = true; - run_test (_this, "t4.dxf.gz", "t4_au.gds.gz", opt); + run_test (_this, "t4.dxf.gz", "t4_au_2.gds.gz", opt); } TEST(5) @@ -127,7 +127,7 @@ TEST(5) TEST(6) { - run_test (_this, "t6.dxf.gz", "t6_au.gds.gz"); + run_test (_this, "t6.dxf.gz", "t6_au_2.gds.gz"); } TEST(7) @@ -140,7 +140,7 @@ TEST(8) db::DXFReaderOptions opt; opt.layer_map = string2lm ("Metal:4,Kommentare:3,Bemassung:2"); opt.create_other_layers = true; - run_test (_this, "t8.dxf.gz", "t8_au.gds.gz", opt); + run_test (_this, "t8.dxf.gz", "t8_au_2.gds.gz", opt); } TEST(9) @@ -148,7 +148,7 @@ TEST(9) db::DXFReaderOptions opt; opt.layer_map = string2lm ("Bemassung:2,Metal:5,Kommentare:4"); opt.create_other_layers = true; - run_test (_this, "t9.dxf.gz", "t9_au.gds.gz", opt); + run_test (_this, "t9.dxf.gz", "t9_au_2.gds.gz", opt); } TEST(10) @@ -156,7 +156,7 @@ TEST(10) db::DXFReaderOptions opt; opt.layer_map = string2lm ("METAL:1,KOMMENTARE:4"); opt.create_other_layers = true; - run_test (_this, "t10.dxf.gz", "t10_au.gds.gz", opt); + run_test (_this, "t10.dxf.gz", "t10_au_2.gds.gz", opt); } TEST(11) @@ -166,7 +166,7 @@ TEST(11) TEST(12) { - run_test (_this, "t12.dxf.gz", "t12_au.gds.gz"); + run_test (_this, "t12.dxf.gz", "t12_au_2.gds.gz"); } TEST(14) @@ -174,7 +174,7 @@ TEST(14) db::DXFReaderOptions opt; opt.layer_map = string2lm ("'A11-STRUKTUR__E_TYP_':10,A21_NITRID:11,'B11-KONTAKT':9,'B11-STRUKTUR':3,HELLFELD:7,MASKE:5,NORM_MIN_MAX_WAFER:6,RASTER:2,_BEGRENZUNG_A11_A21_A31_B1:8"); opt.create_other_layers = true; - run_test (_this, "t14.dxf.gz", "t14_au.gds.gz", opt); + run_test (_this, "t14.dxf.gz", "t14_au_2.gds.gz", opt); } TEST(15) @@ -182,7 +182,7 @@ TEST(15) db::DXFReaderOptions opt; opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); opt.create_other_layers = true; - run_test (_this, "t15.dxf.gz", "t15_au2_2.gds.gz", opt); + run_test (_this, "t15.dxf.gz", "t15_au2_3.gds.gz", opt); } TEST(16) @@ -190,7 +190,7 @@ TEST(16) db::DXFReaderOptions opt; opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); opt.create_other_layers = true; - run_test (_this, "t16.dxf.gz", "t16_au2_2.gds.gz", opt); + run_test (_this, "t16.dxf.gz", "t16_au2_3.gds.gz", opt); } TEST(17) @@ -198,32 +198,32 @@ TEST(17) db::DXFReaderOptions opt; opt.layer_map = string2lm ("TEXT:4,IGBT:5,Wire:7,Ceramic:11,LAYER_1:14,Diode:18,'DBC TOP Plate':19,'Terminal Position':20"); opt.create_other_layers = true; - run_test (_this, "t17.dxf.gz", "t17_au2_2.gds.gz", opt); + run_test (_this, "t17.dxf.gz", "t17_au2_3.gds.gz", opt); } TEST(18) { - run_test (_this, "t18.dxf.gz", "t18_au.gds.gz"); + run_test (_this, "t18.dxf.gz", "t18_au_2.gds.gz"); } TEST(19) { - run_test (_this, "t19.dxf.gz", "t19_au.gds.gz"); + run_test (_this, "t19.dxf.gz", "t19_au_2.gds.gz"); } TEST(20) { - run_test (_this, "t20.dxf.gz", "t20_au.gds.gz"); + run_test (_this, "t20.dxf.gz", "t20_au_2.gds.gz"); } TEST(21) { - run_test (_this, "t21.dxf.gz", "t21_au.gds.gz"); + run_test (_this, "t21.dxf.gz", "t21_au_2.gds.gz"); } TEST(22) { - run_test (_this, "t22.dxf.gz", "t22_au.gds.gz"); + run_test (_this, "t22.dxf.gz", "t22_au_2.gds.gz"); } TEST(23a) @@ -233,7 +233,7 @@ TEST(23a) opt.unit = 1; opt.polyline_mode = 0; opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23a_au.gds.gz", opt); + run_test (_this, "t23.dxf.gz", "t23a_au_2.gds.gz", opt); } TEST(23b) @@ -243,7 +243,7 @@ TEST(23b) opt.unit = 1; opt.polyline_mode = 1; opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23b_au.gds.gz", opt); + run_test (_this, "t23.dxf.gz", "t23b_au_2.gds.gz", opt); } TEST(23c) @@ -253,7 +253,7 @@ TEST(23c) opt.unit = 1; opt.polyline_mode = 2; opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23c_au.gds.gz", opt); + run_test (_this, "t23.dxf.gz", "t23c_au_2.gds.gz", opt); } TEST(23d) @@ -263,7 +263,7 @@ TEST(23d) opt.unit = 1; opt.polyline_mode = 3; opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23d_au.gds.gz", opt); + run_test (_this, "t23.dxf.gz", "t23d_au_2.gds.gz", opt); } TEST(23e) @@ -273,7 +273,7 @@ TEST(23e) opt.unit = 1; opt.polyline_mode = 4; opt.circle_points = 10; - run_test (_this, "t23.dxf.gz", "t23e_au.gds.gz", opt); + run_test (_this, "t23.dxf.gz", "t23e_au_2.gds.gz", opt); } TEST(26a) @@ -283,7 +283,7 @@ TEST(26a) opt.unit = 1; opt.polyline_mode = 0; opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26a_au.gds.gz", opt); + run_test (_this, "t26.dxf.gz", "t26a_au_2.gds.gz", opt); } TEST(26b) @@ -293,7 +293,7 @@ TEST(26b) opt.unit = 1; opt.polyline_mode = 1; opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26b_au.gds.gz", opt); + run_test (_this, "t26.dxf.gz", "t26b_au_2.gds.gz", opt); } TEST(26c) @@ -303,7 +303,7 @@ TEST(26c) opt.unit = 1; opt.polyline_mode = 2; opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26c_au.gds.gz", opt); + run_test (_this, "t26.dxf.gz", "t26c_au_2.gds.gz", opt); } TEST(26d) @@ -313,7 +313,7 @@ TEST(26d) opt.unit = 1; opt.polyline_mode = 3; opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26d_au.gds.gz", opt); + run_test (_this, "t26.dxf.gz", "t26d_au_2.gds.gz", opt); } TEST(26e) @@ -323,7 +323,7 @@ TEST(26e) opt.unit = 1; opt.polyline_mode = 4; opt.circle_points = 100; - run_test (_this, "t26.dxf.gz", "t26e_au.gds.gz", opt); + run_test (_this, "t26.dxf.gz", "t26e_au_2.gds.gz", opt); } TEST(27a) @@ -438,7 +438,7 @@ TEST(30) opt.polyline_mode = 4; opt.circle_points = 1000; opt.circle_accuracy = 0.001; - run_test (_this, "t30.dxf.gz", "t30d_au.gds.gz", opt); + run_test (_this, "t30.dxf.gz", "t30d_au_2.gds.gz", opt); } // accuracy @@ -449,16 +449,16 @@ TEST(31) opt.unit = 1000; opt.contour_accuracy = 0; - run_test (_this, "t31.dxf.gz", "t31a_au.gds.gz", opt); + run_test (_this, "t31.dxf.gz", "t31a_au_2.gds.gz", opt); opt.contour_accuracy = 0.005; - run_test (_this, "t31.dxf.gz", "t31b_au.gds.gz", opt); + run_test (_this, "t31.dxf.gz", "t31b_au_2.gds.gz", opt); opt.contour_accuracy = 0.01; - run_test (_this, "t31.dxf.gz", "t31c_au.gds.gz", opt); + run_test (_this, "t31.dxf.gz", "t31c_au_2.gds.gz", opt); opt.contour_accuracy = 0.02; - run_test (_this, "t31.dxf.gz", "t31d_au.gds.gz", opt); + run_test (_this, "t31.dxf.gz", "t31d_au_2.gds.gz", opt); } // issue #198 @@ -491,20 +491,20 @@ TEST(33) db::DXFReaderOptions opt; opt.polyline_mode = 3; - run_test (_this, "t33.dxf.gz", "t33a_au.gds.gz", opt); + run_test (_this, "t33.dxf.gz", "t33a_au_2.gds.gz", opt); opt.circle_accuracy = 1.0; - run_test (_this, "t33.dxf.gz", "t33b_au.gds.gz", opt); + run_test (_this, "t33.dxf.gz", "t33b_au_2.gds.gz", opt); opt.circle_accuracy = 50.0; - run_test (_this, "t33.dxf.gz", "t33c_au.gds.gz", opt); + run_test (_this, "t33.dxf.gz", "t33c_au_2.gds.gz", opt); opt.circle_accuracy = 0.0; opt.polyline_mode = 4; - run_test (_this, "t33.dxf.gz", "t33d_au.gds.gz", opt); + run_test (_this, "t33.dxf.gz", "t33d_au_2.gds.gz", opt); opt.polyline_mode = 2; - run_test (_this, "t33.dxf.gz", "t33e_au.gds.gz", opt); + run_test (_this, "t33.dxf.gz", "t33e_au_2.gds.gz", opt); } // issue #1173 diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h index cd6c7f769e..c713184c33 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2Format.h @@ -115,6 +115,7 @@ class DB_PLUGIN_PUBLIC GDS2WriterOptions write_timestamps (true), write_cell_properties (false), write_file_properties (false), + extended_features (true), default_text_size (-1.0) { // .. nothing yet .. @@ -184,6 +185,22 @@ class DB_PLUGIN_PUBLIC GDS2WriterOptions */ bool write_file_properties; + /** + * @brief Write extended features + * + * Extended features are: + * - non-numerical property names + * - complex property values + * - file and cell properties without "write_cell_properties" and "write_file_properties" + * - layer names + * + * These extended features require a context cell to be created (unless + * needed for other reasons). Hence this flag is not compatible with + * "write_context_info = false". On the plus side, GDS files written with extended + * features are backward compatible. + */ + bool extended_features; + /** * @brief The default text size if none is given (in fact, if the text size is zero) * diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index a5fba5fbae..fd14848b7e 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -112,7 +112,7 @@ GDS2ReaderBase::finish_element_with_props () const char *value = get_string (); if (m_read_properties) { - properties.insert (tl::Variant (attr), tl::Variant (value)); + properties.insert (map_property_name (attr), map_property_value (value)); any = true; } @@ -135,6 +135,80 @@ GDS2ReaderBase::finish_element_with_props () } +tl::Variant +GDS2ReaderBase::map_property_name (long attr) const +{ + auto i = m_property_names_map.find (attr); + if (i != m_property_names_map.end ()) { + return i->second; + } else { + return tl::Variant (attr); + } +} + +tl::Variant +GDS2ReaderBase::map_property_value (const std::string &value) const +{ + auto i = m_property_values_map.find (value); + if (i != m_property_values_map.end ()) { + return i->second; + } else { + return tl::Variant (value); + } +} + +void +GDS2ReaderBase::digest_context (db::Layout &layout, const std::vector &context) +{ + for (auto c = context.begin (); c != context.end (); ++c) { + + long kn; + std::string kv; + tl::Variant v; + db::ld_type l = 0, dt = 0; + std::string n; + + tl::Extractor ex (c->c_str ()); + if (ex.test ("PROP_NAME") && ex.test ("(") && ex.try_read (kn) && ex.test (")") && ex.test ("=") && ex.try_read (v)) { + + m_property_names_map[kn].swap (v); + + } else if (ex.test ("PROP_VALUE") && ex.test ("(") && ex.try_read_word_or_quoted (kv) && ex.test (")") && ex.test ("=") && ex.try_read (v)) { + + m_property_values_map[kv].swap (v); + + } else if (ex.test ("LNAME") && ex.test ("(") && ex.try_read (l) && ex.test (",") && ex.try_read (dt) && ex.test (")") && ex.test ("=") && ex.try_read_word_or_quoted (n)) { + + // add to the layer name map + tl::interval_map dt_map; + LNameJoinOp1 op1; + dt_map.add (dt, dt + 1, n, op1); + LNameJoinOp2 op2; + layer_names ().add (l, l + 1, dt_map, op2); + + // force a layer entry: this way we can have empty, but existing layers, just by naming them + open_dl (layout, db::LDPair (l, dt)); + + } + + } +} + +void +GDS2ReaderBase::build_properties_from_context (const std::vector &context, db::PropertiesSet &properties) const +{ + for (auto c = context.begin (); c != context.end (); ++c) { + + tl::Variant pn, pv; + + tl::Extractor ex (c->c_str ()); + if (ex.test ("PROP") && ex.test ("(") && ex.try_read (pn) && ex.test (")") && ex.test ("=") && ex.try_read (pv)) { + properties.insert (pn, pv); + } + + } +} + inline db::Point pt_conv (const GDS2XY &p) { @@ -186,6 +260,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) long attr = 0; db::PropertiesSet layout_properties; + std::list > basic_layout_properties; // read until short rec_id = 0; @@ -221,7 +296,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) const char *value = get_string (); if (m_read_properties) { - layout_properties.insert (tl::Variant (attr), tl::Variant (value)); + basic_layout_properties.push_back (std::make_pair (attr, value)); } } else if (rec_id == sUNITS) { @@ -245,11 +320,6 @@ GDS2ReaderBase::do_read (db::Layout &layout) } while (true); - // set the layout properties - if (! layout_properties.empty ()) { - layout.prop_id (db::properties_id (layout_properties)); - } - // this container has been found to grow quite a lot. // using a list instead of a vector should make this more efficient. tl::vector instances; @@ -285,10 +355,24 @@ GDS2ReaderBase::do_read (db::Layout &layout) read_context_info_cell (); + // deserialize global context information + auto ctx = m_context_info.find (std::string ()); + if (ctx != m_context_info.end ()) { + + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + layout.fill_meta_info_from_context (ci); + + build_properties_from_context (ctx->second, layout_properties); + digest_context (layout, ctx->second); + + } + } else { db::cell_index_type cell_index = make_cell (layout, m_cellname); + db::PropertiesSet cell_properties; + bool ignore_cell = false; auto ctx = m_context_info.find (m_cellname); if (ctx != m_context_info.end ()) { @@ -303,6 +387,8 @@ GDS2ReaderBase::do_read (db::Layout &layout) layout.fill_meta_info_from_context (cell_index, ci); + build_properties_from_context (ctx->second, cell_properties); + } db::Cell *cell = 0; @@ -311,7 +397,6 @@ GDS2ReaderBase::do_read (db::Layout &layout) } long attr = 0; - db::PropertiesSet cell_properties; // read cell content while ((rec_id = get_record ()) != sENDSTR) { @@ -330,7 +415,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) const char *value = get_string (); if (m_read_properties) { - cell_properties.insert (tl::Variant (attr), tl::Variant (value)); + cell_properties.insert (map_property_name (attr), map_property_value (value)); } } else if (rec_id == sBOUNDARY) { @@ -393,14 +478,19 @@ GDS2ReaderBase::do_read (db::Layout &layout) } - // deserialize global context information - auto ctx = m_context_info.find (std::string ()); - if (ctx != m_context_info.end ()) { - LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); - layout.fill_meta_info_from_context (ci); + // creat the set the layout properties + + // NOTE: we can only merge now, as we have the property names and values maps + for (auto i = basic_layout_properties.begin (); i != basic_layout_properties.end (); ++i) { + layout_properties.insert (map_property_name (i->first), map_property_value (i->second)); + } + + if (! layout_properties.empty ()) { + layout.prop_id (db::properties_id (layout_properties)); } // check, if the last record is a ENDLIB + if (rec_id != sENDLIB) { error (tl::to_string (tr ("ENDLIB record expected"))); } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h index 2660d9d5e3..ed6d064805 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.h @@ -89,6 +89,8 @@ class DB_PLUGIN_PUBLIC GDS2ReaderBase unsigned int m_box_mode; std::map > m_context_info; std::vector m_all_points; + std::map m_property_names_map; + std::map m_property_values_map; void read_context_info_cell (); void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record); @@ -96,6 +98,10 @@ class DB_PLUGIN_PUBLIC GDS2ReaderBase void read_text (db::Layout &layout, db::Cell &cell); void read_box (db::Layout &layout, db::Cell &cell); void read_ref (db::Layout &layout, db::Cell &cell, bool array, tl::vector &instances, tl::vector &insts_wp); + tl::Variant map_property_name (long attr) const; + tl::Variant map_property_value (const std::string &value) const; + void build_properties_from_context (const std::vector &context, db::PropertiesSet &properties) const; + void digest_context (Layout &layout, const std::vector &context); std::pair finish_element_with_props (); void finish_element (); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index ce62968e29..a2ff169aca 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -209,7 +209,21 @@ GDS2WriterBase::write_context_string (size_t n, const std::string &s) } void -GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) +GDS2WriterBase::get_property_map_context (std::vector &context_strings) +{ + for (auto i = m_prop_name_placeholders.begin (); i != m_prop_name_placeholders.end (); ++i) { + context_strings.push_back (std::string ()); + context_strings.back () = "PROP_NAME(" + tl::to_string (int (i->second)) + ")=" + i->first.to_parsable_string (); + } + + for (auto i = m_prop_value_placeholders.begin (); i != m_prop_value_placeholders.end (); ++i) { + context_strings.push_back (std::string ()); + context_strings.back () = "PROP_VALUE(" + tl::to_quoted_string (i->second) + ")=" + i->first.to_parsable_string (); + } +} + +void +GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells, const std::vector > &layers, const db::GDS2WriterOptions &gds2_options) { write_record_size (4 + 12 * 2); write_record (sBGNSTR); @@ -220,7 +234,37 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, std::vector context_prop_strings; - if (layout.has_context_info ()) { + layout.get_context_info (context_prop_strings); + get_property_map_context (context_prop_strings); + + // Add file properties if needed + + if (layout.prop_id () != 0 && ! gds2_options.write_file_properties && gds2_options.extended_features) { + + const auto &props = db::properties (layout.prop_id ()); + for (auto p = props.begin (); p != props.end (); ++p) { + const tl::Variant &pn = db::property_name (p->first); + const tl::Variant &pv = db::property_value (p->second); + context_prop_strings.push_back (std::string ()); + context_prop_strings.back () = "PROP(" + pn.to_parsable_string () + ")=" + pv.to_parsable_string (); + } + + } + + // Add layer names if needed + + if (gds2_options.extended_features) { + + for (auto l = layers.begin (); l != layers.end (); ++l) { + if (! l->second.name.empty ()) { + context_prop_strings.push_back (std::string ()); + context_prop_strings.back () = "LNAME(" + tl::to_string (l->second.layer) + "," + tl::to_string (l->second.datatype) + ")=" + tl::to_quoted_string (l->second.name); + } + } + + } + + if (! context_prop_strings.empty ()) { // Use a dummy BOUNDARY element to attach the global context @@ -241,18 +285,12 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, write_int (0); } - context_prop_strings.clear (); - - if (layout.get_context_info (context_prop_strings)) { - - // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings - // will arrive) - for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - --s; - size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); - write_context_string (n, *s); - } - + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + --s; + size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); + write_context_string (n, *s); } write_record_size (4); @@ -262,7 +300,26 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - if (layout.has_context_info (*cell)) { + context_prop_strings.clear (); + layout.get_context_info (*cell, context_prop_strings); + + const db::Cell &cell_obj = layout.cell (*cell); + + // Add cell properties if needed + + if (cell_obj.prop_id () != 0 && ! gds2_options.write_cell_properties && gds2_options.extended_features) { + + const auto &props = db::properties (cell_obj.prop_id ()); + for (auto p = props.begin (); p != props.end (); ++p) { + const tl::Variant &pn = db::property_name (p->first); + const tl::Variant &pv = db::property_value (p->second); + context_prop_strings.push_back (std::string ()); + context_prop_strings.back () = "PROP(" + pn.to_parsable_string () + ")=" + pv.to_parsable_string (); + } + + } + + if (! context_prop_strings.empty ()) { write_record_size (4); write_record (sSREF); @@ -274,18 +331,12 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, write_int (0); write_int (0); - context_prop_strings.clear (); - - if (layout.get_context_info (*cell, context_prop_strings)) { - - // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings - // will arrive) - for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - --s; - size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); - write_context_string (n, *s); - } - + // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings + // will arrive) + for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { + --s; + size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); + write_context_string (n, *s); } write_record_size (4); @@ -426,6 +477,128 @@ GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std: write_record (sENDSTR); } +void +GDS2WriterBase::build_property_translations (const db::Layout &layout, const std::vector > &layers, const std::vector &cells, const db::GDS2WriterOptions &gds2_options) +{ + std::set prop_ids; + + if (layout.prop_id () != 0 && gds2_options.write_file_properties) { + prop_ids.insert (layout.prop_id ()); + } + + for (auto c = cells.begin (); c != cells.end (); ++c) { + + const db::Cell &cell = layout.cell (*c); + if (cell.prop_id () != 0 && gds2_options.write_cell_properties) { + prop_ids.insert (cell.prop_id ()); + } + + for (auto i = cell.begin (); ! i.at_end (); ++i) { + if (i->prop_id () != 0) { + prop_ids.insert (i->prop_id ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + const db::Shapes &shapes = cell.shapes (l->first); + for (auto s = shapes.begin (db::ShapeIterator::AllWithProperties); ! s.at_end (); s.finish_array ()) { + if (s->prop_id () != 0) { + prop_ids.insert (s->prop_id ()); + } + } + } + + } + + const size_t max_string_length = 32768 - 6; + + std::set names_taken; + std::set name_ids_to_translate; + std::set value_ids_to_translate; + + for (auto p = prop_ids.begin (); p != prop_ids.end (); ++p) { + + const auto &props = db::properties (*p); + for (auto i = props.begin (); i != props.end (); ++i) { + + const auto &pn = db::property_name (i->first); + const auto &pv = db::property_value (i->second); + + if (pn.is_long ()) { + long iv = pn.to_long (); + if (iv > long (std::numeric_limits::max ()) || iv < 0) { + name_ids_to_translate.insert (i->first); + } else { + names_taken.insert ((unsigned short) iv); + } + } else if (pn.is_ulong ()) { + unsigned long iv = pn.to_ulong (); + if (iv > (unsigned long) (std::numeric_limits::max ())) { + name_ids_to_translate.insert (i->first); + } else { + names_taken.insert ((unsigned short) iv); + } + } else { + name_ids_to_translate.insert (i->first); + } + + if (pv.is_array () || pv.is_list () || pv.is_user () || + (pv.is_a_string () && strlen (pv.to_string ()) > max_string_length)) { + value_ids_to_translate.insert (i->second); + } + + } + + } + + // Assign unique numerical keys to names, starting with big numbers + + for (auto i = name_ids_to_translate.begin (); i != name_ids_to_translate.end (); ++i) { + m_prop_name_placeholders.insert (std::make_pair (db::property_name (*i), (unsigned short) 0)); + } + + unsigned short key = 32768; + for (auto i = m_prop_name_placeholders.begin (); i != m_prop_name_placeholders.end (); ++i) { + while (key > 0 && names_taken.find (--key) != names_taken.end ()) + ; + if (key == 0) { + // if the key reaches zero, we cannot translate further non-numerical property keys + tl::warn << tl::to_string (tr ("Too many non-numerical property keys present - cannot map them to limited GDS property name space")); + m_prop_name_placeholders.clear (); + name_ids_to_translate.clear (); + break; + } else { + i->second = key; + } + } + + + for (auto i = name_ids_to_translate.begin (); i != name_ids_to_translate.end (); ++i) { + const auto &n = db::property_name (*i); + auto p = m_prop_name_placeholders.find (n); + tl_assert (p != m_prop_name_placeholders.end ()); + m_prop_names_map.insert (std::make_pair (*i, tl::Variant (p->second))); + } + + // Assign "unique" placeholder strings for the values + + for (auto i = value_ids_to_translate.begin (); i != value_ids_to_translate.end (); ++i) { + m_prop_value_placeholders.insert (std::make_pair (db::property_value (*i), std::string ())); + } + size_t value_index = 0; + for (auto i = m_prop_value_placeholders.begin (); i != m_prop_value_placeholders.end (); ++i) { + // TODO: check if this value really is unique + i->second = tl::sprintf ("klayout-prop-value#%u:%x", ++value_index, i->first.hash ()); + } + + for (auto i = value_ids_to_translate.begin (); i != value_ids_to_translate.end (); ++i) { + const auto &v = db::property_value (*i); + auto p = m_prop_value_placeholders.find (v); + tl_assert (p != m_prop_value_placeholders.end ()); + m_prop_values_map.insert (std::make_pair (*i, tl::Variant (p->second))); + } +} + void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -471,7 +644,20 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S } } + // collect property translations - these are needed to store properties with non-numerical keys + // and non-scalar values. + + m_prop_names_map.clear (); + m_prop_name_placeholders.clear (); + m_prop_values_map.clear (); + m_prop_value_placeholders.clear (); + + if (options.write_context_info () && gds2_options.extended_features) { + build_property_translations (layout, layers, cells, gds2_options); + } + // get current time + short time_data [6] = { 0, 0, 0, 0, 0, 0 }; if (gds2_options.write_timestamps) { time_t ti = 0; @@ -491,6 +677,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time)); layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time)); + // initialize options + m_keep_instances = options.keep_instances (); m_multi_xy = gds2_options.multi_xy_records; m_max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); @@ -543,9 +731,14 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S write_double (m_dbu / std::max (1e-9, gds2_options.user_units)); write_double (m_dbu * 1e-6); + // build property translation maps if possible + // Property translation maps non-numeric property keys to numeric ones and + // complex-type values to string ones. The neccessary maps are included in + // the meta data + // layout properties - if (gds2_options.write_file_properties && layout.prop_id () != 0) { + if (layout.prop_id () != 0 && gds2_options.write_file_properties) { try { write_properties (layout, layout.prop_id ()); } catch (tl::Exception &ex) { @@ -554,19 +747,36 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S } // write context info + // A context info header ("context cell") is needed, if + // * The layout or the cells explicitly need context info (meta data, library references etc.) + // * layout or cell properties are present and "write_file_properties" or "write_cell_properties" is OFF. + // * Property names or values need to be translated + // * Named layers are present bool has_context = false; if (options.write_context_info ()) { - has_context = layout.has_context_info (); - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) { - has_context = layout.has_context_info (*cell); + + has_context = layout.has_context_info () || + (! m_prop_names_map.empty () || ! m_prop_values_map.empty ()) || + (layout.prop_id () != 0 && ! gds2_options.write_file_properties && gds2_options.extended_features); + + for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) { + has_context = layout.has_context_info (*cell) || + (layout.cell (*cell).prop_id () != 0 && ! gds2_options.write_cell_properties && gds2_options.extended_features); } + + if (gds2_options.extended_features) { + for (auto layer = layers.begin (); layer != layers.end () && ! has_context; ++layer) { + has_context = ! layer->second.name.empty (); + } + } + } if (has_context) { try { - write_context_cell (layout, time_data, cells); + write_context_cell (layout, time_data, cells, layers, gds2_options); } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + tl::to_string (tr (", writing context cell"))); } @@ -1112,10 +1322,14 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Sha void GDS2WriterBase::write_properties (const db::Layout & /*layout*/, db::properties_id_type prop_id) { - auto props = db::properties (prop_id).to_map (); + auto props = db::properties (prop_id); for (auto p = props.begin (); p != props.end (); ++p) { - const tl::Variant &name = p->first; + auto pn = m_prop_names_map.find (p->first); + auto pv = m_prop_values_map.find (p->second); + + const tl::Variant &value = (pv == m_prop_values_map.end ()) ? db::property_value (p->second) : pv->second; + const tl::Variant &name = (pn == m_prop_names_map.end ()) ? db::property_name (p->first) : pn->second; long attr = -1; if (name.can_convert_to_long ()) { @@ -1128,7 +1342,7 @@ GDS2WriterBase::write_properties (const db::Layout & /*layout*/, db::properties_ write_record (sPROPATTR); write_short ((int16_t) attr); - write_string_record (sPROPVALUE, p->second.to_string ()); + write_string_record (sPROPVALUE, value.to_string ()); } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index fda28dcb66..51b18e32e8 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -176,13 +176,19 @@ class DB_PLUGIN_PUBLIC GDS2WriterBase bool m_write_cell_properties; bool m_keep_instances; double m_default_text_size; + std::map m_prop_values_map; + std::map m_prop_names_map; + std::map m_prop_name_placeholders; + std::map m_prop_value_placeholders; void write_properties (const db::Layout &layout, db::properties_id_type prop_id); - void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells); + void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells, const std::vector > &layers, const db::GDS2WriterOptions &gds2_options); void write_context_string (size_t n, const std::string &s); void write_cell (db::Layout &layout, const db::Cell &cref, const std::vector > &layers, const std::set &cell_set, double sf, short *time_data, bool skip_body); void write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf); + void build_property_translations (const db::Layout &layout, const std::vector > &layers, const std::vector &cells, const db::GDS2WriterOptions &gds2_options); + void get_property_map_context (std::vector &context_strings); }; } // namespace db diff --git a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc index 55f9abcbd7..e88b7a5f4f 100644 --- a/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc +++ b/src/plugins/streamers/gds2/db_plugin/gsiDeclDbGDS2.cc @@ -115,6 +115,16 @@ static bool get_gds2_write_timestamps (const db::SaveLayoutOptions *options) return options->get_options ().write_timestamps; } +static void set_gds2_extended_features (db::SaveLayoutOptions *options, bool n) +{ + options->get_options ().extended_features = n; +} + +static bool get_gds2_extended_features(const db::SaveLayoutOptions *options) +{ + return options->get_options ().extended_features; +} + static void set_gds2_default_text_size (db::SaveLayoutOptions *options, const tl::Variant &v) { options->get_options ().default_text_size = v.is_nil () ? -1.0 : v.to_double (); @@ -190,6 +200,29 @@ gsi::ClassExt gds2_writer_options ( "@brief Gets a value indicating whether the current time is written into the GDS2 timestamp fields\n" "\nThis property has been added in version 0.21.16.\n" ) + + gsi::method_ext ("gds2_extended_features=", &set_gds2_extended_features, gsi::arg ("flag"), + "@brief Enables extended features if set to true\n" + "\n" + "With extended features enabled, the GDS2 writer will support the following features:\n" + "\n" + "@ul\n" + "@li Long property value strings and complex types such a lists @/li\n" + "@li Non-numerical property names - i.e. strings @/li\n" + "@li File and cell level properties in a backward compatible way and with the respective option turned off @/li\n" + "@li Layer names - this includes empty layers, so this is a way to indicate the presence of a layer without a shape on it @/li\n" + "@/ul\n" + "\n" + "KLayout uses the context to implement these features. Therefore, this option is not compatible with \\write_context_info off.\n" + "By default, this feature is enabled.\n" + "\n" + "\nThis property has been added in version 0.30.7.\n" + ) + + gsi::method_ext ("gds2_extended_features?", &get_gds2_extended_features, + "@brief Gets a value indicating whether extended features are enabled\n" + "See \\gds2_extended_features= for a description of the extended features.\n" + "\n" + "\nThis property has been added in version 0.30.7.\n" + ) + gsi::method_ext ("gds2_default_text_size=", &set_gds2_default_text_size, gsi::arg ("size"), "@brief Specifies the default text size to use when a text does not have a size\n" "\n" diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc index 36c3936819..cad1fdee19 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc @@ -1577,4 +1577,474 @@ TEST(166) run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt); } +static std::string p2s (db::properties_id_type pid) +{ + return db::properties (pid).to_dict_var ().to_parsable_string (); +} + +namespace { + +/** + * @brief Installs a temporary repository instance for testing + * + * By using a temp instance, we do not disturb other tests. + */ +class TempPropertiesRepository +{ +public: + TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (&m_temp); + } + + ~TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (0); + } + +private: + db::PropertiesRepository m_temp; +}; + +} + +// Layout and cell properties are written to the context cell unless this is allowed by "write_cell/file_properties" +TEST(200_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + gds2_opt.write_cell_properties = false; + gds2_opt.write_file_properties = false; + + db::PropertiesSet ps1; + ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + layout_org.prop_id (db::properties_id (ps1)); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + xcell.prop_id (db::properties_id (ps2)); + + EXPECT_EQ (p2s (layout_org.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (xcell.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_200.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_options (gds2_opt); + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (p2s (layout_read.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + EXPECT_EQ (p2s (layout_read.cell (xc.second).prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); +} + +// Without a context cell, layout and file properties are written if requested, but only +// numerical property keys are supported +TEST(201_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + gds2_opt.write_cell_properties = true; + gds2_opt.write_file_properties = true; + db::SaveLayoutOptions options; + options.set_write_context_info (false); + options.set_options (gds2_opt); + + db::PropertiesSet ps1; + ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + layout_org.prop_id (db::properties_id (ps1)); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + xcell.prop_id (db::properties_id (ps2)); + + EXPECT_EQ (p2s (layout_org.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (xcell.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_201.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (p2s (layout_read.prop_id ()), "{#17=>'2.5'}"); + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + EXPECT_EQ (p2s (layout_read.cell (xc.second).prop_id ()), "{#42=>'A string'}"); +} + +// With a context cell, layout and file properties are written if requested, and property +// name and value translation happens +TEST(202_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + gds2_opt.write_cell_properties = true; + gds2_opt.write_file_properties = true; + db::SaveLayoutOptions options; + options.set_options (gds2_opt); + + db::PropertiesSet ps1; + ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + layout_org.prop_id (db::properties_id (ps1)); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + xcell.prop_id (db::properties_id (ps2)); + + EXPECT_EQ (p2s (layout_org.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (xcell.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_202.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (p2s (layout_read.prop_id ()), "{#17=>'2.5','prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + EXPECT_EQ (p2s (layout_read.cell (xc.second).prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); +} + +// With a context cell, shape and instance properties can have non-numeric names +// and complex types for values +TEST(203_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + db::SaveLayoutOptions options; + options.set_options (gds2_opt); + + db::PropertiesSet ps1; + ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + unsigned int l1 = layout_org.insert_layer (db::LayerProperties (1, 0, "NAME")); + layout_org.insert_layer (db::LayerProperties (2, 17, "U")); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + db::Shape shape = xcell.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 1000, 2000), db::properties_id (ps1))); + + db::Cell &ycell = layout_org.cell (layout_org.add_cell ("Y")); + db::Instance instance = xcell.insert (db::CellInstArrayWithProperties (db::CellInstArray (ycell.cell_index (), db::Trans ()), db::properties_id (ps2))); + + EXPECT_EQ (p2s (shape.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (instance.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_203.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_org.clear (); + + { + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + unsigned int l1 = layout_read.get_layer (db::LayerProperties (1, 0)); + int l2d17 = layout_read.get_layer_maybe (db::LayerProperties (2, 17)); + + // layer names are also persisted, 2/17 is created even as it is empty + EXPECT_EQ (layout_read.get_properties (l1).name, "NAME"); + EXPECT_EQ (l2d17 >= 0, true); + if (l2d17 >= 0) { + EXPECT_EQ (layout_read.get_properties (l2d17).name, "U"); + } + + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + const db::Cell &xcell = layout_read.cell (xc.second); + + auto s = xcell.shapes (l1).begin (db::ShapeIterator::All); + tl_assert (! s.at_end ()); + EXPECT_EQ (p2s (s->prop_id ()), "{#17=>'2.5','prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + + auto i = xcell.begin (); + tl_assert (! i.at_end ()); + EXPECT_EQ (p2s (i->prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + } +} + +// Without a context cell, shape and instance properties cannot have non-numeric names +// or complex types for values +TEST(204_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + db::SaveLayoutOptions options; + options.set_write_context_info (false); + options.set_options (gds2_opt); + + db::PropertiesSet ps1; + ps1.insert (tl::Variant ("prop_name"), db::DBox (0, 0, 1.5, 2.5)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + unsigned int l1 = layout_org.insert_layer (db::LayerProperties (1, 0, "NAME")); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + db::Shape shape = xcell.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 1000, 2000), db::properties_id (ps1))); + + db::Cell &ycell = layout_org.cell (layout_org.add_cell ("Y")); + db::Instance instance = xcell.insert (db::CellInstArrayWithProperties (db::CellInstArray (ycell.cell_index (), db::Trans ()), db::properties_id (ps2))); + + EXPECT_EQ (p2s (shape.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (instance.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_204.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_org.clear (); + + { + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + unsigned int l1 = layout_read.get_layer (db::LayerProperties (1, 0)); + int l2d17 = layout_read.get_layer_maybe (db::LayerProperties (2, 17)); + + // layer names are not persisted, 2/17 is not created + EXPECT_EQ (layout_read.get_properties (l1).name, ""); + EXPECT_EQ (l2d17 >= 0, false); + + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + const db::Cell &xcell = layout_read.cell (xc.second); + + auto s = xcell.shapes (l1).begin (db::ShapeIterator::All); + tl_assert (! s.at_end ()); + EXPECT_EQ (p2s (s->prop_id ()), "{#17=>'2.5'}"); + + auto i = xcell.begin (); + tl_assert (! i.at_end ()); + EXPECT_EQ (p2s (i->prop_id ()), "{#42=>'A string'}"); + } +} + +// Without extended features enabled, shape and instance properties cannot have non-numeric names +// or complex types for values +TEST(205_extended_props) +{ + TempPropertiesRepository temp_pr; + + db::SaveLayoutOptions options; + 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)); + ps1.insert (tl::Variant (17), 2.5); + + db::PropertiesSet ps2; + tl::Variant l = tl::Variant::empty_list (); + l.push (17); + l.push ("X"); + ps2.insert (tl::Variant ("prop_name2"), l); + ps2.insert (tl::Variant (42), "A string"); + + db::Layout layout_org; + unsigned int l1 = layout_org.insert_layer (db::LayerProperties (1, 0, "NAME")); + + db::Cell &xcell = layout_org.cell (layout_org.add_cell ("X")); + db::Shape shape = xcell.shapes (l1).insert (db::BoxWithProperties (db::Box (0, 0, 1000, 2000), db::properties_id (ps1))); + + db::Cell &ycell = layout_org.cell (layout_org.add_cell ("Y")); + db::Instance instance = xcell.insert (db::CellInstArrayWithProperties (db::CellInstArray (ycell.cell_index (), db::Trans ()), db::properties_id (ps2))); + + EXPECT_EQ (p2s (shape.prop_id ()), "{#17=>##2.5,'prop_name'=>[dbox:(0,0;1.5,2.5)]}"); + EXPECT_EQ (p2s (instance.prop_id ()), "{#42=>'A string','prop_name2'=>(#17,'X')}"); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_205.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_org.clear (); + + { + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + unsigned int l1 = layout_read.get_layer (db::LayerProperties (1, 0)); + auto xc = layout_read.cell_by_name ("X"); + tl_assert (xc.first); + const db::Cell &xcell = layout_read.cell (xc.second); + + auto s = xcell.shapes (l1).begin (db::ShapeIterator::All); + tl_assert (! s.at_end ()); + EXPECT_EQ (p2s (s->prop_id ()), "{#17=>'2.5'}"); + + auto i = xcell.begin (); + tl_assert (! i.at_end ()); + EXPECT_EQ (p2s (i->prop_id ()), "{#42=>'A string'}"); + } +} + +// Tests the ability to store huge strings, polygons and lists +TEST(206_large_props) +{ + TempPropertiesRepository temp_pr; + + db::GDS2WriterOptions gds2_opt; + db::SaveLayoutOptions options; + options.set_options (gds2_opt); + + tl::Variant large_list = tl::Variant::empty_list (); + std::string large_string; + large_string.reserve (size_t (150000)); + for (int i = 0; i < 30000; ++i) { + large_string += tl::to_string (i); + large_list.push (i); + } + + int npoints = 10000; + double r = 10000000.0; + std::vector points; + points.reserve (size_t (npoints)); + for (int i = 0; i < npoints; ++i) { + double a = M_PI * 2.0 * i / double (npoints); + points.push_back (db::Point (r * sin (a), r * cos (a))); + } + db::SimplePolygon large_polygon; + large_polygon.assign_hull (points.begin (), points.end ()); + + db::PropertiesSet ps1; + ps1.insert (tl::Variant (1), large_string); + ps1.insert (tl::Variant (2), large_list); + ps1.insert (tl::Variant (3), large_polygon); + + auto ps1_id = db::properties_id (ps1); + + db::Layout layout_org; + layout_org.prop_id (ps1_id); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_206.gds"); + + { + tl::OutputStream out (tmp_file); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_org.clear (); + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.prop_id (), ps1_id); +} diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index 1ddc69a8f1..30269bf134 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -101,7 +101,7 @@ class DB_PLUGIN_PUBLIC OASISWriterOptions * @brief The constructor */ OASISWriterOptions () - : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), + : compression_level (2), enhanced_property_types (true), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*"), tables_at_end (false) { // .. nothing yet .. @@ -116,7 +116,20 @@ class DB_PLUGIN_PUBLIC OASISWriterOptions * 1 - nearest neighbor shape array formation * 2++ - enhanced shape array search algorithm using 2nd and further neighbor distances as well */ - int compression_level; + int compression_level; + + /** + * @brief Enhanced property types + * + * If this option is set to true (the default), complex property types + * such as lists or even objects can be embedded into OASIS files. + * For this, KLayout uses strings with a special annotation + * (i.e. "KLAYOUT_VALUE:..."). + * + * This option also implies that only properties with string values + * (and numerical keys) are written as S_GDS_PROPERTY properties. + */ + bool enhanced_property_types; /** * @brief CBLOCK compression diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 124df7e445..ea95e9e034 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -479,30 +479,6 @@ OASISReader::warn (const std::string &msg, int wl) } } -/** - * @brief A helper class to join two datatype layer name map members - */ -struct LNameJoinOp1 -{ - void operator() (std::string &a, const std::string &b) - { - join_layer_names (a, b); - } -}; - -/** - * @brief A helper class to join two layer map members - * This implementation basically merged the datatype maps. - */ -struct LNameJoinOp2 -{ - void operator() (tl::interval_map &a, const tl::interval_map &b) - { - LNameJoinOp1 op1; - a.add (b.begin (), b.end (), op1); - } -}; - /** * @brief Marks the beginning of a new table * @@ -800,13 +776,13 @@ OASISReader::do_read (db::Layout &layout) get (id); } - if (! m_propnames.insert (std::make_pair (id, name)).second) { + if (! m_propnames.insert (std::make_pair (id, make_prop_value (name))).second) { error (tl::sprintf (tl::to_string (tr ("A PROPNAME with id %ld is present already")), id)); } auto fw = m_propname_forward_references.find (id); if (fw != m_propname_forward_references.end ()) { - fw->second = db::property_names_id (name); + fw->second = db::property_names_id (make_prop_value (name)); } reset_modal_variables (); @@ -842,13 +818,13 @@ OASISReader::do_read (db::Layout &layout) get (id); } - if (! m_propstrings.insert (std::make_pair (id, name)).second) { + if (! m_propstrings.insert (std::make_pair (id, make_prop_value (name))).second) { error (tl::sprintf (tl::to_string (tr ("A PROPSTRING with id %ld is present already")), id)); } - std::map::iterator fw = m_propvalue_forward_references.find (id); + auto fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { - fw->second = name; + fw->second = db::property_values_id (make_prop_value (name)); } reset_modal_variables (); @@ -1052,6 +1028,13 @@ OASISReader::do_read (db::Layout &layout) } } + // all forward references to property values must be resolved + for (std::map ::const_iterator fw = m_propvalue_forward_references.begin (); fw != m_propvalue_forward_references.end (); ++fw) { + if (fw->second == 0) { + error (tl::sprintf (tl::to_string (tr ("No property string defined for property string id %ld")), fw->first)); + } + } + // Resolve forward references for stored shape and instance prop_ids. // This makes these shape and instance property IDs valid @@ -1240,6 +1223,32 @@ OASISReader::has_forward_refs (const db::PropertiesSet &properties) return false; } +const std::string klayout_prop_string_prefix = "KLAYOUT_VALUE:"; + +tl::Variant +OASISReader::make_prop_value (const std::string &s) +{ + if (strncmp (s.c_str (), klayout_prop_string_prefix.c_str (), klayout_prop_string_prefix.size ()) == 0) { + + tl::Extractor ex (s.c_str () + klayout_prop_string_prefix.size ()); + try { + + tl::Variant v; + ex.read (v); + return v; + + } catch (tl::Exception &) { + + warn (tl::sprintf (tl::to_string (tr ("Unable to decode special value string (%s) - keeping as a string")), s)); + return tl::Variant (s); + + } + + } else { + return tl::Variant (s); + } +} + properties_id_type OASISReader::make_forward_properties_id (const db::PropertiesSet &properties) { // NOTE: the forward properties ID scheme makes use of the fact that IDs @@ -1371,9 +1380,9 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v) if (v.is_id ()) { uint64_t id = (uint64_t) v.to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + auto fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { - v = tl::Variant (fw->second); + v = db::property_value (fw->second); } else { error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); } @@ -1395,9 +1404,9 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v) for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { if (ll->is_id ()) { uint64_t id = (uint64_t) ll->to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + auto fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { - *ll = tl::Variant (fw->second); + *ll = db::property_value (fw->second); } else { error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); } @@ -1539,12 +1548,12 @@ OASISReader::read_properties () uint64_t id; get (id); - std::map ::const_iterator cid = m_propnames.find (id); + auto cid = m_propnames.find (id); if (cid == m_propnames.end ()) { mm_last_property_name = db::property_names_id (tl::Variant ((id << 1) + uint64_t (is_sprop ? 1 : 0), true /*dummy for id type*/)); m_propname_forward_references.insert (std::make_pair (id, db::property_names_id_type (0))); } else { - mm_last_property_name = db::property_names_id (tl::Variant (cid->second)); + mm_last_property_name = db::property_names_id (cid->second); } } else { @@ -1553,7 +1562,7 @@ OASISReader::read_properties () warn (tl::to_string (tr ("PROPERTY names must be references to PROPNAME ids in strict mode"))); } - mm_last_property_name = db::property_names_id (tl::Variant (get_str ())); + mm_last_property_name = db::property_names_id (make_prop_value (get_str ())); } } @@ -1602,7 +1611,7 @@ OASISReader::read_properties () } if (m_read_properties) { - mm_last_value_list.get_non_const ().push_back (tl::Variant (get_str ())); + mm_last_value_list.get_non_const ().push_back (make_prop_value (get_str ())); } else { get_str (); } @@ -1612,12 +1621,12 @@ OASISReader::read_properties () uint64_t id; get (id); if (m_read_properties) { - std::map ::const_iterator sid = m_propstrings.find (id); + auto sid = m_propstrings.find (id); if (sid == m_propstrings.end ()) { - m_propvalue_forward_references.insert (std::make_pair (id, std::string ())); + m_propvalue_forward_references.insert (std::make_pair (id, db::property_values_id_type (0))); mm_last_value_list.get_non_const ().push_back (tl::Variant (id, true /*dummy for id type*/)); } else { - mm_last_value_list.get_non_const ().push_back (tl::Variant (sid->second)); + mm_last_value_list.get_non_const ().push_back (sid->second); } } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index 4420e6c45a..32b8430e25 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -166,8 +166,8 @@ class DB_PLUGIN_PUBLIC OASISReader std::map m_cellname_properties; std::map m_textstrings; std::map m_text_forward_references; - std::map m_propstrings; - std::map m_propnames; + std::map m_propstrings; + std::map m_propnames; std::map > m_context_strings_per_cell; @@ -179,7 +179,7 @@ class DB_PLUGIN_PUBLIC OASISReader bool m_read_all_properties; std::map m_propname_forward_references; - std::map m_propvalue_forward_references; + std::map m_propvalue_forward_references; std::map > m_forward_properties_for_shapes; std::map > m_forward_properties_for_instances; std::map m_future_cell_properties; @@ -216,6 +216,8 @@ class DB_PLUGIN_PUBLIC OASISReader void replace_forward_references_in_variant (tl::Variant &v); void extract_context_strings (db::PropertiesSet &properties, std::vector &context_strings); bool has_forward_refs (const db::PropertiesSet &properties); + + tl::Variant make_prop_value (const std::string &s); db::properties_id_type make_forward_properties_id (const db::PropertiesSet &properties); const db::PropertiesSet &forward_properties (db::properties_id_type id) const; bool is_forward_properties_id (db::properties_id_type id) const; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index bebf4d1752..b017630db3 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -78,20 +78,6 @@ struct vector_cmp_y } }; -/** - * @brief Determines whether a property shall be produced as S_GDS_PROPERTY - */ -static bool -make_gds_property (const tl::Variant &name) -{ - // We write S_GDS_PROPERTY properties, because that is the only way to write properties - // with numerical keys - return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) || - (name.is_ulonglong () && name.to_ulonglong () < 0x8000) || - (name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) || - (name.is_ulong () && name.to_ulong () < 0x8000); -} - // --------------------------------------------------------------------------------- /** @@ -967,20 +953,66 @@ OASISWriter::write_ucoord (db::Coord c) } } +const std::string klayout_prop_string_prefix = "KLAYOUT_VALUE:"; + +std::string +OASISWriter::make_prop_string (const tl::Variant &v) const +{ + if (! m_options.enhanced_property_types) { + + return v.to_stdstring (); + + } else if (v.is_a_string ()) { + + std::string s = v.to_stdstring (); + + // if the string starts with the prefix, encode it using the prefixed notation + if (strncmp (s.c_str (), klayout_prop_string_prefix.c_str (), klayout_prop_string_prefix.size ()) == 0) { + return klayout_prop_string_prefix + v.to_parsable_string (); + } else { + return s; + } + + } else { + + return klayout_prop_string_prefix + v.to_parsable_string (); + + } +} + +bool +OASISWriter::make_gds_property (const tl::Variant &name, const tl::Variant &value) const +{ + // Only strings will become GDS properties in enhanced properties mode + if (m_options.enhanced_property_types && !value.is_a_string ()) { + return false; + } + + // We write S_GDS_PROPERTY properties, because that is the only way to write properties + // with numerical keys + return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) || + (name.is_ulonglong () && name.to_ulonglong () < 0x8000) || + (name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) || + (name.is_ulong () && name.to_ulong () < 0x8000); +} + void OASISWriter::emit_propname_def (db::properties_id_type prop_id) { - auto props = db::properties (prop_id).to_map (); + auto props = db::properties (prop_id); for (auto p = props.begin (); p != props.end (); ++p) { - const tl::Variant &name = p->first; - const char *name_str = s_gds_property_name; - if (! make_gds_property (name)) { - name_str = name.to_string (); + const tl::Variant &name = db::property_name (p->first); + const tl::Variant &value = db::property_value (p->second); + + std::string name_str (s_gds_property_name); + if (! make_gds_property (name, value)) { + name_str = make_prop_string (name); } + if (m_propnames.insert (std::make_pair (name_str, m_propname_id)).second) { write_record_id (7); - write_nstring (name_str); + write_nstring (name_str.c_str ()); ++m_propname_id; } @@ -992,38 +1024,50 @@ OASISWriter::emit_propstring_def (db::properties_id_type prop_id) { std::vector pv_list; - auto props = db::properties (prop_id).to_map (); + auto props = db::properties (prop_id); for (auto p = props.begin (); p != props.end (); ++p) { pv_list.clear (); const std::vector *pvl = &pv_list; - const tl::Variant &name = p->first; - if (! make_gds_property (name)) { + const tl::Variant &name = db::property_name (p->first); + const tl::Variant &value = db::property_value (p->second); - if (p->second.is_list ()) { - pvl = &p->second.get_list (); - } else if (!p->second.is_nil ()) { + if (! make_gds_property (name, value)) { + + if (value.is_list ()) { + pvl = &value.get_list (); + } else if (!value.is_nil ()) { pv_list.reserve (1); - pv_list.push_back (p->second); + pv_list.push_back (value); } - } else { + for (std::vector::const_iterator pv = pvl->begin (); pv != pvl->end (); ++pv) { - pv_list.reserve (2); - pv_list.push_back (name.to_ulong ()); - pv_list.push_back (p->second.to_string ()); + if (!pv->is_double () && !pv->is_longlong () && !pv->is_ulonglong () && !pv->is_long () && !pv->is_ulong ()) { - } + std::string v = make_prop_string (*pv); + + if (m_propstrings.insert (std::make_pair (v, m_propstring_id)).second) { + write_record_id (9); + write_bstring (v.c_str ()); + ++m_propstring_id; + } - for (std::vector::const_iterator pv = pvl->begin (); pv != pvl->end (); ++pv) { - if (!pv->is_double () && !pv->is_longlong () && !pv->is_ulonglong () && !pv->is_long () && !pv->is_ulong ()) { - if (m_propstrings.insert (std::make_pair (pv->to_string (), m_propstring_id)).second) { - write_record_id (9); - write_bstring (pv->to_string ()); - ++m_propstring_id; } + + } + + } else { + + std::string v = make_prop_string (value); + + if (m_propstrings.insert (std::make_pair (v, m_propstring_id)).second) { + write_record_id (9); + write_bstring (v.c_str ()); + ++m_propstring_id; } + } } @@ -2110,37 +2154,37 @@ OASISWriter::write_props (db::properties_id_type prop_id) { std::vector pv_list; - auto props = db::properties (prop_id).to_map (); - + auto props = db::properties (prop_id); for (auto p = props.begin (); p != props.end (); ++p) { - m_progress.set (mp_stream->pos ()); + const tl::Variant &name = db::property_name (p->first); + const tl::Variant &value = db::property_value (p->second); - const tl::Variant &name = p->first; + m_progress.set (mp_stream->pos ()); - const char *name_str = s_gds_property_name; + std::string name_str (s_gds_property_name); bool sflag = true; pv_list.clear (); const std::vector *pvl = &pv_list; - if (! make_gds_property (name)) { + if (! make_gds_property (name, value)) { - name_str = name.to_string (); + name_str = make_prop_string (name); sflag = false; - if (p->second.is_list ()) { - pvl = &p->second.get_list (); - } else if (!p->second.is_nil ()) { + if (value.is_list ()) { + pvl = &value.get_list (); + } else if (!value.is_nil ()) { pv_list.reserve (1); - pv_list.push_back (p->second); + pv_list.push_back (value); } } else { pv_list.reserve (2); pv_list.push_back (name.to_ulong ()); - pv_list.push_back (p->second.to_string ()); + pv_list.push_back (value); } @@ -2150,7 +2194,7 @@ OASISWriter::write_props (db::properties_id_type prop_id) } void -OASISWriter::write_property_def (const char *name_str, const tl::Variant &pv, bool sflag) +OASISWriter::write_property_def (const std::string &name_str, const tl::Variant &pv, bool sflag) { std::vector pvl; pvl.reserve (1); @@ -2159,7 +2203,7 @@ OASISWriter::write_property_def (const char *name_str, const tl::Variant &pv, bo } void -OASISWriter::write_property_def (const char *name_str, const std::vector &pvl, bool sflag) +OASISWriter::write_property_def (const std::string &name_str, const std::vector &pvl, bool sflag) { bool same_name = (mm_last_property_name == name_str); bool same_value = (mm_last_value_list == pvl); @@ -2196,7 +2240,7 @@ OASISWriter::write_property_def (const char *name_str, const std::vector::const_iterator pvi = m_propstrings.find (pvs); // In strict mode always write property string ID's: before we have issued the table we can @@ -2257,11 +2301,11 @@ OASISWriter::write_property_def (const char *name_str, const std::vectorsecond); } else { - write_byte (10 + string_type (pvs)); - write_bstring (pvs); + write_byte (10 + string_type (pvs.c_str ())); + write_bstring (pvs.c_str ()); } } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index be28a45279..ba605d8765 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -300,6 +300,9 @@ class DB_PLUGIN_PUBLIC OASISWriter void reset_modal_variables (); + std::string make_prop_string (const tl::Variant &v) const; + bool make_gds_property (const tl::Variant &name, const tl::Variant &value) const; + void emit_propname_def (db::properties_id_type prop_id); void emit_propstring_def (db::properties_id_type prop_id); void write_insts (const std::set &cell_set); @@ -307,8 +310,8 @@ class DB_PLUGIN_PUBLIC OASISWriter void write_shapes (const db::LayerProperties &lprops, const db::Shapes &shapes); void write_props (db::properties_id_type prop_id); - void write_property_def (const char *name_str, const std::vector &pvl, bool sflag); - void write_property_def (const char *name_str, const tl::Variant &pv, bool sflag); + void write_property_def (const std::string &name_str, const std::vector &pvl, bool sflag); + void write_property_def (const std::string &name_str, const tl::Variant &pv, bool sflag); void write_pointlist (const std::vector &pointlist, bool for_polygons); void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep); diff --git a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc index 90fd665def..41d4892b54 100644 --- a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc +++ b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc @@ -133,6 +133,16 @@ static int get_oasis_write_std_properties_ext (const db::SaveLayoutOptions *opti return options->get_options ().write_std_properties; } +static bool get_oasis_enhanced_properties (const db::SaveLayoutOptions *options) +{ + return options->get_options ().enhanced_property_types; +} + +static void set_oasis_enhanced_properties (db::SaveLayoutOptions *options, bool f) +{ + options->get_options ().enhanced_property_types = f; +} + static void set_oasis_write_cell_bounding_boxes (db::SaveLayoutOptions *options, bool f) { db::OASISWriterOptions &oasis_options = options->get_options (); @@ -278,6 +288,21 @@ gsi::ClassExt oasis_writer_options ( // this method is mainly provided as access point for the generic interface "@hide" ) + + gsi::method_ext ("oasis_enhanced_properties=", &set_oasis_enhanced_properties, gsi::arg ("flag"), + "@brief Sets a value indicating whether to write enhanced property values\n" + "With this option set to true (the default), non-standard types like lists or objects are supported " + "for property names and values. These types are encoded in specially annotated strings.\n" + "KLayout's OASIS reader will convert them back to the original types.\n" + "With this option set to false, such values are translated into strings.\n" + "\n" + "This attribute has been introduced in version 0.30.7." + ) + + gsi::method_ext ("oasis_enhanced_properties?", &get_oasis_enhanced_properties, + "@brief Gets a value indicating whether to write enhanced property values\n" + "See \\oasis_enhanced_properties= for details.\n" + "\n" + "This attribute has been introduced in version 0.30.7." + ) + gsi::method_ext ("oasis_compression_level=", &set_oasis_compression, gsi::arg ("level"), "@brief Set the OASIS compression level\n" "The OASIS compression level is an integer number between 0 and 10. 0 basically is no compression, " diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index e748fe08c7..9349f59095 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -33,6 +33,37 @@ #include +namespace { + +static std::string p2s (db::properties_id_type pid) +{ + return db::properties (pid).to_dict_var ().to_parsable_string (); +} + +/** + * @brief Installs a temporary repository instance for testing + * + * By using a temp instance, we do not disturb other tests. + */ +class TempPropertiesRepository +{ +public: + TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (&m_temp); + } + + ~TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (0); + } + +private: + db::PropertiesRepository m_temp; +}; + +} + void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end) { { @@ -1533,14 +1564,14 @@ TEST(116) "set props {\n" " {42 {42}}\n" " {{S_BOUNDING_BOX} {(0,0,100,1000,1100)}}\n" - " {{S_CELL_OFFSET} {231}}\n" + " {{S_CELL_OFFSET} {247}}\n" "}\n" "begin_cellp $props {$1}\n" "path 1 0 0 0 0 {0 100} {1000 1200}\n" "end_cell\n" "set props {\n" " {{S_BOUNDING_BOX} {(2,0,0,0,0)}}\n" - " {{S_CELL_OFFSET} {229}}\n" + " {{S_CELL_OFFSET} {245}}\n" "}\n" "begin_cellp $props {$2}\n" "end_cell\n" @@ -1598,13 +1629,13 @@ TEST(116) "begin_libp $props 0.001\n" "set props {\n" " {42 {42}}\n" - " {{S_CELL_OFFSET} {182}}\n" + " {{S_CELL_OFFSET} {198}}\n" "}\n" "begin_cellp $props {$1}\n" "path 1 0 0 0 0 {0 100} {1000 1200}\n" "end_cell\n" "set props {\n" - " {{S_CELL_OFFSET} {180}}\n" + " {{S_CELL_OFFSET} {196}}\n" "}\n" "begin_cellp $props {$2}\n" "end_cell\n" @@ -2100,3 +2131,68 @@ TEST(140) db::compare_layouts (_this, gg, tl::testdata () + "/oasis/dbOASISWriter40_au.gds", db::NoNormalization); } } + +// Writing enhanced properties to OASIS +TEST(150) +{ + TempPropertiesRepository temp_pr; + + db::Layout layout_org; + + db::PropertiesSet ps; + tl::Variant list = tl::Variant::empty_list (); + list.push (tl::Variant (-1)); + list.push (tl::Variant (2.5)); + list.push (tl::Variant ("KLAYOUT_VALUE:h*ll*")); // a string that clashes with the annotation scheme + ps.insert (tl::Variant (17), list); + ps.insert (tl::Variant ("x"), tl::Variant (db::DBox (0, 0, 1.5, 2.5))); + + db::properties_id_type ps_id = db::properties_id (ps); + EXPECT_EQ (p2s (ps_id), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}"); + + layout_org.prop_id (ps_id); + + std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150a.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), true); + db::Writer writer (options); + writer.write (layout_org, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.set_warnings_as_errors (true); + reader.read (gg); + + EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}"); + + } + + tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150b.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_option_by_name ("oasis_enhanced_properties", false); + EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.read (gg); + + EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>'(-1,2.5,KLAYOUT_VALUE:h*ll*)','x'=>'(0,0;1.5,2.5)'}"); + + } +} diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 5328616eb2..913818d01f 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3722,12 +3722,98 @@ 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::max () && l >= (long long) std::numeric_limits::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 { + + double gg = t.to_double () * f; + + if (! m_sloppy) { + // check, if the result is an integer + double g = floor (0.5 + gg); + 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); + } + } + + t = tl::Variant (gg); + + } + + } + + return true; +} + void Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr &n, int am) { - double g = 0.0; int match_group = 0; std::string t; + tl::Variant g; ExpressionParserContext ex1 = ex; if (ex.test ("(")) { @@ -3881,83 +3967,9 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr n.reset (new ConstantExpressionNode (ex1, tl::Variant (x))); - } else if (ex.try_read (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 (); - } - } + } else if (read_number (ex, g)) { - 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))); - - } + n.reset (new ConstantExpressionNode (ex1, g)); } else if (ex.try_read_quoted (t)) { diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index 63a9413971..fe3947772c 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -616,6 +616,7 @@ class TL_PUBLIC Eval void eval_unary (ExpressionParserContext &context, std::unique_ptr &v); void eval_atomic (ExpressionParserContext &context, std::unique_ptr &v, int am); void eval_suffix (ExpressionParserContext &context, std::unique_ptr &v); + bool read_number (ExpressionParserContext &ex, tl::Variant &t); static Eval m_global; }; diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 98e64a69c8..ef862b33e9 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -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")); } @@ -1217,3 +1263,71 @@ TEST(20) EXPECT_EQ (v.to_string (), std::string ("0.3")); } +namespace +{ + +class MyContextHandler + : public tl::ContextHandler +{ +public: + MyContextHandler () { } + + virtual tl::Variant eval_bracket (const std::string &content) const + { + return tl::Variant (content); + } + + virtual tl::Variant eval_double_bracket (const std::string &content) const + { + return tl::Variant ("{" + content + "}"); + } + + virtual double dbu () const + { + return 0.25; + } +}; + +} + +// with context handler +TEST(21) +{ + tl::Eval e; + MyContextHandler ctx; + e.set_ctx_handler (&ctx); + + tl::Variant v; + v = e.parse ("1um").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##4")); + v = e.parse ("250nm").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##1")); + v = e.parse ("2mm").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##8000")); + v = e.parse ("2m").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##8000000")); + v = e.parse ("1um2+0.5um2").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##24")); + v = e.parse ("1mm2").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##16000000")); + v = e.parse ("1m2").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##16000000000000")); + v = e.parse ("<>").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("'{xxx}'")); + v = e.parse ("").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("'yyy'")); + + e.set_ctx_handler (0); + try { + v = e.parse ("1um").execute (); + EXPECT_EQ (0, 1); + } catch (...) { + } + + tl::Eval es (0, true /*sloppy*/); + v = es.parse ("1.5").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("##1.5")); + v = es.parse ("1.5um").execute (); + EXPECT_EQ (v.to_parsable_string (), std::string ("nil")); +} + diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds index 7527f097c9..491fe26094 100644 Binary files a/testdata/algo/antenna_au1.gds and b/testdata/algo/antenna_au1.gds differ diff --git a/testdata/algo/bipolar_devices_nets.gds b/testdata/algo/bipolar_devices_nets.gds.1 similarity index 51% rename from testdata/algo/bipolar_devices_nets.gds rename to testdata/algo/bipolar_devices_nets.gds.1 index 9e813195c7..80d04c36c3 100644 Binary files a/testdata/algo/bipolar_devices_nets.gds and b/testdata/algo/bipolar_devices_nets.gds.1 differ diff --git a/testdata/algo/bipolar_devices_nets.gds.2 b/testdata/algo/bipolar_devices_nets.gds.2 new file mode 100644 index 0000000000..fdd11571f0 Binary files /dev/null and b/testdata/algo/bipolar_devices_nets.gds.2 differ diff --git a/testdata/algo/device_extract_au1.gds b/testdata/algo/device_extract_au1.gds index 7769910047..5d381a13f2 100644 Binary files a/testdata/algo/device_extract_au1.gds and b/testdata/algo/device_extract_au1.gds differ diff --git a/testdata/algo/device_extract_au10.gds.1 b/testdata/algo/device_extract_au10.gds.1 index 613e8b0ca8..8aa950fc82 100644 Binary files a/testdata/algo/device_extract_au10.gds.1 and b/testdata/algo/device_extract_au10.gds.1 differ diff --git a/testdata/algo/device_extract_au10.gds.2 b/testdata/algo/device_extract_au10.gds.2 index 6b279ff193..34f447f3f0 100644 Binary files a/testdata/algo/device_extract_au10.gds.2 and b/testdata/algo/device_extract_au10.gds.2 differ diff --git a/testdata/algo/device_extract_au10.gds.3 b/testdata/algo/device_extract_au10.gds.3 deleted file mode 100644 index f0be9a4406..0000000000 Binary files a/testdata/algo/device_extract_au10.gds.3 and /dev/null differ diff --git a/testdata/algo/device_extract_au10.gds.4 b/testdata/algo/device_extract_au10.gds.4 deleted file mode 100644 index cf84d9b77b..0000000000 Binary files a/testdata/algo/device_extract_au10.gds.4 and /dev/null differ diff --git a/testdata/algo/device_extract_au10.gds.5 b/testdata/algo/device_extract_au10.gds.5 deleted file mode 100644 index 5a077c2fbb..0000000000 Binary files a/testdata/algo/device_extract_au10.gds.5 and /dev/null differ diff --git a/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds b/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds index a010888e38..2924752a74 100644 Binary files a/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds and b/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au1_flat.gds.1 b/testdata/algo/device_extract_au1_flat.gds.1 new file mode 100644 index 0000000000..9e4483748b Binary files /dev/null and b/testdata/algo/device_extract_au1_flat.gds.1 differ diff --git a/testdata/algo/device_extract_au1_flat.gds.2 b/testdata/algo/device_extract_au1_flat.gds.2 new file mode 100644 index 0000000000..8f4978dd97 Binary files /dev/null and b/testdata/algo/device_extract_au1_flat.gds.2 differ diff --git a/testdata/algo/device_extract_au1_flat.gds b/testdata/algo/device_extract_au1_flat.gds.3 similarity index 60% rename from testdata/algo/device_extract_au1_flat.gds rename to testdata/algo/device_extract_au1_flat.gds.3 index 0c65698c9d..553f85ea17 100644 Binary files a/testdata/algo/device_extract_au1_flat.gds and b/testdata/algo/device_extract_au1_flat.gds.3 differ diff --git a/testdata/algo/device_extract_au1_implicit_nets.gds b/testdata/algo/device_extract_au1_implicit_nets.gds index 50703c4d90..b0ab0b6445 100644 Binary files a/testdata/algo/device_extract_au1_implicit_nets.gds and b/testdata/algo/device_extract_au1_implicit_nets.gds differ diff --git a/testdata/algo/device_extract_au1_join_nets.gds b/testdata/algo/device_extract_au1_join_nets.gds index 0de18dc616..33ffd8d6a2 100644 Binary files a/testdata/algo/device_extract_au1_join_nets.gds and b/testdata/algo/device_extract_au1_join_nets.gds differ diff --git a/testdata/algo/device_extract_au1_joined_nets.gds b/testdata/algo/device_extract_au1_joined_nets.gds index 7ce83b1e2d..b2574869e0 100644 Binary files a/testdata/algo/device_extract_au1_joined_nets.gds and b/testdata/algo/device_extract_au1_joined_nets.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_ff.gds b/testdata/algo/device_extract_au1_rebuild_ff.gds index c6ec960bd6..80524d9c5e 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_ff.gds and b/testdata/algo/device_extract_au1_rebuild_ff.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_fr.gds b/testdata/algo/device_extract_au1_rebuild_fr.gds index 1f2f6ab762..54cf13ae32 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_fr.gds and b/testdata/algo/device_extract_au1_rebuild_fr.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_nf.gds b/testdata/algo/device_extract_au1_rebuild_nf.gds index a684cd03a5..4465928290 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_nf.gds and b/testdata/algo/device_extract_au1_rebuild_nf.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_nr.gds b/testdata/algo/device_extract_au1_rebuild_nr.gds index e4b741123a..dc7d7ee517 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_nr.gds and b/testdata/algo/device_extract_au1_rebuild_nr.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_pf.gds b/testdata/algo/device_extract_au1_rebuild_pf.gds index 05aa0b9805..9926236eb0 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_pf.gds and b/testdata/algo/device_extract_au1_rebuild_pf.gds differ diff --git a/testdata/algo/device_extract_au1_rebuild_pr.gds b/testdata/algo/device_extract_au1_rebuild_pr.gds index 9725c79bf9..61871842f0 100644 Binary files a/testdata/algo/device_extract_au1_rebuild_pr.gds and b/testdata/algo/device_extract_au1_rebuild_pr.gds differ diff --git a/testdata/algo/device_extract_au1_with_rec_nets.gds b/testdata/algo/device_extract_au1_with_rec_nets.gds index 7539914d75..ab618b07ea 100644 Binary files a/testdata/algo/device_extract_au1_with_rec_nets.gds and b/testdata/algo/device_extract_au1_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_au1a.gds b/testdata/algo/device_extract_au1a.gds index 47ac063c7e..896f5edc5a 100644 Binary files a/testdata/algo/device_extract_au1a.gds and b/testdata/algo/device_extract_au1a.gds differ diff --git a/testdata/algo/device_extract_au9.gds b/testdata/algo/device_extract_au9.gds index f8c19fbd8f..128913580b 100644 Binary files a/testdata/algo/device_extract_au9.gds and b/testdata/algo/device_extract_au9.gds differ diff --git a/testdata/algo/device_extract_capres_nets.gds.1 b/testdata/algo/device_extract_capres_nets.gds.1 index 63b4ef0e11..a6334a0982 100644 Binary files a/testdata/algo/device_extract_capres_nets.gds.1 and b/testdata/algo/device_extract_capres_nets.gds.1 differ diff --git a/testdata/algo/device_extract_capres_nets.gds.2 b/testdata/algo/device_extract_capres_nets.gds.2 index 15a993e30b..6fc46523c5 100644 Binary files a/testdata/algo/device_extract_capres_nets.gds.2 and b/testdata/algo/device_extract_capres_nets.gds.2 differ diff --git a/testdata/algo/device_extract_capres_with_bulk_nets.gds b/testdata/algo/device_extract_capres_with_bulk_nets.gds deleted file mode 100644 index 42331829d1..0000000000 Binary files a/testdata/algo/device_extract_capres_with_bulk_nets.gds and /dev/null differ diff --git a/testdata/algo/device_extract_capres_with_bulk_nets.gds.1 b/testdata/algo/device_extract_capres_with_bulk_nets.gds.1 new file mode 100644 index 0000000000..9c3808c68a Binary files /dev/null and b/testdata/algo/device_extract_capres_with_bulk_nets.gds.1 differ diff --git a/testdata/algo/device_extract_capres_with_bulk_nets.gds.2 b/testdata/algo/device_extract_capres_with_bulk_nets.gds.2 new file mode 100644 index 0000000000..6c6561c9b4 Binary files /dev/null and b/testdata/algo/device_extract_capres_with_bulk_nets.gds.2 differ diff --git a/testdata/algo/device_extract_issue954_au.gds b/testdata/algo/device_extract_issue954_au.gds index 5a7bdc6106..adb820444d 100644 Binary files a/testdata/algo/device_extract_issue954_au.gds and b/testdata/algo/device_extract_issue954_au.gds differ diff --git a/testdata/algo/diode_devices_nets.gds b/testdata/algo/diode_devices_nets.gds index cd88cb5e8f..870c382d1e 100644 Binary files a/testdata/algo/diode_devices_nets.gds and b/testdata/algo/diode_devices_nets.gds differ diff --git a/testdata/algo/l2n_reader_au_p.oas b/testdata/algo/l2n_reader_au_p.oas index 83a8f061a2..c2a0d61bb9 100644 Binary files a/testdata/algo/l2n_reader_au_p.oas and b/testdata/algo/l2n_reader_au_p.oas differ diff --git a/testdata/algo/l2n_writer_au.gds b/testdata/algo/l2n_writer_au.gds index ed3ae89985..a2dac5c650 100644 Binary files a/testdata/algo/l2n_writer_au.gds and b/testdata/algo/l2n_writer_au.gds differ diff --git a/testdata/algo/l2n_writer_au_2.gds b/testdata/algo/l2n_writer_au_2.gds index a4f45414f4..8c4db6d234 100644 Binary files a/testdata/algo/l2n_writer_au_2.gds and b/testdata/algo/l2n_writer_au_2.gds differ diff --git a/testdata/algo/net_proc_au1.gds b/testdata/algo/net_proc_au1.gds index be6e154984..c493c17ecb 100644 Binary files a/testdata/algo/net_proc_au1.gds and b/testdata/algo/net_proc_au1.gds differ diff --git a/testdata/algo/net_proc_au2.gds b/testdata/algo/net_proc_au2.gds index fd53350d78..5c1a5be3e5 100644 Binary files a/testdata/algo/net_proc_au2.gds and b/testdata/algo/net_proc_au2.gds differ diff --git a/testdata/algo/net_proc_au3.gds b/testdata/algo/net_proc_au3.gds index 2968b88056..15d224b956 100644 Binary files a/testdata/algo/net_proc_au3.gds and b/testdata/algo/net_proc_au3.gds differ diff --git a/testdata/algo/soft_connections_au.gds b/testdata/algo/soft_connections_au.gds index 8a34b60f76..898276db3c 100644 Binary files a/testdata/algo/soft_connections_au.gds and b/testdata/algo/soft_connections_au.gds differ diff --git a/testdata/cif/issue_305_au.gds b/testdata/cif/issue_305_au.gds index 02c2372e92..f554972cb9 100644 Binary files a/testdata/cif/issue_305_au.gds and b/testdata/cif/issue_305_au.gds differ diff --git a/testdata/cif/issue_972_au.gds b/testdata/cif/issue_972_au.gds index 1aa839432f..6575acd926 100644 Binary files a/testdata/cif/issue_972_au.gds and b/testdata/cif/issue_972_au.gds differ diff --git a/testdata/cif/lasi_au.gds.gz b/testdata/cif/lasi_au.gds.gz index d59f20c40b..c4463fb55b 100644 Binary files a/testdata/cif/lasi_au.gds.gz and b/testdata/cif/lasi_au.gds.gz differ diff --git a/testdata/drc/drcSimpleTests_au121.gds b/testdata/drc/drcSimpleTests_au121.gds index e873870f59..79a18e821d 100644 Binary files a/testdata/drc/drcSimpleTests_au121.gds and b/testdata/drc/drcSimpleTests_au121.gds differ diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds index e89e303303..60b7fbc086 100644 Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/drc/drcSimpleTests_au6.gds b/testdata/drc/drcSimpleTests_au6.gds index 752f13b5a6..47b06727f5 100644 Binary files a/testdata/drc/drcSimpleTests_au6.gds and b/testdata/drc/drcSimpleTests_au6.gds differ diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds index c06215b97b..06c4facc1b 100644 Binary files a/testdata/drc/drcSimpleTests_au7.gds and b/testdata/drc/drcSimpleTests_au7.gds differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.1 b/testdata/drc/drcSimpleTests_au70.gds.1 index ea3998d16e..74f0e58d89 100644 Binary files a/testdata/drc/drcSimpleTests_au70.gds.1 and b/testdata/drc/drcSimpleTests_au70.gds.1 differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.2 b/testdata/drc/drcSimpleTests_au70.gds.2 index 313ee77cea..7a799b7c50 100644 Binary files a/testdata/drc/drcSimpleTests_au70.gds.2 and b/testdata/drc/drcSimpleTests_au70.gds.2 differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.3 b/testdata/drc/drcSimpleTests_au70.gds.3 index ddccef174d..9c4d433dbc 100644 Binary files a/testdata/drc/drcSimpleTests_au70.gds.3 and b/testdata/drc/drcSimpleTests_au70.gds.3 differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.4 b/testdata/drc/drcSimpleTests_au70.gds.4 deleted file mode 100644 index d6b609d4f9..0000000000 Binary files a/testdata/drc/drcSimpleTests_au70.gds.4 and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_au70.gds.5 b/testdata/drc/drcSimpleTests_au70.gds.5 deleted file mode 100644 index 45c44d0224..0000000000 Binary files a/testdata/drc/drcSimpleTests_au70.gds.5 and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.1 b/testdata/drc/drcSimpleTests_au70d.gds.1 index 64400f0af9..5f609de5e1 100644 Binary files a/testdata/drc/drcSimpleTests_au70d.gds.1 and b/testdata/drc/drcSimpleTests_au70d.gds.1 differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.2 b/testdata/drc/drcSimpleTests_au70d.gds.2 index f97c70db76..0f8e531706 100644 Binary files a/testdata/drc/drcSimpleTests_au70d.gds.2 and b/testdata/drc/drcSimpleTests_au70d.gds.2 differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.3 b/testdata/drc/drcSimpleTests_au70d.gds.3 index 8ae2681d6b..6dd7d40ac6 100644 Binary files a/testdata/drc/drcSimpleTests_au70d.gds.3 and b/testdata/drc/drcSimpleTests_au70d.gds.3 differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.4 b/testdata/drc/drcSimpleTests_au70d.gds.4 deleted file mode 100644 index 1b302b0e51..0000000000 Binary files a/testdata/drc/drcSimpleTests_au70d.gds.4 and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_au70d.gds.5 b/testdata/drc/drcSimpleTests_au70d.gds.5 deleted file mode 100644 index d553ddd7f7..0000000000 Binary files a/testdata/drc/drcSimpleTests_au70d.gds.5 and /dev/null differ diff --git a/testdata/drc/drcSimpleTests_au71.gds b/testdata/drc/drcSimpleTests_au71.gds index d54a17202e..af9805450e 100644 Binary files a/testdata/drc/drcSimpleTests_au71.gds and b/testdata/drc/drcSimpleTests_au71.gds differ diff --git a/testdata/drc/drcSimpleTests_au71d.gds b/testdata/drc/drcSimpleTests_au71d.gds index 03554c7d1c..3d5a5ecf90 100644 Binary files a/testdata/drc/drcSimpleTests_au71d.gds and b/testdata/drc/drcSimpleTests_au71d.gds differ diff --git a/testdata/dxf/issue_1173_au.gds.gz b/testdata/dxf/issue_1173_au.gds.gz index 7c871771e9..50f1ce36db 100644 Binary files a/testdata/dxf/issue_1173_au.gds.gz and b/testdata/dxf/issue_1173_au.gds.gz differ diff --git a/testdata/dxf/issue_1422a_au.gds.gz b/testdata/dxf/issue_1422a_au.gds.gz index ede4e28ccf..358409f04e 100644 Binary files a/testdata/dxf/issue_1422a_au.gds.gz and b/testdata/dxf/issue_1422a_au.gds.gz differ diff --git a/testdata/dxf/issue_1422b_au.gds.gz b/testdata/dxf/issue_1422b_au.gds.gz index d6568bc32c..6e81c02884 100644 Binary files a/testdata/dxf/issue_1422b_au.gds.gz and b/testdata/dxf/issue_1422b_au.gds.gz differ diff --git a/testdata/dxf/issue_1422c_au.gds.gz b/testdata/dxf/issue_1422c_au.gds.gz index d1e7ab657d..30bdf278d3 100644 Binary files a/testdata/dxf/issue_1422c_au.gds.gz and b/testdata/dxf/issue_1422c_au.gds.gz differ diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index d7a7282bc6..80f1e17bf6 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -1146,8 +1146,8 @@ def test_bug109(self): 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() diff --git a/testdata/python/tlTest.py b/testdata/python/tlTest.py index 764864e88b..2e6f710f08 100644 --- a/testdata/python/tlTest.py +++ b/testdata/python/tlTest.py @@ -40,12 +40,12 @@ def test_1(self): self.assertEqual(str(type(res)).replace("class", "type"), "") 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"), "") 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"), "") self.assertEqual(str(res), "3.0") diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index 76b04f60ee..c6a64d8d7d 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -40,14 +40,18 @@ def test_1_Expression assert_equal(res.to_s, "") expr = RBA::Expression.eval("1+2") + assert_equal(expr.class.to_s == "Fixnum" || expr.class.to_s == "Integer", true) + 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 == "Fixnum" || res.class.to_s == "Integer", true) + assert_equal(res.to_s, "3") expr = RBA::Expression::new expr.var("a", 5)