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.
This commit is contained in:
Zahary Karadjov 2014-03-20 01:15:20 +02:00
commit 8a0509b3b2
5 changed files with 72 additions and 27 deletions

View file

@ -188,6 +188,10 @@ type
nkStmtListType, # a statement list ending in a type; for macros nkStmtListType, # a statement list ending in a type; for macros
nkBlockType, # a statement block ending in a type; for macros nkBlockType, # a statement block ending in a type; for macros
# types as syntactic trees: # types as syntactic trees:
nkWith, # distinct with `foo`
nkWithout, # distinct without `foo`
nkTypeOfExpr, # type(1+2) nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body nkObjectTy, # object body
nkTupleTy, # tuple body nkTupleTy, # tuple body

View file

@ -67,7 +67,7 @@ proc optPar*(p: var TParser)
proc optInd*(p: var TParser, n: PNode) proc optInd*(p: var TParser, n: PNode)
proc indAndComment*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode)
proc setBaseFlags*(n: PNode, base: TNumericalBase) 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 parseTry(p: var TParser): PNode
proc parseCase(p: var TParser): PNode proc parseCase(p: var TParser): PNode
# implementation # implementation
@ -273,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) =
eat(p, tkColon) eat(p, tkColon)
skipComment(p, n) skipComment(p, n)
proc parseSymbol(p: var TParser): PNode = proc parseSymbol(p: var TParser, allowNil = false): PNode =
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
#| | IDENT #| | IDENT
case p.tok.tokType case p.tok.tokType
@ -311,6 +311,10 @@ proc parseSymbol(p: var TParser): PNode =
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break break
eat(p, tkAccent) eat(p, tkAccent)
else:
if allowNil and p.tok.tokType == tkNil:
result = newNodeP(nkNilLit, p)
getTok(p)
else: else:
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
@ -965,6 +969,15 @@ proc isExprStart(p: TParser): bool =
result = true result = true
else: result = false else: result = false
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, proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
mode: TPrimaryMode): PNode = mode: TPrimaryMode): PNode =
result = newNodeP(kind, p) result = newNodeP(kind, p)
@ -972,6 +985,13 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
optInd(p, result) optInd(p, result)
if not isOperator(p.tok) and isExprStart(p): if not isOperator(p.tok) and isExprStart(p):
addSon(result, primary(p, mode)) 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 = proc parseExpr(p: var TParser): PNode =
#| expr = (ifExpr #| expr = (ifExpr
@ -988,7 +1008,6 @@ proc parseExpr(p: var TParser): PNode =
proc parseEnum(p: var TParser): PNode proc parseEnum(p: var TParser): PNode
proc parseObject(p: var TParser): PNode proc parseObject(p: var TParser): PNode
proc parseDistinct(p: var TParser): PNode
proc parseTypeClass(p: var TParser): PNode proc parseTypeClass(p: var TParser): PNode
proc primary(p: var TParser, mode: TPrimaryMode): PNode = proc primary(p: var TParser, mode: TPrimaryMode): PNode =
@ -1743,13 +1762,6 @@ proc parseTypeClass(p: var TParser): PNode =
else: else:
addSon(result, parseStmt(p)) 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 = proc parseTypeDef(p: var TParser): PNode =
#| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
#| indAndComment? #| indAndComment?

View file

@ -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 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 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 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) + of nkDistinctTy:
len("Distinct") 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) + of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
len("static[]") len("static[]")
of nkTypeDef: result = lsons(n) + 3 of nkTypeDef: result = lsons(n) + 3
@ -1020,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
else: else:
put(g, tkVar, "var") put(g, tkVar, "var")
of nkDistinctTy: of nkDistinctTy:
if sonsLen(n) > 0: if n.len > 0:
putWithSpace(g, tkDistinct, "distinct") putWithSpace(g, tkDistinct, "distinct")
gsub(g, n.sons[0]) 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: else:
put(g, tkDistinct, "distinct") put(g, tkDistinct, "distinct")
of nkTypeDef: of nkTypeDef:

View file

@ -141,11 +141,10 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType =
result = newConstraint(c, tyVar) result = newConstraint(c, tyVar)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType = proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 1: if n.len == 0: return newConstraint(c, tyDistinct)
result = newOrPrevType(tyDistinct, prev, c) result = newOrPrevType(tyDistinct, prev, c)
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
else: if n.len > 1: result.n = n[1]
result = newConstraint(c, tyDistinct)
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
assert isRange(n) assert isRange(n)

View file

@ -491,6 +491,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
return isGeneric 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 = proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types: # 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) assert(aOrig != nil)
# var and static arguments match regular modifier-free types # 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 if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar}).kind notin {