From 35fef5c60522f20a945c43385aeaeb4a60d5f242 Mon Sep 17 00:00:00 2001 From: Andrea Ferretti Date: Fri, 3 Jul 2015 21:04:28 +0200 Subject: [PATCH] Added equality --- README.md | 2 +- patty.nim | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e3a554a..9964c2c 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ A couple of limitations fo the `adt` macro: * field names must be unique across branches (that is, different variants cannot have two fields with the same name). This is actually a limitation of Nim. * the shortcut that groups field names by type does not seem to work, that is, in the above example one could not write `Rectangle(w, h: float)`. -In the future, Patty will also add a proper definition of equality and copy constructors. Also, some work needs to be done to make it easier to use the generated contructors with `ref` types, in particular for the important case of recursive algebraic data types. +In the future, Patty may also add copy constructors. Also, some work needs to be done to make it easier to use the generated contructors with `ref` types, in particular for the important case of recursive algebraic data types. Things that do not work (yet) ----------------------------- diff --git a/patty.nim b/patty.nim index 0521604..9bd572c 100644 --- a/patty.nim +++ b/patty.nim @@ -1,4 +1,4 @@ -import macros +import macros, sequtils const enumSuffix = "E" @@ -109,14 +109,61 @@ proc defineConstructor(e, n: NimNode): NimNode {. compileTime .} = else: error("Invalid ADT case: " & $(toStrLit(n))) +proc eqFor(n: NimNode): NimNode {. compileTime .} = + if n.kind == nnkObjConstr: + result = newNimNode(nnkOfBranch).add(n[0] & enumSuffix) + var comparisons: seq[NimNode] = @[] + + for c in tail(n): + comparisons.add(infix(newDotExpr(ident("a"), c[0]), "==", newDotExpr(ident("b"), c[0]))) + + let body = foldr(comparisons, infix(a, "and", b)) + + result.add(newStmtList(newNimNode(nnkReturnStmt).add(body))) + elif n.kind == nnkIdent: + result = newNimNode(nnkOfBranch).add(n & enumSuffix) + result.add(newStmtList(newNimNode(nnkReturnStmt).add(ident("true")))) + else: + error("Invalid ADT case: " & $(toStrLit(n))) + + +proc defineEquality(tp, body: NimNode): NimNode {. compileTime .} = + # template compare(content, tp: NimNode) = + # proc `==`(a, b: tp): bool = + # if a.kind == b.kind: content + # else: false + var condition = newNimNode(nnkCaseStmt).add(newDotExpr(ident("a"), ident("kind"))) + for child in children(body): + condition.add(eqFor(child)) + + var body = newNimNode(nnkIfExpr).add( + newNimNode(nnkElifBranch).add( + infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))), + condition + ), + newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false")))) + ) + + result = newProc( + name = ident("`==`"), + params = [ident("bool"), newIdentDefs(ident("a"), tp), newIdentDefs(ident("b"), tp)], + body = body + ) + # result = getAst(compare(condition, tp)) + macro adt*(e: expr, body: stmt): stmt {. immediate .} = - result = newStmtList(defineTypes(e, body)) + result = newStmtList(defineTypes(e, body), defineEquality(e, body)) for child in children(body): result.add(defineConstructor(e, child)) when defined(pattydebug): echo toStrLit(result) +adt Shape: + Circle(r: float, x: float, y: float) + Rectangle(w: float, h: float) + Square(side: int) + macro match*(e: expr, body: stmt): stmt {. immediate .} = # A fresh symbol used to hold the evaluation of e let sym = genSym()