cleanups for underscores in tuple unpacking

This commit is contained in:
Araq 2015-04-24 13:08:42 +02:00
commit 6ca38472a1
4 changed files with 67 additions and 42 deletions

View file

@ -868,9 +868,14 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
tok.tokType = tkAccent tok.tokType = tkAccent
inc(L.bufpos) inc(L.bufpos)
of '_': of '_':
tok.tokType = tkSymbol
tok.ident = getIdent("_")
inc(L.bufpos) inc(L.bufpos)
if L.buf[L.bufpos] notin SymChars:
tok.tokType = tkSymbol
tok.ident = getIdent("_")
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
of '\"': of '\"':
# check for extended raw string literal: # check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars

View file

@ -369,9 +369,10 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
else: else:
result.add identDefs result.add identDefs
proc isDiscardUnderscore(n: PNode): bool = proc isDiscardUnderscore(v: PSym): bool =
if n.kind != nkIdent: return false if v.name.s == "_":
return n.ident.s == "_" v.flags.incl(sfGenSym)
result = true
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
@ -436,10 +437,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags and if sfGenSym notin v.flags and not isDiscardUnderscore(v):
not isDiscardUnderscore(a.sons[j]): addInterfaceDecl(c, v) addInterfaceDecl(c, v)
if isDiscardUnderscore(a.sons[j]):
v.flags.incl(sfGenSym)
when oKeepVariableNames: when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else: else:
@ -554,7 +553,8 @@ proc semForVars(c: PContext, n: PNode): PNode =
if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) n.sons[i] = newSymNode(v)
if sfGenSym notin v.flags: addForVarDecl(c, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addForVarDecl(c, v)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
n.sons[length-1] = semStmt(c, n.sons[length-1]) n.sons[length-1] = semStmt(c, n.sons[length-1])
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)

View file

