diff options
33 files changed, 169 insertions, 103 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index d80f2289e..3d07fda07 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -144,11 +144,11 @@ type nkForStmt, # a for statement nkWhileStmt, # a while statement nkCaseStmt, # a case statement + nkTypeSection, # a type section (consists of type definitions) nkVarSection, # a var section nkLetSection, # a let section nkConstSection, # a const section nkConstDef, # a const definition - nkTypeSection, # a type section (consists of type definitions) nkTypeDef, # a type definition nkYieldStmt, # the yield statement as a tree nkTryStmt, # a try statement @@ -315,8 +315,9 @@ type skTemp, # a temporary variable (introduced by compiler) skModule, # module identifier skType, # a type - skConst, # a constant skVar, # a variable + skLet, # a 'let' symbol + skConst, # a constant skResult, # special 'result' variable skProc, # a proc skMethod, # a method @@ -580,7 +581,7 @@ const ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, tyTuple, tySequence} ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, - skMacro, skTemplate, skConverter, skEnumField, skStub} + skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst} namePos* = 0 genericParamsPos* = 1 diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index dbb9190d2..1c57479ae 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -53,7 +53,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool = # this does not work reliably because of forwarding + inlining can break it case n.kind of nkSym: - if n.sym.kind in {skVar, skResult, skTemp} and p.prc != nil: + if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil: result = p.prc.id == n.sym.owner.id of nkDotExpr, nkBracketExpr: if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 4138aecd2..caaab1ab5 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -139,7 +139,7 @@ proc getStorageLoc(n: PNode): TStorageLoc = case n.sym.kind of skParam, skForVar, skTemp: result = OnStack - of skVar, skResult: + of skVar, skResult, skLet: if sfGlobal in n.sym.flags: result = OnHeap else: result = OnStack of skConst: @@ -1607,7 +1607,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = genComplexConst(p, sym, d) of skEnumField: putIntoDest(p, d, e.typ, toRope(sym.position)) - of skVar, skResult: + of skVar, skResult, skLet: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: InternalError(e.info, "expr: var not init " & sym.name.s) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index ca6f1fd91..da9a05188 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -666,7 +666,7 @@ proc genStmts(p: BProc, t: PNode) = of nkBlockStmt: genBlock(p, t, a) of nkIfStmt: genIfStmt(p, t) of nkWhileStmt: genWhileStmt(p, t) - of nkVarSection: genVarStmt(p, t) + of nkVarSection, nkLetSection: genVarStmt(p, t) of nkConstSection: genConstStmt(p, t) of nkForStmt: internalError(t.info, "for statement not eliminated") of nkCaseStmt: genCaseStmt(p, t) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bfd8c8723..490282fae 100755 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -37,7 +37,7 @@ proc mangleName(s: PSym): PRope = case s.kind of skProc, skMethod, skConverter, skConst: result = toRope("@") - of skVar, skResult: + of skVar, skResult, skLet: if sfGlobal in s.flags: result = toRope("@") else: result = toRope("%") of skForVar, skTemp, skParam, skType, skEnumField, skModule: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ca58e4b1c..5c730af80 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -339,6 +339,7 @@ proc assignLocalVar(p: BProc, s: PSym) = # for each module that uses them! if s.loc.k == locNone: fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) + if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)) if sfRegister in s.flags: app(p.s[cpsLocals], " register") if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): @@ -473,7 +474,7 @@ proc cgsym(m: BModule, name: string): PRope = if sym != nil: case sym.kind of skProc, skMethod, skConverter: genProc(m, sym) - of skVar, skResult: genVarPrototype(m, sym) + of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) else: InternalError("cgsym: " & name) else: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e76545c22..5e1e4b59c 100755 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -785,18 +785,12 @@ proc generateDoc(d: PDoc, n: PNode) = of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate) of nkConverterDef: genItem(d, n, n.sons[namePos], skConverter) - of nkVarSection: - for i in countup(0, sonsLen(n) - 1): + of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: + for i in countup(0, sonsLen(n) - 1): if n.sons[i].kind != nkCommentStmt: - genItem(d, n.sons[i], n.sons[i].sons[0], skVar) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind != nkCommentStmt: - genItem(d, n.sons[i], n.sons[i].sons[0], skConst) - of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind != nkCommentStmt: - genItem(d, n.sons[i], n.sons[i].sons[0], skType) + # order is always 'type var let const': + genItem(d, n.sons[i], n.sons[i].sons[0], + succ(skType, ord(n.kind)-ord(nkTypeSection))) of nkStmtList: for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) of nkWhenStmt: @@ -810,7 +804,7 @@ proc generateDoc(d: PDoc, n: PNode) = proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ - "Imports", "Types", "Consts", "Vars", "Vars", "Procs", "Methods", + "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", "Iterators", "Converters", "Macros", "Templates" ] if d.section[kind] == nil: return diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 3d9e99d44..588abfc93 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -835,7 +835,7 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = s = n.sons[0].sym if s.loc.r == nil: InternalError(n.info, "genAddr: 3") case s.kind - of skVar, skResult: + of skVar, skLet, skResult: if mapType(n.typ) == etyObject: # make addr() a no-op: r.kind = etyNone @@ -866,7 +866,7 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) = if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) case s.kind - of skVar, skParam, skTemp, skResult: + of skVar, skLet, skParam, skTemp, skResult: var k = mapType(s.typ) if k == etyBaseIndex: r.kind = etyBaseIndex @@ -1339,7 +1339,7 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = of nkBlockStmt: genBlock(p, n, r) of nkIfStmt: genIfStmt(p, n, r) of nkWhileStmt: genWhileStmt(p, n, r) - of nkVarSection: genVarStmt(p, n, r) + of nkVarSection, nkLetSection: genVarStmt(p, n, r) of nkConstSection: genConstStmt(p, n, r) of nkForStmt: internalError(n.info, "for statement not eliminated") of nkCaseStmt: genCaseStmt(p, n, r) diff --git a/compiler/evals.nim b/compiler/evals.nim index c92143a5b..c44ea3aeb 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -452,7 +452,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = case s.kind of skProc, skConverter, skMacro: result = s.getBody - of skVar, skForVar, skTemp, skResult: + of skVar, skLet, skForVar, skTemp, skResult: if sfGlobal notin s.flags: result = evalVariable(c.tos, s, flags) else: @@ -1195,7 +1195,7 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) of nkWhileStmt: result = evalWhile(c, n) of nkCaseStmt: result = evalCase(c, n) - of nkVarSection: result = evalVar(c, n) + of nkVarSection, nkLetSection: result = evalVar(c, n) of nkTryStmt: result = evalTry(c, n) of nkRaiseStmt: result = evalRaise(c, n) of nkReturnStmt: result = evalReturn(c, n) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index a1fd73c88..1b218ad7e 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -92,7 +92,7 @@ type errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errWrongSymbolX, + errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errWrongSymbolX, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -320,6 +320,7 @@ const errXisNoMacroOrTemplate: "\'$1\' is no macro or template", errXhasSideEffects: "\'$1\' can have side effects", errIteratorExpected: "iterator within for loop context expected", + errLetNeedsInit: "'let' symbol requires an initialization", errWrongSymbolX: "usage of \'$1\' is a user-defined error", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", diff --git a/compiler/nversion.nim b/compiler/nversion.nim index b0a40f204..a0deda76e 100755 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -18,5 +18,5 @@ const VersionPatch* = 13 VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch - RodFileVersion* = "1032" # modify this if the rod-format changes! + RodFileVersion* = "1033" # modify this if the rod-format changes! diff --git a/compiler/parser.nim b/compiler/parser.nim index 361c297fb..e3bf3a748 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1352,7 +1352,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkConverter: result = parseRoutine(p, nkConverterDef) of tkType: result = parseSection(p, nkTypeSection, parseTypeDef) of tkConst: result = parseSection(p, nkConstSection, parseConstant) - of tkLet: result = parseSection(p, nkLetSection, parseConstant) + of tkLet: result = parseSection(p, nkLetSection, parseVariable) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkBind: result = parseBind(p) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5eb6263e9..d4ea3226d 100755 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -52,6 +52,7 @@ const wImportcpp, wImportobjc, wError, wNoInit} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportcpp, wImportobjc, wError} + letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect, wThread} allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas diff --git a/compiler/renderer.nim b/compiler/renderer.nim index a38fba907..09539f23d 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -385,7 +385,7 @@ proc lsub(n: PNode): int = of nkProcTy: result = lsons(n) + len("proc_") of nkEnumTy: result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_") of nkEnumFieldDef: result = lsons(n) + 3 - of nkVarSection: + of nkVarSection, nkLetSection: if sonsLen(n) > 1: result = maxLineLen + 1 else: result = lsons(n) + len("var_") of nkReturnStmt: result = lsub(n.sons[0]) + len("return_") @@ -650,7 +650,7 @@ proc gident(g: var TSrcGen, n: PNode) = else: t = tkOpr put(g, t, s) - if (n.kind == nkSym) and (renderIds in g.flags): put(g, tkIntLit, $(n.sym.id)) + if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id) proc gsub(g: var TSrcGen, n: PNode, c: TContext) = var @@ -825,7 +825,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkDerefExpr: gsub(g, n.sons[0]) putWithSpace(g, tkOpr, "^") - # unfortunately this requires a space, because ^. would be only one operator + # unfortunately this requires a space, because ^. would be only one opr of nkAccQuoted: put(g, tkAccent, "`") if n.len > 0: gsub(g, n.sons[0]) @@ -960,10 +960,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = initContext(a) incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") - of nkVarSection: + of nkVarSection, nkLetSection: L = sonsLen(n) - if L == 0: return - putWithSpace(g, tkVar, "var") + if L == 0: return + if n.kind == nkVarSection: putWithSpace(g, tkVar, "var") + else: putWithSpace(g, tkLet, "let") if L > 1: gcoms(g) indentNL(g) @@ -1084,21 +1085,23 @@ proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = gsub(g, n) result = g.buf -proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) = - var +proc renderModule(n: PNode, filename: string, + renderFlags: TRenderFlags = {}) = + var f: tfile g: TSrcGen initSrcGen(g, renderFlags) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) optNL(g) case n.sons[i].kind - of nkTypeSection, nkConstSection, nkVarSection, nkCommentStmt: putNL(g) + of nkTypeSection, nkConstSection, nkVarSection, nkLetSection, + nkCommentStmt: putNL(g) else: nil gcoms(g) - if optStdout in gGlobalOptions: + if optStdout in gGlobalOptions: write(stdout, g.buf) - elif open(f, filename, fmWrite): + elif open(f, filename, fmWrite): write(f, g.buf) close(f) else: diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index ee56ec0a9..e08d78ae2 100755 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -531,17 +531,10 @@ proc process(c: PPassContext, n: PNode): PNode = if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or sfForward notin s.flags: addInterfaceSym(w, s) - of nkVarSection: + of nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind != nkIdentDefs: InternalError(a.info, "rodwrite.process") - addInterfaceSym(w, a.sons[0].sym) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind != nkConstDef: InternalError(a.info, "rodwrite.process") + if a.kind == nkCommentStmt: continue addInterfaceSym(w, a.sons[0].sym) of nkTypeSection: for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d21f557d4..cedce3c9c 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -81,7 +81,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) of skMacro: result = semMacroExpr(c, n, s) of skTemplate: result = semTemplateExpr(c, n, s) - of skVar, skResult: + of skVar, skLet, skResult: markUsed(n, s) # if a proc accesses a global variable, it is not side effect free: if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) @@ -372,6 +372,7 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult = result = arNone case n.kind of nkSym: + # don't list 'skLet' here: if n.sym.kind in {skVar, skResult, skTemp}: if c.p.owner.id == n.sym.owner.id: result = arLocalLValue else: result = arLValue diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 488bdcf8a..a81347eda 100755 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -163,7 +163,7 @@ proc semGenericStmt(c: PContext, n: PNode, for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, toBind) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, toBind) - of nkVarSection: + of nkVarSection, nkLetSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2818ecbd1..60c0d5913 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -222,15 +222,15 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = incl(result.flags, sfGlobal) else: result = semIdentWithPragma(c, kind, n, {}) - -proc semVar(c: PContext, n: PNode): PNode = + +proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) if a.kind == nkCommentStmt: continue - if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a) + if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: IllFormedAst(a) checkMinSonsLen(a, 3) var length = sonsLen(a) var typ: PType @@ -247,22 +247,24 @@ proc semVar(c: PContext, n: PNode): PNode = else: typ = def.typ else: def = ast.emptyNode + if symkind == skLet: GlobalError(a.info, errLetNeedsInit) + # this can only happen for errornous var statements: if typ == nil: continue - if not typeAllowed(typ, skVar): + if not typeAllowed(typ, symkind): GlobalError(a.info, errXisNoType, typeToString(typ)) var tup = skipTypes(typ, {tyGenericInst}) if a.kind == nkVarTuple: if tup.kind != tyTuple: GlobalError(a.info, errXExpected, "tuple") - if length - 2 != sonsLen(tup): + if length-2 != sonsLen(tup): GlobalError(a.info, errWrongNumberOfVariables) b = newNodeI(nkVarTuple, a.info) newSons(b, length) - b.sons[length - 2] = ast.emptyNode # no type desc - b.sons[length - 1] = def + b.sons[length-2] = ast.emptyNode # no type desc + b.sons[length-1] = def addSon(result, b) - for j in countup(0, length-3): - var v = semIdentDef(c, a.sons[j], skVar) + for j in countup(0, length-3): + var v = semIdentDef(c, a.sons[j], symkind) addInterfaceDecl(c, v) if def != nil and def.kind != nkEmpty: # this is only needed for the evaluation pass: @@ -277,7 +279,7 @@ proc semVar(c: PContext, n: PNode): PNode = else: v.typ = tup.sons[j] b.sons[j] = newSymNode(v) - + proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) for i in countup(0, sonsLen(n) - 1): @@ -823,7 +825,8 @@ proc SemStmt(c: PContext, n: PNode): PNode = of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil else: localError(n.sons[j].info, errStmtInvalidAfterReturn) of nkRaiseStmt: result = semRaise(c, n) - of nkVarSection: result = semVar(c, n) + of nkVarSection: result = semVarOrLet(c, n, skVar) + of nkLetSection: result = semVarOrLet(c, n, skLet) of nkConstSection: result = semConst(c, n) of nkTypeSection: result = SemTypeSection(c, n) of nkIfStmt: result = SemIf(c, n) diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index dcb38ff90..e402463cf 100755 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -107,7 +107,7 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner = result = c.mapping[v.id] if result != toUndefined: return case v.kind - of skVar, skResult: + of skVar, skLet, skResult: result = toNil if sfGlobal in v.flags: if sfThread in v.flags: @@ -348,7 +348,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = var a = analyse(c, n.sons[0]) if a != toMine: Message(n.info, warnDifferentHeaps) result = toVoid - of nkVarSection: result = analyseVarSection(c, n) + of nkVarSection, nkLetSection: result = analyseVarSection(c, n) of nkConstSection: result = analyseConstSection(c, n) of nkTypeSection, nkCommentStmt: result = toVoid of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index ca7988aeb..9e78f98e3 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -242,9 +242,10 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, # process pragmas later, because result.typ has not been set yet of skField: pragma(c, result, n.sons[1], fieldPragmas) of skVar: pragma(c, result, n.sons[1], varPragmas) + of skLet: pragma(c, result, n.sons[1], letPragmas) of skConst: pragma(c, result, n.sons[1], constPragmas) else: nil - else: + else: result = semIdentVis(c, kind, n, allowed) proc checkForOverlap(c: PContext, t, ex: PNode, branchIndex: int) = diff --git a/compiler/transf.nim b/compiler/transf.nim index 3c715be6d..5f3becaf6 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -294,7 +294,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: # nothing to be done for leaves: result = PTransNode(n) - of nkVarSection: + of nkVarSection, nkLetSection: result = transformVarSection(c, n) else: result = newTransNode(n) @@ -518,7 +518,7 @@ proc gatherVars(c: PTransf, n: PNode, marked: var TIntSet, owner: PSym, var s = n.sym var found = false case s.kind - of skVar: found = sfGlobal notin s.flags + of skVar, skLet: found = sfGlobal notin s.flags of skTemp, skForVar, skParam, skResult: found = true else: nil if found and owner.id != s.owner.id and not ContainsOrIncl(marked, s.id): @@ -714,7 +714,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkConstSection: # do not replace ``const c = 3`` with ``const 3 = 3`` return transformConstSection(c, n) - of nkVarSection: + of nkVarSection, nkLetSection: if c.inlining > 0: # we need to copy the variables for multiple yield statements: result = transformVarSection(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index 31c94236a..6ce4c6e48 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -816,7 +816,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], result = a.kind == last proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = - assert(kind in {skVar, skConst, skParam, skResult}) + assert(kind in {skVar, skLet, skConst, skParam, skResult}) # if we have already checked the type, return true, because we stop the # evaluation if something is wrong: result = true diff --git a/doc/manual.txt b/doc/manual.txt index 36eed6a4a..164410a68 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1555,7 +1555,6 @@ variables of the same type: If an initializer is given the type can be omitted: the variable is then of the same type as the initializing expression. Variables are always initialized - with a default value if there is no initializing expression. The default value depends on the type and is always a zero in binary. @@ -1586,6 +1585,19 @@ The implicit initialization can be avoided for optimization reasons with the a {.noInit.}: array [0..1023, char] + +let statement +~~~~~~~~~~~~~ + +A `Let`:idx: statement declares new local and global `single assignment`:idx: +variables and binds a value to them. The syntax is the of the ``var`` +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 +nor can their address be taken. They cannot be assigned new values. + +For let variables the same pragmas are available as for ordinary variables. + + Const section ~~~~~~~~~~~~~ diff --git a/doc/tut1.txt b/doc/tut1.txt index 98ef5629c..546c6e57c 100755 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -213,6 +213,17 @@ constants: z = y + 5 # computations are possible +The let statement +================= +The ``let`` statement works like the ``var`` statement but the declared +symbols are *single assignment* variables: After the initialization their +value cannot change: + +.. code-block:: + let x = "abc" # introduces a new variable `x` and binds a value to it + x = "xyz" # Illegal: assignment to `x` + + Control flow statements ======================= @@ -227,7 +238,7 @@ If statement The if statement is one way to branch the control flow: .. code-block:: nimrod - var name = readLine(stdin) + let name = readLine(stdin) if name == "": echo("Poor soul, you lost your name?") elif name == "name": @@ -247,7 +258,7 @@ Another way to branch is provided by the case statement. A case statement is a multi-branch: .. code-block:: nimrod - var name = readLine(stdin) + let name = readLine(stdin) case name of "": echo("Poor soul, you lost your name?") @@ -270,7 +281,7 @@ For integers or other ordinal types value ranges are also possible: from strutils import parseInt Echo("A number please: ") - var n = parseInt(readLine(stdin)) + let n = parseInt(readLine(stdin)) case n of 0..2, 4..7: Echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}") of 3, 8: Echo("The number is 3 or 8") @@ -410,7 +421,7 @@ the next iteration immediately: .. code-block:: nimrod while true: - var x = readLine(stdin) + let x = readLine(stdin) if x == "": continue Echo(x) @@ -717,8 +728,8 @@ However, this cannot be done for mutually recursive procedures: Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be introduced to the compiler before it is completely defined. The syntax for -such a `forward declaration` is simple: just omit the ``=`` and the procedure's -body. +such a `forward declaration`:idx: is simple: just omit the ``=`` and the +procedure's body. Iterators @@ -846,7 +857,7 @@ to mark them to be of another integer type: .. code-block:: nimrod - var + let x = 0 # x is of type ``int`` y = 0'i8 # y is of type ``int8`` z = 0'i64 # z is of type ``int64`` diff --git a/doc/tut2.txt b/doc/tut2.txt index 5d757c28b..9017bd7e0 100755 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -580,13 +580,13 @@ via a special ``:`` syntax: .. code-block:: nimrod template withFile(f: expr, filename: string, mode: TFileMode, - actions: stmt): stmt = + body: stmt): stmt = block: - var fn = filename + let fn = filename var f: TFile if open(f, fn, mode): try: - actions + body finally: close(f) else: @@ -596,10 +596,10 @@ via a special ``:`` syntax: txt.writeln("line 1") txt.writeln("line 2") -In the example the two ``writeln`` statements are bound to the ``actions`` +In the example the two ``writeln`` statements are bound to the ``body`` parameter. The ``withFile`` template contains boilerplate code and helps to avoid a common bug: to forget to close the file. Note how the -``var fn = filename`` statement ensures that ``filename`` is evaluated only +``let fn = filename`` statement ensures that ``filename`` is evaluated only once. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fbc47fb63..11208e508 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -37,8 +37,8 @@ type nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt, - nnkVarSection, nnkLetSection, nnkConstSection, - nnkConstDef, nnkTypeSection, nnkTypeDef, + nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, + nnkConstDef, nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, diff --git a/lib/system/threads.nim b/lib/system/threads.nim index ce07f0c3b..7c26cf8ee 100755 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -360,7 +360,8 @@ proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} = result = addr(t) proc myThreadId*[TArg](): TThreadId[TArg] = - ## returns the thread ID of the thread that calls this proc. + ## returns the thread ID of the thread that calls this proc. This is unsafe + ## because the type ``TArg`` is not checked for consistency! result = cast[TThreadId[TArg]](ThreadVarGetValue(globalsSlot)) when false: diff --git a/tests/reject/tlet.nim b/tests/reject/tlet.nim new file mode 100644 index 000000000..3d36432fb --- /dev/null +++ b/tests/reject/tlet.nim @@ -0,0 +1,11 @@ +discard """ + line: "10" + errormsg: "'name' cannot be assigned to" +""" + +Echo("What's your name? ") +let name = readLine(stdin) +while name == "": + Echo("Please tell me your name: ") + name = readLine(stdin) + diff --git a/tests/reject/tlet2.nim b/tests/reject/tlet2.nim new file mode 100644 index 000000000..8b1ddf940 --- /dev/null +++ b/tests/reject/tlet2.nim @@ -0,0 +1,16 @@ +discard """ + line: "13" + errormsg: "for a 'var' type a variable needs to be passed" +""" + +proc divmod(a, b: int, res, remainder: var int) = + res = a div b # integer division + remainder = a mod b # integer modulo operation + +let + x = 9 + y = 3 +divmod(8, 5, x, y) # modifies x and y +echo(x) +echo(y) + diff --git a/tests/run/tlet.nim b/tests/run/tlet.nim new file mode 100644 index 000000000..ba355c5d8 --- /dev/null +++ b/tests/run/tlet.nim @@ -0,0 +1,19 @@ +discard """ + output: '''Very funny, your name is name. +nameabc''' +""" + +proc main = + let name = "name" + if name == "": + echo("Poor soul, you lost your name?") + elif name == "name": + echo("Very funny, your name is name.") + else: + Echo("Hi, ", name, "!") + + let (x, y) = ("abc", name) + echo y, x + +main() + diff --git a/tests/tester.nim b/tests/tester.nim index d50b4f766..15b1bb045 100755 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -103,7 +103,7 @@ proc parseSpec(filename: string): TSpec = # ---------------------------------------------------------------------------- -var +let pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error'/'Warning') ':' \s* {.*}" pegOtherError = peg"'Error:' \s* {.*}" @@ -111,10 +111,10 @@ var pegOfInterest = pegLineError / pegOtherError proc callCompiler(cmdTemplate, filename, options: string): TSpec = - var c = parseCmdLine(cmdTemplate % [options, filename]) + let c = parseCmdLine(cmdTemplate % [options, filename]) var p = startProcess(command=c[0], args=c[1.. -1], options={poStdErrToStdOut, poUseShell}) - var outp = p.outputStream + let outp = p.outputStream var suc = "" var err = "" var x = newStringOfCap(120) @@ -222,7 +222,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: string) = inc(r.passed) proc rejectSingleTest(r: var TResults, test, options: string) = - var test = test.addFileExt(".nim") + let test = test.addFileExt(".nim") var t = extractFilename(test) inc(r.total) echo t @@ -240,10 +240,10 @@ proc reject(r: var TResults, dir, options: string) = proc compile(r: var TResults, pattern, options: string) = for test in os.walkFiles(pattern): - var t = extractFilename(test) + let t = extractFilename(test) echo t inc(r.total) - var expected = parseSpec(test) + let expected = parseSpec(test) if expected.disabled: r.addResult(t, "", reIgnored) inc(r.skipped) @@ -253,11 +253,11 @@ proc compile(r: var TResults, pattern, options: string) = if not given.err: inc(r.passed) proc compileSingleTest(r: var TResults, test, options: string) = - var test = test.addFileExt(".nim") - var t = extractFilename(test) + let test = test.addFileExt(".nim") + let t = extractFilename(test) inc(r.total) echo t - var given = callCompiler(cmdTemplate, test, options) + let given = callCompiler(cmdTemplate, test, options) r.addResult(t, given.msg, if given.err: reFailure else: reSuccess) if not given.err: inc(r.passed) diff --git a/todo.txt b/todo.txt index 4211b49de..e8ad9a8dc 100755 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,10 @@ version 0.8.14 ============== - bug: compiler uses full file names again -- stdlib and compiler should not use deprecated endOfFile and readline +- implicit invokation of `items`/`pairs` seems nice +- warning for implicit openArray -> varargs convention +- implement explicit varargs; **but** ``len(varargs)`` problem remains! + --> solve by implicit conversion from varargs to openarray version 0.9.0 ============= @@ -11,18 +14,13 @@ version 0.9.0 escape analysis for string/seq seems to be easy to do too - dead code elim for JS backend; 'of' operator for JS backend - test the sort implementation again -- 'let x = y' - const ptr/ref - unsigned ints and bignums; requires abstract integer literal type: use tyInt+node for that - implement the high level optimizer -- warning for implicit openArray -> varargs convention -- implement explicit varargs; **but** ``len(varargs)`` problem remains! - --> solve by implicit conversion from varargs to openarray - change overloading resolution - implement closures; implement proper coroutines - implement ``partial`` pragma for partial evaluation -- implicit invokation of `items`/`pairs` seems nice - we need to support iteration of 2 different data structures in parallel - make exceptions compatible with C++ exceptions - ``=`` should be overloadable; requires specialization for ``=`` @@ -52,7 +50,6 @@ Bugs - bug: stress testing basic method example (eval example) without ``-d:release`` leaks memory; good way to figure out how a fixed amount of stack can hold an arbitrary number of GC roots! -- bug: osproc.execProcess() should raise an exception if the exit code is not 0 version 0.9.XX diff --git a/web/news.txt b/web/news.txt index 6ef636cf6..a6405eced 100755 --- a/web/news.txt +++ b/web/news.txt @@ -74,6 +74,7 @@ Language Additions heart's content. - ``bind`` (used for symbol binding in templates and generics) is now a declarative statement. +- Nimrod now supports single assignment variables via the ``let`` statement. - The slice assignment ``a[i..j] = b`` where ``a`` is a sequence or string now supports *splicing*. @@ -114,7 +115,6 @@ Compiler Additions Library Additions ----------------- -- Added ``system.mainThreadId``. - Added ``system.allocShared``, ``system.allocShared0``, ``system.deallocShared``, ``system.reallocShared``. - Slicing as implemented by the system module now supports *splicing*. |