implemented 'let' statement

This commit is contained in:
Araq 2011-11-29 17:27:48 +01:00
commit 7fcbdc6d42
33 changed files with 169 additions and 103 deletions

View file

@ -144,11 +144,11 @@ type
nkForStmt, # a for statement nkForStmt, # a for statement
nkWhileStmt, # a while statement nkWhileStmt, # a while statement
nkCaseStmt, # a case statement nkCaseStmt, # a case statement
nkTypeSection, # a type section (consists of type definitions)
nkVarSection, # a var section nkVarSection, # a var section
nkLetSection, # a let section nkLetSection, # a let section
nkConstSection, # a const section nkConstSection, # a const section
nkConstDef, # a const definition nkConstDef, # a const definition
nkTypeSection, # a type section (consists of type definitions)
nkTypeDef, # a type definition nkTypeDef, # a type definition
nkYieldStmt, # the yield statement as a tree nkYieldStmt, # the yield statement as a tree
nkTryStmt, # a try statement nkTryStmt, # a try statement
@ -315,8 +315,9 @@ type
skTemp, # a temporary variable (introduced by compiler) skTemp, # a temporary variable (introduced by compiler)
skModule, # module identifier skModule, # module identifier
skType, # a type skType, # a type
skConst, # a constant
skVar, # a variable skVar, # a variable
skLet, # a 'let' symbol
skConst, # a constant
skResult, # special 'result' variable skResult, # special 'result' variable
skProc, # a proc skProc, # a proc
skMethod, # a method skMethod, # a method
@ -580,7 +581,7 @@ const
ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence} tyTuple, tySequence}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skStub} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
namePos* = 0 namePos* = 0
genericParamsPos* = 1 genericParamsPos* = 1

View file

@ -53,7 +53,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
# this does not work reliably because of forwarding + inlining can break it # this does not work reliably because of forwarding + inlining can break it
case n.kind case n.kind
of nkSym: 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 result = p.prc.id == n.sym.owner.id
of nkDotExpr, nkBracketExpr: of nkDotExpr, nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}: if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}:

View file

@ -139,7 +139,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
case n.sym.kind case n.sym.kind
of skParam, skForVar, skTemp: of skParam, skForVar, skTemp:
result = OnStack result = OnStack
of skVar, skResult: of skVar, skResult, skLet:
if sfGlobal in n.sym.flags: result = OnHeap if sfGlobal in n.sym.flags: result = OnHeap
else: result = OnStack else: result = OnStack
of skConst: of skConst:
@ -1607,7 +1607,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
genComplexConst(p, sym, d) genComplexConst(p, sym, d)
of skEnumField: of skEnumField:
putIntoDest(p, d, e.typ, toRope(sym.position)) 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 sfGlobal in sym.flags: genVarPrototype(p.module, sym)
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
InternalError(e.info, "expr: var not init " & sym.name.s) InternalError(e.info, "expr: var not init " & sym.name.s)

View file

@ -666,7 +666,7 @@ proc genStmts(p: BProc, t: PNode) =
of nkBlockStmt: genBlock(p, t, a) of nkBlockStmt: genBlock(p, t, a)
of nkIfStmt: genIfStmt(p, t) of nkIfStmt: genIfStmt(p, t)
of nkWhileStmt: genWhileStmt(p, t) of nkWhileStmt: genWhileStmt(p, t)
of nkVarSection: genVarStmt(p, t) of nkVarSection, nkLetSection: genVarStmt(p, t)
of nkConstSection: genConstStmt(p, t) of nkConstSection: genConstStmt(p, t)
of nkForStmt: internalError(t.info, "for statement not eliminated") of nkForStmt: internalError(t.info, "for statement not eliminated")
of nkCaseStmt: genCaseStmt(p, t) of nkCaseStmt: genCaseStmt(p, t)

View file

