Skip to content

Commit

Permalink
unify tuple expressions (nim-lang#13793)
Browse files Browse the repository at this point in the history
* unify tuple expressions

* fix test

* fix test

* apply feedback

* Handle empty tuples

* Fix rendering named unary tuple

* Protect static NimNode against stripping

* Slightly less hacky

* Revert "Slightly less hacky"

This reverts commit 170c5ae.

* Slightly less hacky

* Cleanup

* Fix test

* Fix another test

* Add condsym

* Rebase fallout

* changelog: Move from compiler changes to language changes

* Add stricter tests

* Add empty tuple example to doc/astspec

* Fix test

Co-authored-by: Clyybber <darkmine956@gmail.com>
  • Loading branch information
krux02 and Clyybber authored Mar 30, 2021
1 parent 35655cd commit 159c06e
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 32 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@

- Custom numeric literals (e.g. `-128'bignum`) are now supported.

- Tuple expressions are now parsed consistently as
`nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`.


## Compiler changes

Expand Down
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasHintAsError")
defineSymbol("nimHasSpellSuggest")
defineSymbol("nimHasCustomLiterals")
defineSymbol("nimHasUnifiedTuple")
15 changes: 9 additions & 6 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,9 @@ proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) =
var a = exprColonEqExpr(p)
result.add(a)
if p.tok.tokType != tkComma: break
getTok(p)
# (1,) produces a tuple expression
if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar:
elif result.kind == nkPar:
result.transitionSonsKind(nkTupleConstr)
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, endTok)
Expand Down Expand Up @@ -584,7 +583,10 @@ proc parsePar(p: var Parser): PNode =
semiStmtList(p, result)
elif p.tok.tokType == tkCurlyDotLe:
result.add(parseStmtPragma(p))
elif p.tok.tokType != tkParRi:
elif p.tok.tokType == tkParRi:
# Empty tuple '()'
result.transitionSonsKind(nkTupleConstr)
else:
var a = simpleExpr(p)
if p.tok.tokType == tkDo:
result = postExprBlocks(p, a)
Expand All @@ -605,13 +607,14 @@ proc parsePar(p: var Parser): PNode =
semiStmtList(p, result)
else:
a = colonOrEquals(p, a)
if a.kind == nkExprColonExpr:
result.transitionSonsKind(nkTupleConstr)
result.add(a)
if p.tok.tokType == tkComma:
getTok(p)
skipComment(p, a)
# (1,) produces a tuple expression:
if p.tok.tokType == tkParRi:
result.transitionSonsKind(nkTupleConstr)
result.transitionSonsKind(nkTupleConstr)
# progress guaranteed
while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
var a = exprColonEqExpr(p)
Expand Down
2 changes: 1 addition & 1 deletion compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkTupleConstr:
put(g, tkParLe, "(")
gcomma(g, n, c)
if n.len == 1: put(g, tkComma, ",")
if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",")
put(g, tkParRi, ")")
of nkCurly:
put(g, tkCurlyLe, "{")
Expand Down
6 changes: 6 additions & 0 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,12 @@ proc prepareVMValue(arg: PNode): PNode =
if arg.kind in nkLiterals:
return arg

if arg.kind == nkExprColonExpr and arg[0].typ != nil and
arg[0].typ.sym != nil and arg[0].typ.sym.magic == mPNimrodNode:
# Poor mans way of protecting static NimNodes
# XXX: Maybe we need a nkNimNode?
return arg

result = copyNode(arg)
if arg.kind == nkTupleConstr:
for child in arg:
Expand Down
45 changes: 41 additions & 4 deletions doc/astspec.txt
Original file line number Diff line number Diff line change
Expand Up @@ -329,19 +329,56 @@ AST:
Parentheses
-----------

Parentheses for affecting operator precedence or tuple construction
are built with the ``nnkPar`` node.
Parentheses for affecting operator precedence use the ``nnkPar`` node.

