summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2014-03-20 01:15:20 +0200
committerZahary Karadjov <zahary@gmail.com>2014-03-20 01:16:50 +0200
commit8a0509b3b255c2aef10b9db468810d98ae0d4215 (patch)
tree02df5079131c7172ed83140a62ed32cd2b77ba9a /compiler
parentd508384d39461966ee247a01ad637bc57d3f62af (diff)
downloadNim-8a0509b3b255c2aef10b9db468810d98ae0d4215.tar.gz
implements ``distinct with/without X, Y``
This still doesn't work quite right, because some common operations like array
indexing lay completely outside the scope/symbol lookup system - they are not
even magics.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim6
-rw-r--r--compiler/parser.nim44
-rw-r--r--compiler/renderer.nim15
-rw-r--r--compiler/semtypes.nim11
-rw-r--r--compiler/sigmatch.nim23
5 files changed, 72 insertions, 27 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ae1b34fd9..30778c02d 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -188,6 +188,10 @@ type
     nkStmtListType,       # a statement list ending in a type; for macros
     nkBlockType,          # a statement block ending in a type; for macros
                           # types as syntactic trees:
+
+    nkWith,               # distinct with `foo`
+    nkWithout,            # distinct without `foo`
+    
     nkTypeOfExpr,         # type(1+2)
     nkObjectTy,           # object body
     nkTupleTy,            # tuple body
@@ -418,7 +422,7 @@ type
     nfExplicitCall # x.y() was used instead of x.y
     nfExprCall  # this is an attempt to call a regular expression
     nfIsRef     # this node is a 'ref' node; used for the VM
-
+ 
   TNodeFlags* = set[TNodeFlag]
   TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 23)
     tfVarargs,        # procedure has C styled varargs
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 060629518..d59e013d7 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -67,7 +67,7 @@ proc optPar*(p: var TParser)
 proc optInd*(p: var TParser, n: PNode)
 proc indAndComment*(p: var TParser, n: PNode)
 proc setBaseFlags*(n: PNode, base: TNumericalBase)
-proc parseSymbol*(p: var TParser): PNode
+proc parseSymbol*(p: var TParser, allowNil = false): PNode
 proc parseTry(p: var TParser): PNode
 proc parseCase(p: var TParser): PNode
 # implementation
@@ -273,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) =
   eat(p, tkColon)
   skipComment(p, n)
 
-proc parseSymbol(p: var TParser): PNode =
+proc parseSymbol(p: var TParser, allowNil = false): PNode =
   #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
   #|        | IDENT
   case p.tok.tokType
@@ -312,9 +312,13 @@ proc parseSymbol(p: var TParser): PNode =
         break
     eat(p, tkAccent)
   else:
-    parMessage(p, errIdentifierExpected, p.tok)
-    getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
-    result = ast.emptyNode
+    if allowNil and p.tok.tokType == tkNil:
+      result = newNodeP(nkNilLit, p)
+      getTok(p)
+    else:
+      parMessage(p, errIdentifierExpected, p.tok)
+      getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
+      result = ast.emptyNode
 
 proc indexExpr(p: var TParser): PNode = 
   #| indexExpr = expr
@@ -964,14 +968,30 @@ proc isExprStart(p: TParser): bool =
      tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared:
     result = true
   else: result = false
-  
-proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, 
-                       mode: TPrimaryMode): PNode = 
+
+proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
+  while true:
+    var s = parseSymbol(p, allowNil)
+    if s.kind == nkEmpty: break
+    addSon(result, s)
+    if p.tok.tokType != tkComma: break
+    getTok(p)
+    optInd(p, s)
+
+proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
+                       mode: TPrimaryMode): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
   if not isOperator(p.tok) and isExprStart(p):
     addSon(result, primary(p, mode))
