diff --git a/compiler/ast.nim b/compiler/ast.nim index 5f5f296cbc..9ac27cb577 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -222,7 +222,8 @@ type nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation nkFuncDef, # a func - nkTupleConstr # a tuple constructor + nkTupleConstr, # a tuple constructor + nkConstTuple # a ``const (a, b) = expr`` construct TNodeKinds* = set[TNodeKind] diff --git a/compiler/parser.nim b/compiler/parser.nim index 54b360a243..01da560c29 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1764,21 +1764,43 @@ proc parseSection(p: var TParser, kind: TNodeKind, else: parMessage(p, errIdentifierExpected, p.tok) -proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment - result = newNodeP(nkConstDef, p) - addSon(result, identWithPragma(p)) - if p.tok.tokType == tkColon: +proc parseConstTuple(p: var TParser): PNode = + result = newNodeP(nkConstTuple, p) + getTok(p) # skip '(' + optInd(p, result) + while p.tok.tokType in {tkSymbol, tkAccent}: + addSon(result, identWithPragma(p)) + if p.tok.tokType == tkColon: + getTok(p) + optInd(p, result) + addSon(result, parseTypeDesc(p)) + if p.tok.tokType != tkComma: break getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - else: - addSon(result, p.emptyNode) + + addSon(result, p.emptyNode) + eat(p, tkParRi) eat(p, tkEquals) optInd(p, result) addSon(result, parseExpr(p)) indAndComment(p, result) +proc parseConstant(p: var TParser): PNode = + #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment + if p.tok.tokType == tkParLe: result = parseConstTuple(p) + else: + result = newNodeP(nkConstDef, p) + addSon(result, identWithPragma(p)) + if p.tok.tokType == tkColon: + getTok(p) + optInd(p, result) + addSon(result, parseTypeDesc(p)) + else: + addSon(result, p.emptyNode) + eat(p, tkEquals) + optInd(p, result) + addSon(result, parseExpr(p)) + indAndComment(p, result) + proc parseEnum(p: var TParser): PNode = #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4d6c6dfb01..9bc5fa4329 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -542,17 +542,17 @@ proc semConst(c: PContext, n: PNode): PNode = var a = n.sons[i] if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if a.kind != nkConstDef: illFormedAst(a, c.config) - checkSonsLen(a, 3, c.config) - var v = semIdentDef(c, a.sons[0], skConst) - styleCheckDef(c.config, v) - onDef(a[0].info, v) - var typ: PType = nil - if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) + if a.kind notin {nkConstDef, nkConstTuple}: illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) + var length = sonsLen(a) - var def = semConstExpr(c, a.sons[2]) + var typ: PType = nil + if a.sons[length-2].kind != nkEmpty: + typ = semTypeNode(c, a.sons[length-2], nil) + + var def = semConstExpr(c, a.sons[length-1]) if def == nil: - localError(c.config, a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[length-1].info, errConstExprExpected) continue # check type compatibility between def.typ and typ: if typ != nil: @@ -560,21 +560,43 @@ proc semConst(c: PContext, n: PNode): PNode = else: typ = def.typ if typ == nil: - localError(c.config, a.sons[2].info, errConstExprExpected) + localError(c.config, a.sons[length-1].info, errConstExprExpected) continue if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) continue - setVarType(c, v, typ) - v.ast = def # no need to copy - if sfGenSym notin v.flags: addInterfaceDecl(c, v) - elif v.owner == nil: v.owner = getCurrOwner(c) - var b = newNodeI(nkConstDef, a.info) - if importantComments(c.config): b.comment = a.comment - addSon(b, newSymNode(v)) - addSon(b, a.sons[1]) - addSon(b, copyTree(def)) - addSon(result, b) + + var b: PNode + if a.kind == nkConstTuple: + if typ.kind != tyTuple: + localError(c.config, a.info, errXExpected, "tuple") + elif int(length/2) != sonsLen(typ): + localError(c.config, a.info, errWrongNumberOfVariables) + b = newNodeI(nkConstTuple, a.info) + newSons(b, length) + b.sons[length-2] = a.sons[length-2] + b.sons[length-1] = def + + for j in countup(0, length-3): + var v = semIdentDef(c, a.sons[j], skConst) + if sfGenSym notin v.flags: addInterfaceDecl(c, v) + elif v.owner == nil: v.owner = getCurrOwner(c) + styleCheckDef(c.config, v) + onDef(a[j].info, v) + + if a.kind != nkConstTuple: + setVarType(c, v, typ) + v.ast = def # no need to copy + b = newNodeI(nkConstDef, a.info) + if importantComments(c.config): b.comment = a.comment + addSon(b, newSymNode(v)) + addSon(b, a.sons[1]) + addSon(b, copyTree(def)) + else: + setVarType(c, v, typ.sons[j]) + v.ast = def[j] + b.sons[j] = newSymNode(v) + addSon(result,b) include semfields diff --git a/lib/core/macros.nim b/lib/core/macros.nim index f45ca3f821..7f0bda080b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -81,7 +81,8 @@ type nnkState, nnkBreakState, nnkFuncDef, - nnkTupleConstr + nnkTupleConstr, + nnkConstTuple NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum # some types are no longer used, see ast.nim