@ -37,7 +37,7 @@ proc mangleName(s: PSym): PRope =
case s.kind case s.kind
of skProc, skMethod, skConverter, skConst: of skProc, skMethod, skConverter, skConst:
result = toRope("@") result = toRope("@")
of skVar, skResult: of skVar, skResult, skLet:
if sfGlobal in s.flags: result = toRope("@") if sfGlobal in s.flags: result = toRope("@")
else: result = toRope("%") else: result = toRope("%")
of skForVar, skTemp, skParam, skType, skEnumField, skModule: of skForVar, skTemp, skParam, skType, skEnumField, skModule:

View file

@ -339,6 +339,7 @@ proc assignLocalVar(p: BProc, s: PSym) =
# for each module that uses them! # for each module that uses them!
if s.loc.k == locNone: if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) 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)) app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register") if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
@ -473,7 +474,7 @@ proc cgsym(m: BModule, name: string): PRope =
if sym != nil: if sym != nil:
case sym.kind case sym.kind
of skProc, skMethod, skConverter: genProc(m, sym) 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) of skType: discard getTypeDesc(m, sym.typ)
else: InternalError("cgsym: " & name) else: InternalError("cgsym: " & name)
else: else:

View file

@ -785,18 +785,12 @@ proc generateDoc(d: PDoc, n: PNode) =
of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate) of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef: genItem(d, n, n.sons[namePos], skConverter) of nkConverterDef: genItem(d, n, n.sons[namePos], skConverter)
of nkVarSection: of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt: if n.sons[i].kind != nkCommentStmt:
genItem(d, n.sons[i], n.sons[i].sons[0], skVar) # order is always 'type var let const':
of nkConstSection: genItem(d, n.sons[i], n.sons[i].sons[0],
for i in countup(0, sonsLen(n) - 1): succ(skType, ord(n.kind)-ord(nkTypeSection)))
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)
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
of nkWhenStmt: of nkWhenStmt:
@ -810,7 +804,7 @@ proc generateDoc(d: PDoc, n: PNode) =
proc genSection(d: PDoc, kind: TSymKind) = proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [ 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" "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return

View file

@ -835,7 +835,7 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) =
s = n.sons[0].sym s = n.sons[0].sym
if s.loc.r == nil: InternalError(n.info, "genAddr: 3") if s.loc.r == nil: InternalError(n.info, "genAddr: 3")
case s.kind case s.kind
of skVar, skResult: of skVar, skLet, skResult:
if mapType(n.typ) == etyObject: if mapType(n.typ) == etyObject:
# make addr() a no-op: # make addr() a no-op:
r.kind = etyNone r.kind = etyNone
@ -866,7 +866,7 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) =
if s.loc.r == nil: if s.loc.r == nil:
InternalError(n.info, "symbol has no generated name: " & s.name.s) InternalError(n.info, "symbol has no generated name: " & s.name.s)
case s.kind case s.kind
of skVar, skParam, skTemp, skResult: of skVar, skLet, skParam, skTemp, skResult:
var k = mapType(s.typ) var k = mapType(s.typ)
if k == etyBaseIndex: if k == etyBaseIndex:
r.kind = 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 nkBlockStmt: genBlock(p, n, r)
of nkIfStmt: genIfStmt(p, n, r) of nkIfStmt: genIfStmt(p, n, r)
of nkWhileStmt: genWhileStmt(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 nkConstSection: genConstStmt(p, n, r)
of nkForStmt: internalError(n.info, "for statement not eliminated") of nkForStmt: internalError(n.info, "for statement not eliminated")
of nkCaseStmt: genCaseStmt(p, n, r) of nkCaseStmt: genCaseStmt(p, n, r)

View file

@ -452,7 +452,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
case s.kind case s.kind
of skProc, skConverter, skMacro: of skProc, skConverter, skMacro:
result = s.getBody result = s.getBody
of skVar, skForVar, skTemp, skResult: of skVar, skLet, skForVar, skTemp, skResult:
if sfGlobal notin s.flags: if sfGlobal notin s.flags:
result = evalVariable(c.tos, s, flags) result = evalVariable(c.tos, s, flags)
else: else:
@ -1195,7 +1195,7 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n)
of nkWhileStmt: result = evalWhile(c, n) of nkWhileStmt: result = evalWhile(c, n)
of nkCaseStmt: result = evalCase(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 nkTryStmt: result = evalTry(c, n)
of nkRaiseStmt: result = evalRaise(c, n) of nkRaiseStmt: result = evalRaise(c, n)
of nkReturnStmt: result = evalReturn(c, n) of nkReturnStmt: result = evalReturn(c, n)

View file

@ -92,7 +92,7 @@ type
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errWrongSymbolX, errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errWrongSymbolX,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -320,6 +320,7 @@ const
errXisNoMacroOrTemplate: "\'$1\' is no macro or template", errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
errXhasSideEffects: "\'$1\' can have side effects", errXhasSideEffects: "\'$1\' can have side effects",
errIteratorExpected: "iterator within for loop context expected", errIteratorExpected: "iterator within for loop context expected",
errLetNeedsInit: "'let' symbol requires an initialization",
errWrongSymbolX: "usage of \'$1\' is a user-defined error", errWrongSymbolX: "usage of \'$1\' is a user-defined error",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",

View file

@ -18,5 +18,5 @@ const
VersionPatch* = 13 VersionPatch* = 13
VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch
RodFileVersion* = "1032" # modify this if the rod-format changes! RodFileVersion* = "1033" # modify this if the rod-format changes!

View file

@ -1352,7 +1352,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkConverter: result = parseRoutine(p, nkConverterDef) of tkConverter: result = parseRoutine(p, nkConverterDef)
of tkType: result = parseSection(p, nkTypeSection, parseTypeDef) of tkType: result = parseSection(p, nkTypeSection, parseTypeDef)
of tkConst: result = parseSection(p, nkConstSection, parseConstant) 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 tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p) of tkBind: result = parseBind(p)