Concrete syntax:

.. code-block:: nim
(1, 2, (3))
(a + b) * c

AST:

.. code-block:: nim
nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))
nnkInfix(nnkIdent("*"),
nnkPar(
nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))),
nnkIdent("c"))

Tuple Constructors
------------------

Nodes for tuple construction are built with the ``nnkTupleConstr`` node.

Concrete syntax:

.. code-block:: nim
(1, 2, 3)
(a: 1, b: 2, c: 3)
()

AST:

.. code-block:: nim
nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
nnkTupleConstr(
nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)),
nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)),
nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3)))

Since the one tuple would be syntactically identical to parentheses
with an expression in them, the parser expects a trailing comma for
them. For tuple constructors with field names, this is not necessary.

.. code-block:: nim
(1,)
(a: 1)

AST:

.. code-block:: nim
nnkTupleConstr(nnkIntLit(1))
nnkTupleConstr(
nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)))

Curly braces
------------
Expand Down
4 changes: 2 additions & 2 deletions tests/ast_pattern_matching.nim
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ proc matchLengthKind*(arg: NimNode; kind: NimNodeKind; length: int): MatchingErr
matchLengthKind(arg, {kind}, length)

proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: SomeInteger): MatchingError {.compileTime.} =
let kindFail = not(kind.card == 0 or arg.kind in kind)
let valueFail = arg.intVal != int(value)
template kindFail: bool = not(kind.card == 0 or arg.kind in kind)
template valueFail: bool = arg.intVal != int(value)
if kindFail or valueFail:
result.node = arg
result.kind = WrongKindValue
Expand Down
36 changes: 30 additions & 6 deletions tests/astspec/tastspec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -327,19 +327,43 @@ static:

testArrayAccessOperator(x[y])



## Parentheses

scope:

let ast = myquote:
(1, 2, (3))
(a + b) * c

ast.matchAst:
of nnkPar(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkPar(nnkIntLit(intVal = 3))):
echo "ok"
of nnkInfix(ident"*", nnkPar(nnkInfix(ident"+", ident"a", ident"b")), ident"c"):
echo "parentheses ok"

## Tuple Constructors

scope:
let ast = myquote:
(1, 2, 3)
(a: 1, b: 2, c: 3)
(1,)
(a: 1)
()

for it in ast:
echo it.lispRepr
it.matchAst:
of nnkTupleConstr(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
echo "simple tuple ok"
of nnkTupleConstr(
nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1)),
nnkExprColonExpr(ident"b", nnkIntLit(intVal = 2)),
nnkExprColonExpr(ident"c", nnkIntLit(intVal = 3))
):
echo "named tuple ok"
of nnkTupleConstr(nnkIntLit(intVal = 1)):
echo "one tuple ok"
of nnkTupleConstr(nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1))):
echo "named one tuple ok"
of nnkTupleConstr():
echo "empty tuple ok"

## Curly braces

Expand Down
2 changes: 1 addition & 1 deletion tests/compiler/tprefixmatches.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro check(val, body: untyped): untyped =
result = newStmtList()
expectKind body, nnkStmtList
for b in body:
expectKind b, nnkPar
expectKind b, nnkTupleConstr
expectLen b, 2
let p = b[0]
let s = b[1]
Expand Down
2 changes: 1 addition & 1 deletion tests/lexer/tunary_minus.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ template main =
doAssert lispReprStr([-1]) == """(Bracket (IntLit -1))"""
doAssert (-1, 2)[0] == minusOne:
"unable to handle negatives after parenthesis"
doAssert lispReprStr((-1, 2)) == """(Par (IntLit -1) (IntLit 2))"""
doAssert lispReprStr((-1, 2)) == """(TupleConstr (IntLit -1) (IntLit 2))"""
proc x(): int =
var a = 1;-1 # the -1 should act as the return value
doAssert x() == minusOne:
Expand Down
31 changes: 31 additions & 0 deletions tests/macros/tdumpast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,34 @@ macro fun3(): untyped =
int | float | array | seq | object | ptr | pointer | float32
doAssert n.repr == "int | float | array | seq | object | ptr | pointer | float32", n.repr
fun3()