+  if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
+    let nodeKind = if p.tok.tokType == tkWith: nkWith
+                   else: nkWithout
+    getTok(p)
+    let list = newNodeP(nodeKind, p)
+    result.addSon list
+    parseSymbolList(p, list, allowNil = true)
 
 proc parseExpr(p: var TParser): PNode = 
   #| expr = (ifExpr
@@ -988,7 +1008,6 @@ proc parseExpr(p: var TParser): PNode =
 
 proc parseEnum(p: var TParser): PNode
 proc parseObject(p: var TParser): PNode
-proc parseDistinct(p: var TParser): PNode
 proc parseTypeClass(p: var TParser): PNode
 
 proc primary(p: var TParser, mode: TPrimaryMode): PNode = 
@@ -1743,13 +1762,6 @@ proc parseTypeClass(p: var TParser): PNode =
   else:
     addSon(result, parseStmt(p))
 
-proc parseDistinct(p: var TParser): PNode = 
-  #| distinct = 'distinct' optInd typeDesc
-  result = newNodeP(nkDistinctTy, p)
-  getTok(p)
-  optInd(p, result)
-  addSon(result, parseTypeDesc(p))
-
 proc parseTypeDef(p: var TParser): PNode = 
   #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
   #|             indAndComment?
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index d68cb91c0..2d2310914 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -424,8 +424,11 @@ proc lsub(n: PNode): int =
   of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
   of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
   of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
-  of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) +
-                                                         len("Distinct")
+  of nkDistinctTy:
+    result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0)
+    if n.len > 1:
+      result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
+      result += lcomma(n[1])
   of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
                                                          len("static[]")
   of nkTypeDef: result = lsons(n) + 3
@@ -1020,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     else:
       put(g, tkVar, "var")
   of nkDistinctTy: 
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkDistinct, "distinct")
       gsub(g, n.sons[0])
+      if n.len > 1:
+        if n[1].kind == nkWith:
+          putWithSpace(g, tkWith, " with")
+        else:
+          putWithSpace(g, tkWithout, " without")
+        gcomma(g, n[1])
     else:
       put(g, tkDistinct, "distinct")
   of nkTypeDef: 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 269676624..c53dc0f7d 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -139,13 +139,12 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType =
     addSonSkipIntLit(result, base)
   else:
     result = newConstraint(c, tyVar)
-  
+
 proc semDistinct(c: PContext, n: PNode, prev: PType): PType = 
-  if sonsLen(n) == 1:
-    result = newOrPrevType(tyDistinct, prev, c)
-    addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
-  else:
-    result = newConstraint(c, tyDistinct)
+  if n.len == 0: return newConstraint(c, tyDistinct)
+  result = newOrPrevType(tyDistinct, prev, c)
+  addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
+  if n.len > 1: result.n = n[1]
   
 proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   assert isRange(n)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index c09964638..ae31f1630 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -491,6 +491,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     
   return isGeneric
 
+proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
+  if rules.kind == nkWith:
+    for r in rules:
+      if r.considerAcc == callIdent: return true
+    return false
+  else:
+    for r in rules:
+      if r.considerAcc == callIdent: return false
+    return true
+
+proc maybeSkipDistinct(t: PType, callee: PSym): PType =
+  if t != nil and t.kind == tyDistinct and t.n != nil and
+     shouldSkipDistinct(t.n, callee.name):
+    result = t.base
+  else:
+    result = t
+
 proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   # typeRel can be used to establish various relationships between types:
   #
@@ -518,7 +535,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   assert(aOrig != nil)
 
   # var and static arguments match regular modifier-free types
-  let a = aOrig.skipTypes({tyStatic, tyVar})
+  let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
+  # XXX: Theoretically, maybeSkipDistinct could be called before we even
+  # start the param matching process. This could be done in `prepareOperand`
+  # for example, but unfortunately `prepareOperand` is not called in certain
+  # situation when nkDotExpr are rotated to nkDotCalls
   
   if a.kind == tyGenericInst and
       skipTypes(f, {tyVar}).kind notin {