View file

@ -52,6 +52,7 @@ const
wImportcpp, wImportobjc, wError, wNoInit} wImportcpp, wImportobjc, wError, wNoInit}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportcpp, wImportobjc, wError} wExtern, wImportcpp, wImportobjc, wError}
letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect, procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
wThread} wThread}
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas

View file

@ -385,7 +385,7 @@ proc lsub(n: PNode): int =
of nkProcTy: result = lsons(n) + len("proc_") of nkProcTy: result = lsons(n) + len("proc_")
of nkEnumTy: result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_") of nkEnumTy: result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_")
of nkEnumFieldDef: result = lsons(n) + 3 of nkEnumFieldDef: result = lsons(n) + 3
of nkVarSection: of nkVarSection, nkLetSection:
if sonsLen(n) > 1: result = maxLineLen + 1 if sonsLen(n) > 1: result = maxLineLen + 1
else: result = lsons(n) + len("var_") else: result = lsons(n) + len("var_")
of nkReturnStmt: result = lsub(n.sons[0]) + len("return_") of nkReturnStmt: result = lsub(n.sons[0]) + len("return_")
@ -650,7 +650,7 @@ proc gident(g: var TSrcGen, n: PNode) =
else: else:
t = tkOpr t = tkOpr
put(g, t, s) 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) = proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
var var
@ -825,7 +825,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkDerefExpr: of nkDerefExpr:
gsub(g, n.sons[0]) gsub(g, n.sons[0])
putWithSpace(g, tkOpr, "^") 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: of nkAccQuoted:
put(g, tkAccent, "`") put(g, tkAccent, "`")
if n.len > 0: gsub(g, n.sons[0]) if n.len > 0: gsub(g, n.sons[0])
@ -960,10 +960,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
initContext(a) initContext(a)
incl(a.flags, rfInConstExpr) incl(a.flags, rfInConstExpr)
gsection(g, n, a, tkConst, "const") gsection(g, n, a, tkConst, "const")
of nkVarSection: of nkVarSection, nkLetSection:
L = sonsLen(n) L = sonsLen(n)
if L == 0: return if L == 0: return
putWithSpace(g, tkVar, "var") if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
else: putWithSpace(g, tkLet, "let")
if L > 1: if L > 1:
gcoms(g) gcoms(g)
indentNL(g) indentNL(g)
@ -1084,7 +1085,8 @@ proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
gsub(g, n) gsub(g, n)
result = g.buf result = g.buf
proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) = proc renderModule(n: PNode, filename: string,
renderFlags: TRenderFlags = {}) =
var var
f: tfile f: tfile
g: TSrcGen g: TSrcGen
@ -1093,7 +1095,8 @@ proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) =
gsub(g, n.sons[i]) gsub(g, n.sons[i])
optNL(g) optNL(g)
case n.sons[i].kind case n.sons[i].kind
of nkTypeSection, nkConstSection, nkVarSection, nkCommentStmt: putNL(g) of nkTypeSection, nkConstSection, nkVarSection, nkLetSection,
nkCommentStmt: putNL(g)
else: nil else: nil
gcoms(g) gcoms(g)
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:

