TestMatch.hx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. package unit;
  2. import haxe.ds.Option;
  3. import haxe.macro.Expr;
  4. using unit.TestMatch;
  5. enum Tree<T> {
  6. Leaf(t:T);
  7. Node(l:Tree<T>, r:Tree<T>);
  8. }
  9. enum A<T> {
  10. TA<Q>(q : Q) : A<Q>;
  11. TB(v : Bool) : A<Bool>;
  12. TC(v : Bool) : A<String>;
  13. }
  14. enum X<A> {
  15. U1(x:Int):X<Int>;
  16. U2:X<String>;
  17. }
  18. enum NE {
  19. A(?x:Int);
  20. }
  21. enum MiniType {
  22. MTString(t:MiniRef<String>, tl:Array<MiniType>);
  23. MTInt(t:MiniRef<Int>, tl:Array<MiniType>);
  24. }
  25. typedef MiniRef<T> = {
  26. public function get():T;
  27. }
  28. class TestMatchMacro {
  29. static public macro function getErrorMessage(e:Expr) {
  30. var result = try {
  31. haxe.macro.Context.typeof(e);
  32. "no error";
  33. } catch (e:Dynamic) Std.string(e.message);
  34. return macro $v{result};
  35. }
  36. }
  37. class TestMatch extends Test {
  38. static function switchNormal(e:Expr):String {
  39. return switch(e.expr) {
  40. case EConst(CString(s)): s;
  41. case EParenthesis( { expr : EConst(CString(s)) } )
  42. | EUntyped( { expr : EConst(CString(s)) } ):
  43. s;
  44. case EField(_, s):
  45. s;
  46. case EArray(_, { expr : EConst(CInt(i) | CFloat(i)) } ):
  47. Std.string(i);
  48. case EIn(_, { expr : e, pos : _ }) :
  49. Std.string(e);
  50. case _:
  51. "not_found";
  52. }
  53. }
  54. static function switchCapture(e:Expr) {
  55. return switch(e) {
  56. case { expr : EConst(const = (CString("foobar") | CInt("9"))) } :
  57. const;
  58. case _:
  59. null;
  60. }
  61. }
  62. static function switchArray(e:Expr):String {
  63. return switch(e.expr) {
  64. case EArrayDecl([]):
  65. "[]";
  66. case EArrayDecl([a]):
  67. "[" + Std.string(a.expr) + "]";
  68. case EArrayDecl([a,b]):
  69. "[" + Std.string(a.expr) + "," + Std.string(b.expr) + "]";
  70. case _:
  71. "_";
  72. }
  73. }
  74. static function switchArray2(a:Array<String>):String {
  75. return switch(a) {
  76. case ["a", "b"]: "0";
  77. case ["a"]: "1";
  78. case ["b"]: "2";
  79. case [a]: "3:" + a;
  80. case [a, b]: "4:" + a + "," +b;
  81. case a if (a.length == 3): "5:" + a.length;
  82. case []: "6";
  83. case _: "7";
  84. }
  85. }
  86. static function switchStructure(a: { foo:String, bar:String } ) {
  87. return switch(a) {
  88. case { foo: "val1", bar:"val2" } : "0";
  89. case { foo: "val1" } : "1";
  90. case { bar: "val2" } : "2";
  91. case { bar: a } : a;
  92. }
  93. }
  94. static function switchCrazy(e:Expr) {
  95. return switch(e.expr) {
  96. case EUntyped( { expr : EParenthesis( { expr : EArray( { expr: a = EConst(CString(_)) }, { expr : EConst(CInt(b)) } ) } ) } ):
  97. Std.string(a) + ":" +b;
  98. case _:
  99. "_";
  100. }
  101. }
  102. static function switchGuard(e:Expr):String {
  103. return switch(e.expr) {
  104. case EConst(CString(s)) if (StringTools.startsWith(s, "foo")):
  105. "1";
  106. case EConst(CString(s)) if (StringTools.startsWith(s, "bar")):
  107. "2";
  108. case EConst(CInt(i)) if (switch(Std.parseInt(i) * 2) { case 4: true; case _: false; }):
  109. "3";
  110. case EConst(_):
  111. "4";
  112. case _:
  113. "5";
  114. }
  115. }
  116. static function switchClass<T>(cl:Class<T>) {
  117. return switch(cl) {
  118. case String: "String";
  119. case MyClass: "unit.MyClass";
  120. case a: "other: " +Type.getClassName(a);
  121. }
  122. }
  123. function testBasic() {
  124. eq("bar", switchNormal(macro "bar"));
  125. eq("bar", switchNormal(macro ("bar")));
  126. eq("bar", switchNormal(macro untyped "bar"));
  127. eq("foo", switchNormal(macro null.foo));
  128. eq("22", switchNormal(macro null[22]));
  129. eq("22.5", switchNormal(macro null[22.5]));
  130. eq("EConst(CInt(0))", switchNormal(macro 1 in 0));
  131. eq("not_found", switchNormal(macro null["22"]));
  132. t(null != switchCapture(macro "foobar"));
  133. t(null == switchCapture(macro "fooba"));
  134. t(null != switchCapture(macro 9));
  135. t(null == switchCapture(macro 10));
  136. eq("[]", switchArray(macro []));
  137. eq("_", switchArray(macro 2));
  138. eq("[EConst(CInt(22))]", switchArray(macro [22]));
  139. eq("[EConst(CInt(22)),EConst(CString(foo))]", switchArray(macro [22,"foo"]));
  140. eq("_", switchArray(macro [22, "foo", "bar"]));
  141. eq("0", switchArray2(["a", "b"]));
  142. eq("1", switchArray2(["a"]));
  143. eq("2", switchArray2(["b"]));
  144. eq("3:c", switchArray2(["c"]));
  145. eq("4:a,a", switchArray2(["a","a"]));
  146. eq("4:b,a", switchArray2(["b","a"]));
  147. eq("5:3", switchArray2(["a","a","a"]));
  148. eq("6", switchArray2([]));
  149. eq("7", switchArray2(["a", "a", "a", "b"]));
  150. eq("EConst(CString(foobar)):12", switchCrazy(macro untyped ("foobar"[12])));
  151. eq("1", switchGuard(macro "foobar"));
  152. eq("2", switchGuard(macro "barfoo"));
  153. eq("3", switchGuard(macro 2));
  154. eq("4", switchGuard(macro 5));
  155. eq("4", switchGuard(macro "bazfoo"));
  156. eq("5", switchGuard(macro []));
  157. eq("0", switch ([true, 1, "foo"]) {
  158. case [true, 1, "foo"]: "0";
  159. case [true, 1, _]: "1";
  160. case _: "_";
  161. });
  162. eq("0", switch [true, 1, "foo"] {
  163. case [true, 1, "foo"]: "0";
  164. case [true, 1, _]: "1";
  165. case _: "_";
  166. });
  167. eq("1", switch [true, 1, "bar"] {
  168. case [true, 1, "foo"]: "0";
  169. case [true, 1, _]: "1";
  170. case _: "_";
  171. });
  172. eq("_", switch [false, 1, "foo"] {
  173. case [true, 1, "foo"]: "0";
  174. case [true, 1, _]: "1";
  175. case _: "_";
  176. });
  177. eq("1", switch [1, 2] {
  178. case [0, 0] | [1, 2]: "1";
  179. case [1, 1]: "2";
  180. case _: "_";
  181. });
  182. var t = TA("foo");
  183. eq("0", switch(t) {
  184. case TA("foo"): "0";
  185. case TA(_): "1";
  186. case TC(_): "2";
  187. });
  188. }
  189. function testTuple() {
  190. function test(a:Int, b:Int, c:Int) return switch [a, b, c] {
  191. case [x, 1, 2] | [1, 2, x] | [1, x, 2]: '0|x:$x';
  192. case [3, 4, z] | [z, 3, 4] | [3, z, 4]: '1|z:$z';
  193. case [1, y, z] | [2, z, y]: '2|y:$y,z:$z';
  194. case [x, y, z]: '_:x:$x,y:$y,z:$z';
  195. }
  196. eq("0|x:9", test(9, 1, 2));
  197. eq("0|x:9", test(1, 2, 9));
  198. eq("0|x:9", test(1, 9, 2));
  199. eq("1|z:12", test(3, 4, 12));
  200. eq("1|z:12", test(12, 3, 4));
  201. eq("1|z:12", test(3, 12, 4));
  202. eq("2|y:9,z:8", test(1, 9, 8));
  203. eq("2|y:9,z:8", test(2, 8, 9));
  204. eq("_:x:9,y:8,z:7", test(9, 8, 7));
  205. }
  206. function testGrouping() {
  207. function test(v) return switch(v) {
  208. case 1, 2, 3: "0";
  209. case val = (4 | 5 | 6) if (val == 5): "1";
  210. case 4, 5, 6: "2";
  211. case 8, 9: "3";
  212. case x: '_:$x';
  213. }
  214. var results = ["_:0", "0", "0", "0", "2", "1", "2", "_:7", "3", "3", "_:10"];
  215. for (i in 0...results.length) {
  216. eq(results[i], test(i));
  217. }
  218. }
  219. function testSubtyping() {
  220. var c = new MyClass.InitBase();
  221. var r = switch(c) {
  222. case { s: "foo" } :
  223. "s = foo";
  224. case _:
  225. "_";
  226. }
  227. eq("s = foo", r);
  228. eq("0", switchStructure( { foo:"val1", bar:"val2" } ));
  229. eq("1", switchStructure( { foo:"val1", bar:"val1" } ));
  230. eq("2", switchStructure( { foo:"val2", bar:"val2" } ));
  231. eq("val1", switchStructure( { foo:"val2", bar:"val1" } ));
  232. }
  233. public static function toStringX<Z>(x1:X<Z>) {
  234. return switch (x1) {
  235. case U1(x) if (x > 1): ">1";
  236. case U1(x) if (x <= 1): "<=1";
  237. case U1(_): throw "this is impossible to reach actually";
  238. case U2: "U2";
  239. }
  240. }
  241. function testGadt() {
  242. eq("<=1", toStringX(U1(1)));
  243. eq(">1", toStringX(U1(2)));
  244. eq("U2", toStringX(U2));
  245. }
  246. function testClassSwitch() {
  247. eq("String", switchClass(String));
  248. eq("unit.MyClass", switchClass(MyClass));
  249. eq("other: unit.TestMatch", switchClass(TestMatch));
  250. }
  251. function testOr() {
  252. var i1 = macro 1;
  253. var i2 = macro 2;
  254. var f1 = macro 3.9;
  255. var f2 = macro 4.8;
  256. eq("11", orMatch(i1, i1));
  257. eq("12", orMatch(i1, i2));
  258. eq("13.9", orMatch(i1, f1));
  259. eq("14.8", orMatch(i1, f2));
  260. eq("21", orMatch(i2, i1));
  261. eq("22", orMatch(i2, i2));
  262. eq("23.9", orMatch(i2, f1));
  263. eq("24.8", orMatch(i2, f2));
  264. eq("3.91", orMatch(f1, i1));
  265. eq("3.92", orMatch(f1, i2));
  266. eq("3.93.9", orMatch(f1, f1));
  267. eq("3.94.8", orMatch(f1, f2));
  268. eq("4.81", orMatch(f2, i1));
  269. eq("4.82", orMatch(f2, i2));
  270. eq("4.83.9", orMatch(f2, f1));
  271. eq("4.84.8", orMatch(f2, f2));
  272. }
  273. function testStaticNull() {
  274. var v = A();
  275. var r = switch(v) {
  276. case A(x):
  277. if (x == null) "null";
  278. else "not null";
  279. }
  280. eq("null", r);
  281. }
  282. static function orMatch(e1, e2) {
  283. return switch([e1.expr, e2.expr]) {
  284. case [EConst(CFloat(a) | CInt(a)), EConst(CFloat(b) | CInt(b))]: a + b;
  285. case _: null;
  286. }
  287. }
  288. function testNonExhaustiveness() {
  289. eq("Unmatched patterns: false", TestMatchMacro.getErrorMessage(switch(true) {
  290. case true:
  291. }));
  292. eq("Unmatched patterns: OpNegBits | OpNeg", TestMatchMacro.getErrorMessage(switch(OpIncrement) {
  293. case OpIncrement:
  294. case OpDecrement:
  295. case OpNot:
  296. }));
  297. eq("Unmatched patterns: Node(Leaf(_),_)", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  298. case Node(Leaf("foo"), _):
  299. case Leaf(_):
  300. }));
  301. eq("Unmatched patterns: Leaf", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  302. case Node(_, _):
  303. case Leaf(_) if (false):
  304. }));
  305. eq("Unmatched patterns: Leaf(_)", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  306. case Node(_, _):
  307. case Leaf("foo"):
  308. }));
  309. eq("Unmatched patterns: [_,false,_]", TestMatchMacro.getErrorMessage(switch [1, true, "foo"] {
  310. case [_, true, _]:
  311. }));
  312. //var x:Null<Bool> = true;
  313. //eq("Unmatched patterns: null", TestMatchMacro.getErrorMessage(switch x {
  314. //case true:
  315. //case false:
  316. //}));
  317. //var t:Null<Tree<String>> = null;
  318. //eq("Unmatched patterns: null", TestMatchMacro.getErrorMessage(switch t {
  319. //case Leaf(_):
  320. //case Node(_):
  321. //}));
  322. }
  323. function testInvalidBinding() {
  324. eq("Variable y must appear exactly once in each sub-pattern", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  325. case Leaf(x) | Leaf(y):
  326. }));
  327. eq("Variable y must appear exactly once in each sub-pattern", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  328. case Leaf(x) | Leaf(x) | Leaf(y):
  329. }));
  330. eq("Variable x must appear exactly once in each sub-pattern", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  331. case Leaf(x) | Leaf(x) | Leaf(_):
  332. }));
  333. eq("Variable l must appear exactly once in each sub-pattern", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  334. case Node(l = Leaf(x),_) | Node(Leaf(x), _):
  335. }));
  336. eq("Variable l must appear exactly once in each sub-pattern", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  337. case Node(l = Leaf(l), _):
  338. }));
  339. eq("String should be unit.Tree<String>", TestMatchMacro.getErrorMessage(switch(Leaf("foo")) {
  340. case Node(l = Leaf(_), _) | Leaf(l):
  341. }));
  342. }
  343. function testNullPattern() {
  344. var i:Null<Int> = null;
  345. var r = switch(i) {
  346. case 1: 1;
  347. case null: 2;
  348. case _: 3;
  349. }
  350. eq(2, r);
  351. // this should not compile because the argument is not explicitly Null
  352. //var e = EConst(null);
  353. //var r = switch(e) {
  354. //case EConst(null): 1;
  355. //case _: 2;
  356. //}
  357. var t:Null<Tree<String>> = null;
  358. var r = switch(t) {
  359. case Leaf(_): 1;
  360. case null if (i != null): 2;
  361. case null: 3;
  362. case Node(_): 4;
  363. }
  364. eq(r, 3);
  365. var e1 = macro if (1) 2;
  366. var e2 = macro if (1) 2 else 3;
  367. function matchIf(e) {
  368. return switch(e.expr) {
  369. case EIf(_, _, null): 1;
  370. case EIf(_, _, _): 2;
  371. case _: 3;
  372. }
  373. }
  374. eq(1, matchIf(e1));
  375. eq(2, matchIf(e2));
  376. var t = Leaf("foo");
  377. function f(t) return switch(t) {
  378. case Leaf(null): "null";
  379. case Leaf(e): e;
  380. case Node(_): "default";
  381. }
  382. eq(f(t), "foo");
  383. function f(a) {
  384. return switch(a:{a: Int}) {
  385. case {a: 1}: 1;
  386. case null: 2;
  387. default: 3;
  388. }
  389. }
  390. eq(f(null), 2);
  391. eq(f({a: 1}), 1);
  392. eq(f({a: 2}), 3);
  393. }
  394. function testFakeEnumAbstract() {
  395. #if !macro
  396. var a = unit.MyAbstract.FakeEnumAbstract.NotFound;
  397. var r = switch(a) {
  398. case NotFound: 1;
  399. case _: 2;
  400. }
  401. eq(r, 1);
  402. eq("Unmatched patterns: MethodNotAllowed", TestMatchMacro.getErrorMessage(switch(a) {
  403. case NotFound:
  404. }));
  405. #end
  406. }
  407. function testExtractors() {
  408. function f(i) {
  409. return switch(i) {
  410. case 1,2,3: 1;
  411. case _.even() => true: 2;
  412. case 4: throw "unreachable";
  413. case _: 3;
  414. }
  415. }
  416. eq(1, f(1));
  417. eq(1, f(2));
  418. eq(1, f(3));
  419. eq(2, f(4));
  420. eq(3, f(5));
  421. eq(3, f(7));
  422. eq(3, f(9));
  423. eq(2, f(6));
  424. eq(2, f(8));
  425. function ref<T>(t:T):MiniRef<T> return {
  426. get: function() return t
  427. }
  428. function f(t:MiniType) {
  429. return switch (t) {
  430. case MTString(_.deref() => "Foo", []): "Foo";
  431. case MTString(_.deref() => "Bar" | "Baz", _): "BarBaz";
  432. case MTInt(_.deref() => i, []): 'Int:$i';
  433. case MTString(_): "OtherString";
  434. case _: "Other";
  435. }
  436. }
  437. eq("Foo", f(MTString(ref("Foo"), [])));
  438. eq("BarBaz", f(MTString(ref("Bar"), [])));
  439. eq("BarBaz", f(MTString(ref("Baz"), [])));
  440. eq("OtherString", f(MTString(ref("a"), [])));
  441. eq("OtherString", f(MTString(ref(""), [])));
  442. eq("Int:12", f(MTInt(ref(12), [])));
  443. eq("Other", f(MTInt(ref(12), [MTInt(ref(10),[])])));
  444. function g(i : Array<Int>) {
  445. return switch(i) {
  446. case [x]: 1;
  447. case isPair(_) => Some(p) : p.a+p.b;
  448. case arr: 3;
  449. }
  450. }
  451. eq(3, g([]));
  452. eq(1, g([1]));
  453. eq(5, g([2, 3]));
  454. eq(3, g([2, 3, 4]));
  455. var anon = {
  456. odd: function(i) return i & 1 != 0
  457. };
  458. var i = 9;
  459. var r = switch(i) {
  460. case 1: 1;
  461. case anon.odd(_) => true: 2;
  462. case 9: 3;
  463. case _: 4;
  464. }
  465. eq(2, r);
  466. function mul(i1,i2) return i1 * i2;
  467. function check(i) {
  468. return switch(i) {
  469. case 1: 1;
  470. case mul(_, 4) => 8: 2;
  471. case mul(_, 5) => 15: 3;
  472. case _: 4;
  473. }
  474. }
  475. eq(1, check(1));
  476. eq(2, check(2));
  477. eq(3, check(3));
  478. eq(4, check(4));
  479. function is<T>(pred : T -> Bool) return function (x : T) {
  480. return pred(x)?Some(x):None;
  481. }
  482. function isNot<T>(pred : T -> Bool) return function (x : T) {
  483. return (!pred(x))?Some(x):None;
  484. }
  485. function testArgs<T>(i:Int, s:String, t:T) {
  486. return Std.string(t);
  487. }
  488. function h(i : Array<Int>) {
  489. return switch(i) {
  490. case [x]: 1;
  491. case isPair(_) => Some({ a : a, b : b }) if (a < 0): 42;
  492. case isPair(_) => Some({ a : is(even)(_) => Some(a), b : b }) : a+b;
  493. case isPair(_) => Some({ a : isNot(even)(_) => Some(a), b : b }) : a*b;
  494. case testArgs(1, "foo", _) => "[99,98,97]": 99;
  495. case arr: 3;
  496. }
  497. }
  498. eq(3, h([]));
  499. eq(1, h([1]));
  500. eq(1, h([2]));
  501. eq(5, h([2, 3]));
  502. eq(3, h([1, 3]));
  503. eq(3, h([2, 3, 4]));
  504. eq(42, h([-1, 3]));
  505. eq(99, h([99,98,97]));
  506. }
  507. static function isPair<T>(t:Array<T>) return t.length == 2 ? Some({a:t[0], b:t[1]}) : None;
  508. static function even(i:Int) {
  509. return i & 1 == 0;
  510. }
  511. static function deref<T>(ref:MiniRef<T>) return ref.get();
  512. #if false
  513. //all lines marked as // unused should give an error
  514. function testRedundance() {
  515. switch(true) {
  516. case false:
  517. case true:
  518. case false: // unused
  519. }
  520. switch(true) {
  521. case false | true:
  522. case true: // unused
  523. case false: // unused
  524. }
  525. switch(true) {
  526. case false
  527. | false: // unused
  528. case true:
  529. }
  530. switch(Leaf(true)) {
  531. case Leaf(true):
  532. case Leaf(false):
  533. case Leaf(x): // unused
  534. case Node(_):
  535. }
  536. switch({s:"foo"}) {
  537. case { s : "foo" } :
  538. case { s : a } :
  539. }
  540. switch( { s:"foo", t:"bar" } ) {
  541. case { s : "foo" }:
  542. case { t : "bar" }:
  543. case { s : "foo", t:"bar" }: // unused
  544. case _:
  545. }
  546. }
  547. #end
  548. }