interpreter.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. mod common;
  2. use common::get_test_context;
  3. use insta::assert_snapshot;
  4. use numbat::markup::{Formatter, PlainTextFormatter};
  5. use numbat::resolver::CodeSource;
  6. use numbat::{pretty_print::PrettyPrint, Context, InterpreterResult};
  7. #[track_caller]
  8. fn expect_output_with_context(ctx: &mut Context, code: &str, expected_output: impl AsRef<str>) {
  9. let expected_output = expected_output.as_ref();
  10. println!("Expecting output '{expected_output}' for code '{code}'");
  11. if let InterpreterResult::Value(val) = ctx.interpret(code, CodeSource::Internal).unwrap().1 {
  12. let fmt = PlainTextFormatter {};
  13. let actual_output = fmt.format(&val.pretty_print(), false);
  14. assert_eq!(actual_output.trim(), expected_output);
  15. } else {
  16. panic!();
  17. }
  18. }
  19. #[track_caller]
  20. fn expect_output(code: &str, expected_output: impl AsRef<str>) {
  21. let mut ctx = get_test_context();
  22. expect_output_with_context(&mut ctx, code, expected_output)
  23. }
  24. #[track_caller]
  25. fn expect_failure(code: &str, msg_part: &str) {
  26. let mut ctx = get_test_context();
  27. if let Err(e) = ctx.interpret(code, CodeSource::Internal) {
  28. let error_message = e.to_string();
  29. println!("{}", error_message);
  30. assert!(error_message.contains(msg_part));
  31. } else {
  32. panic!();
  33. }
  34. }
  35. #[track_caller]
  36. fn get_error_message(code: &str) -> String {
  37. let mut ctx = get_test_context();
  38. if let Err(e) = ctx.interpret(code, CodeSource::Internal) {
  39. e.to_string()
  40. } else {
  41. panic!();
  42. }
  43. }
  44. #[test]
  45. fn simple_value() {
  46. expect_output("0", "0");
  47. expect_output("0_0", "0");
  48. expect_output("0_0.0_0", "0");
  49. expect_output(".0", "0");
  50. expect_failure("_.0", "Unexpected character in identifier: '.'");
  51. expect_failure("._0", "Expected digit");
  52. expect_output(".0_0", "0");
  53. expect_failure(".0_", "Unexpected character in number literal: '_'");
  54. expect_output("0b0", "0");
  55. expect_output("0b01", "1");
  56. expect_output("0b0_0", "0");
  57. expect_failure("0b012", "Expected base-2 digit");
  58. expect_failure("0b", "Expected base-2 digit");
  59. expect_failure("0b_", "Expected base-2 digit");
  60. expect_failure("0b_0", "Expected base-2 digit");
  61. expect_failure("0b0_", "Expected base-2 digit");
  62. expect_failure("0b0.0", "Expected base-2 digit");
  63. expect_output("0o0", "0");
  64. expect_output("0o01234567", "342_391");
  65. expect_output("0o0_0", "0");
  66. expect_failure("0o012345678", "Expected base-8 digit");
  67. expect_failure("0o", "Expected base-8 digit");
  68. expect_failure("0o_", "Expected base-8 digit");
  69. expect_failure("0o_0", "Expected base-8 digit");
  70. expect_failure("0o0_", "Expected base-8 digit");
  71. expect_failure("0o0.0", "Expected base-8 digit");
  72. expect_output("0x0", "0");
  73. expect_output("0x0123456789abcdef", "8.19855e+16");
  74. expect_output("0x0_0", "0");
  75. expect_failure("0x0123456789abcdefg", "Expected base-16 digit");
  76. expect_failure("0x", "Expected base-16 digit");
  77. expect_failure("0x_", "Expected base-16 digit");
  78. expect_failure("0x_0", "Expected base-16 digit");
  79. expect_failure("0x0_", "Expected base-16 digit");
  80. expect_failure("0x0.0", "Expected base-16 digit");
  81. }
  82. #[test]
  83. fn test_factorial() {
  84. expect_output("0!", "1");
  85. expect_output("4!", "24");
  86. expect_output("4.0!", "24");
  87. expect_output("4 !", "24");
  88. expect_output(" 4 !", "24");
  89. expect_output("(4)!", "24");
  90. expect_output("3!^3", "216");
  91. // Not supported, at least for now.
  92. // expect_output("3!³", "216");
  93. expect_output("(3!)^3", "216");
  94. expect_output("3^3!", "729");
  95. expect_output("-5!", "-120");
  96. expect_output("-(5!)", "-120");
  97. expect_output("-(5)!", "-120");
  98. expect_failure(
  99. "(-1)!",
  100. "Expected factorial argument to be a non-negative integer",
  101. );
  102. expect_failure(
  103. "1.5!",
  104. "Expected factorial argument to be a finite integer number",
  105. );
  106. expect_failure(
  107. "(-1.5)!",
  108. "Expected factorial argument to be a non-negative integer",
  109. );
  110. expect_failure(
  111. "(2m)!",
  112. "Argument of factorial needs to be dimensionless (got Length).",
  113. );
  114. }
  115. #[test]
  116. fn test_exponentiation() {
  117. expect_output("3²*2", "18");
  118. expect_output("3² 2", "18");
  119. expect_output("3²·2", "18");
  120. expect_output("3³*2", "54");
  121. expect_output("3³(2)", "54");
  122. expect_output("(1+2)²", "9");
  123. expect_output("2²pi", "12.5664");
  124. expect_output("2² pi", "12.5664");
  125. expect_output("2²·pi", "12.5664");
  126. expect_output("5m² to cm·m", "500 cm·m");
  127. expect_output("2⁵", "32");
  128. expect_output("-4¹", "-4");
  129. expect_output("2⁻¹", "0.5");
  130. expect_output("2⁻²", "0.25");
  131. expect_output("10⁻⁵", "0.00001");
  132. }
  133. #[test]
  134. fn test_conversions() {
  135. expect_output("2in to cm", "5.08 cm");
  136. expect_output("5m^2 -> m*cm", "500 m·cm");
  137. expect_output("5m^2 -> cm*m", "500 cm·m");
  138. expect_output("1 kB / 10 ms -> MB/s", "0.1 MB/s");
  139. expect_output("55! / (6! (55 - 6)!) -> million", "28.9897 million");
  140. }
  141. #[test]
  142. fn test_implicit_conversion() {
  143. let mut ctx = get_test_context();
  144. let _ = ctx.interpret("let x = 5 m", CodeSource::Internal).unwrap();
  145. expect_output_with_context(&mut ctx, "x", "5 m");
  146. expect_output_with_context(&mut ctx, "2x", "10 m");
  147. expect_output_with_context(&mut ctx, "2 x", "10 m");
  148. expect_output_with_context(&mut ctx, "x x", "25 m²");
  149. expect_output_with_context(&mut ctx, "x²", "25 m²");
  150. expect_failure("x2", "Unknown identifier 'x2'");
  151. }
  152. #[test]
  153. fn test_reset_after_runtime_error() {
  154. let mut ctx = get_test_context();
  155. let _ = ctx.interpret("let x = 1", CodeSource::Internal).unwrap();
  156. let res = ctx.interpret("1/0", CodeSource::Internal);
  157. assert!(res.is_err());
  158. expect_output_with_context(&mut ctx, "x", "1");
  159. }
  160. #[test]
  161. fn test_function_inverses() {
  162. expect_output("sin(asin(0.1234))", "0.1234");
  163. expect_output("cos(acos(0.1234))", "0.1234");
  164. expect_output("tan(atan(0.1234))", "0.1234");
  165. expect_output("sinh(asinh(0.1234))", "0.1234");
  166. expect_output("cosh(acosh(1.1234))", "1.1234");
  167. expect_output("tanh(atanh(0.1234))", "0.1234");
  168. expect_output("log(exp(0.1234))", "0.1234");
  169. expect_output("log10(10^0.1234)", "0.1234");
  170. expect_output("log2(2^0.1234)", "0.1234");
  171. expect_output("sqr(sqrt(0.1234))", "0.1234");
  172. expect_output("asin(sin(0.1234))", "0.1234");
  173. expect_output("acos(cos(0.1234))", "0.1234");
  174. expect_output("atan(tan(0.1234))", "0.1234");
  175. expect_output("asinh(sinh(0.1234))", "0.1234");
  176. expect_output("acosh(cosh(1.1234))", "1.1234");
  177. expect_output("atanh(tanh(0.1234))", "0.1234");
  178. expect_output("exp(log(0.1234))", "0.1234");
  179. expect_output("10^(log10(0.1234))", "0.1234");
  180. expect_output("2^(log2(0.1234))", "0.1234");
  181. expect_output("sqrt(sqr(0.1234))", "0.1234");
  182. }
  183. #[test]
  184. fn test_math() {
  185. expect_output("sin(90°)", "1");
  186. expect_output("sin(30°)", "0.5");
  187. expect_output("sin(pi/2)", "1");
  188. expect_output("atan2(10, 0) / (pi / 2)", "1");
  189. expect_output("atan2(100 cm, 1 m) / (pi / 4)", "1");
  190. expect_failure(
  191. "atan2(100 cm, 1 m²)",
  192. "parameter type: Length\n argument type: Length²",
  193. );
  194. expect_output("mod(5, 3)", "2");
  195. expect_output("mod(-1, 4)", "3");
  196. expect_output("mod(8 cm, 5 cm)", "3 cm");
  197. expect_output("mod(235 cm, 1 m)", "35 cm");
  198. expect_output("mod(2 m, 7 cm)", "0.04 m");
  199. expect_failure(
  200. "mod(8 m, 5 s)",
  201. "parameter type: Length\n argument type: Time",
  202. )
  203. }
  204. #[test]
  205. fn test_incompatible_dimension_errors() {
  206. assert_snapshot!(
  207. get_error_message("kg m / s^2 + kg m^2"),
  208. @r###"
  209. left hand side: Length × Mass × Time⁻² [= Force]
  210. right hand side: Length² × Mass [= MomentOfInertia]
  211. "###
  212. );
  213. assert_snapshot!(
  214. get_error_message("1 + m"),
  215. @r###"
  216. left hand side: Scalar [= Angle, Scalar, SolidAngle]
  217. right hand side: Length
  218. Suggested fix: divide the expression on the right hand side by a `Length` factor
  219. "###
  220. );
  221. assert_snapshot!(
  222. get_error_message("m / s + K A"),
  223. @r###"
  224. left hand side: Length / Time [= Velocity]
  225. right hand side: Current × Temperature
  226. "###
  227. );
  228. assert_snapshot!(
  229. get_error_message("m + 1 / m"),
  230. @r###"
  231. left hand side: Length
  232. right hand side: Length⁻¹ [= Wavenumber]
  233. Suggested fix: invert the expression on the right hand side
  234. "###
  235. );
  236. assert_snapshot!(
  237. get_error_message("kW -> J"),
  238. @r###"
  239. left hand side: Length² × Mass × Time⁻³ [= Power]
  240. right hand side: Length² × Mass × Time⁻² [= Energy, Torque]
  241. Suggested fix: divide the expression on the right hand side by a `Time` factor
  242. "###
  243. );
  244. assert_snapshot!(
  245. get_error_message("sin(1 meter)"),
  246. @r###"
  247. parameter type: Scalar [= Angle, Scalar, SolidAngle]
  248. argument type: Length
  249. Suggested fix: divide the function argument by a `Length` factor
  250. "###
  251. );
  252. assert_snapshot!(
  253. get_error_message("let x: Acceleration = 4 m / s"),
  254. @r###"
  255. specified dimension: Length × Time⁻² [= Acceleration]
  256. actual dimension: Length × Time⁻¹ [= Velocity]
  257. Suggested fix: divide the right hand side expression by a `Time` factor
  258. "###
  259. );
  260. assert_snapshot!(
  261. get_error_message("unit x: Acceleration = 4 m / s"),
  262. @r###"
  263. specified dimension: Length × Time⁻² [= Acceleration]
  264. actual dimension: Length × Time⁻¹ [= Velocity]
  265. Suggested fix: divide the right hand side expression by a `Time` factor
  266. "###
  267. );
  268. assert_snapshot!(
  269. get_error_message("fn acceleration(length: Length, time: Time) -> Acceleration = length / time"),
  270. @r###"
  271. specified return type: Length × Time⁻² [= Acceleration]
  272. actual return type: Length × Time⁻¹ [= Velocity]
  273. Suggested fix: divide the expression in the function body by a `Time` factor
  274. "###
  275. );
  276. }
  277. #[test]
  278. fn test_temperature_conversions() {
  279. expect_output("from_celsius(11.5)", "284.65 K");
  280. expect_output("from_fahrenheit(89.3)", "304.983 K");
  281. expect_output("to_celsius(0 K)", "-273.15");
  282. expect_output("to_fahrenheit(30 K)", "-405.67");
  283. expect_output("to_celsius(from_celsius(100))", "100");
  284. expect_output("to_fahrenheit(from_fahrenheit(100))", "100.0");
  285. expect_output("from_celsius(to_celsius(123 K))", "123 K");
  286. expect_output("from_fahrenheit(to_fahrenheit(123 K))", "123 K");
  287. expect_output("-40 // from_fahrenheit // to_celsius", "-40");
  288. }
  289. #[test]
  290. fn test_other_functions() {
  291. expect_output("sqrt(4)", "2");
  292. expect_output("log10(100000)", "5");
  293. expect_output("log(e^15)", "15");
  294. expect_output("ln(e^15)", "15");
  295. expect_output("ceil(3.1)", "4");
  296. expect_output("floor(3.9)", "3");
  297. expect_output("round(3.9)", "4");
  298. expect_output("round(3.1)", "3");
  299. }
  300. #[test]
  301. fn test_last_result_identifier() {
  302. let mut ctx = get_test_context();
  303. let _ = ctx.interpret("2 + 3", CodeSource::Internal).unwrap();
  304. expect_output_with_context(&mut ctx, "ans", "5");
  305. let _ = ctx.interpret("1 + 2", CodeSource::Internal).unwrap();
  306. expect_output_with_context(&mut ctx, "_", "3");
  307. }
  308. #[test]
  309. fn test_misc_examples() {
  310. expect_output("1920/16*9", "1080");
  311. expect_output("2^32", "4_294_967_296");
  312. expect_output("sqrt(1.4^2 + 1.5^2) * cos(pi/3)^2", "0.512957");
  313. expect_output("2min + 30s", "2.5 min");
  314. expect_output("2min + 30s -> sec", "150 s");
  315. expect_output("4/3 * pi * (6000km)³", "9.04779e+11 km³");
  316. expect_output("40kg * 9.8m/s^2 * 150cm", "588 kg·m²/s²");
  317. expect_output("sin(30°)", "0.5");
  318. expect_output("60mph -> m/s", "26.8224 m/s");
  319. expect_output("240km/day -> km/h", "10 km/h");
  320. expect_output("1mrad -> °", "0.0572958°");
  321. expect_output("52weeks -> days", "364 day");
  322. expect_output("5in + 2ft -> cm", "73.66 cm");
  323. expect_output("atan(30cm / 2m) -> deg", "8.53077°");
  324. expect_output("6Mbit/s * 1.5h -> GB", "4.05 GB");
  325. expect_output("6Mbit/s * 1.5h -> GiB", "3.77186 GiB");
  326. expect_output("3m/4m", "0.75");
  327. expect_output("4/2*2", "4");
  328. expect_output("1/2 Hz -> s", "0.5 s");
  329. }
  330. #[test]
  331. fn test_bohr_radius_regression() {
  332. // Make sure that the unit is 'm', and not 'F·J²/(C²·kg·m·Hz²)', like we had before
  333. expect_output("bohr_radius", "5.29177e-11 m");
  334. }
  335. #[test]
  336. fn test_full_simplify() {
  337. expect_output("5 cm/m", "0.05");
  338. expect_output("hour/second", "3600");
  339. expect_output("5 to cm/m", "500 cm/m");
  340. expect_output(
  341. "fn f(x: Scalar) -> Scalar = x to cm/m
  342. f(5)",
  343. "500 cm/m",
  344. );
  345. expect_output("1 Wh/W", "1 Wh/W"); // This output is not great (and should be improved). But we keep this as a regression test for a bug in previous versions.
  346. expect_output("1 × (m/s)^2/(m/s)", "1 m/s");
  347. }
  348. #[test]
  349. fn test_prefixes() {
  350. expect_output("hertz second", "1");
  351. expect_output("kilohertz millisecond", "1");
  352. expect_output("megahertz microsecond", "1");
  353. expect_output("gigahertz nanosecond", "1");
  354. expect_output("terahertz picosecond", "1");
  355. expect_output("petahertz femtosecond", "1");
  356. expect_output("exahertz attosecond", "1");
  357. expect_output("zettahertz zeptosecond", "1");
  358. expect_output("yottahertz yoctosecond", "1");
  359. expect_output("ronnahertz rontosecond", "1");
  360. expect_output("quettahertz quectosecond", "1");
  361. }
  362. #[test]
  363. fn test_parse_errors() {
  364. expect_failure(
  365. "3kg+",
  366. "Expected one of: number, identifier, parenthesized expression",
  367. );
  368. expect_failure("let print=2", "Expected identifier after 'let' keyword");
  369. expect_failure(
  370. "fn print(x: Scalar) = 1",
  371. "Expected identifier after 'fn' keyword",
  372. );
  373. }
  374. #[test]
  375. fn test_name_clash_errors() {
  376. expect_failure("let kg=2", "Identifier is already in use: 'kg'");
  377. expect_failure("fn kg(x: Scalar) = 1", "Identifier is already in use: 'kg'");
  378. expect_failure("fn _()=0", "Reserved identifier");
  379. }
  380. #[test]
  381. fn test_type_check_errors() {
  382. expect_failure("foo", "Unknown identifier 'foo'");
  383. expect_failure("let sin=2", "This name is already used by a function");
  384. expect_failure("fn pi() = 1", "This name is already used by a constant");
  385. expect_failure(
  386. "fn sin(x)=0",
  387. "This name is already used by a foreign function",
  388. );
  389. }
  390. #[test]
  391. fn test_runtime_errors() {
  392. expect_failure("1/0", "Division by zero");
  393. }
  394. #[test]
  395. fn test_comparisons() {
  396. expect_output("2 < 3", "true");
  397. expect_output("2 m < 3 m", "true");
  398. expect_output("20 cm < 3 m", "true");
  399. expect_output("2 m < 100 cm", "false");
  400. expect_output("2 > 3", "false");
  401. expect_output("2 m > 3 m", "false");
  402. expect_output("20 cm > 3 m", "false");
  403. expect_output("2 m > 100 cm", "true");
  404. expect_output("2 <= 2", "true");
  405. expect_output("2.1 <= 2", "false");
  406. expect_output("2 >= 2", "true");
  407. expect_output("2 >= 2.1", "false");
  408. expect_output("200 cm == 2 m", "true");
  409. expect_output("201 cm == 2 m", "false");
  410. expect_output("200 cm != 2 m", "false");
  411. expect_output("201 cm != 2 m", "true");
  412. }
  413. #[test]
  414. fn test_conditionals() {
  415. expect_output("if 1 < 2 then 3 else 4", "3");
  416. expect_output("if 4 < 3 then 2 else 1", "1");
  417. expect_output(
  418. "if 4 > 3 then \"four is larger!\" else \"four is not larger!\"",
  419. "four is larger!",
  420. );
  421. }
  422. #[test]
  423. fn test_string_interpolation() {
  424. expect_output("\"pi = {pi}!\"", "pi = 3.14159!");
  425. expect_output("\"1 + 2 = {1 + 2}\"", "1 + 2 = 3");
  426. }
  427. #[test]
  428. fn test_overwrite_regular_function() {
  429. expect_output(
  430. "
  431. fn f(x)=0
  432. fn f(x)=1
  433. f(2)",
  434. "1",
  435. );
  436. }
  437. #[test]
  438. fn test_overwrite_inner_function() {
  439. expect_output(
  440. "
  441. fn inner() = 0
  442. fn outer() = inner()
  443. fn inner(x) = 1
  444. outer()
  445. ",
  446. "0",
  447. );
  448. }
  449. #[test]
  450. fn test_override_constants() {
  451. expect_output("let x = 1\nlet x = 2\nx", "2");
  452. expect_output("let pi = 4\npi", "4");
  453. }
  454. #[test]
  455. fn test_overwrite_captured_constant() {
  456. expect_output(
  457. "
  458. let x = 1
  459. fn f() = sin(x)
  460. let x = 1 m
  461. f()
  462. ",
  463. "0.841471",
  464. );
  465. }