View file

@ -531,17 +531,10 @@ proc process(c: PPassContext, n: PNode): PNode =
if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
sfForward notin s.flags: sfForward notin s.flags:
addInterfaceSym(w, s) addInterfaceSym(w, s)
of nkVarSection: of nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue 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")
addInterfaceSym(w, a.sons[0].sym) addInterfaceSym(w, a.sons[0].sym)
of nkTypeSection: of nkTypeSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):

View file

@ -81,7 +81,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, s) of skMacro: result = semMacroExpr(c, n, s)
of skTemplate: result = semTemplateExpr(c, n, s) of skTemplate: result = semTemplateExpr(c, n, s)
of skVar, skResult: of skVar, skLet, skResult:
markUsed(n, s) markUsed(n, s)
# if a proc accesses a global variable, it is not side effect free: # if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect)
@ -372,6 +372,7 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = arNone result = arNone
case n.kind case n.kind
of nkSym: of nkSym:
# don't list 'skLet' here:
if n.sym.kind in {skVar, skResult, skTemp}: if n.sym.kind in {skVar, skResult, skTemp}:
if c.p.owner.id == n.sym.owner.id: result = arLocalLValue if c.p.owner.id == n.sym.owner.id: result = arLocalLValue
else: result = arLValue else: result = arLValue

View file

@ -163,7 +163,7 @@ proc semGenericStmt(c: PContext, n: PNode,
for j in countup(0, L-2): for j in countup(0, L-2):
a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, toBind) a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, toBind)
a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, 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): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue

View file

@ -223,14 +223,14 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
else: else:
result = semIdentWithPragma(c, kind, n, {}) result = semIdentWithPragma(c, kind, n, {})
proc semVar(c: PContext, n: PNode): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) if gCmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue 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) checkMinSonsLen(a, 3)
var length = sonsLen(a) var length = sonsLen(a)
var typ: PType var typ: PType
@ -247,22 +247,24 @@ proc semVar(c: PContext, n: PNode): PNode =
else: typ = def.typ else: typ = def.typ
else: else:
def = ast.emptyNode def = ast.emptyNode
if symkind == skLet: GlobalError(a.info, errLetNeedsInit)
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
if not typeAllowed(typ, skVar): if not typeAllowed(typ, symkind):
GlobalError(a.info, errXisNoType, typeToString(typ)) GlobalError(a.info, errXisNoType, typeToString(typ))
var tup = skipTypes(typ, {tyGenericInst}) var tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: GlobalError(a.info, errXExpected, "tuple") if tup.kind != tyTuple: GlobalError(a.info, errXExpected, "tuple")
if length - 2 != sonsLen(tup): if length-2 != sonsLen(tup):
GlobalError(a.info, errWrongNumberOfVariables) GlobalError(a.info, errWrongNumberOfVariables)
b = newNodeI(nkVarTuple, a.info) b = newNodeI(nkVarTuple, a.info)
newSons(b, length) newSons(b, length)
b.sons[length - 2] = ast.emptyNode # no type desc b.sons[length-2] = ast.emptyNode # no type desc
b.sons[length - 1] = def b.sons[length-1] = def
addSon(result, b) addSon(result, b)
for j in countup(0, length-3): for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], skVar) var v = semIdentDef(c, a.sons[j], symkind)
addInterfaceDecl(c, v) addInterfaceDecl(c, v)
if def != nil and def.kind != nkEmpty: if def != nil and def.kind != nkEmpty:
# this is only needed for the evaluation pass: # this is only needed for the evaluation pass:
@ -823,7 +825,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
of nkRaiseStmt: result = semRaise(c, n) 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 nkConstSection: result = semConst(c, n)
of nkTypeSection: result = SemTypeSection(c, n) of nkTypeSection: result = SemTypeSection(c, n)
of nkIfStmt: result = SemIf(c, n) of nkIfStmt: result = SemIf(c, n)

