cleanups for underscores in tuple unpacking
This commit is contained in:
parent
a5f321ea8f
commit
6ca38472a1
4 changed files with 67 additions and 42 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue