From b38eb2e2a84537c315f28e9f823c10363be8cdb1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 31 Mar 2015 00:39:23 +0100 Subject: [PATCH 1/5] Implements #2154. When unpacking tuples in var/let declarations a part of the tuple can now be discarded using a single underscore. --- compiler/ast.nim | 1 + compiler/docgen.nim | 3 ++- compiler/lexer.nim | 6 +++++- compiler/lookups.nim | 1 + compiler/parser.nim | 10 ++++++++-- compiler/renderer.nim | 3 ++- compiler/semstmts.nim | 3 ++- tests/parser/ttupleunpack.nim | 18 ++++++++++++++++++ 8 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 tests/parser/ttupleunpack.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 10f2a71da2..8ff38a12fb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,6 +221,7 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation + nkUnderscore, # underscore inside a tuple unpack ``(_, x) = foo()`` TNodeKinds* = set[TNodeKind] type diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4af69745be..4145883d6d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -384,7 +384,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, + tkUnderscore: dispA(result, "$1", "\\spanOther{$1}", [toRope(esc(d.target, literal))]) inc(d.id) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 78266ef4d3..5d98b872b2 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -62,6 +62,7 @@ type tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkOpr, tkComment, tkAccent, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, + tkUnderscore TTokTypes* = set[TTokType] @@ -96,7 +97,7 @@ const ":", "::", "=", ".", "..", "tkOpr", "tkComment", "`", "tkSpaces", "tkInfixOpr", - "tkPrefixOpr", "tkPostfixOpr"] + "tkPrefixOpr", "tkPostfixOpr", "_"] type TNumericalBase* = enum @@ -874,6 +875,9 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = of '`': tok.tokType = tkAccent inc(L.bufpos) + of '_': + tok.tokType = tkUnderscore + inc(L.bufpos) of '\"': # check for extended raw string literal: var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 88e32404af..8cfb5ed250 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -20,6 +20,7 @@ proc considerQuotedIdent*(n: PNode): PIdent = case n.kind of nkIdent: result = n.ident of nkSym: result = n.sym.name + of nkUnderscore: result = getIdent"_" of nkAccQuoted: case n.len of 0: diff --git a/compiler/parser.nim b/compiler/parser.nim index dcd5401e8a..0b2619b01d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -296,12 +296,15 @@ proc colcom(p: var TParser, n: PNode) = skipComment(p, n) proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' + #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'='|'_')+)+ '`' #| | IDENT | 'addr' | 'type' case p.tok.tokType of tkSymbol, tkAddr, tkType: result = newIdentNodeP(p.tok.ident, p) getTok(p) + of tkUnderscore: + result = newNodeP(nkUnderscore, p) + getTok(p) of tkAccent: result = newNodeP(nkAccQuoted, p) getTok(p) @@ -643,6 +646,9 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = of tkNil: result = newNodeP(nkNilLit, p) getTok(p) + of tkUnderscore: + result = newNodeP(nkUnderscore, p) + getTok(p) of tkParLe: # () constructor if mode in {pmTypeDesc, pmTypeDef}: @@ -1814,7 +1820,7 @@ proc parseVarTuple(p: var TParser): PNode = result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent}: + while p.tok.tokType in {tkSymbol, tkAccent, tkUnderscore}: var a = identWithPragma(p) addSon(result, a) if p.tok.tokType != tkComma: break diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ce818e3cd6..2e614dde08 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -349,6 +349,7 @@ proc atom(n: PNode): string = else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64" of nkNilLit: result = "nil" + of nkUnderscore: result = "_" of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s else: result = "[type node]" @@ -805,7 +806,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal) of nkEmpty: discard of nkType: put(g, tkInvalid, atom(n)) - of nkSym, nkIdent: gident(g, n) + of nkSym, nkIdent, nkUnderscore: gident(g, n) of nkIntLit: put(g, tkIntLit, atom(n)) of nkInt8Lit: put(g, tkInt8Lit, atom(n)) of nkInt16Lit: put(g, tkInt16Lit, atom(n)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7263b21b9a..5b7cca338f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -403,7 +403,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = for j in countup(0, length-3): var v = semIdentDef(c, a.sons[j], symkind) - if sfGenSym notin v.flags: addInterfaceDecl(c, v) + if sfGenSym notin v.flags and + a.sons[j].kind != nkUnderscore: addInterfaceDecl(c, v) when oKeepVariableNames: if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) else: diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim new file mode 100644 index 0000000000..0479871e8b --- /dev/null +++ b/tests/parser/ttupleunpack.nim @@ -0,0 +1,18 @@ +discard """ + file: "ttupleunpack.nim" + output: "" + exitcode: 0 +""" +proc foo(): tuple[x, y, z: int] = + return (4, 2, 3) + +var (x, _, y) = foo() +doAssert x == 4 +doAssert y == 3 + +iterator bar(): tuple[x, y, z: int] = + yield (1,2,3) + +for x, y, _ in bar(): + doAssert x == 1 + doAssert y == 2 From c35fc2bb03860a4f1e1f81cda050d899652bce20 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 5 Apr 2015 15:46:56 +0100 Subject: [PATCH 2/5] Rewrite in order to not introduce a new node kind. --- compiler/ast.nim | 1 - compiler/lookups.nim | 1 - compiler/parser.nim | 6 +++--- compiler/renderer.nim | 3 +-- compiler/semstmts.nim | 6 +++++- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 8ff38a12fb..10f2a71da2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,7 +221,6 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation - nkUnderscore, # underscore inside a tuple unpack ``(_, x) = foo()`` TNodeKinds* = set[TNodeKind] type diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 8cfb5ed250..88e32404af 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -20,7 +20,6 @@ proc considerQuotedIdent*(n: PNode): PIdent = case n.kind of nkIdent: result = n.ident of nkSym: result = n.sym.name - of nkUnderscore: result = getIdent"_" of nkAccQuoted: case n.len of 0: diff --git a/compiler/parser.nim b/compiler/parser.nim index 0b2619b01d..49b4430ab8 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -296,14 +296,14 @@ proc colcom(p: var TParser, n: PNode) = skipComment(p, n) proc parseSymbol(p: var TParser, allowNil = false): PNode = - #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'='|'_')+)+ '`' + #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | 'addr' | 'type' case p.tok.tokType of tkSymbol, tkAddr, tkType: result = newIdentNodeP(p.tok.ident, p) getTok(p) of tkUnderscore: - result = newNodeP(nkUnderscore, p) + result = newIdentNodeP(getIdent("_"), p) getTok(p) of tkAccent: result = newNodeP(nkAccQuoted, p) @@ -647,7 +647,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = result = newNodeP(nkNilLit, p) getTok(p) of tkUnderscore: - result = newNodeP(nkUnderscore, p) + result = newIdentNodeP(getIdent("_"), p) getTok(p) of tkParLe: # () constructor diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 2e614dde08..ce818e3cd6 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -349,7 +349,6 @@ proc atom(n: PNode): string = else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64" of nkNilLit: result = "nil" - of nkUnderscore: result = "_" of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s else: result = "[type node]" @@ -806,7 +805,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal) of nkEmpty: discard of nkType: put(g, tkInvalid, atom(n)) - of nkSym, nkIdent, nkUnderscore: gident(g, n) + of nkSym, nkIdent: gident(g, n) of nkIntLit: put(g, tkIntLit, atom(n)) of nkInt8Lit: put(g, tkInt8Lit, atom(n)) of nkInt16Lit: put(g, tkInt16Lit, atom(n)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5b7cca338f..f49ab264d7 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -340,6 +340,10 @@ proc checkNilable(v: PSym) = elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags: message(v.info, warnProveInit, v.name.s) +proc isDiscardUnderscore(n: PNode): bool = + if n.kind != nkIdent: return false + return n.ident.s == "_" + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) @@ -404,7 +408,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = for j in countup(0, length-3): var v = semIdentDef(c, a.sons[j], symkind) if sfGenSym notin v.flags and - a.sons[j].kind != nkUnderscore: addInterfaceDecl(c, v) + not isDiscardUnderscore(a.sons[j]): addInterfaceDecl(c, v) when oKeepVariableNames: if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) else: From ea505f36131f7dc809be49d24bd0187d856c40e7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 8 Apr 2015 20:08:45 +0100 Subject: [PATCH 3/5] Get rid of tkUnderscore. Map _ to tkSymbol. --- compiler/docgen.nim | 3 +-- compiler/lexer.nim | 8 ++++---- compiler/parser.nim | 8 +------- tests/parser/ttupleunpack.nim | 3 +++ 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4145883d6d..4af69745be 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -384,8 +384,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent, tkColonColon, - tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, - tkUnderscore: + tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: dispA(result, "$1", "\\spanOther{$1}", [toRope(esc(d.target, literal))]) inc(d.id) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 5d98b872b2..e29be7cf83 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -61,8 +61,7 @@ type tkComma, tkSemiColon, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkOpr, tkComment, tkAccent, - tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, - tkUnderscore + tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr TTokTypes* = set[TTokType] @@ -97,7 +96,7 @@ const ":", "::", "=", ".", "..", "tkOpr", "tkComment", "`", "tkSpaces", "tkInfixOpr", - "tkPrefixOpr", "tkPostfixOpr", "_"] + "tkPrefixOpr", "tkPostfixOpr"] type TNumericalBase* = enum @@ -876,7 +875,8 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = tok.tokType = tkAccent inc(L.bufpos) of '_': - tok.tokType = tkUnderscore + tok.tokType = tkSymbol + tok.ident = getIdent("_") inc(L.bufpos) of '\"': # check for extended raw string literal: diff --git a/compiler/parser.nim b/compiler/parser.nim index 49b4430ab8..dcd5401e8a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -302,9 +302,6 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode = of tkSymbol, tkAddr, tkType: result = newIdentNodeP(p.tok.ident, p) getTok(p) - of tkUnderscore: - result = newIdentNodeP(getIdent("_"), p) - getTok(p) of tkAccent: result = newNodeP(nkAccQuoted, p) getTok(p) @@ -646,9 +643,6 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = of tkNil: result = newNodeP(nkNilLit, p) getTok(p) - of tkUnderscore: - result = newIdentNodeP(getIdent("_"), p) - getTok(p) of tkParLe: # () constructor if mode in {pmTypeDesc, pmTypeDef}: @@ -1820,7 +1814,7 @@ proc parseVarTuple(p: var TParser): PNode = result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' optInd(p, result) - while p.tok.tokType in {tkSymbol, tkAccent, tkUnderscore}: + while p.tok.tokType in {tkSymbol, tkAccent}: var a = identWithPragma(p) addSon(result, a) if p.tok.tokType != tkComma: break diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim index 0479871e8b..3a1b77ea1b 100644 --- a/tests/parser/ttupleunpack.nim +++ b/tests/parser/ttupleunpack.nim @@ -10,6 +10,9 @@ var (x, _, y) = foo() doAssert x == 4 doAssert y == 3 +var (a, _, _) = foo() +doAssert a == 4 + iterator bar(): tuple[x, y, z: int] = yield (1,2,3) From 13a5ecda320ada29f19432df805dfc4538f8e103 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 8 Apr 2015 20:11:28 +0100 Subject: [PATCH 4/5] Updated news.txt. --- web/news.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/news.txt b/web/news.txt index 0b28c29bf6..8730b631e8 100644 --- a/web/news.txt +++ b/web/news.txt @@ -114,6 +114,8 @@ News - Array and seq indexing can now use the builtin ``^`` operator to access things from backwards: ``a[^1]`` is like Python's ``a[-1]``. + - A single underscore can now be used to discard values when unpacking tuples. + Library additions ----------------- From f0f0062a5d37614b552c4ecc40ba2a189b00b008 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Apr 2015 00:29:16 +0100 Subject: [PATCH 5/5] Add sfGenSym for (_). --- compiler/semstmts.nim | 2 ++ tests/parser/ttupleunpack.nim | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 24c1357670..c0c48782ed 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -442,6 +442,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var v = semIdentDef(c, a.sons[j], symkind) if sfGenSym notin v.flags and not isDiscardUnderscore(a.sons[j]): addInterfaceDecl(c, v) + if isDiscardUnderscore(a.sons[j]): + v.flags.incl(sfGenSym) when oKeepVariableNames: if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) else: diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim index 3a1b77ea1b..581e6e9403 100644 --- a/tests/parser/ttupleunpack.nim +++ b/tests/parser/ttupleunpack.nim @@ -3,19 +3,27 @@ discard """ output: "" exitcode: 0 """ -proc foo(): tuple[x, y, z: int] = - return (4, 2, 3) -var (x, _, y) = foo() -doAssert x == 4 -doAssert y == 3 +proc main() = -var (a, _, _) = foo() -doAssert a == 4 + proc foo(): tuple[x, y, z: int] = + return (4, 2, 3) -iterator bar(): tuple[x, y, z: int] = - yield (1,2,3) + var (x, _, y) = foo() + doAssert x == 4 + doAssert y == 3 -for x, y, _ in bar(): - doAssert x == 1 - doAssert y == 2 + var (a, _, _) = foo() + doAssert a == 4 + + var (a, _, _xx) = foo() + doAssert a == 4 + + iterator bar(): tuple[x, y, z: int] = + yield (1,2,3) + + for x, y, _ in bar(): + doAssert x == 1 + doAssert y == 2 + +main()