View file

@ -107,7 +107,7 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner =
result = c.mapping[v.id] result = c.mapping[v.id]
if result != toUndefined: return if result != toUndefined: return
case v.kind case v.kind
of skVar, skResult: of skVar, skLet, skResult:
result = toNil result = toNil
if sfGlobal in v.flags: if sfGlobal in v.flags:
if sfThread 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]) var a = analyse(c, n.sons[0])
if a != toMine: Message(n.info, warnDifferentHeaps) if a != toMine: Message(n.info, warnDifferentHeaps)
result = toVoid result = toVoid
of nkVarSection: result = analyseVarSection(c, n) of nkVarSection, nkLetSection: result = analyseVarSection(c, n)
of nkConstSection: result = analyseConstSection(c, n) of nkConstSection: result = analyseConstSection(c, n)
of nkTypeSection, nkCommentStmt: result = toVoid of nkTypeSection, nkCommentStmt: result = toVoid
of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt,

View file

@ -242,6 +242,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
# process pragmas later, because result.typ has not been set yet # process pragmas later, because result.typ has not been set yet
of skField: pragma(c, result, n.sons[1], fieldPragmas) of skField: pragma(c, result, n.sons[1], fieldPragmas)
of skVar: pragma(c, result, n.sons[1], varPragmas) 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) of skConst: pragma(c, result, n.sons[1], constPragmas)
else: nil else: nil
else: else:

View file

@ -294,7 +294,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkVarSection: of nkVarSection, nkLetSection:
result = transformVarSection(c, n) result = transformVarSection(c, n)
else: else:
result = newTransNode(n) result = newTransNode(n)
@ -518,7 +518,7 @@ proc gatherVars(c: PTransf, n: PNode, marked: var TIntSet, owner: PSym,
var s = n.sym var s = n.sym
var found = false var found = false
case s.kind 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 of skTemp, skForVar, skParam, skResult: found = true
else: nil else: nil
if found and owner.id != s.owner.id and not ContainsOrIncl(marked, s.id): 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: of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3`` # do not replace ``const c = 3`` with ``const 3 = 3``
return transformConstSection(c, n) return transformConstSection(c, n)
of nkVarSection: of nkVarSection, nkLetSection:
if c.inlining > 0: if c.inlining > 0:
# we need to copy the variables for multiple yield statements: # we need to copy the variables for multiple yield statements:
result = transformVarSection(c, n) result = transformVarSection(c, n)

View file

@ -816,7 +816,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
result = a.kind == last result = a.kind == last
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = 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 # if we have already checked the type, return true, because we stop the
# evaluation if something is wrong: # evaluation if something is wrong:
result = true result = true

View file

@ -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 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 same type as the initializing expression. Variables are always initialized
with a default value if there is no initializing expression. The default with a default value if there is no initializing expression. The default
value depends on the type and is always a zero in binary. 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] 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 Const section
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View file