macro fun4() =
let n = quote do:
(a: 1)
doAssert n.repr == "(a: 1)", n.repr
fun4()

# nkTupleConstr vs nkPar tests:
block: # lispRepr
macro lispRepr2(a: untyped): string = newLit a.lispRepr

doAssert lispRepr2(()) == """(TupleConstr)"""
doAssert lispRepr2((a: 1)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)))"""
doAssert lispRepr2((a: 1, b: 2)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)) (ExprColonExpr (Ident "b") (IntLit 2)))"""
doAssert lispRepr2((1,)) == """(TupleConstr (IntLit 1))"""
doAssert lispRepr2((1, 2)) == """(TupleConstr (IntLit 1) (IntLit 2))"""
doAssert lispRepr2((1, 2, 3.0)) == """(TupleConstr (IntLit 1) (IntLit 2) (FloatLit 3.0))"""
doAssert lispRepr2((1)) == """(Par (IntLit 1))"""
doAssert lispRepr2((1+2)) == """(Par (Infix (Ident "+") (IntLit 1) (IntLit 2)))"""

block: # repr
macro repr2(a: untyped): string = newLit a.repr

doAssert repr2(()) == "()"
doAssert repr2((a: 1)) == "(a: 1)"
doAssert repr2((a: 1, b: 2)) == "(a: 1, b: 2)"
doAssert repr2((1,)) == "(1,)"
doAssert repr2((1, 2)) == "(1, 2)"
doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)"
doAssert repr2((1)) == "(1)"
doAssert repr2((1+2)) == "(1 + 2)"
6 changes: 3 additions & 3 deletions tests/parser/tpostexprblocks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ StmtList
DiscardStmt
Empty
OfBranch
Par
TupleConstr
Ident "a"
Ident "b"
StmtList
Expand All @@ -476,7 +476,7 @@ StmtList
DiscardStmt
Empty
ElifBranch
Par
TupleConstr
Ident "a"
Ident "b"
StmtList
Expand All @@ -488,7 +488,7 @@ StmtList
DiscardStmt
Empty
ExceptBranch
Par
TupleConstr
Ident "a"
Ident "b"
StmtList
Expand Down
15 changes: 7 additions & 8 deletions tests/parser/ttypemodifiers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ StmtList
Ident "PtrTuple"
Empty
PtrTy
Par
TupleConstr
Ident "int"
Ident "string"
TypeDef
Expand All @@ -43,14 +43,14 @@ StmtList
Ident "RefTupleType"
Empty
RefTy
Par
TupleConstr
Ident "int"
Ident "string"
TypeDef
Ident "RefTupleVars"
Empty
RefTy
Par
TupleConstr
Ident "a"
Ident "b"
TypeDef
Expand Down Expand Up @@ -80,7 +80,7 @@ StmtList
Empty
Command
Ident "static"
Par
TupleConstr
Ident "int"
Ident "string"
TypeDef
Expand Down Expand Up @@ -155,15 +155,15 @@ StmtList
Empty
Command
Ident "type"
Par
TupleConstr
Ident "a"
Ident "b"
TypeDef
Ident "TypeTuple"
Empty
Command
Ident "type"
Par
TupleConstr
Ident "int"
Ident "string"
TypeDef
Expand Down Expand Up @@ -287,7 +287,7 @@ StmtList
IdentDefs
Ident "refTuple2"
RefTy
Par
TupleConstr
Ident "int"
Ident "string"
Empty
Expand Down Expand Up @@ -524,4 +524,3 @@ dumpTree:
static:
staticStmtList1
staticStmtList2

0 comments on commit 159c06e

Please sign in to comment.