test_placeholder_parser.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #include <catch2/catch.hpp>
  2. #include "libslic3r/PlaceholderParser.hpp"
  3. #include "libslic3r/PrintConfig.hpp"
  4. using namespace Slic3r;
  5. SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
  6. PlaceholderParser parser;
  7. auto config = DynamicPrintConfig::full_print_config();
  8. // To test the "first_layer_extrusion_width" over "nozzle_diameter" chain.
  9. config.set_deserialize_strict( {
  10. { "printer_notes", " PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 " },
  11. { "nozzle_diameter", "0.6;0.6;0.6;0.6" },
  12. { "temperature", "357;359;363;378" }
  13. });
  14. // To test the "first_layer_extrusion_width" over "first_layer_heigth". <- not supported anymore
  15. // "first_layer_heigth" over "layer_height" is no more supported after first_layer_height was moved from PrintObjectConfig to PrintConfig.
  16. // config.option<ConfigOptionFloatOrPercent>("first_layer_height")->value = 150.;
  17. // config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent = true;
  18. config.option<ConfigOptionFloatOrPercent>("first_layer_height")->value = 1.5 * config.opt_float("layer_height");
  19. config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent = false;
  20. // To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know
  21. // a percent to what.
  22. config.option<ConfigOptionFloatOrPercent>("first_layer_speed")->value = 50.;
  23. config.option<ConfigOptionFloatOrPercent>("first_layer_speed")->percent = true;
  24. ConfigOptionFloatsNullable *opt_filament_retract_length = config.option<ConfigOptionFloatsNullable>("filament_retract_length", true);
  25. opt_filament_retract_length->values = { 5., ConfigOptionFloatsNullable::nil_value(), 3. };
  26. config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_width")->value = 150.;
  27. config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_width")->percent = true;
  28. config.option<ConfigOptionFloatOrPercent>("support_material_xy_spacing")->value = 50.;
  29. config.option<ConfigOptionFloatOrPercent>("support_material_xy_spacing")->percent = true;
  30. parser.apply_config(config);
  31. parser.set("foo", 0);
  32. parser.set("bar", 2);
  33. parser.set("num_extruders", 4);
  34. parser.set("gcode_flavor", "marlin");
  35. SECTION("nested config options (legacy syntax)") { REQUIRE(parser.process("[temperature_[foo]]") == "357"); }
  36. SECTION("array reference") { REQUIRE(parser.process("{temperature[foo]}") == "357"); }
  37. SECTION("whitespaces and newlines are maintained") { REQUIRE(parser.process("test [ temperature_ [foo] ] \n hu") == "test 357 \n hu"); }
  38. SECTION("nullable is not null") { REQUIRE(parser.process("{is_nil(filament_retract_length[0])}") == "false"); }
  39. SECTION("nullable is null") { REQUIRE(parser.process("{is_nil(filament_retract_length[1])}") == "true"); }
  40. SECTION("nullable is not null 2") { REQUIRE(parser.process("{is_nil(filament_retract_length[2])}") == "false"); }
  41. SECTION("multiple expressions") { REQUIRE(parser.process("{temperature[foo];temperature[foo]}") == "357357"); }
  42. SECTION("multiple expressions with semicolons") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];}") == "357357"); }
  43. SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); }
  44. SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); }
  45. SECTION("parsing string with escaped characters") { REQUIRE(parser.process("{\"hu\\nha\\\\\\\"ha\\\"\"}") == "hu\nha\\\"ha\""); }
  46. WHEN("An UTF-8 character is used inside the code block") {
  47. THEN("A std::runtime_error exception is thrown.") {
  48. // full-width plus sign instead of plain +
  49. REQUIRE_THROWS_AS(parser.process("{1\xEF\xBC\x8B 3}"), std::runtime_error);
  50. }
  51. }
  52. WHEN("An UTF-8 character is used inside a string") {
  53. THEN("UTF-8 sequence is processed correctly when quoted") {
  54. // japanese "cool" or "stylish"
  55. REQUIRE(parser.process("{1+\"\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84\"+\" \"+3}") == "1\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84 3");
  56. }
  57. }
  58. WHEN("An UTF-8 character is used inside a string") {
  59. THEN("UTF-8 sequence is processed correctly outside of code blocks") {
  60. // japanese "cool" or "stylish"
  61. REQUIRE(parser.process("{1+3}\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84") == "4\xE3\x81\x8B\xE3\x81\xA3\xE3\x81\x93\xE3\x81\x84\xE3\x81\x84");
  62. }
  63. }
  64. // Test the math expressions.
  65. SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
  66. SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); }
  67. SECTION("math: 2*3/12") { REQUIRE(parser.process("{2*3/12}") == "0"); }
  68. SECTION("math: 2.*3/12") { REQUIRE(std::stod(parser.process("{2.*3/12}")) == Approx(0.5)); }
  69. SECTION("math: 10 % 2.5") { REQUIRE(std::stod(parser.process("{10%2.5}")) == Approx(0.)); }
  70. SECTION("math: 11 % 2.5") { REQUIRE(std::stod(parser.process("{11%2.5}")) == Approx(1.)); }
  71. SECTION("math: 2*(3-12)") { REQUIRE(parser.process("{2*(3-12)}") == "-18"); }
  72. SECTION("math: 2*foo*(3-12)") { REQUIRE(parser.process("{2*foo*(3-12)}") == "0"); }
  73. SECTION("math: 2*bar*(3-12)") { REQUIRE(parser.process("{2*bar*(3-12)}") == "-36"); }
  74. SECTION("math: 2.5*bar*(3-12)") { REQUIRE(std::stod(parser.process("{2.5*bar*(3-12)}")) == Approx(-45)); }
  75. SECTION("math: min(12, 14)") { REQUIRE(parser.process("{min(12, 14)}") == "12"); }
  76. SECTION("math: max(12, 14)") { REQUIRE(parser.process("{max(12, 14)}") == "14"); }
  77. SECTION("math: min(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{min(13.4, -1238.1)}")) == Approx(-1238.1)); }
  78. SECTION("math: max(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{max(13.4, -1238.1)}")) == Approx(13.4)); }
  79. SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); }
  80. SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); }
  81. SECTION("math: round(13.4)") { REQUIRE(parser.process("{round(13.4)}") == "13"); }
  82. SECTION("math: round(-13.4)") { REQUIRE(parser.process("{round(-13.4)}") == "-13"); }
  83. SECTION("math: round(13.6)") { REQUIRE(parser.process("{round(13.6)}") == "14"); }
  84. SECTION("math: round(-13.6)") { REQUIRE(parser.process("{round(-13.6)}") == "-14"); }
  85. SECTION("math: digits(5, 15)") { REQUIRE(parser.process("{digits(5, 15)}") == " 5"); }
  86. SECTION("math: digits(5., 15)") { REQUIRE(parser.process("{digits(5., 15)}") == " 5"); }
  87. SECTION("math: zdigits(5, 15)") { REQUIRE(parser.process("{zdigits(5, 15)}") == "000000000000005"); }
  88. SECTION("math: zdigits(5., 15)") { REQUIRE(parser.process("{zdigits(5., 15)}") == "000000000000005"); }
  89. SECTION("math: digits(5, 15, 8)") { REQUIRE(parser.process("{digits(5, 15, 8)}") == " 5.00000000"); }
  90. SECTION("math: digits(5., 15, 8)") { REQUIRE(parser.process("{digits(5, 15, 8)}") == " 5.00000000"); }
  91. SECTION("math: zdigits(5, 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
  92. SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
  93. SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); }
  94. SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); }
  95. SECTION("math: ternary1") { REQUIRE(parser.process("{12 == 12 ? 1 - 3 : 2 * 2 * unknown_symbol}") == "-2"); }
  96. SECTION("math: ternary2") { REQUIRE(parser.process("{12 == 21/2 ? 1 - 1 - unknown_symbol : 2 * 2}") == "4"); }
  97. SECTION("math: ternary3") { REQUIRE(parser.process("{12 == 13 ? 1 - 1 * unknown_symbol : 2 * 2}") == "4"); }
  98. SECTION("math: ternary4") { REQUIRE(parser.process("{12 == 2 * 6 ? 1 - 1 : 2 * unknown_symbol}") == "0"); }
  99. SECTION("math: ternary nested") { REQUIRE(parser.process("{12 == 2 * 6 ? 3 - 1 != 2 ? does_not_exist : 0 * 0 - 0 / 1 + 12345 : bull ? 3 - cokoo : 2 * unknown_symbol}") == "12345"); }
  100. SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Approx(13.84375892476)); }
  101. SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Approx(13.)); }
  102. SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Approx(20.)); }
  103. // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
  104. SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); }
  105. // first_layer_extrusion_width ratio_over nozzle_diameter, 150% of 0.6 is 0.9
  106. SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); }
  107. // support_material_xy_spacing is ratio over external_perimeter_extrusion_width
  108. // external_perimeter_extrusion_width is at 0 by default, and so will be 1.05f * nozzle = 0.63 do 50% of that is 0.315
  109. SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.315)); }
  110. // external_perimeter_speed over perimeter_speed
  111. SECTION("external_perimeter_speed") { REQUIRE(std::stod(parser.process("{external_perimeter_speed}")) == Approx(30.)); }
  112. // infill_overlap over perimeter_extrusion_width
  113. SECTION("infill_overlap") { REQUIRE(std::stod(parser.process("{infill_overlap}")) == Approx(0.16875)); }
  114. // If first_layer_speed is set to percent, then it is applied over respective extrusion types by overriding their respective speeds.
  115. // The PlaceholderParser has no way to know which extrusion type the caller has in mind, therefore it throws.
  116. SECTION("first_layer_speed") { REQUIRE_THROWS(parser.process("{first_layer_speed}")); }
  117. // Test the boolean expression parser.
  118. auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); };
  119. SECTION("boolean expression parser: 12 == 12") { REQUIRE(boolean_expression("12 == 12")); }
  120. SECTION("boolean expression parser: 12 != 12") { REQUIRE(! boolean_expression("12 != 12")); }
  121. SECTION("boolean expression parser: regex matches") { REQUIRE(boolean_expression("\"has some PATTERN embedded\" =~ /.*PATTERN.*/")); }
  122. SECTION("boolean expression parser: regex does not match") { REQUIRE(! boolean_expression("\"has some PATTERN embedded\" =~ /.*PTRN.*/")); }
  123. SECTION("boolean expression parser: accessing variables, equal") { REQUIRE(boolean_expression("foo + 2 == bar")); }
  124. SECTION("boolean expression parser: accessing variables, not equal") { REQUIRE(! boolean_expression("foo + 3 == bar")); }
  125. SECTION("boolean expression parser: (12 == 12) and (13 != 14)") { REQUIRE(boolean_expression("(12 == 12) and (13 != 14)")); }
  126. SECTION("boolean expression parser: (12 == 12) && (13 != 14)") { REQUIRE(boolean_expression("(12 == 12) && (13 != 14)")); }
  127. SECTION("boolean expression parser: (12 == 12) or (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) or (13 == 14)")); }
  128. SECTION("boolean expression parser: (12 == 12) || (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) || (13 == 14)")); }
  129. SECTION("boolean expression parser: (12 == 12) and not (13 == 14)") { REQUIRE(boolean_expression("(12 == 12) and not (13 == 14)")); }
  130. SECTION("boolean expression parser: ternary true") { REQUIRE(boolean_expression("(12 == 12) ? (1 - 1 == 0) : (2 * 2 == 3 * unknown_symbol)")); }
  131. SECTION("boolean expression parser: ternary false") { REQUIRE(! boolean_expression("(12 == 21/2) ? (1 - 1 == 0 - unknown_symbol) : (2 * 2 == 3)")); }
  132. SECTION("boolean expression parser: ternary false 2") { REQUIRE(boolean_expression("(12 == 13) ? (1 - 1 == 3 * unknown_symbol) : (2 * 2 == 4)")); }
  133. SECTION("boolean expression parser: ternary true 2") { REQUIRE(! boolean_expression("(12 == 2 * 6) ? (1 - 1 == 3) : (2 * 2 == 4 * unknown_symbol)")); }
  134. SECTION("boolean expression parser: lower than - false") { REQUIRE(! boolean_expression("12 < 3")); }
  135. SECTION("boolean expression parser: lower than - true") { REQUIRE(boolean_expression("12 < 22")); }
  136. SECTION("boolean expression parser: greater than - true") { REQUIRE(boolean_expression("12 > 3")); }
  137. SECTION("boolean expression parser: greater than - false") { REQUIRE(! boolean_expression("12 > 22")); }
  138. SECTION("boolean expression parser: lower than or equal- false") { REQUIRE(! boolean_expression("12 <= 3")); }
  139. SECTION("boolean expression parser: lower than or equal - true") { REQUIRE(boolean_expression("12 <= 22")); }
  140. SECTION("boolean expression parser: greater than or equal - true") { REQUIRE(boolean_expression("12 >= 3")); }
  141. SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); }
  142. SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); }
  143. SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); }
  144. SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); }
  145. SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); }
  146. SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); }
  147. SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); }
  148. SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); }
  149. SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); }
  150. SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); }
  151. SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); }
  152. SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); }
  153. SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); }
  154. SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); }
  155. SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); }
  156. SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); }
  157. SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); }
  158. SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }
  159. SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); }
  160. SECTION("write to a scalar variable") {
  161. DynamicConfig config_outputs;
  162. config_outputs.set_key_value("writable_string", new ConfigOptionString());
  163. parser.process("{writable_string = \"Written\"}", 0, nullptr, &config_outputs, nullptr);
  164. REQUIRE(parser.process("{writable_string}", 0, nullptr, &config_outputs, nullptr) == "Written");
  165. }
  166. SECTION("write to a vector variable") {
  167. DynamicConfig config_outputs;
  168. config_outputs.set_key_value("writable_floats", new ConfigOptionFloats({ 0., 0., 0. }));
  169. parser.process("{writable_floats[1] = 33}", 0, nullptr, &config_outputs, nullptr);
  170. REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
  171. }
  172. }
  173. SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
  174. PlaceholderParser parser;
  175. auto config = DynamicPrintConfig::full_print_config();
  176. config.set_deserialize_strict({
  177. { "filament_notes", "testnotes" },
  178. { "enable_dynamic_fan_speeds", "1" },
  179. { "nozzle_diameter", "0.6;0.6;0.6;0.6" },
  180. { "temperature", "357;359;363;378" }
  181. });
  182. PlaceholderParser::ContextData context_with_global_dict;
  183. context_with_global_dict.global_config = std::make_unique<DynamicConfig>();
  184. SECTION("create an int local variable") { REQUIRE(parser.process("{local myint = 33+2}{myint}", 0, nullptr, nullptr, nullptr) == "35"); }
  185. SECTION("create a string local variable") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
  186. SECTION("create a bool local variable") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, nullptr) == "true"); }
  187. SECTION("create an int global variable") { REQUIRE(parser.process("{global myint = 33+2}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "35"); }
  188. SECTION("create a string global variable") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
  189. SECTION("create a bool global variable") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
  190. SECTION("create an int local variable and overwrite it") { REQUIRE(parser.process("{local myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
  191. SECTION("create a string local variable and overwrite it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
  192. SECTION("create a bool local variable and overwrite it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
  193. SECTION("create an int global variable and overwrite it") { REQUIRE(parser.process("{global myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
  194. SECTION("create a string global variable and overwrite it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
  195. SECTION("create a bool global variable and overwrite it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
  196. SECTION("create an int local variable and redefine it") { REQUIRE(parser.process("{local myint = 33+2}{local myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
  197. SECTION("create a string local variable and redefine it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{local mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
  198. SECTION("create a bool local variable and redefine it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{local mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
  199. SECTION("create an int global variable and redefine it") { REQUIRE(parser.process("{global myint = 33+2}{global myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
  200. SECTION("create a string global variable and redefine it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{global mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
  201. SECTION("create a bool global variable and redefine it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{global mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
  202. SECTION("create an ints local variable with repeat()") { REQUIRE(parser.process("{local myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); }
  203. SECTION("create a strings local variable with repeat()") { REQUIRE(parser.process("{local mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
  204. SECTION("create a bools local variable with repeat()") { REQUIRE(parser.process("{local mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); }
  205. SECTION("create an ints global variable with repeat()") { REQUIRE(parser.process("{global myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
  206. SECTION("create a strings global variable with repeat()") { REQUIRE(parser.process("{global mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
  207. SECTION("create a bools global variable with repeat()") { REQUIRE(parser.process("{global mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
  208. SECTION("create an ints local variable with initializer list") { REQUIRE(parser.process("{local myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, nullptr) == "24"); }
  209. SECTION("create a strings local variable with initializer list") { REQUIRE(parser.process("{local mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
  210. SECTION("create a bools local variable with initializer list") { REQUIRE(parser.process("{local mybool = (3*3 == 8, 1 + 1 == 2)}{mybool[1]}", 0, nullptr, nullptr, nullptr) == "true"); }
  211. SECTION("create an ints global variable with initializer list") { REQUIRE(parser.process("{global myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
  212. SECTION("create a strings global variable with initializer list") { REQUIRE(parser.process("{global mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
  213. SECTION("create a bools global variable with initializer list") { REQUIRE(parser.process("{global mybool = (2*3 == 8, 1 + 1 == 2, 5*5 != 33)}{mybool[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
  214. SECTION("create an ints local variable by a copy") { REQUIRE(parser.process("{local myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); }
  215. SECTION("create a strings local variable by a copy") { REQUIRE(parser.process("{local mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); }
  216. SECTION("create a bools local variable by a copy") { REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); }
  217. SECTION("create an ints global variable by a copy") { REQUIRE(parser.process("{global myint = temperature}{myint[0]}", 0, &config, nullptr, &context_with_global_dict) == "357"); }
  218. SECTION("create a strings global variable by a copy") { REQUIRE(parser.process("{global mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, &context_with_global_dict) == "testnotes"); }
  219. SECTION("create a bools global variable by a copy") { REQUIRE(parser.process("{global mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, &context_with_global_dict) == "true"); }
  220. SECTION("create an ints local variable by a copy and overwrite it") {
  221. REQUIRE(parser.process("{local myint = temperature}{myint = repeat(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24");
  222. REQUIRE(parser.process("{local myint = temperature}{myint = (2*3, 4*6)}{myint[1]}", 0, &config, nullptr, nullptr) == "24");
  223. REQUIRE(parser.process("{local myint = temperature}{myint = (1)}{myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357");
  224. }
  225. SECTION("create a strings local variable by a copy and overwrite it") {
  226. REQUIRE(parser.process("{local mystr = filament_notes}{mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine");
  227. REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr[1]}", 0, &config, nullptr, nullptr) == "mineonlymine");
  228. REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes");
  229. }
  230. SECTION("create a bools local variable by a copy and overwrite it") {
  231. REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = repeat(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true");
  232. REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true");
  233. REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true");
  234. }
  235. SECTION("size() of a non-empty vector returns the right size") { REQUIRE(parser.process("{local myint = (0, 1, 2, 3)}{size(myint)}", 0, nullptr, nullptr, nullptr) == "4"); }
  236. SECTION("size() of a an empty vector returns the right size") { REQUIRE(parser.process("{local myint = (0);myint=();size(myint)}", 0, nullptr, nullptr, nullptr) == "0"); }
  237. SECTION("empty() of a non-empty vector returns false") { REQUIRE(parser.process("{local myint = (0, 1, 2, 3)}{empty(myint)}", 0, nullptr, nullptr, nullptr) == "false"); }
  238. SECTION("empty() of a an empty vector returns true") { REQUIRE(parser.process("{local myint = (0);myint=();empty(myint)}", 0, nullptr, nullptr, nullptr) == "true"); }
  239. SECTION("nested if with new variables") {
  240. std::string script =
  241. "{if 1 == 1}{local myints = (5, 4, 3, 2, 1)}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}"
  242. "{myints[1]},{size(myints)}";
  243. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "4,5");
  244. }
  245. SECTION("nested if with new variables 2") {
  246. std::string script =
  247. "{if 1 == 0}{local myints = (5, 4, 3, 2, 1)}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}"
  248. "{size(myfloats)}";
  249. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
  250. }
  251. SECTION("nested if with new variables 2, mixing }{ with ;") {
  252. std::string script =
  253. "{if 1 == 0 then local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}"
  254. "{size(myfloats)}";
  255. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
  256. }
  257. SECTION("nested if with new variables, two level") {
  258. std::string script =
  259. "{if 1 == 1}{if 2 == 3}{nejaka / haluz}{else}{local myints = (6, 5, 4, 3, 2, 1)}{endif}{else}{if zase * haluz}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}{endif}"
  260. "{size(myints)}";
  261. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
  262. }
  263. SECTION("if with empty block and ;") {
  264. std::string script =
  265. "{if false then else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}"
  266. "{size(myfloats)}";
  267. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
  268. }
  269. SECTION("nested if with new variables, two level, mixing }{ with ;") {
  270. std::string script =
  271. "{if 1 == 1 then if 2 == 3}nejaka / haluz{else local myints = (6, 5, 4, 3, 2, 1) endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}"
  272. "{size(myints)}";
  273. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
  274. }
  275. SECTION("nested if with new variables, two level, mixing }{ with ; 2") {
  276. std::string script =
  277. "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}"
  278. "{size(myints)}";
  279. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
  280. }
  281. SECTION("nested if with new variables, two level, mixing }{ with ; 3") {
  282. std::string script =
  283. "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else}{if zase * haluz}{else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif}{endif}"
  284. "{size(myints)}";
  285. REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6");
  286. }
  287. SECTION("if else completely empty") { REQUIRE(parser.process("{if false then elsif false then else endif}", 0, nullptr, nullptr, nullptr) == ""); }
  288. }