@ -213,6 +213,17 @@ constants:
z = y + 5 # computations are possible 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 Control flow statements
======================= =======================
@ -227,7 +238,7 @@ If statement
The if statement is one way to branch the control flow: The if statement is one way to branch the control flow:
.. code-block:: nimrod .. code-block:: nimrod
var name = readLine(stdin) let name = readLine(stdin)
if name == "": if name == "":
echo("Poor soul, you lost your name?") echo("Poor soul, you lost your name?")
elif name == "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: a multi-branch:
.. code-block:: nimrod .. code-block:: nimrod
var name = readLine(stdin) let name = readLine(stdin)
case name case name
of "": of "":
echo("Poor soul, you lost your name?") 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 from strutils import parseInt
Echo("A number please: ") Echo("A number please: ")
var n = parseInt(readLine(stdin)) let n = parseInt(readLine(stdin))
case n case n
of 0..2, 4..7: Echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}") 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") of 3, 8: Echo("The number is 3 or 8")
@ -410,7 +421,7 @@ the next iteration immediately:
.. code-block:: nimrod .. code-block:: nimrod
while true: while true:
var x = readLine(stdin) let x = readLine(stdin)
if x == "": continue if x == "": continue
Echo(x) 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 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 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 such a `forward declaration`:idx: is simple: just omit the ``=`` and the
body. procedure's body.
Iterators Iterators
@ -846,7 +857,7 @@ to mark them to be of another integer type:
.. code-block:: nimrod .. code-block:: nimrod
var let
x = 0 # x is of type ``int`` x = 0 # x is of type ``int``
y = 0'i8 # y is of type ``int8`` y = 0'i8 # y is of type ``int8``
z = 0'i64 # z is of type ``int64`` z = 0'i64 # z is of type ``int64``

View file

@ -580,13 +580,13 @@ via a special ``:`` syntax:
.. code-block:: nimrod .. code-block:: nimrod
template withFile(f: expr, filename: string, mode: TFileMode, template withFile(f: expr, filename: string, mode: TFileMode,
actions: stmt): stmt = body: stmt): stmt =
block: block:
var fn = filename let fn = filename
var f: TFile var f: TFile
if open(f, fn, mode): if open(f, fn, mode):
try: try:
actions body
finally: finally:
close(f) close(f)
else: else:
@ -596,10 +596,10 @@ via a special ``:`` syntax:
txt.writeln("line 1") txt.writeln("line 1")
txt.writeln("line 2") 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 parameter. The ``withFile`` template contains boilerplate code and helps to
avoid a common bug: to forget to close the file. Note how the 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. once.

View file

@ -37,8 +37,8 @@ type
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt,
nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt,
nnkVarSection, nnkLetSection, nnkConstSection, nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
nnkConstDef, nnkTypeSection, nnkTypeDef, nnkConstDef, nnkTypeDef,
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,

View file

@ -360,7 +360,8 @@ proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} =
result = addr(t) result = addr(t)
proc myThreadId*[TArg](): TThreadId[TArg] = 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)) result = cast[TThreadId[TArg]](ThreadVarGetValue(globalsSlot))
when false: when false:

11
tests/reject/tlet.nim Normal file
View file

@ -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)

16
tests/reject/tlet2.nim Normal file
View file

@ -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)

19
tests/run/tlet.nim Normal file
View file

@ -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()

View file

