diff options
Diffstat (limited to 'compiler/semtempl.nim')
-rwxr-xr-x | compiler/semtempl.nim | 122 |
1 files changed, 103 insertions, 19 deletions
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index f77dbf1a2..efdfce78f 100755 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -89,7 +89,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode = for x in items(sc): toBind.incl(x.sym.id) else: illFormedAst(a) - result = newNodeI(nkEmpty, n.info) + result = newNodeI(nkNilLit, n.info) proc replaceIdentBySym(n: var PNode, s: PNode) = case n.kind @@ -98,16 +98,10 @@ proc replaceIdentBySym(n: var PNode, s: PNode) = of nkIdent, nkAccQuoted, nkSym: n = s else: illFormedAst(n) -# This code here is the first pass over a template's body. The same code also -# implements the first pass over a pattern's body: - type - TBodyKind = enum - bkTemplate, bkPattern TemplCtx {.pure, final.} = object c: PContext toBind: TIntSet - bodyKind: TBodyKind owner: PSym proc getIdentNode(c: var TemplCtx, n: PNode): PNode = @@ -188,8 +182,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) elif Contains(c.toBind, s.id): result = symChoice(c.c, n, s, scClosed) - elif c.bodyKind == bkPattern: - result = symChoice(c.c, n, s, scOpen) elif s.owner == c.owner and sfGenSym in s.flags: # template tmp[T](x: var seq[T]) = # var yz: T @@ -305,8 +297,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # so we use the generic code for nkDotExpr too if n.kind == nkDotExpr or n.kind == nkAccQuoted: let s = QualifiedLookUp(c.c, n, {}) - if s != nil and Contains(c.toBind, s.id): - return symChoice(c.c, n, s, scClosed) + if s != nil: + if Contains(c.toBind, s.id): + return symChoice(c.c, n, s, scClosed) result = n for i in countup(0, sonsLen(n) - 1): result.sons[i] = semTemplBody(c, n.sons[i]) @@ -405,7 +398,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = ctx.toBind = initIntSet() ctx.c = c ctx.owner = s - ctx.bodyKind = bkTemplate if sfDirty in s.flags: n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) else: @@ -426,17 +418,109 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: SymTabReplace(c.tab.stack[curScope], proto, s) +proc semPatternBody(c: var TemplCtx, n: PNode): PNode = + template templToExpand(s: expr): expr = + s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags) + + proc handleSym(c: var TemplCtx, n: PNode, s: PSym): PNode = + if s != nil: + if s.owner == c.owner and s.kind == skParam: + incl(s.flags, sfUsed) + result = newSymNode(s, n.info) + elif Contains(c.toBind, s.id): + result = symChoice(c.c, n, s, scClosed) + elif templToExpand(s): + result = semPatternBody(c, semTemplateExpr(c.c, n, s, false)) + else: + result = symChoice(c.c, n, s, scOpen) + else: + result = n + + proc expectParam(c: var TemplCtx, n: PNode): PNode = + let s = QualifiedLookUp(c.c, n, {}) + if s != nil and s.owner == c.owner and s.kind == skParam: + incl(s.flags, sfUsed) + result = newSymNode(s, n.info) + else: + localError(n.info, errInvalidExpression) + result = n + + result = n + case n.kind + of nkIdent: + let s = QualifiedLookUp(c.c, n, {}) + result = handleSym(c, n, s) + of nkBindStmt: + result = semBindStmt(c.c, n, c.toBind) + of nkEmpty, nkSym..nkNilLit: nil + of nkCurlyExpr: + # we support '(pattern){x}' to bind a subpattern to a parameter 'x': + if n.len != 2 or n.sons[1].kind != nkIdent: + localError(n.info, errInvalidExpression) + else: + n.sons[0] = semPatternBody(c, n.sons[0]) + n.sons[1] = expectParam(c, n.sons[1]) + of nkCallKinds: + let s = QualifiedLookUp(c.c, n.sons[0], {}) + if s != nil: + if s.owner == c.owner and s.kind == skParam: nil + elif Contains(c.toBind, s.id): nil + elif templToExpand(s): + return semPatternBody(c, semTemplateExpr(c.c, n, s, false)) + + if n.kind == nkInfix and n.sons[0].kind == nkIdent: + # we interpret `*` and `|` only as pattern operators if they occur in + # infix notation, so that '`*`(a, b)' can be used for verbatim matching: + let opr = n.sons[0] + if opr.ident.s == "*": + result = newNodeI(nkPattern, n.info, n.len) + result.sons[0] = opr + result.sons[1] = semPatternBody(c, n.sons[1]) + result.sons[2] = expectParam(c, n.sons[2]) + return + elif opr.ident.s == "|": + result = newNodeI(nkPattern, n.info, n.len) + result.sons[0] = opr + result.sons[1] = semPatternBody(c, n.sons[1]) + result.sons[2] = semPatternBody(c, n.sons[2]) + return + + if n.kind == nkPrefix and n.sons[0].kind == nkIdent: + let opr = n.sons[0] + if opr.ident.s == "~": + result = newNodeI(nkPattern, n.info, n.len) + result.sons[0] = opr + result.sons[1] = semPatternBody(c, n.sons[1]) + return + + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semPatternBody(c, n.sons[i]) + else: + # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam', + # so we use the generic code for nkDotExpr too + case n.kind + of nkDotExpr, nkAccQuoted: + let s = QualifiedLookUp(c.c, n, {}) + if s != nil: + if Contains(c.toBind, s.id): + return symChoice(c.c, n, s, scClosed) + return symChoice(c.c, n, s, scOpen) + of nkPar: + if n.len == 1: return semPatternBody(c, n.sons[0]) + else: nil + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semPatternBody(c, n.sons[i]) + proc semPattern(c: PContext, n: PNode): PNode = - # not much to do here: We don't replace operators ``$``, ``*``, ``+``, - # ``|``, ``~`` as meta operators and strip the leading ``\`` of all - # operators. openScope(c.tab) var ctx: TemplCtx ctx.toBind = initIntSet() ctx.c = c ctx.owner = getCurrOwner() - ctx.bodyKind = bkPattern - result = semTemplBody(ctx, n) - if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1: - result = result.sons[0] + result = flattenStmts(semPatternBody(ctx, n)) + if result.kind in {nkStmtList, nkStmtListExpr}: + if result.len == 1: + result = result.sons[0] + elif result.len == 0: + LocalError(n.info, errInvalidExpression) closeScope(c.tab) |