TestMatch.hx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package unit;
  2. import haxe.macro.Expr;
  3. enum Tree<T> {
  4. Leaf(t:T);
  5. Node(l:Tree<T>, r:Tree<T>);
  6. }
  7. enum A<T> {
  8. TA<Q>(q : Q) : A<Q>;
  9. TB(v : Bool) : A<Bool>;
  10. TC(v : Bool) : A<String>;
  11. }
  12. enum X<A> {
  13. U1(x:Int):X<Int>;
  14. U2:X<String>;
  15. }
  16. enum NE {
  17. A(?x:Int);
  18. }
  19. class TestMatch extends Test {
  20. static macro function getErrorMessage(e:Expr) {
  21. var result = try {
  22. haxe.macro.Context.typeof(e);
  23. "no error";
  24. } catch (e:Dynamic) Std.string(e.message);
  25. return macro $v{result};
  26. }
  27. static function switchNormal(e:Expr):String {
  28. return switch(e.expr) {
  29. case EConst(CString(s)): s;
  30. case EParenthesis( { expr : EConst(CString(s)) } )
  31. | EUntyped( { expr : EConst(CString(s)) } ):
  32. s;
  33. case EField(_, s):
  34. s;
  35. case EArray(_, { expr : EConst(CInt(i) | CFloat(i)) } ):
  36. Std.string(i);
  37. case EIn(_, { expr : e, pos : _ }) :
  38. Std.string(e);
  39. case _:
  40. "not_found";
  41. }
  42. }
  43. static function switchCapture(e:Expr) {
  44. return switch(e) {
  45. case { expr : EConst(const = (CString("foobar") | CInt("9"))) } :
  46. const;
  47. case _:
  48. null;
  49. }
  50. }
  51. static function switchArray(e:Expr):String {
  52. return switch(e.expr) {
  53. case EArrayDecl([]):
  54. "[]";
  55. case EArrayDecl([a]):
  56. "[" + Std.string(a.expr) + "]";
  57. case EArrayDecl([a,b]):
  58. "[" + Std.string(a.expr) + "," + Std.string(b.expr) + "]";
  59. case _:
  60. "_";
  61. }
  62. }
  63. static function switchArray2(a:Array<String>):String {
  64. return switch(a) {
  65. case ["a", "b"]: "0";
  66. case ["a"]: "1";
  67. case ["b"]: "2";
  68. case [a]: "3:" + a;
  69. case [a, b]: "4:" + a + "," +b;
  70. case a if (a.length == 3): "5:" + a.length;
  71. case []: "6";
  72. case _: "7";
  73. }
  74. }
  75. static function switchStructure(a: { foo:String, bar:String } ) {
  76. return switch(a) {
  77. case { foo: "val1", bar:"val2" } : "0";
  78. case { foo: "val1" } : "1";
  79. case { bar: "val2" } : "2";
  80. case { bar: a } : a;
  81. }
  82. }
  83. static function switchCrazy(e:Expr) {
  84. return switch(e.expr) {
  85. case EUntyped( { expr : EParenthesis( { expr : EArray( { expr: a = EConst(CString(_)) }, { expr : EConst(CInt(b)) } ) } ) } ):
  86. Std.string(a) + ":" +b;
  87. case _:
  88. "_";
  89. }
  90. }
  91. static function switchGuard(e:Expr):String {
  92. return switch(e.expr) {
  93. case EConst(CString(s)) if (StringTools.startsWith(s, "foo")):
  94. "1";
  95. case EConst(CString(s)) if (StringTools.startsWith(s, "bar")):
  96. "2";
  97. case EConst(CInt(i)) if (switch(Std.parseInt(i) * 2) { case 4: true; case _: false; }):
  98. "3";
  99. case EConst(_):
  100. "4";
  101. case _:
  102. "5";
  103. }
  104. }
  105. static function switchClass<T>(cl:Class<T>) {
  106. return switch(cl) {
  107. case String: "String";
  108. case MyClass: "unit.MyClass";
  109. case a: "other: " +Type.getClassName(a);
  110. }
  111. }
  112. function testBasic() {
  113. eq("bar", switchNormal(macro "bar"));
  114. eq("bar", switchNormal(macro ("bar")));
  115. eq("bar", switchNormal(macro untyped "bar"));
  116. eq("foo", switchNormal(macro null.foo));
  117. eq("22", switchNormal(macro null[22]));
  118. eq("22.5", switchNormal(macro null[22.5]));
  119. eq("EConst(CInt(0))", switchNormal(macro 1 in 0));
  120. eq("not_found", switchNormal(macro null["22"]));
  121. t(null != switchCapture(macro "foobar"));
  122. t(null == switchCapture(macro "fooba"));
  123. t(null != switchCapture(macro 9));
  124. t(null == switchCapture(macro 10));
  125. eq("[]", switchArray(macro []));
  126. eq("_", switchArray(macro 2));
  127. eq("[EConst(CInt(22))]", switchArray(macro [22]));
  128. eq("[EConst(CInt(22)),EConst(CString(foo))]", switchArray(macro [22,"foo"]));
  129. eq("_", switchArray(macro [22, "foo", "bar"]));
  130. eq("0", switchArray2(["a", "b"]));
  131. eq("1", switchArray2(["a"]));
  132. eq("2", switchArray2(["b"]));
  133. eq("3:c", switchArray2(["c"]));
  134. eq("4:a,a", switchArray2(["a","a"]));
  135. eq("4:b,a", switchArray2(["b","a"]));
  136. eq("5:3", switchArray2(["a","a","a"]));
  137. eq("6", switchArray2([]));
  138. eq("7", switchArray2(["a", "a", "a", "b"]));
  139. eq("EConst(CString(foobar)):12", switchCrazy(macro untyped ("foobar"[12])));
  140. eq("1", switchGuard(macro "foobar"));
  141. eq("2", switchGuard(macro "barfoo"));
  142. eq("3", switchGuard(macro 2));
  143. eq("4", switchGuard(macro 5));
  144. eq("4", switchGuard(macro "bazfoo"));
  145. eq("5", switchGuard(macro []));
  146. eq("0", switch ([true, 1, "foo"]) {
  147. case [true, 1, "foo"]: "0";
  148. case [true, 1, _]: "1";
  149. case _: "_";
  150. });
  151. eq("0", switch [true, 1, "foo"] {
  152. case [true, 1, "foo"]: "0";
  153. case [true, 1, _]: "1";
  154. case _: "_";
  155. });
  156. eq("1", switch [true, 1, "bar"] {
  157. case [true, 1, "foo"]: "0";
  158. case [true, 1, _]: "1";
  159. case _: "_";
  160. });
  161. eq("_", switch [false, 1, "foo"] {
  162. case [true, 1, "foo"]: "0";
  163. case [true, 1, _]: "1";
  164. case _: "_";
  165. });
  166. eq("1", switch [1, 2] {
  167. case [0, 0] | [1, 2]: "1";
  168. case [1, 1]: "2";
  169. case _: "_";
  170. });
  171. var t = TA("foo");
  172. eq("0", switch(t) {
  173. case TA("foo"): "0";
  174. case TA(_): "1";
  175. case TC(_): "2";
  176. });
  177. }
  178. function testTuple() {
  179. function test(a:Int, b:Int, c:Int) return switch [a, b, c] {
  180. case [x, 1, 2] | [1, 2, x] | [1, x, 2]: '0|x:$x';
  181. case [3, 4, z] | [z, 3, 4] | [3, z, 4]: '1|z:$z';
  182. case [1, y, z] | [2, z, y]: '2|y:$y,z:$z';
  183. case [x, y, z]: '_:x:$x,y:$y,z:$z';
  184. }
  185. eq("0|x:9", test(9, 1, 2));
  186. eq("0|x:9", test(1, 2, 9));
  187. eq("0|x:9", test(1, 9, 2));
  188. eq("1|z:12", test(3, 4, 12));
  189. eq("1|z:12", test(12, 3, 4));
  190. eq("1|z:12", test(3, 12, 4));
  191. eq("2|y:9,z:8", test(1, 9, 8));
  192. eq("2|y:9,z:8", test(2, 8, 9));
  193. eq("_:x:9,y:8,z:7", test(9, 8, 7));
  194. }
  195. function testGrouping() {
  196. function test(v) return switch(v) {
  197. case 1, 2, 3: "0";
  198. case val = (4 | 5 | 6) if (val == 5): "1";
  199. case 4, 5, 6: "2";
  200. case 8, 9: "3";
  201. case x: '_:$x';
  202. }
  203. var results = ["_:0", "0", "0", "0", "2", "1", "2", "_:7", "3", "3", "_:10"];
  204. for (i in 0...results.length) {
  205. eq(results[i], test(i));
  206. }
  207. }
  208. function testSubtyping() {
  209. var c = new MyClass.InitBase();
  210. var r = switch(c) {
  211. case { s: "foo" } :
  212. "s = foo";
  213. case _:
  214. "_";
  215. }
  216. eq("s = foo", r);
  217. eq("0", switchStructure( { foo:"val1", bar:"val2" } ));
  218. eq("1", switchStructure( { foo:"val1", bar:"val1" } ));
  219. eq("2", switchStructure( { foo:"val2", bar:"val2" } ));
  220. eq("val1", switchStructure( { foo:"val2", bar:"val1" } ));
  221. }
  222. public static function toStringX<Z>(x1:X<Z>) {
  223. return switch (x1) {
  224. case U1(x) if (x > 1): ">1";
  225. case U1(x) if (x <= 1): "<=1";
  226. case U1(_): throw "this is impossible to reach actually";
  227. case U2: "U2";
  228. }
  229. }
  230. function testGadt() {
  231. eq("<=1", toStringX(U1(1)));
  232. eq(">1", toStringX(U1(2)));
  233. eq("U2", toStringX(U2));
  234. }
  235. function testClassSwitch() {
  236. eq("String", switchClass(String));
  237. eq("unit.MyClass", switchClass(MyClass));
  238. eq("other: unit.TestMatch", switchClass(TestMatch));
  239. }
  240. function testOr() {
  241. var i1 = macro 1;
  242. var i2 = macro 2;
  243. var f1 = macro 3.9;
  244. var f2 = macro 4.8;
  245. eq("11", orMatch(i1, i1));
  246. eq("12", orMatch(i1, i2));
  247. eq("13.9", orMatch(i1, f1));
  248. eq("14.8", orMatch(i1, f2));
  249. eq("21", orMatch(i2, i1));
  250. eq("22", orMatch(i2, i2));
  251. eq("23.9", orMatch(i2, f1));
  252. eq("24.8", orMatch(i2, f2));
  253. eq("3.91", orMatch(f1, i1));
  254. eq("3.92", orMatch(f1, i2));
  255. eq("3.93.9", orMatch(f1, f1));
  256. eq("3.94.8", orMatch(f1, f2));
  257. eq("4.81", orMatch(f2, i1));
  258. eq("4.82", orMatch(f2, i2));
  259. eq("4.83.9", orMatch(f2, f1));
  260. eq("4.84.8", orMatch(f2, f2));
  261. }
  262. function testStaticNull() {
  263. var v = A();
  264. var r = switch(v) {
  265. case A(x):
  266. if (x == null) "null";
  267. else "not null";
  268. }
  269. eq("null", r);
  270. }
  271. static function orMatch(e1, e2) {
  272. return switch([e1.expr, e2.expr]) {
  273. case [EConst(CFloat(a) | CInt(a)), EConst(CFloat(b) | CInt(b))]: a + b;
  274. case _: null;
  275. }
  276. }
  277. function testNonExhaustiveness() {
  278. eq("Unmatched patterns: false", getErrorMessage(switch(true) {
  279. case true:
  280. }));
  281. eq("Unmatched patterns: OpNegBits | OpNeg", getErrorMessage(switch(OpIncrement) {
  282. case OpIncrement:
  283. case OpDecrement:
  284. case OpNot:
  285. }));
  286. eq("Unmatched patterns: Node(Leaf(_),_)", getErrorMessage(switch(Leaf("foo")) {
  287. case Node(Leaf("foo"), _):
  288. case Leaf(_):
  289. }));
  290. eq("Unmatched patterns: Leaf", getErrorMessage(switch(Leaf("foo")) {
  291. case Node(_, _):
  292. case Leaf(_) if (false):
  293. }));
  294. eq("Unmatched patterns: Leaf(_)", getErrorMessage(switch(Leaf("foo")) {
  295. case Node(_, _):
  296. case Leaf("foo"):
  297. }));
  298. eq("Unmatched patterns: [_,false,_]", getErrorMessage(switch [1, true, "foo"] {
  299. case [_, true, _]:
  300. }));
  301. }
  302. function testInvalidBinding() {
  303. eq("Variable y must appear exactly once in each sub-pattern", getErrorMessage(switch(Leaf("foo")) {
  304. case Leaf(x) | Leaf(y):
  305. }));
  306. eq("Variable y must appear exactly once in each sub-pattern", getErrorMessage(switch(Leaf("foo")) {
  307. case Leaf(x) | Leaf(x) | Leaf(y):
  308. }));
  309. eq("Variable x must appear exactly once in each sub-pattern", getErrorMessage(switch(Leaf("foo")) {
  310. case Leaf(x) | Leaf(x) | Leaf(_):
  311. }));
  312. eq("Variable l must appear exactly once in each sub-pattern", getErrorMessage(switch(Leaf("foo")) {
  313. case Node(l = Leaf(x),_) | Node(Leaf(x), _):
  314. }));
  315. eq("Variable l must appear exactly once in each sub-pattern", getErrorMessage(switch(Leaf("foo")) {
  316. case Node(l = Leaf(l), _):
  317. }));
  318. eq("String should be unit.Tree<String>", getErrorMessage(switch(Leaf("foo")) {
  319. case Node(l = Leaf(_), _) | Leaf(l):
  320. }));
  321. }
  322. #if false
  323. //all lines marked as // unused should give an error
  324. function testRedundance() {
  325. switch(true) {
  326. case false:
  327. case true:
  328. case false: // unused
  329. }
  330. switch(true) {
  331. case false | true:
  332. case true: // unused
  333. case false: // unused
  334. }
  335. switch(true) {
  336. case false
  337. | false: // unused
  338. case true:
  339. }
  340. switch(Leaf(true)) {
  341. case Leaf(true):
  342. case Leaf(false):
  343. case Leaf(x): // unused
  344. case Node(_):
  345. }
  346. switch({s:"foo"}) {
  347. case { s : "foo" } :
  348. case { s : a } :
  349. case _: // unused
  350. }
  351. switch( { s:"foo", t:"bar" } ) {
  352. case { s : "foo" }:
  353. case { t : "bar" }:
  354. case { s : "foo", t:"bar" }: // unused
  355. case _:
  356. }
  357. }
  358. #end
  359. }