@ -103,7 +103,7 @@ proc parseSpec(filename: string): TSpec =
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
var let
pegLineError = pegLineError =
peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error'/'Warning') ':' \s* {.*}" peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error'/'Warning') ':' \s* {.*}"
pegOtherError = peg"'Error:' \s* {.*}" pegOtherError = peg"'Error:' \s* {.*}"
@ -111,10 +111,10 @@ var
pegOfInterest = pegLineError / pegOtherError pegOfInterest = pegLineError / pegOtherError
proc callCompiler(cmdTemplate, filename, options: string): TSpec = 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], var p = startProcess(command=c[0], args=c[1.. -1],
options={poStdErrToStdOut, poUseShell}) options={poStdErrToStdOut, poUseShell})
var outp = p.outputStream let outp = p.outputStream
var suc = "" var suc = ""
var err = "" var err = ""
var x = newStringOfCap(120) var x = newStringOfCap(120)
@ -222,7 +222,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: string) =
inc(r.passed) inc(r.passed)
proc rejectSingleTest(r: var TResults, test, options: string) = proc rejectSingleTest(r: var TResults, test, options: string) =
var test = test.addFileExt(".nim") let test = test.addFileExt(".nim")
var t = extractFilename(test) var t = extractFilename(test)
inc(r.total) inc(r.total)
echo t echo t
@ -240,10 +240,10 @@ proc reject(r: var TResults, dir, options: string) =
proc compile(r: var TResults, pattern, options: string) = proc compile(r: var TResults, pattern, options: string) =
for test in os.walkFiles(pattern): for test in os.walkFiles(pattern):
var t = extractFilename(test) let t = extractFilename(test)
echo t echo t
inc(r.total) inc(r.total)
var expected = parseSpec(test) let expected = parseSpec(test)
if expected.disabled: if expected.disabled:
r.addResult(t, "", reIgnored) r.addResult(t, "", reIgnored)
inc(r.skipped) inc(r.skipped)
@ -253,11 +253,11 @@ proc compile(r: var TResults, pattern, options: string) =
if not given.err: inc(r.passed) if not given.err: inc(r.passed)
proc compileSingleTest(r: var TResults, test, options: string) = proc compileSingleTest(r: var TResults, test, options: string) =
var test = test.addFileExt(".nim") let test = test.addFileExt(".nim")
var t = extractFilename(test) let t = extractFilename(test)
inc(r.total) inc(r.total)
echo t 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) r.addResult(t, given.msg, if given.err: reFailure else: reSuccess)
if not given.err: inc(r.passed) if not given.err: inc(r.passed)

View file

@ -2,7 +2,10 @@ version 0.8.14
============== ==============
- bug: compiler uses full file names again - 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 version 0.9.0
============= =============
@ -11,18 +14,13 @@ version 0.9.0
escape analysis for string/seq seems to be easy to do too escape analysis for string/seq seems to be easy to do too
- dead code elim for JS backend; 'of' operator for JS backend - dead code elim for JS backend; 'of' operator for JS backend
- test the sort implementation again - test the sort implementation again
- 'let x = y'
- const ptr/ref - const ptr/ref
- unsigned ints and bignums; requires abstract integer literal type: - unsigned ints and bignums; requires abstract integer literal type:
use tyInt+node for that use tyInt+node for that
- implement the high level optimizer - 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 - change overloading resolution
- implement closures; implement proper coroutines - implement closures; implement proper coroutines
- implement ``partial`` pragma for partial evaluation - 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 - we need to support iteration of 2 different data structures in parallel
- make exceptions compatible with C++ exceptions - make exceptions compatible with C++ exceptions
- ``=`` should be overloadable; requires specialization for ``=`` - ``=`` should be overloadable; requires specialization for ``=``
@ -52,7 +50,6 @@ Bugs
- bug: stress testing basic method example (eval example) - bug: stress testing basic method example (eval example)
without ``-d:release`` leaks memory; good way to figure out how a without ``-d:release`` leaks memory; good way to figure out how a
fixed amount of stack can hold an arbitrary number of GC roots! 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 version 0.9.XX

View file

@ -74,6 +74,7 @@ Language Additions
heart's content. heart's content.
- ``bind`` (used for symbol binding in templates and generics) is now a - ``bind`` (used for symbol binding in templates and generics) is now a
declarative statement. 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 - The slice assignment ``a[i..j] = b`` where ``a`` is a sequence or string
now supports *splicing*. now supports *splicing*.
@ -114,7 +115,6 @@ Compiler Additions
Library Additions Library Additions
----------------- -----------------
- Added ``system.mainThreadId``.
- Added ``system.allocShared``, ``system.allocShared0``, - Added ``system.allocShared``, ``system.allocShared0``,
``system.deallocShared``, ``system.reallocShared``. ``system.deallocShared``, ``system.reallocShared``.
- Slicing as implemented by the system module now supports *splicing*. - Slicing as implemented by the system module now supports *splicing*.