@ -2,7 +2,7 @@ Statements and expressions
========================== ==========================
Nim uses the common statement/expression paradigm: Statements do not Nim uses the common statement/expression paradigm: Statements do not
produce a value in contrast to expressions. However, some expressions are produce a value in contrast to expressions. However, some expressions are
statements. statements.
Statements are separated into `simple statements`:idx: and Statements are separated into `simple statements`:idx: and
@ -16,9 +16,9 @@ statements always have to be intended. The details can be found in the grammar.
Statement list expression Statement list expression
------------------------- -------------------------
Statements can also occur in an expression context that looks Statements can also occur in an expression context that looks
like ``(stmt1; stmt2; ...; ex)``. This is called like ``(stmt1; stmt2; ...; ex)``. This is called
an statement list expression or ``(;)``. The type an statement list expression or ``(;)``. The type
of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
``(;)`` does not introduce a new scope. ``(;)`` does not introduce a new scope.
@ -30,24 +30,24 @@ Discard statement
Example: Example:
.. code-block:: nim .. code-block:: nim
proc p(x, y: int): int = proc p(x, y: int): int =
result = x + y result = x + y
discard p(3, 4) # discard the return value of `p` discard p(3, 4) # discard the return value of `p`
The ``discard`` statement evaluates its expression for side-effects and The ``discard`` statement evaluates its expression for side-effects and
throws the expression's resulting value away. throws the expression's resulting value away.
Ignoring the return value of a procedure without using a discard statement is Ignoring the return value of a procedure without using a discard statement is
a static error. a static error.
The return value can be ignored implicitly if the called proc/iterator has The return value can be ignored implicitly if the called proc/iterator has
been declared with the `discardable`:idx: pragma: been declared with the `discardable`:idx: pragma:
.. code-block:: nim .. code-block:: nim
proc p(x, y: int): int {.discardable.} = proc p(x, y: int): int {.discardable.} =
result = x + y result = x + y
p(3, 4) # now valid p(3, 4) # now valid
An empty ``discard`` statement is often used as a null statement: An empty ``discard`` statement is often used as a null statement:
@ -98,11 +98,11 @@ T = enum cast[T](0); this may be an invalid value
The implicit initialization can be avoided for optimization reasons with the The implicit initialization can be avoided for optimization reasons with the
`noinit`:idx: pragma: `noinit`:idx: pragma:
.. code-block:: nim .. code-block:: nim
var var
a {.noInit.}: array [0..1023, char] a {.noInit.}: array [0..1023, char]
If a proc is annotated with the ``noinit`` pragma this refers to its implicit If a proc is annotated with the ``noinit`` pragma this refers to its implicit
``result`` variable: ``result`` variable:
@ -113,13 +113,13 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
The implicit initialization can be also prevented by the `requiresInit`:idx: The implicit initialization can be also prevented by the `requiresInit`:idx:
type pragma. The compiler requires an explicit initialization then. However type pragma. The compiler requires an explicit initialization then. However
it does a `control flow analysis`:idx: to prove the variable has been it does a `control flow analysis`:idx: to prove the variable has been
initialized and does not rely on syntactic properties: initialized and does not rely on syntactic properties:
.. code-block:: nim .. code-block:: nim
type type
MyObject = object {.requiresInit.} MyObject = object {.requiresInit.}
proc p() = proc p() =
# the following is valid: # the following is valid:
var x: MyObject var x: MyObject
@ -129,11 +129,12 @@ initialized and does not rely on syntactic properties:
x = a() x = a()
use x use x
let statement let statement
------------- -------------
A ``let`` statement declares new local and global `single assignment`:idx: A ``let`` statement declares new local and global `single assignment`:idx:
variables and binds a value to them. The syntax is the same as that of the ``var`` variables and binds a value to them. The syntax is the same as that of the ``var``
statement, except that the keyword ``var`` is replaced by the keyword ``let``. statement, except that the keyword ``var`` is replaced by the keyword ``let``.
Let variables are not l-values and can thus not be passed to ``var`` parameters Let variables are not l-values and can thus not be passed to ``var`` parameters
nor can their address be taken. They cannot be assigned new values. nor can their address be taken. They cannot be assigned new values.
@ -141,6 +142,19 @@ nor can their address be taken. They cannot be assigned new values.
For let variables the same pragmas are available as for ordinary variables. For let variables the same pragmas are available as for ordinary variables.
Tuple unpacking
---------------
In a ``var`` or ``let`` statement tuple unpacking can be performed. The special
identifier ``_`` can be used to ignore some parts of the tuple:
.. code-block:: nim
proc returnsTuple(): (int, int, int) = (4, 2, 3)
let (x, _, z) = returnsTuple()
Const section Const section
------------- -------------
@ -157,33 +171,33 @@ have no side-effect can be used in constant expressions too:
constEval = contains("abc", 'b') # computed at compile time! constEval = contains("abc", 'b') # computed at compile time!
The rules for compile-time computability are: The rules for compile-time computability are:
1. Literals are compile-time computable. 1. Literals are compile-time computable.
2. Type conversions are compile-time computable. 2. Type conversions are compile-time computable.
3. Procedure calls of the form ``p(X)`` are compile-time computable if 3. Procedure calls of the form ``p(X)`` are compile-time computable if
``p`` is a proc without side-effects (see the `noSideEffect pragma`_ ``p`` is a proc without side-effects (see the `noSideEffect pragma`_
for details) and if ``X`` is a (possibly empty) list of compile-time for details) and if ``X`` is a (possibly empty) list of compile-time
computable arguments. computable arguments.
Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can
they contain such a type. they contain such a type.
Static statement/expression Static statement/expression
--------------------------- ---------------------------
A static statement/expression can be used to enforce compile A static statement/expression can be used to enforce compile
time evaluation explicitly. Enforced compile time evaluation can even evaluate time evaluation explicitly. Enforced compile time evaluation can even evaluate
code that has side effects: code that has side effects:
.. code-block:: .. code-block::
static: static:
echo "echo at compile time" echo "echo at compile time"
It's a static error if the compiler cannot perform the evaluation at compile It's a static error if the compiler cannot perform the evaluation at compile
time. time.
The current implementation poses some restrictions for compile time The current implementation poses some restrictions for compile time
@ -217,7 +231,7 @@ the ``:`` are executed. This goes on until the last ``elif``. If all
conditions fail, the ``else`` part is executed. If there is no ``else`` conditions fail, the ``else`` part is executed. If there is no ``else``
part, execution continues with the statement after the ``if`` statement. part, execution continues with the statement after the ``if`` statement.
The scoping for an ``if`` statement is slightly subtle to support an important The scoping for an ``if`` statement is slightly subtle to support an important
use case. A new scope starts for the ``if``/``elif`` condition and ends after use case. A new scope starts for the ``if``/``elif`` condition and ends after
the corresponding *then* block: the corresponding *then* block:
@ -229,7 +243,7 @@ the corresponding *then* block:
else: else:
# 'm' not declared here # 'm' not declared here
In the example the scopes have been enclosed in ``{| |}``. In the example the scopes have been enclosed in ``{| |}``.
Case statement Case statement
@ -244,7 +258,7 @@ Example:
echo("permission denied") echo("permission denied")
of "go-for-a-walk": echo("please yourself") of "go-for-a-walk": echo("please yourself")
else: echo("unknown command") else: echo("unknown command")
# indentation of the branches is also allowed; and so is an optional colon # indentation of the branches is also allowed; and so is an optional colon
# after the selecting expression: # after the selecting expression:
case readline(stdin): case readline(stdin):
@ -252,15 +266,15 @@ Example:
echo("permission denied") echo("permission denied")
of "go-for-a-walk": echo("please yourself") of "go-for-a-walk": echo("please yourself")
else: echo("unknown command") else: echo("unknown command")
The ``case`` statement is similar to the if statement, but it represents The ``case`` statement is similar to the if statement, but it represents
a multi-branch selection. The expression after the keyword ``case`` is a multi-branch selection. The expression after the keyword ``case`` is
evaluated and if its value is in a *slicelist* the corresponding statements evaluated and if its value is in a *slicelist* the corresponding statements
(after the ``of`` keyword) are executed. If the value is not in any (after the ``of`` keyword) are executed. If the value is not in any
given *slicelist* the ``else`` part is executed. If there is no ``else`` given *slicelist* the ``else`` part is executed. If there is no ``else``
part and not all possible values that ``expr`` can hold occur in a part and not all possible values that ``expr`` can hold occur in a
``slicelist``, a static error occurs. This holds only for expressions of ``slicelist``, a static error occurs. This holds only for expressions of
ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
type. To suppress the static error an ``else`` part with an type. To suppress the static error an ``else`` part with an
empty ``discard`` statement should be used. empty ``discard`` statement should be used.
@ -281,7 +295,7 @@ expanded into a list of its elements:
of SymChars, '_': echo "an identifier" of SymChars, '_': echo "an identifier"
of '0'..'9': echo "a number" of '0'..'9': echo "a number"
else: echo "other" else: echo "other"
# is equivalent to: # is equivalent to:
proc classify(s: string) = proc classify(s: string) =
case s[0] case s[0]
@ -580,14 +594,14 @@ A table constructor is syntactic sugar for an array constructor:
.. code-block:: nim .. code-block:: nim
{"key1": "value1", "key2", "key3": "value2"} {"key1": "value1", "key2", "key3": "value2"}
# is the same as: # is the same as:
[("key1", "value1"), ("key2", "value2"), ("key3", "value2")] [("key1", "value1"), ("key2", "value2"), ("key3", "value2")]
The empty table can be written ``{:}`` (in contrast to the empty set The empty table can be written ``{:}`` (in contrast to the empty set
which is ``{}``) which is thus another way to write as the empty array which is ``{}``) which is thus another way to write as the empty array
constructor ``[]``. This slightly unusal way of supporting tables constructor ``[]``. This slightly unusal way of supporting tables
has lots of advantages: has lots of advantages:
* The order of the (key,value)-pairs is preserved, thus it is easy to * The order of the (key,value)-pairs is preserved, thus it is easy to

View file

@ -4,6 +4,11 @@ discard """
exitcode: 0 exitcode: 0
""" """
proc returnsTuple(): (int, int, int) = (4, 2, 3)
proc main2 =
let (x, _, z) = returnsTuple()
proc main() = proc main() =
proc foo(): tuple[x, y, z: int] = proc foo(): tuple[x, y, z: int] =
@ -16,8 +21,8 @@ proc main() =
var (a, _, _) = foo() var (a, _, _) = foo()
doAssert a == 4 doAssert a == 4
var (a, _, _xx) = foo() var (aa, _, _) = foo()
doAssert a == 4 doAssert aa == 4
iterator bar(): tuple[x, y, z: int] = iterator bar(): tuple[x, y, z: int] =
yield (1,2,3) yield (1,2,3)
@ -27,3 +32,4 @@ proc main() =
doAssert y == 2 doAssert y == 2
main() main()
main2()