diff --git a/Cargo.lock b/Cargo.lock index 149e67763f..8496553b61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -233,9 +233,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -302,9 +302,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" @@ -323,15 +323,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.28" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time", "wasm-bindgen", "windows-targets 0.48.5", ] @@ -355,9 +354,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.2" +version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive 4.4.2", @@ -383,7 +382,7 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "syn 1.0.109", ] @@ -395,9 +394,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -714,6 +713,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "flume" version = "0.10.14" @@ -886,7 +891,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1092,9 +1097,9 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b185e7d068d6820411502efa14d8fbf010750485399402156b72dd2a548ef8e9" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -1137,19 +1142,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.11", + "rustix 0.38.13", "windows-sys 0.48.0", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.11.0" @@ -1194,9 +1190,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libm" @@ -1229,9 +1225,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lld_rs" @@ -1295,7 +1291,7 @@ checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" dependencies = [ "beef", "fnv", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "regex-syntax 0.6.29", "syn 1.0.109", @@ -1312,9 +1308,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.2" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memoffset" @@ -1499,9 +1495,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -1512,9 +1508,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.92" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -1593,9 +1589,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -1723,7 +1719,7 @@ dependencies = [ "html-escape", "indexmap 2.0.0", "insta", - "itertools 0.11.0", + "itertools", "logos", "plc_ast", "plc_diagnostics", @@ -1771,7 +1767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "syn 1.0.109", "version_check", @@ -1783,7 +1779,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "version_check", ] @@ -1799,9 +1795,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1831,7 +1827,7 @@ version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", ] [[package]] @@ -1897,9 +1893,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", @@ -1909,9 +1905,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", @@ -1968,14 +1964,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.11" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.5", + "linux-raw-sys 0.4.7", "windows-sys 0.48.0", ] @@ -1991,7 +1987,7 @@ dependencies = [ "indexmap 2.0.0", "inkwell", "insta", - "itertools 0.11.0", + "itertools", "lazy_static", "lld_rs", "log", @@ -2082,16 +2078,16 @@ version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2118,9 +2114,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -2219,11 +2215,11 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ - "itertools 0.10.5", + "itertools", "nom", "unicode_categories", ] @@ -2288,7 +2284,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "208e3165167afd7f3881b16c1ef3f2af69fa75980897aac8874a0696516d12c2" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "sqlx-core", "sqlx-macros-core", @@ -2307,7 +2303,7 @@ dependencies = [ "heck", "hex", "once_cell", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "serde", "serde_json", @@ -2425,10 +2421,11 @@ dependencies = [ [[package]] name = "stringprep" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -2462,18 +2459,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.29" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", "unicode-ident", ] @@ -2502,7 +2499,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.0", "redox_syscall", - "rustix 0.38.11", + "rustix 0.38.13", "windows-sys 0.48.0", ] @@ -2523,33 +2520,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.33", ] [[package]] @@ -2595,9 +2581,9 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", ] [[package]] @@ -2623,9 +2609,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2707,12 +2693,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2738,9 +2718,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", "wasm-bindgen-shared", ] @@ -2772,9 +2752,9 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.66", + "proc-macro2 1.0.67", "quote 1.0.33", - "syn 2.0.29", + "syn 2.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2797,13 +2777,14 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.13", ] [[package]] @@ -3005,7 +2986,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-std", - "clap 4.4.2", + "clap 4.4.3", "plc_ast", "plc_source", "rusty", diff --git a/compiler/plc_xml/src/lib.rs b/compiler/plc_xml/src/lib.rs index 4e1ff81915..3823988b1b 100644 --- a/compiler/plc_xml/src/lib.rs +++ b/compiler/plc_xml/src/lib.rs @@ -17,4 +17,4 @@ pub(crate) mod model { pub mod variables; } mod reader; -mod serializer; +pub(crate) mod serializer; diff --git a/compiler/plc_xml/src/model/block.rs b/compiler/plc_xml/src/model/block.rs index 884929a4fd..fe3b07db38 100644 --- a/compiler/plc_xml/src/model/block.rs +++ b/compiler/plc_xml/src/model/block.rs @@ -37,6 +37,7 @@ impl<'xml> Parseable for Block<'xml> { let Some(tag) = tag else { unreachable!() }; let attributes = get_attributes(tag.attributes())?; let mut variables = Vec::new(); + loop { match reader.read_event().map_err(Error::ReadEvent)? { Event::Start(tag) => match tag.name().as_ref() { @@ -67,23 +68,18 @@ mod tests { use crate::{ model::block::Block, reader::{get_start_tag, Reader}, - serializer::{XBlock, XInOutVariables, XInputVariables, XOutputVariables, XVariable}, + serializer::{SBlock, SVariable}, xml_parser::Parseable, }; #[test] fn add_block() { - let content = XBlock::init("1", "ADD", "0") - .with_input_variables( - XInputVariables::new() - .with_variable(XVariable::init("a", false).with_connection_in_initialized("1")) - .with_variable(XVariable::init("b", false).with_connection_in_initialized("2")), - ) - .with_inout_variables(XInOutVariables::new().close()) - .with_output_variables( - XOutputVariables::new() - .with_variable(XVariable::init("c", false).with_connection_out_initialized()), - ) + let content = SBlock::init("ADD", 1, 0) + .with_input(vec![ + &SVariable::new().with_name("a").connect(1), + &SVariable::new().with_name("b").connect(2), + ]) + .with_output(vec![&SVariable::new().with_name("c")]) .serialize(); let mut reader = Reader::new(&content); diff --git a/compiler/plc_xml/src/model/body.rs b/compiler/plc_xml/src/model/body.rs index ffccd9b679..420dc2fd22 100644 --- a/compiler/plc_xml/src/model/body.rs +++ b/compiler/plc_xml/src/model/body.rs @@ -43,13 +43,13 @@ mod tests { use crate::{ model::body::Body, reader::{get_start_tag, Reader}, - serializer::{XBlock, XBody, XFbd, XInOutVariables, XInputVariables, XOutputVariables, XVariable}, + serializer::{SBlock, SBody, SVariable}, xml_parser::Parseable, }; #[test] fn empty() { - let content = XBody::new().with_fbd(XFbd::new().close()).serialize(); + let content = SBody::new().with_fbd(vec![]).serialize(); let mut reader = Reader::new(&content); assert_debug_snapshot!(Body::visit(&mut reader, None).unwrap()); @@ -57,26 +57,14 @@ mod tests { #[test] fn fbd_with_add_block() { - let content = XBody::new() - .with_fbd( - XFbd::new().with_block( - XBlock::init("1", "ADD", "0") - .with_input_variables( - XInputVariables::new() - .with_variable( - XVariable::init("a", false).with_connection_in_initialized("1"), - ) - .with_variable( - XVariable::init("b", false).with_connection_in_initialized("2"), - ), - ) - .with_inout_variables(XInOutVariables::new().close()) - .with_output_variables( - XOutputVariables::new() - .with_variable(XVariable::init("c", false).with_connection_out_initialized()), - ), - ), - ) + let content = SBody::new() + .with_fbd(vec![&SBlock::init("ADD", 1, 0) + .with_input(vec![ + &SVariable::new().with_name("a").connect(1), + &SVariable::new().with_name("b").connect(2), + ]) + .with_output(vec![&SVariable::new().with_name("c")]) + .with_inout(vec![])]) .serialize(); let mut reader = Reader::new(&content); diff --git a/compiler/plc_xml/src/model/fbd.rs b/compiler/plc_xml/src/model/fbd.rs index 2639f146c2..5951948407 100644 --- a/compiler/plc_xml/src/model/fbd.rs +++ b/compiler/plc_xml/src/model/fbd.rs @@ -114,6 +114,7 @@ impl<'xml> Node<'xml> { impl<'xml> Parseable for FunctionBlockDiagram<'xml> { fn visit(reader: &mut Reader, _tag: Option) -> Result { let mut nodes = IndexMap::new(); + loop { match reader.read_event().map_err(Error::ReadEvent)? { Event::Start(tag) => match tag.name().as_ref() { @@ -292,16 +293,13 @@ impl<'xml> ConnectionResolver<'xml> for NodeIndex<'xml> { #[cfg(test)] mod tests { + use crate::serializer::{SBlock, SInVariable, SOutVariable, SVariable, YFbd}; use crate::{ model::{ connector::Connector, fbd::FunctionBlockDiagram, pou::Pou, project::Project, variables::FunctionBlockVariable, }, reader::{get_start_tag, Reader}, - serializer::{ - XBlock, XConnection, XConnectionPointIn, XConnectionPointOut, XExpression, XFbd, XInOutVariables, - XInVariable, XInputVariables, XOutVariable, XOutputVariables, XPosition, XRelPosition, XVariable, - }, xml_parser::Parseable, }; use insta::assert_debug_snapshot; @@ -311,47 +309,18 @@ mod tests { #[test] fn add_block() { - let content = XFbd::new() - .with_block( - XBlock::init("1", "ADD", "0") - .with_input_variables( - XInputVariables::new() - .with_variable(XVariable::init("a", false).with_connection_in_initialized("1")) - .with_variable(XVariable::init("b", false).with_connection_in_initialized("2")), - ) - .with_inout_variables(XInOutVariables::new().close()) - .with_output_variables( - XOutputVariables::new() - .with_variable(XVariable::init("c", false).with_connection_out_initialized()), - ), - ) - .with_in_variable( - XInVariable::init("2", false) - .with_position(XPosition::new().close()) - .with_connection_point_out( - XConnectionPointOut::new().with_rel_position(XRelPosition::init()), - ) - .with_expression(XExpression::new().with_data("a")), - ) - .with_in_variable( - XInVariable::init("3", false) - .with_position(XPosition::new().close()) - .with_connection_point_out( - XConnectionPointOut::new().with_rel_position(XRelPosition::init()), - ) - .with_expression(XExpression::new().with_data("b")), - ) - .with_out_variable( - XOutVariable::init("4", false) - .with_position(XPosition::new().close()) - .with_attribute("executionOrderId", "1") - .with_connection_point_in( - XConnectionPointIn::new() - .with_rel_position(XRelPosition::init()) - .with_connection(XConnection::new().with_attribute("refLocalId", "1")), - ) - .with_expression(XExpression::new().with_data("c")), - ) + let content = YFbd::new() + .children(vec![ + &SBlock::init("ADD", 1, 0) + .with_input(vec![ + &SVariable::new().with_name("a").connect(1), + &SVariable::new().with_name("b").connect(2), + ]) + .with_output(vec![&SVariable::new().with_name("c")]), + &SInVariable::new().with_id(2).with_expression("a"), + &SInVariable::new().with_id(3).with_expression("b"), + &SOutVariable::new().with_id(4).with_expression("c").with_execution_id(1).connect(1), + ]) .serialize(); let mut reader = Reader::new(&content); diff --git a/compiler/plc_xml/src/model/pou.rs b/compiler/plc_xml/src/model/pou.rs index 096a95e916..412aa537e9 100644 --- a/compiler/plc_xml/src/model/pou.rs +++ b/compiler/plc_xml/src/model/pou.rs @@ -121,50 +121,24 @@ impl FromStr for PouType { mod tests { use insta::assert_debug_snapshot; + use crate::serializer::SPou; use crate::{ model::pou::Pou, reader::{get_start_tag, Reader}, - serializer::{ - XAddData, XBody, XContent, XData, XFbd, XInterface, XLocalVars, XPou, XTextDeclaration, - }, xml_parser::{self, Parseable}, }; #[test] fn empty() { - let content = XPou::new() - .with_attribute("xmlns", "http://www.plcopen.org/xml/tc6_0201") - .with_attribute("name", "foo") - .with_attribute("pouType", "program") - .with_interface( - XInterface::new().with_local_vars(XLocalVars::new().close()).with_add_data( - XAddData::new().with_data_data( - XData::new() - .with_attribute("name", "www.bachmann.at/plc/plcopenxml") - .with_attribute("handleUnknown", "implementation") - .with_text_declaration(XTextDeclaration::new().with_content( - XContent::new().with_data( - r#" -PROGRAM foo -VAR - -END_VAR - "#, - ), - )), - ), - ), - ) - .with_body(XBody::new().with_fbd(XFbd::new().close())) - .serialize(); + let declaration = "PROGRAM foo VAR END_VAR"; + let content = SPou::init("foo", "program", declaration).with_fbd(vec![]).serialize(); assert_debug_snapshot!(xml_parser::visit(&content)); } #[test] fn poutype_program() { - let content = - XPou::new().with_attribute("name", "foo").with_attribute("pouType", "program").serialize(); + let content = SPou::init("foo", "program", "").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -173,8 +147,7 @@ END_VAR #[test] fn poutype_function() { - let content = - XPou::new().with_attribute("name", "foo").with_attribute("pouType", "function").serialize(); + let content = SPou::init("foo", "function", "").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -183,8 +156,7 @@ END_VAR #[test] fn poutype_function_block() { - let content = - XPou::new().with_attribute("name", "foo").with_attribute("pouType", "functionBlock").serialize(); + let content = SPou::init("foo", "functionBlock", "").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -193,8 +165,7 @@ END_VAR #[test] fn poutype_unknown() { - let content = - XPou::new().with_attribute("name", "foo").with_attribute("pouType", "asdasd").serialize(); + let content = SPou::init("foo", "asdasd", "").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); diff --git a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__empty.snap b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__empty.snap index d969903446..a8a59fc7b8 100644 --- a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__empty.snap +++ b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__empty.snap @@ -18,7 +18,7 @@ Ok( Interface { add_data: Some( Data { - content: "PROGRAM foo\nVAR\n\nEND_VAR\nEND_PROGRAM", + content: "PROGRAM foo VAR END_VAR\nEND_PROGRAM", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function.snap b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function.snap index f3d9c30906..7b49a25bf3 100644 --- a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function.snap +++ b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function.snap @@ -12,6 +12,10 @@ Ok( }, }, actions: [], - interface: None, + interface: Some( + Interface { + add_data: None, + }, + ), }, ) diff --git a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function_block.snap b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function_block.snap index f7c6d393d1..e903493f46 100644 --- a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function_block.snap +++ b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_function_block.snap @@ -12,6 +12,10 @@ Ok( }, }, actions: [], - interface: None, + interface: Some( + Interface { + add_data: None, + }, + ), }, ) diff --git a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_program.snap b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_program.snap index 1579ff3e12..2e12edeca2 100644 --- a/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_program.snap +++ b/compiler/plc_xml/src/model/snapshots/plc_xml__model__pou__tests__poutype_program.snap @@ -12,6 +12,10 @@ Ok( }, }, actions: [], - interface: None, + interface: Some( + Interface { + add_data: None, + }, + ), }, ) diff --git a/compiler/plc_xml/src/model/variables.rs b/compiler/plc_xml/src/model/variables.rs index 72f9d9ecee..c34db5a8f2 100644 --- a/compiler/plc_xml/src/model/variables.rs +++ b/compiler/plc_xml/src/model/variables.rs @@ -223,19 +223,18 @@ impl Parseable for BlockVariable { mod tests { use insta::assert_debug_snapshot; + use crate::serializer::{ + SInOutVariables, SInVariable, SInputVariables, SOutVariable, SOutputVariables, SVariable, + }; use crate::{ model::variables::{BlockVariable, FunctionBlockVariable}, reader::{get_start_tag, Reader}, - serializer::{ - XExpression, XInOutVariables, XInVariable, XInputVariables, XOutVariable, XOutputVariables, - XVariable, - }, xml_parser::Parseable, }; #[test] fn block_input_variable() { - let content = XInputVariables::new().with_variable(XVariable::init("", false)).serialize(); + let content = SInputVariables::new().children(vec![&SVariable::new().with_name("")]).serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -245,7 +244,7 @@ mod tests { #[test] fn block_output_variable() { - let content = XOutputVariables::new().with_variable(XVariable::init("", false)).serialize(); + let content = SOutputVariables::new().children(vec![&SVariable::new().with_name("")]).serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -255,7 +254,7 @@ mod tests { #[test] fn block_inout_variable() { - let content = XInOutVariables::new().with_variable(XVariable::init("", false)).serialize(); + let content = SInOutVariables::new().children(vec![&SVariable::new().with_name("")]).serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -265,8 +264,7 @@ mod tests { #[test] fn fbd_in_variable() { - let content = - XInVariable::init("0", false).with_expression(XExpression::new().with_data("a")).serialize(); + let content = SInVariable::id(0).with_expression("a").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -275,8 +273,7 @@ mod tests { #[test] fn fbd_out_variable() { - let content = - XOutVariable::init("0", false).with_expression(XExpression::new().with_data("a")).serialize(); + let content = SOutVariable::id(0).with_expression("a").serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); diff --git a/compiler/plc_xml/src/serializer.rs b/compiler/plc_xml/src/serializer.rs index 1ddfe50ff3..6085b33461 100644 --- a/compiler/plc_xml/src/serializer.rs +++ b/compiler/plc_xml/src/serializer.rs @@ -1,415 +1,365 @@ -type Attributes = Vec<(&'static str, &'static str)>; +use std::collections::HashMap; -/// Number of spaces to use when indenting XML -const INDENT_SPACES: usize = 4; +#[derive(Clone)] +pub struct Node { + name: &'static str, + children: Vec, + + /// XML attributes, e.g. `` where `x` is the attribute + /// + /// Design Note: We use a HashMap here to avoid duplicates but also update existing values in case of + /// repeated function calls, e.g. `with_attribute("x", 1)` and `with_attribute("x", 2)` where the value of + /// x has been updated from 1 to 2. + attributes: HashMap<&'static str, &'static str>, -#[derive(Debug, Default)] -enum Content { - Node(Vec), - Data(&'static str), + /// Indicates if an element has a closed form, e.g. `` + closed: bool, - #[default] - Empty, + /// Indicates if an element has some text wrapped inside itself, e.g. `a + b` + content: Option<&'static str>, } -impl<'b> Content { - fn push(&mut self, node: Node) { - let mut nodes = match self.take() { - Content::Node(nodes) => nodes, - Content::Empty => vec![], - _ => unreachable!("cannot push onto data field"), - }; +pub trait IntoNode { + fn inner(&self) -> Node; +} - nodes.push(node); +impl Node { + fn new(name: &'static str) -> Self { + Self { name, attributes: HashMap::new(), children: Vec::new(), closed: false, content: None } + } - self.replace(Content::Node(nodes)); + fn attribute(mut self, key: &'static str, value: &'static str) -> Self { + self.attributes.insert(key, value); + self } - fn replace(&'b mut self, other: Content) -> Content { - std::mem::replace(self, other) + fn child(mut self, node: &dyn IntoNode) -> Self { + self.children.push(node.inner()); + self } - fn take(&'b mut self) -> Content { - std::mem::take(self) + fn children(mut self, nodes: Vec<&dyn IntoNode>) -> Self { + self.children.extend(nodes.into_iter().map(IntoNode::inner)); + self } - fn iter(self) -> impl Iterator { - match self { - Content::Node(nodes) => nodes, - _ => vec![], + fn close(mut self) -> Self { + self.closed = true; + self + } + + fn indent(level: usize) -> String { + " ".repeat(level * 4) + } + + fn serialize_content(indent: &str, name: &'static str, content: &'static str) -> String { + format!("{indent}<{name}>{content}\n") + } + + #[allow(unused_assignments)] + pub fn serialize(&self, level: usize) -> String { + let (name, indent) = (self.name, Node::indent(level)); + let attributes = self.attributes.iter().map(|(key, value)| format!("{key}=\"{value}\"")); + let attributes_str = attributes.collect::>().join(" "); + let mut result = String::new(); + + if self.closed { + return format!("{indent}<{name} {attributes_str}/>\n"); } - .into_iter() + + if let Some(content) = self.content { + return Node::serialize_content(&indent, name, content); + } + + result = format!("{indent}<{name} {attributes_str}>\n"); + self.children.iter().for_each(|child| result = format!("{result}{}", child.serialize(level + 1))); + result = format!("{result}{indent}\n"); + + result } } -#[derive(Debug)] -struct Node { - name: &'static str, - attributes: Attributes, - closed: bool, - content: Content, -} +macro_rules! newtype_impl { + ($name_struct:ident, $name_node:expr, $negatable:expr) => { + pub struct $name_struct(Node); -impl Node { - fn attributes(&self) -> String { - let mut fmt = String::new(); - for attr in &self.attributes { - fmt = format!(r#"{fmt}{key}="{value}" "#, key = attr.0, value = attr.1) + impl IntoNode for $name_struct { + fn inner(&self) -> Node { + self.0.clone() + } } - fmt - } - - // TODO: ✨ Beautify ✨ this - fn serialize(self, level: usize) -> String { - let (indent, name, attributes) = (" ".repeat(level * INDENT_SPACES), self.name, self.attributes()); - let mut fmt = String::new(); - match self.content { - Content::Data(data) => fmt = format!("{indent}<{name} {attributes}>{data}\n",), - _ => { - if self.closed { - fmt = format!( - "{indent}<{name} {attributes}/>\n", - indent = " ".repeat(level * INDENT_SPACES), - name = self.name, - attributes = self.attributes() - ); + impl $name_struct { + pub fn new() -> Self { + match $negatable { + true => Self(Node::new($name_node).attribute("negated", "false")), + false => Self(Node::new($name_node)), } + } - if !self.closed { - fmt = format!( - "{indent}<{name} {attributes}>\n", - indent = " ".repeat(level * INDENT_SPACES), - name = self.name, - attributes = self.attributes() - ); - } + pub fn id(local_id: i32) -> Self { + let new = $name_struct::new(); + new.with_id(local_id) + } - for node in self.content.iter() { - fmt = format!("{fmt}{}", node.serialize(level + 1)); - } + fn attribute(self, key: &'static str, value: &'static str) -> Self { + Self(self.inner().attribute(key, value)) + } - if !self.closed { - fmt = format!( - "{fmt}{indent}\n", - indent = " ".repeat(level * INDENT_SPACES), - name = &self.name - ); + fn maybe_attribute(self, key: &'static str, value: Option<&'static str>) -> Self { + match value { + Some(value) => Self(self.inner().attribute(key, value)), + None => self, } } - }; - #[cfg(feature = "debug")] - println!("{fmt}"); + fn child(self, node: &dyn IntoNode) -> Self { + Self(self.inner().child(node)) + } - fmt - } + pub fn children(self, nodes: Vec<&dyn IntoNode>) -> Self { + Self(self.inner().children(nodes)) + } + + pub fn serialize(self) -> String { + self.inner().serialize(0) + } + + pub fn with_id(self, id: T) -> Self { + self.attribute("localId", Box::leak(id.to_string().into_boxed_str())) + } + + pub fn with_ref_id(self, id: T) -> Self { + self.attribute("refLocalId", Box::leak(id.to_string().into_boxed_str())) + } + + pub fn with_execution_id(self, id: T) -> Self { + self.attribute("executionOrderId", Box::leak(id.to_string().into_boxed_str())) + } + + fn close(self) -> Self { + Self(self.inner().close()) + } + } + }; } -pub(crate) fn with_header(data: &str) -> String { - let header = r#""#; - format!("{header}\n{data}") +newtype_impl!(SInVariable, "inVariable", true); +newtype_impl!(SOutVariable, "outVariable", true); +newtype_impl!(SInOutVariable, "inOutVariable", true); +newtype_impl!(SInterface, "interface", false); +newtype_impl!(SLocalVars, "localVars", false); +newtype_impl!(SAddData, "addData", false); +newtype_impl!(SData, "data", false); +newtype_impl!(STextDeclaration, "textDeclaration", false); +newtype_impl!(SContent, "content", false); +newtype_impl!(SPosition, "position", false); +newtype_impl!(SConnectionPointIn, "connectionPointIn", false); +newtype_impl!(SConnectionPointOut, "connectionPointOut", false); +newtype_impl!(SRelPosition, "relPosition", false); +newtype_impl!(SConnection, "connection", false); +newtype_impl!(SBlock, "block", false); +newtype_impl!(SBody, "body", false); +newtype_impl!(SPou, "pou", false); +newtype_impl!(SInputVariables, "inputVariables", false); +newtype_impl!(SOutputVariables, "outputVariables", false); +newtype_impl!(SInOutVariables, "inOutVariables", false); +newtype_impl!(SVariable, "variable", true); +newtype_impl!(YFbd, "FBD", false); +newtype_impl!(SExpression, "expression", false); +newtype_impl!(SReturn, "return", false); +newtype_impl!(SNegate, "negated", false); +newtype_impl!(SConnector, "connector", false); +newtype_impl!(SContinuation, "continuation", false); +newtype_impl!(SJump, "jump", false); +newtype_impl!(SLabel, "label", false); + +impl SInVariable { + pub fn connect(mut self, ref_local_id: i32) -> Self { + self = self.child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id))); + self + } + + pub fn with_expression(self, expression: &'static str) -> Self { + self.child(&SExpression::expression(expression)) + } } -// For `declare_type_and_extend_if_needed! { (Pou, "pou", (Body, with_body)) }` the macro will expand to -// -// pub(crate) struct Pou(Node); -// -// impl Pou { -// pub fn new() -> Self { -// Self(Node { name: "pou", attributes: vec![], closed: false, nodes: vec![] }) -// } -// -// ... the remaining non-optional functions in the impl block ... -// -// ... the optional extend method -// pub(crate) fn with_body(arg: Body) -> Self { -// self.get_inner_ref_mut().nodes.push(arg.get_inner()); -// self -// } -// } -// TODO: revisit visiblity of functions -macro_rules! declare_type_and_extend_if_needed { - ($(($name:ident, $name_xml:expr, $(($arg:ty, $fn_name:ident)),*),) +) => { - $( - // will be implemented for every $name - #[derive(Debug)] - pub(crate) struct $name(Node); - impl $name { - pub fn new() -> Self { - Self(Node { name: $name_xml, attributes: vec![], closed: false, content: Content::Empty }) - } +impl SOutVariable { + pub fn connect(mut self, ref_local_id: i32) -> Self { + self = self + .child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id).close())); + self + } - pub fn with_attribute(mut self, key: &'static str, value: &'static str) -> Self { - self.0.attributes.push((key, value)); - self - } + pub fn connect_name(mut self, ref_local_id: i32, name: &'static str) -> Self { + self = + self.child(&SConnectionPointIn::new().child( + &SConnection::new().with_ref_id(ref_local_id).attribute("formalParameter", name).close(), + )); + self + } - pub fn with_local_id(mut self, value: &'static str) -> Self { - self.0.attributes.push(("localId", value)); - self - } + pub fn with_expression(self, expression: &'static str) -> Self { + self.child(&SExpression::expression(expression)) + } +} - pub fn with_execution_order_id(mut self, value: &'static str) -> Self { - self.0.attributes.push(("executionOrderId", value)); - self - } +impl SInOutVariable { + pub fn with_expression(self, expression: &'static str) -> Self { + self.child(&SExpression::expression(expression)) + } +} - pub fn with_data(mut self, data: &'static str) -> Self { - self.0.content = Content::Data(data); - self - } +impl SReturn { + pub fn init(local_id: i32, execution_order: i32) -> Self { + Self::new().with_id(local_id).with_execution_id(execution_order) + } - pub fn close(mut self) -> Self { - self.0.closed = true; - self - } + pub fn connect(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id))) + } - pub fn serialize(self) -> String { - self.0.serialize(0) - } + pub fn negate(self, value: bool) -> Self { + self.child(&SAddData::new().child(&SData::new().child( + &SNegate::new().attribute("value", Box::leak(value.to_string().into_boxed_str())).close(), + ))) + } +} - fn get_inner(self) -> Node { - self.0 - } +impl SContent { + pub fn with_declaration(mut self, content: &'static str) -> Self { + self.0.content = Some(content); + self + } +} - fn get_inner_ref_mut(&mut self) -> &mut Node { - &mut self.0 - } +impl SPou { + pub fn init(name: &'static str, kind: &'static str, declaration: &'static str) -> Self { + Self::new() + .attribute("xmlns", "http://www.plcopen.org/xml/tc6_0201") + .attribute("name", name) + .attribute("pouType", kind) + .child(&SInterface::new().children(vec![ + &SLocalVars::new().close(), + &SAddData::new().child( + &SData::new() + .attribute("name", "www.bachmann.at/plc/plcopenxml") + .attribute("handleUnknown", "implementation") + .child( + &STextDeclaration::new() + .child(&SContent::new().with_declaration(declaration)), + ), + ), + ])) + } - // this part is optional. - $( - pub(crate) fn $fn_name(mut self, value: $arg) -> Self { - self.get_inner_ref_mut().content.push(value.get_inner()); - self - })* - })* + /// Implicitly wraps the fbd in a block node, i.e. ... + pub fn with_fbd(self, children: Vec<&dyn IntoNode>) -> Self { + self.child(&SBody::new().child(&YFbd::new().children(children))) } } -declare_type_and_extend_if_needed! { - ( - XPou, "pou", - (XBody, with_body), - (XInterface, with_interface) - ), - ( - XBlock, "block", - (XInOutVariables, with_inout_variables), - (XInputVariables, with_input_variables), - (XOutputVariables, with_output_variables) - ), - ( - - XBody, "body", - (XFbd, with_fbd) - ), - ( - XConnectionPointIn, "connectionPointIn", - (XConnection, with_connection), - (XRelPosition, with_rel_position) - ), - ( - XConnectionPointOut, "connectionPointOut", - (XConnection, with_connection), - (XRelPosition, with_rel_position) - ), - ( - XFbd, "FBD", - (XBlock, with_block), - (XInVariable, with_in_variable), - (XOutVariable, with_out_variable), - (XContinuation, with_continuation), - (XConnector, with_connector), - (XReturn, with_return), - (XJump, with_jump), - (XLabel, with_label) - ), - ( - XVariable, "variable", - (XConnectionPointIn, with_connection_in), - (XConnectionPointOut, with_connection_out) - ), - ( - XInVariable, "inVariable", - (XConnectionPointOut, with_connection_point_out), - (XPosition, with_position), - (XExpression, with_expression) - ), - ( - XOutVariable, "outVariable", - (XPosition, with_position), - (XConnectionPointIn, with_connection_point_in), - (XExpression, with_expression) - ), - ( - XInOutVariables, "inOutVariables", - (XVariable, with_variable) - ), - ( - XInputVariables, "inputVariables", - (XVariable, with_variable) - ), - ( - XOutputVariables, "outputVariables", - (XVariable, with_variable) - ), - ( - XInterface, "interface", - (XLocalVars, with_local_vars), - (XAddData, with_add_data) - ), - (XLocalVars, "localVars",), - ( - XAddData, "addData", - (XData, with_data_data) - ), - ( - XData, "data", - (XTextDeclaration, with_text_declaration), - (XNegated, with_negated_field) - ), - ( - XTextDeclaration, "textDeclaration", - (XContent, with_content) - ), - (XContent, "content",), - - // these are not being extended: - (XPosition, "position",), - (XRelPosition, "relPosition",), - (XConnection, "connection",), - (XExpression, "expression",), - - ( - XContinuation, "continuation", - (XPosition, with_position), - (XConnectionPointOut, with_connection_point_out) - ), - ( - XConnector, "connector", - (XPosition, with_position), - (XConnectionPointIn, with_connection_point_in) - ), - ( - XReturn, "return", - (XConnectionPointIn, with_connection_point_in), - (XAddData, with_add_data) - ), - ( - XJump, "jump", - (XConnectionPointIn, with_connection_point_in), - (XAddData, with_add_data) - ), - (XLabel, "label",), - ( - XNegated, "negated", - ), +impl SBlock { + pub fn init(name: &'static str, local_id: i32, execution_order_id: i32) -> Self { + Self::new().with_name(name).with_id(local_id).with_execution_id(execution_order_id) + } + + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("typeName", name) + } + + pub fn with_input(self, variables: Vec<&dyn IntoNode>) -> Self { + self.child(&SInputVariables::new().children(variables)) + } + + pub fn with_output(self, variables: Vec<&dyn IntoNode>) -> Self { + self.child(&SOutputVariables::new().children(variables)) + } + + pub fn with_inout(self, variables: Vec<&dyn IntoNode>) -> Self { + self.child(&SInOutVariables::new().children(variables)) + } } -#[cfg(test)] -mod tests { +impl SBody { + pub fn with_fbd(self, children: Vec<&dyn IntoNode>) -> Self { + Self::new().child(&YFbd::new().children(children)) + } +} - use crate::serializer::{XConnection, XConnectionPointIn, XConnectionPointOut, XRelPosition}; +impl SInputVariables { + pub fn with_variables(variables: Vec<&dyn IntoNode>) -> Self { + Self::new().children(variables) + } +} - use super::{ - XAddData, XBlock, XContent, XData, XInVariable, XInterface, XNegated, XOutVariable, XPou, - XTextDeclaration, XVariable, - }; +impl SOutputVariables { + pub fn with_variables(variables: Vec<&dyn IntoNode>) -> Self { + Self::new().children(variables) + } +} - // convenience methods to reduce amount of boiler-plate-code - impl XVariable { - pub(crate) fn init(name: &'static str, negated: bool) -> Self { - XVariable::new() - .with_attribute("formalParameter", name) - .with_attribute("negated", if negated { "true" } else { "false" }) - } +impl SVariable { + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("formalParameter", name) + } - pub(crate) fn with_connection_in_initialized(self, ref_lid: &'static str) -> Self { - self.with_connection_in( - XConnectionPointIn::new() - .with_rel_position(XRelPosition::init().close()) - .with_connection(XConnection::new().with_attribute("refLocalId", ref_lid).close()), - ) - } + pub fn connect(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id).close())) + } - pub(crate) fn with_connection_out_initialized(self) -> Self { - self.with_connection_out( - XConnectionPointOut::new().with_rel_position(XRelPosition::init().close()), - ) - } + pub fn connect_out(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointOut::new().child(&SConnection::new().with_ref_id(ref_local_id).close())) } +} - impl XRelPosition { - pub(crate) fn init() -> Self { - XRelPosition::new().with_attribute("x", "0").with_attribute("y", "0").close() - } +impl SExpression { + pub fn expression(expression: &'static str) -> Self { + let mut node = Self::new(); + node.0.content = Some(expression); + node } +} - impl XConnectionPointIn { - pub(crate) fn with_ref(ref_local_id: &'static str) -> Self { - XConnectionPointIn::new() - .with_rel_position(XRelPosition::init().close()) - .with_connection(XConnection::new().with_attribute("refLocalId", ref_local_id).close()) - } +impl SConnector { + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("name", name) } - impl XConnectionPointOut { - pub(crate) fn with_rel_pos() -> Self { - XConnectionPointOut::new().with_rel_position(XRelPosition::init().close()) - } + pub fn connect(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id).close())) } +} - impl XInVariable { - pub(crate) fn init(local_id: &'static str, negated: bool) -> Self { - XInVariable::new() - .with_attribute("localId", local_id) - .with_attribute("negated", if negated { "true" } else { "false" }) - } +impl SContinuation { + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("name", name) } - impl XOutVariable { - pub(crate) fn init(local_id: &'static str, negated: bool) -> Self { - XOutVariable::new() - .with_attribute("localId", local_id) - .with_attribute("negated", if negated { "true" } else { "false" }) - } + pub fn connect_out(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointOut::new().child(&SConnection::new().with_ref_id(ref_local_id).close())) } +} - impl XBlock { - pub(crate) fn init(local_id: &'static str, type_name: &'static str, exec_id: &'static str) -> Self { - XBlock::new() - .with_attribute("localId", local_id) - .with_attribute("typeName", type_name) - .with_attribute("executionOrderId", exec_id) - } +impl SLabel { + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("label", name) } +} - impl XPou { - pub(crate) fn init(name: &'static str, pou_type: &'static str, declaration: &'static str) -> Self { - XPou::new() - .with_attribute("xmlns", "http://www.plcopen.org/xml/tc6_0201") - .with_attribute("name", name) - .with_attribute("pouType", pou_type) - .with_interface(XInterface::init_with_text_declaration(declaration)) - } +impl SJump { + pub fn with_name(self, name: &'static str) -> Self { + self.attribute("label", name) } - impl XInterface { - pub(crate) fn init_with_text_declaration(declaration: &'static str) -> Self { - XInterface::new().with_add_data(XAddData::new().with_data_data( - XData::new().with_text_declaration( - XTextDeclaration::new().with_content(XContent::new().with_data(declaration)), - ), - )) - } + pub fn connect(self, ref_local_id: i32) -> Self { + self.child(&SConnectionPointIn::new().child(&SConnection::new().with_ref_id(ref_local_id).close())) } - impl XAddData { - pub(crate) fn negated(is_negated: bool) -> Self { - XAddData::new().with_data_data(XData::new().with_negated_field( - XNegated::new().with_attribute("value", if is_negated { "true" } else { "false" }), - )) - } + pub fn negate(self) -> Self { + self.child( + &SAddData::new().child(&SData::new().child(&SNegate::new().attribute("value", "true").close())), + ) } } diff --git a/compiler/plc_xml/src/xml_parser/control.rs b/compiler/plc_xml/src/xml_parser/control.rs index 49142be9e0..dc296331f7 100644 --- a/compiler/plc_xml/src/xml_parser/control.rs +++ b/compiler/plc_xml/src/xml_parser/control.rs @@ -99,111 +99,48 @@ mod tests { use ast::provider::IdProvider; use insta::assert_debug_snapshot; + use crate::serializer::{SInVariable, SJump, SLabel, SOutVariable, SPou, SReturn}; use crate::{ model::control::Control, reader::{get_start_tag, Reader}, - serializer::{XAddData, XConnectionPointIn, XJump, XLabel, XReturn}, xml_parser::{self, Parseable}, }; #[test] fn simple_return() { - let content = XReturn::new() - .with_local_id("1") - .with_execution_order_id("2") - .with_connection_point_in(XConnectionPointIn::with_ref("3")) - .with_add_data(XAddData::negated(false)) - .serialize(); + let content = SReturn::id(1).with_execution_id(2).connect(3).negate(false).serialize(); - let reader = &mut Reader::new(&content); + let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); - assert_debug_snapshot!(Control::visit(reader, tag).unwrap()); + assert_debug_snapshot!(Control::visit(&mut reader, tag).unwrap()); } #[test] fn simple_negated_return() { - let content = XReturn::new() - .with_local_id("1") - .with_execution_order_id("2") - .with_connection_point_in(XConnectionPointIn::with_ref("3")) - .with_add_data(XAddData::negated(true)) - .serialize(); + let content = SReturn::id(1).with_execution_id(2).connect(3).negate(true).serialize(); - let reader = &mut Reader::new(&content); + let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); - assert_debug_snapshot!(Control::visit(reader, tag).unwrap()); + assert_debug_snapshot!(Control::visit(&mut reader, tag).unwrap()); } #[test] fn jump_to_label() { - let content = r###" - - - - - - - - - PROGRAM program_0 - VAR - x: BOOL := 0; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###; - - assert_debug_snapshot!(xml_parser::visit(content)); + let declaration = "PROGRAM program_0 VAR x : BOOL := 0; END_VAR"; + let content = SPou::init("program_0", "program", declaration).with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_name("lbl").with_execution_id(0), + &SJump::id(2).with_name("lbl").with_execution_id(1).connect(0), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]); + + assert_debug_snapshot!(xml_parser::visit(&content.serialize())); } #[test] fn unconnected_jump_to_label() { - let content = XJump::new().with_local_id("1").with_execution_order_id("2").serialize(); + let content = SJump::id(1).with_execution_id(2).serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -212,74 +149,40 @@ mod tests { #[test] fn negated_jump() { - let content = r###" - - - - - - - - - PROGRAM program_0 - VAR - x: BOOL := 0; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###; - - assert_debug_snapshot!(xml_parser::visit(content)); + let declaration = "PROGRAM program_0 VAR x : BOOL := 0; END_VAR"; + let content = SPou::init("program_0", "program", declaration).with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_name("lbl").with_execution_id(0), + &SJump::id(2).with_name("lbl").with_execution_id(1).connect(0).negate(), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]); + + assert_debug_snapshot!(xml_parser::visit(&content.serialize())); + } + + #[test] + fn negated_jump_ast() { + let declaration = "PROGRAM program_0 VAR x : BOOL := 0; END_VAR"; + let content = SPou::init("program_0", "program", declaration) + .with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_name("lbl").with_execution_id(0), + &SJump::id(2).with_name("lbl").with_execution_id(1).connect(0).negate(), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]) + .serialize(); + + let (ast, diagnostics) = + xml_parser::parse(&content.into(), ast::ast::LinkageType::Internal, IdProvider::default()); + assert_debug_snapshot!(ast); + assert!(diagnostics.is_empty()); } #[test] fn label_parsed() { - let content = XLabel::new().with_local_id("1").with_execution_order_id("2").serialize(); + let content = SLabel::id(1).with_execution_id(2).serialize(); let mut reader = Reader::new(&content); let tag = get_start_tag(reader.read_event().unwrap()); @@ -288,67 +191,16 @@ mod tests { #[test] fn jump_and_label_converted_to_ast() { - let content = r###" - - - - - - - - - PROGRAM program_0 - VAR - x: BOOL := 0; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###.to_string(); + let declaration = "PROGRAM program_0 VAR x : BOOL := 0; END_VAR"; + let content = SPou::init("program_0", "program", declaration) + .with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_name("lbl").with_execution_id(0), + &SJump::id(2).with_name("lbl").with_execution_id(1).connect(0), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]) + .serialize(); let (ast, diagnostics) = xml_parser::parse(&content.into(), ast::ast::LinkageType::Internal, IdProvider::default()); @@ -358,63 +210,16 @@ mod tests { #[test] fn unconnected_jump_generated_as_empty_statement() { - let content = r###" - - - - - - - - - PROGRAM program_0 - VAR - x: BOOL := 0; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###.to_string(); + let declaration = "PROGRAM program_0 VAR x : BOOL := 0; END_VAR"; + let content = SPou::init("program_0", "program", declaration) + .with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_name("lbl").with_execution_id(0), + &SJump::id(2).with_name("lbl").with_execution_id(1), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]) + .serialize(); let (ast, diagnostics) = xml_parser::parse(&content.into(), ast::ast::LinkageType::Internal, IdProvider::default()); @@ -422,136 +227,17 @@ mod tests { assert_debug_snapshot!(diagnostics); } - #[test] - fn negated_jump_ast() { - let content = r###" - - - - - - - - - PROGRAM program_0 - VAR - x: BOOL := 0; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###.to_string(); - - let (ast, diagnostics) = - xml_parser::parse(&content.into(), ast::ast::LinkageType::Internal, IdProvider::default()); - assert_debug_snapshot!(ast); - assert!(diagnostics.is_empty()); - } - #[test] fn unnamed_controls() { - let content = r###" - - - - - - - - - PROGRAM program_0 - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - x - - - - - - - FALSE - - - - - "###.to_string(); + let content = SPou::init("program_0", "program", "PROGRAM program_0") + .with_fbd(vec![ + &SInVariable::id(0).with_expression("x"), + &SLabel::id(1).with_execution_id(0), + &SJump::id(2).with_execution_id(1).connect(0), + &SOutVariable::id(3).with_execution_id(2).with_expression("x").connect(4), + &SInVariable::id(4).with_expression("FALSE"), + ]) + .serialize(); let (ast, diagnostics) = xml_parser::parse(&content.into(), ast::ast::LinkageType::Internal, IdProvider::default()); diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__jump_to_label.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__jump_to_label.snap index da4b2450ae..6e60905cfa 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__jump_to_label.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__jump_to_label.snap @@ -1,6 +1,6 @@ --- source: compiler/plc_xml/src/xml_parser/control.rs -expression: "xml_parser::visit(content)" +expression: "xml_parser::visit(&content.serialize())" --- Ok( Project { @@ -83,7 +83,7 @@ Ok( Interface { add_data: Some( Data { - content: "PROGRAM program_0\n VAR\r\n x: BOOL := 0;\r\n END_VAR\nEND_PROGRAM", + content: "PROGRAM program_0 VAR x : BOOL := 0; END_VAR\nEND_PROGRAM", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__negated_jump.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__negated_jump.snap index e04ff85786..7b98d059c9 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__negated_jump.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__control__tests__negated_jump.snap @@ -1,6 +1,6 @@ --- source: compiler/plc_xml/src/xml_parser/control.rs -expression: "xml_parser::visit(content)" +expression: "xml_parser::visit(&content.serialize())" --- Ok( Project { @@ -83,7 +83,7 @@ Ok( Interface { add_data: Some( Data { - content: "PROGRAM program_0\n VAR\r\n x: BOOL := 0;\r\n END_VAR\nEND_PROGRAM", + content: "PROGRAM program_0 VAR x : BOOL := 0; END_VAR\nEND_PROGRAM", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__ast_generates_locations-7.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__ast_generates_locations-7.snap index 6c11fd06b0..7c777269d0 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__ast_generates_locations-7.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__ast_generates_locations-7.snap @@ -3,8 +3,8 @@ source: compiler/plc_xml/src/xml_parser/tests.rs expression: impl1 --- Implementation { - name: "program_0", - type_name: "program_0", + name: "foo", + type_name: "foo", linkage: Internal, pou_type: Program, statements: [ diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__direct_connection_of_sink_to_other_source_generates_correct_model.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__direct_connection_of_sink_to_other_source_generates_correct_model.snap index b8e4d87a4f..0a0ba14b6f 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__direct_connection_of_sink_to_other_source_generates_correct_model.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__direct_connection_of_sink_to_other_source_generates_correct_model.snap @@ -1,6 +1,6 @@ --- source: compiler/plc_xml/src/xml_parser/tests.rs -expression: visit_and_desugar(content).unwrap() +expression: visit_and_desugar(&content.serialize()).unwrap() --- Project { pous: [ @@ -42,7 +42,7 @@ Project { Interface { add_data: Some( Data { - content: "FUNCTION myConnection : DINT\n VAR_INPUT\n x: DINT;\n END_VAR\nEND_FUNCTION", + content: "FUNCTION myConnection : DINT\n VAR_INPUT\n x: DINT;\n END_VAR\nEND_FUNCTION", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__function_returns.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__function_returns.snap index 6d9432a8a0..d07070c5b5 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__function_returns.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__function_returns.snap @@ -1,11 +1,11 @@ --- source: compiler/plc_xml/src/xml_parser/tests.rs -expression: visit(&content).unwrap() +expression: "xml_parser::visit(&content.serialize()).unwrap()" --- Project { pous: [ Pou { - name: "FuncyReturn", + name: "foo", pou_type: Function, body: Body { function_block_diagram: FunctionBlockDiagram { @@ -25,7 +25,7 @@ Project { kind: Output, local_id: 2, negated: false, - expression: "FuncyReturn", + expression: "foo", execution_order_id: Some( 0, ), @@ -42,7 +42,7 @@ Project { Interface { add_data: Some( Data { - content: "FUNCTION FuncyReturn : DINT\n VAR_INPUT\n a : DINT;\n END_VAR\nEND_FUNCTION", + content: "FUNCTION foo : DINT VAR_INPUT a : DINT; END_VAR\nEND_FUNCTION", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__model_is_sorted_by_execution_order.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__model_is_sorted_by_execution_order.snap index 8b0907ed32..8d2627cc20 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__model_is_sorted_by_execution_order.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__model_is_sorted_by_execution_order.snap @@ -1,11 +1,11 @@ --- source: compiler/plc_xml/src/xml_parser/tests.rs -expression: "visit(content::EXEC_SORTING).unwrap()" +expression: "xml_parser::visit(&content.serialize()).unwrap()" --- Project { pous: [ Pou { - name: "thistimereallyeasy", + name: "foo", pou_type: Program, body: Body { function_block_diagram: FunctionBlockDiagram { @@ -70,7 +70,7 @@ Project { Interface { add_data: Some( Data { - content: "PROGRAM thistimereallyeasy\n VAR\n a, b, c, d : DINT;\n END_VAR\nEND_PROGRAM", + content: "PROGRAM foo VAR a, b, c, d : DINT; END_VAR\nEND_PROGRAM", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__variable_assignment.snap b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__variable_assignment.snap index 5cdeef7a26..36a3d68d0d 100644 --- a/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__variable_assignment.snap +++ b/compiler/plc_xml/src/xml_parser/snapshots/plc_xml__xml_parser__tests__variable_assignment.snap @@ -5,7 +5,7 @@ expression: pou Project { pous: [ Pou { - name: "thistimereallyeasy", + name: "foo", pou_type: Program, body: Body { function_block_diagram: FunctionBlockDiagram { @@ -42,7 +42,7 @@ Project { Interface { add_data: Some( Data { - content: "PROGRAM thistimereallyeasy\n VAR\n a, b : DINT;\n END_VAR\nEND_PROGRAM", + content: "PROGRAM foo VAR a, b : DINT; END_VAR\nEND_PROGRAM", handle: Implementation, }, ), diff --git a/compiler/plc_xml/src/xml_parser/tests.rs b/compiler/plc_xml/src/xml_parser/tests.rs index 584786c358..a142359318 100644 --- a/compiler/plc_xml/src/xml_parser/tests.rs +++ b/compiler/plc_xml/src/xml_parser/tests.rs @@ -9,14 +9,10 @@ use insta::assert_debug_snapshot; use plc_diagnostics::diagnostics::Diagnostic; use plc_source::{source_location::SourceLocationFactory, SourceCode, SourceCodeFactory}; -use crate::{ - model::project::Project, - serializer::{ - with_header, XBody, XConnection, XConnectionPointIn, XExpression, XFbd, XInVariable, XOutVariable, - XPou, XRelPosition, - }, - xml_parser, +use crate::serializer::{ + SBlock, SConnector, SContinuation, SInOutVariable, SInVariable, SOutVariable, SPou, SReturn, SVariable, }; +use crate::{model::project::Project, xml_parser}; fn parse(content: &str) -> (CompilationUnit, Vec) { let source_code = SourceCode::new(content, "test.cfc"); @@ -36,23 +32,57 @@ fn visit_and_desugar(content: &str) -> Result> { #[test] fn variable_assignment() { - let pou = visit(content::ASSIGNMENT_A_B).unwrap(); + let content = SPou::init("foo", "program", "PROGRAM foo VAR a, b : DINT; END_VAR") + .with_fbd(vec![ + &SInVariable::id(1).with_expression("a"), + &SOutVariable::id(2).with_execution_id(0).with_expression("b").connect(1), + ]) + .serialize(); + + let pou = xml_parser::visit(&content).unwrap(); assert_debug_snapshot!(pou); } #[test] fn conditional_return() { - let statements = &parse(content::CONDITIONAL_RETURN).0.implementations[0].statements; + let declaration = r#" + FUNCTION_BLOCK conditional_return + VAR_INPUT + val : DINT; + END_VAR + "#; + + let content = SPou::init("conditional_return", "functionBlock", declaration).with_fbd(vec![ + &SInVariable::id(1).with_expression("val = 5"), + &SReturn::id(2).with_execution_id(0).connect(1).negate(false), + &SInVariable::id(3).with_expression("10"), + &SOutVariable::id(4).with_execution_id(1).connect(3).with_expression("val"), + &SInOutVariable::id(5).with_expression("a"), + ]); + + let statements = &parse(&content.serialize()).0.implementations[0].statements; assert_eq!(statements.len(), 2); assert_debug_snapshot!(statements[0]); } #[test] fn conditional_return_negated() { - let content = - &content::CONDITIONAL_RETURN.replace(r#""#, r#""#); + let declaration = r#" + FUNCTION_BLOCK conditional_return + VAR_INPUT + val : DINT; + END_VAR + "#; - let statements = &parse(content).0.implementations[0].statements; + let content = SPou::init("conditional_return", "functionBlock", declaration).with_fbd(vec![ + &SInVariable::id(1).with_expression("val = 5"), + &SReturn::id(2).with_execution_id(0).negate(true).connect(1), + &SInVariable::id(3).with_expression("10"), + &SOutVariable::id(4).with_execution_id(1).connect(3).with_expression("val"), + &SInOutVariable::id(5).with_expression("a"), + ]); + + let statements = &parse(&content.serialize()).0.implementations[0].statements; assert_eq!(statements.len(), 2); assert_debug_snapshot!(statements[0]); @@ -60,26 +90,86 @@ fn conditional_return_negated() { #[test] fn conditional_return_without_connection() { - let (_, diagnostics) = parse(content::CONDITIONAL_RETURN_WITHOUT_CONNECTION); + let declaration = r#" + FUNCTION_BLOCK conditional_return + VAR_INPUT + val : DINT; + END_VAR + "#; + + let content = SPou::init("conditional_return", "functionBlock", declaration).with_fbd(vec![ + &SInVariable::id(1).with_expression("val = 5"), + &SReturn::id(2).with_execution_id(0).negate(false), // This return isn't connected to any other node + &SInVariable::id(3).with_expression("10"), + &SOutVariable::id(4).with_execution_id(1).with_expression("val").connect(3), + &SInOutVariable::id(5).with_expression("a"), + ]); + + let (_, diagnostics) = parse(&content.serialize()); assert_eq!(diagnostics.len(), 1); assert_debug_snapshot!(diagnostics); } #[test] fn conditional_return_chained_to_another_conditional_return() { - let (_, diagnostics) = parse(content::CONDITIONAL_RETURN_CHAINED_TO_ANOTHER_CONDITIONAL_RETURN); + let declaration = r#" + FUNCTION_BLOCK conditional_return + VAR_INPUT + val : DINT; + END_VAR + "#; + + let content = SPou::init("conditional_return", "functionBlock", declaration).with_fbd(vec![ + &SReturn::id(1).with_execution_id(0), + &SReturn::id(2).with_execution_id(1).connect(1), + ]); + + let (_, diagnostics) = parse(&content.serialize()); assert_eq!(diagnostics.len(), 2); assert_debug_snapshot!(diagnostics); } #[test] fn model_is_sorted_by_execution_order() { - assert_debug_snapshot!(visit(content::EXEC_SORTING).unwrap()); + let content = SPou::init("foo", "program", "PROGRAM foo VAR a, b, c, d : DINT; END_VAR").with_fbd(vec![ + &SInVariable::id(1).with_expression("a"), + &SOutVariable::id(2).with_execution_id(2).with_expression("b").connect(1), + &SOutVariable::id(3).with_execution_id(0).with_expression("c").connect(1), + &SOutVariable::id(4).with_execution_id(1).with_expression("d").connect(1), + ]); + + assert_debug_snapshot!(xml_parser::visit(&content.serialize()).unwrap()); } #[test] fn connection_variable_source_to_multiple_sinks_parses() { - assert_debug_snapshot!(parse(content::VAR_SOURCE_TO_MULTI_SINK).0.implementations[0].statements); + let declaration = r#" + FUNCTION myConnection : DINT + VAR_INPUT + x: DINT; + END_VAR + VAR_TEMP + y: DINT; + END_VAR + "#; + + #[rustfmt::skip] + let content = SPou::init("myConnection", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1").connect(2), + &SContinuation::id(3).with_name("s1"), + &SInVariable::id(2).with_expression("x"), + &SOutVariable::id(4).with_expression("myConnection").with_execution_id(2).connect_name(9, "myAdd"), + &SInVariable::id(7).with_expression("y"), + &SOutVariable::id(8).with_expression("y").with_execution_id(0).connect(3), + &SBlock::init("myAdd", 9, 1) + .with_input(vec![ + &SVariable::new().with_name("a").connect(7), + &SVariable::new().with_name("b").connect(3), + ]) + .with_output(vec![&SVariable::new().with_name("myAdd")]), + ]).serialize(); + + assert_debug_snapshot!(parse(&content).0.implementations[0].statements); } #[test] @@ -90,24 +180,79 @@ fn connection_block_source_to_multiple_sinks_parses() { #[test] fn direct_connection_of_sink_to_other_source_generates_correct_model() { - let content = content::SINK_TO_SOURCE; - assert_debug_snapshot!(visit_and_desugar(content).unwrap()); + let declaration = r#" + FUNCTION myConnection : DINT + VAR_INPUT + x: DINT; + END_VAR + "#; + + let content = SPou::init("myConnection", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1").connect(16), + &SContinuation::id(3).with_name("s1"), + &SOutVariable::id(4).with_expression("myConnection").with_execution_id(3).connect(20), + &SInVariable::id(16).with_expression("x"), + &SConnector::id(21).with_name("s2").connect(3), + &SContinuation::id(20).with_name("s2"), + ]); + + assert_debug_snapshot!(visit_and_desugar(&content.serialize()).unwrap()); } #[test] fn direct_connection_of_sink_to_other_source_ast_parses() { - assert_debug_snapshot!(parse(content::SINK_TO_SOURCE).0.implementations[0].statements); + let declaration = r#" + FUNCTION myConnection : DINT + VAR_INPUT + x: DINT; + END_VAR + "#; + + let content = SPou::init("myConnection", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1").connect(16), + &SContinuation::id(3).with_name("s1"), + &SOutVariable::id(4).with_expression("myConnection").with_execution_id(3).connect(20), + &SInVariable::id(16).with_expression("x"), + &SConnector::id(21).with_name("s2").connect(3), + &SContinuation::id(20).with_name("s2"), + ]); + + assert_debug_snapshot!(parse(&content.serialize()).0.implementations[0].statements); } #[test] fn return_connected_to_sink_parses() { - assert_debug_snapshot!(parse(content::RETURN_TO_CONNECTION).0.implementations[0].statements); + let declaration = "FUNCTION positivOrZero : DINT VAR_INPUT x : DINT; END_VAR"; + + #[rustfmt::skip] + let content = SPou::init("positiveOrZero", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1").connect(2), + &SContinuation::id(3).with_name("s1"), + &SConnector::id(4).with_name("s2").connect(3), + &SContinuation::id(5).with_name("s2"), + &SReturn::id(6).with_execution_id(0).connect(5), + &SInVariable::id(2).with_expression("x < 0"), // TODO: The less-than symbol has to be written this way? + &SOutVariable::id(7).with_execution_id(1).with_expression("positiveOrZero").connect(8), + &SInVariable::id(8).with_expression("x"), + ]); + + assert_debug_snapshot!(parse(&content.serialize()).0.implementations[0].statements); } #[test] fn sink_source_data_recursion_does_not_overflow_the_stack() { - let content = content::SINK_SOURCE_CYCLE; - let Err(diagnostics) = visit_and_desugar(content) else { + let declaration = "FUNCTION myConnection : DINT VAR_INPUT x: DINT; END_VAR"; + + let content = SPou::init("myConnection", "function", declaration).with_fbd(vec![ + &SConnector::id(22).with_name("s1").connect(23), + &SContinuation::id(24).with_name("s1"), + &SConnector::id(25).with_name("s2").connect(24), + &SContinuation::id(26).with_name("s2"), + &SConnector::id(27).with_name("s3").connect(26), + &SContinuation::id(23).with_name("s3"), + ]); + + let Err(diagnostics) = visit_and_desugar(&content.serialize()) else { panic!("Expected test to report data recursion!") }; assert_debug_snapshot!(diagnostics); @@ -115,8 +260,16 @@ fn sink_source_data_recursion_does_not_overflow_the_stack() { #[test] fn unconnected_connections() { - let content = content::UNCONNECTED_CONNECTIONS; - let Err(diagnostics) = visit_and_desugar(content) else { + let declaration = "FUNCTION unconnectedConnections : DINT VAR_INPUT x : DINT; END_VAR"; + + let content = SPou::init("unconnectedConnections", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1"), + &SContinuation::id(2).with_name("s1"), + &SConnector::id(3).with_name("s2").connect(2), + &SContinuation::id(4).with_name("s2"), + ]); + + let Err(diagnostics) = visit_and_desugar(&content.serialize()) else { panic!("Expected test to report unconnected source!") }; assert_debug_snapshot!(diagnostics); @@ -124,8 +277,16 @@ fn unconnected_connections() { #[test] fn unassociated_connections() { - let content = content::UNASSOCIATED_CONNECTIONS; - let Err(diagnostics) = visit_and_desugar(content) else { + let declaration = "FUNCTION unconnectedConnections : DINT VAR_INPUT x : DINT; END_VAR"; + + let content = SPou::init("unassociatedSink", "function", declaration).with_fbd(vec![ + &SConnector::id(1).with_name("s1").connect(2), + &SContinuation::id(3).with_name("s2"), + &SInVariable::id(2).with_expression("x"), + &SOutVariable::id(4).with_expression("unassociatedSink").with_execution_id(0).connect(3), + ]); + + let Err(diagnostics) = visit_and_desugar(&content.serialize()) else { panic!("Expected test to report unassociated sink!") }; assert_debug_snapshot!(diagnostics); @@ -133,44 +294,32 @@ fn unassociated_connections() { #[test] fn function_returns() { - let content = with_header( - &XPou::init( - "FuncyReturn", - "function", - "FUNCTION FuncyReturn : DINT - VAR_INPUT - a : DINT; - END_VAR", - ) - .with_body( - XBody::new().with_fbd( - XFbd::new() - .with_in_variable( - XInVariable::init("1", false).with_expression(XExpression::new().with_data("a")), - ) - .with_out_variable( - XOutVariable::init("2", false) - .with_attribute("executionOrderId", "0") - .with_expression(XExpression::new().with_data("FuncyReturn")) - .with_connection_point_in( - XConnectionPointIn::new() - .with_rel_position(XRelPosition::init().close()) - .with_connection( - XConnection::new().with_attribute("refLocalId", "1").close(), - ), - ), - ), - ), - ) - .serialize(), - ); + let content = + SPou::init("foo", "function", "FUNCTION foo : DINT VAR_INPUT a : DINT; END_VAR").with_fbd(vec![ + &SInVariable::id(1).with_expression("a"), + &SOutVariable::id(2).with_execution_id(0).with_expression("foo").connect(1), + ]); - assert_debug_snapshot!(visit(&content).unwrap()); + assert_debug_snapshot!(xml_parser::visit(&content.serialize()).unwrap()); } #[test] fn ast_generates_locations() { - let source_code = SourceCode::new(content::CALL_BLOCK, ".cfc"); + let content = SPou::init("foo", "program", "PROGRAM foo VAR a, x : DINT; END_VAR").with_fbd(vec![ + &SInVariable::id(1).with_expression("x"), + &SOutVariable::id(2).with_expression("a").with_execution_id(0).connect(1), + &SBlock::init("ADD", 3, 1) + .with_input(vec![ + &SVariable::new().with_name("").connect(4), + &SVariable::new().with_name("").connect(5), + ]) + .with_output(vec![&SVariable::new().with_name("")]) + .with_inout(vec![]), + &SInVariable::id(4).with_expression("a"), + &SInVariable::id(5).with_expression("1"), + ]); + + let source_code = SourceCode::new(&content.serialize(), ".cfc"); let (units, diagnostics) = xml_parser::parse(&source_code, LinkageType::Internal, IdProvider::default()); let impl1 = &units.implementations[0]; //Deconstruct assignment and get locations @@ -212,7 +361,12 @@ fn actions_generated_correctly() { #[test] #[ignore = "Validation is not implemented on CFC tests yet, we need to be able to change parsers on the test utils level"] fn ast_diagnostic_locations() { - let source_code = SourceCode::new(content::ASSIGNMENT_TO_UNRESOLVED_REFERENCE, ".cfc"); + let content = SPou::init("foo", "program", "PROGRAM foo VAR x : DINT; END_VAR").with_fbd(vec![ + &SInVariable::id(1).with_expression("x"), + &SOutVariable::id(2).with_execution_id(0).with_expression("a").connect(1), // "a" isn't declared anywhere, hence the error + ]); + + let source_code = SourceCode::new(&content.serialize(), ".cfc"); let (units, diagnostics) = xml_parser::parse(&source_code, LinkageType::Internal, IdProvider::default()); let impl1 = &units.implementations[0]; assert_debug_snapshot!(impl1); @@ -222,411 +376,7 @@ fn ast_diagnostic_locations() { } mod content { - pub(super) const ASSIGNMENT_A_B: &str = r#" - - - - - - - - - PROGRAM thistimereallyeasy - VAR - a, b : DINT; - END_VAR - - - - - - - - - - - - - - b - - - - - - - a - - - - - "#; - - pub(super) const ASSIGNMENT_TO_UNRESOLVED_REFERENCE: &str = r#" - - - - - - - - - PROGRAM program_0 - VAR - x : DINT; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - a - - - - - - "#; - - pub(super) const CALL_BLOCK: &str = r#" - - - - - - - - - PROGRAM program_0 - VAR - x : DINT; - a : DINT; - END_VAR - - - - - - - - - - - - - x - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - 1 - - - - - - "#; - - pub(super) const CONDITIONAL_RETURN: &str = r#" - - - - - - - - - FUNCTION_BLOCK conditional_return - VAR_INPUT - val : DINT; - END_VAR - - - - - - - - - - - - val = 5 - - - - - - - - - - - - - - - - - - - 10 - - - - - - - - val - - - - - - - - - - a - - - - - "#; - - pub(super) const CONDITIONAL_RETURN_WITHOUT_CONNECTION: &str = r#" - - - - - - - - - FUNCTION_BLOCK conditional_return - VAR_INPUT - val : DINT; - END_VAR - - - - - - - - - - - - val = 5 - - - - - - - - - - - - - - - 10 - - - - - - - - val - - - - - - - - - - a - - - - - "#; - - pub(super) const CONDITIONAL_RETURN_CHAINED_TO_ANOTHER_CONDITIONAL_RETURN: &str = r#" - - - - - - - - - FUNCTION_BLOCK conditional_return - VAR_INPUT - val : DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "#; - - pub(super) const EXEC_SORTING: &str = r#" - - - - - - - - - PROGRAM thistimereallyeasy - VAR - a, b, c, d : DINT; - END_VAR - - - - - - - - - - - - - a - - - - - - - - b - - - - - - - - c - - - - - - - - d - - - - - "#; - - pub(super) const VAR_SOURCE_TO_MULTI_SINK: &str = r#" + pub(super) const BLOCK_SOURCE_TO_MULTI_SINK: &str = r#" @@ -649,135 +399,23 @@ mod content { - + - + - + - - - - - - x - - - + + - - - - - - - - myConnection - - - - - - - y - - - - - - - - y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "#; - - pub(super) const BLOCK_SOURCE_TO_MULTI_SINK: &str = r#" - - - - - - - - FUNCTION myConnection : DINT - VAR_INPUT - x: DINT; - END_VAR - VAR_TEMP - y: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - + myConnection @@ -870,325 +508,6 @@ mod content { "#; - pub(super) const SINK_TO_SOURCE: &str = r#" - - - - - - - - FUNCTION myConnection : DINT - VAR_INPUT - x: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - - myConnection - - - - - - - x - - - - - - - - - - - - - - - - - - "#; - - pub(super) const SINK_SOURCE_CYCLE: &str = r#" - - - - - - - - FUNCTION myConnection : DINT - VAR_INPUT - x: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "#; - - pub(super) const RETURN_TO_CONNECTION: &str = r#" - - - - - - - - - FUNCTION positiveOrZero : DINT - VAR_INPUT - x: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x < 0 - - - - - - - - positiveOrZero - - - - - - - x - - - - "#; - - pub(super) const UNCONNECTED_CONNECTIONS: &str = r#" - - - - - - - - FUNCTION unconnectedConnections : DINT - VAR_INPUT - x: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "#; - - pub(super) const UNASSOCIATED_CONNECTIONS: &str = r#" - - - - - - - - FUNCTION unassociatedSink : DINT - VAR_INPUT - x: DINT; - END_VAR - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - unassociatedSink - - - - - - "#; - pub(super) const ACTION_TEST: &str = r###" diff --git a/tests/integration/data/cfc/conditional_return.cfc b/tests/integration/data/cfc/conditional_return.cfc index efb00a4f15..822d88de38 100644 --- a/tests/integration/data/cfc/conditional_return.cfc +++ b/tests/integration/data/cfc/conditional_return.cfc @@ -1,15 +1,15 @@ - + -FUNCTION_BLOCK conditional_return -VAR_INPUT - val : DINT; -END_VAR + FUNCTION_BLOCK conditional_return + VAR_INPUT + val : DINT; + END_VAR @@ -17,49 +17,51 @@ END_VAR - + - + val = 5 - + - - + + - + - + - + 10 - - + + - - + + val - - + + - + - + a - + \ No newline at end of file