diff options
37 files changed, 336 insertions, 148 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 93630979b..9c9dfce9a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -469,7 +469,8 @@ type skResult, # special 'result' variable skProc, # a proc skMethod, # a method - skIterator, # an iterator + skIterator, # an inline iterator + skClosureIterator, # a resumable closure iterator skConverter, # a type converter skMacro, # a macro skTemplate, # a template; currently also misused for user-defined @@ -485,8 +486,8 @@ type TSymKinds* = set[TSymKind] const - routineKinds* = {skProc, skMethod, skIterator, skConverter, - skMacro, skTemplate} + routineKinds* = {skProc, skMethod, skIterator, skClosureIterator, + skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs tfUncheckedArray* = tfVarargs tfUnion* = tfNoSideEffect @@ -822,8 +823,8 @@ type # the poor naming choices in the standard library. const - OverloadableSyms* = {skProc, skMethod, skIterator, skConverter, - skModule, skTemplate, skMacro} + OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, + skConverter, skModule, skTemplate, skMacro} GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, tyGenericParam} @@ -845,7 +846,8 @@ const tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, tyProc, tyString, tyError} - ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, + ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, + skIterator, skClosureIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, @@ -871,7 +873,10 @@ const nkStrKinds* = {nkStrLit..nkTripleStrLit} skLocalVars* = {skVar, skLet, skForVar, skParam, skResult} - skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} + skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator, + skMethod, skConverter} + + skIterators* = {skIterator, skClosureIterator} lfFullExternalName* = lfParamCopy # \ # only used when 'gCmd == cmdPretty': Indicates that the symbol has been @@ -1481,8 +1486,7 @@ proc originatingModule*(s: PSym): PSym = while result.kind != skModule: result = result.owner proc isRoutine*(s: PSym): bool {.inline.} = - result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, - skConverter} + result = s.kind in skProcKinds proc hasPattern*(s: PSym): bool {.inline.} = result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 07e932b28..3bc4eb029 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -81,7 +81,7 @@ proc hashSym(c: var MD5Context, s: PSym) = proc hashTree(c: var MD5Context, n: PNode) = if n == nil: - c &= "noTreeKind" + c &= "\255" return var k = n.kind md5Update(c, cast[cstring](addr(k)), 1) @@ -107,7 +107,7 @@ proc hashTree(c: var MD5Context, n: PNode) = proc hashType(c: var MD5Context, t: PType) = # modelled after 'typeToString' if t == nil: - c &= "noTypeKind" + c &= "\254" return var k = t.kind @@ -168,7 +168,7 @@ proc canonConst(n: PNode): TUid = c.hashType(n.typ) md5Final(c, MD5Digest(result)) -proc canonSym(s: PSym): TUid +proc canonSym(s: PSym): TUid = var c: MD5Context md5Init(c) c.hashSym(s) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 40ebcbfa8..a4ba412a4 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1803,7 +1803,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) - of skProc, skConverter, skIterator: + of skProc, skConverter, skIterators: genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: internalError(n.info, "expr: proc not init " & sym.name.s) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 4576a54b5..990153fc7 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -863,7 +863,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = res.add(t.sons[i].strVal) of nkSym: var sym = t.sons[i].sym - if sym.kind in {skProc, skIterator, skMethod}: + if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: var a: TLoc initLocExpr(p, t.sons[i], a) res.add(rdLoc(a).ropeToStr) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 156ebee5e..321462044 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -69,7 +69,7 @@ proc mangleName(s: PSym): PRope = if result == nil: if gCmd == cmdCompileToLLVM: case s.kind - of skProc, skMethod, skConverter, skConst, skIterator: + of skProc, skMethod, skConverter, skConst, skIterators: result = ~"@" of skVar, skForVar, skResult, skLet: if sfGlobal in s.flags: result = ~"@" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c3a28527e..683aed069 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -705,7 +705,7 @@ proc cgsym(m: BModule, name: string): PRope = var sym = magicsys.getCompilerProc(name) if sym != nil: case sym.kind - of skProc, skMethod, skConverter, skIterator: genProc(m, sym) + of skProc, skMethod, skConverter, skIterators: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) else: internalError("cgsym: " & name) @@ -1192,7 +1192,7 @@ proc nullify[T](arr: var T) = for i in low(arr)..high(arr): arr[i] = nil -proc resetModule*(m: var BModule) = +proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw # away all the data that was written to disk initLinkedList(m.headerFiles) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0df7bb6dc..e7d818556 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -146,7 +146,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = result.nestedTryStmts = @[] result.finallySafePoints = @[] -iterator cgenModules*: var BModule = +iterator cgenModules*: BModule = for i in 0..high(gModules): # ultimately, we are iterating over the file ids here. # some "files" won't have an associated cgen module (like stdin) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index c05b55184..d8c439c9c 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -366,7 +366,7 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", - "Iterators", "Converters", "Macros", "Templates" + "Iterators", "Iterators", "Converters", "Macros", "Templates" ] if d.section[kind] == nil: return var title = sectionNames[kind].toRope diff --git a/compiler/importer.nim b/compiler/importer.nim index 078a90c98..7a73f2bbf 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -103,7 +103,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = internalError(n.info, "importSymbol: 2") # for an enumeration we have to add all identifiers case s.kind - of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter: + of skProcKinds: # for a overloadable syms add all overloaded routines var it: TIdentIter var e = initIdentIter(it, fromMod.tab, s.name) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6e0bd5e44..0cbd4c364 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1154,7 +1154,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = proc isIndirect(v: PSym): bool = result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and - v.kind notin {skProc, skConverter, skMethod, skIterator} + v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} proc genVarInit(p: PProc, v: PSym, n: PNode) = var diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 3738f89b2..67cd364dc 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -232,9 +232,9 @@ proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = initIdNodeTable(result.localsToAccess) initIdTable(result.localsToEnv) initIdTable(result.lambdasToEnv) - result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure + result.isIter = fn.kind == skClosureIterator if result.isIter: initIterContext(result, fn) - + proc newInnerContext(fn: PSym): PInnerContext = new(result) result.fn = fn @@ -292,8 +292,7 @@ proc newCall(a, b: PSym): PNode = result.add newSymNode(b) proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = (s.kind in {skProc, skMethod, skConverter} or - s.kind == skIterator and s.typ.callConv == ccClosure) and + result = s.kind in {skProc, skMethod, skConverter, skClosureIterator} and s.skipGenericOwner == outerProc #s.typ.callConv == ccClosure @@ -653,7 +652,7 @@ proc outerProcSons(o: POuterContext, n: PNode) = proc liftIterSym*(n: PNode): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) let iter = n.sym - assert iter.kind == skIterator + assert iter.kind == skClosureIterator result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -679,7 +678,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = var closure = PEnv(idTableGet(o.lambdasToEnv, local)) - if local.kind == skIterator and local.typ.callConv == ccClosure: + if local.kind == skClosureIterator: # consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the # closure generation code again: @@ -843,10 +842,10 @@ proc liftForLoop*(body: PNode): PNode = # static binding? var env: PSym - if call[0].kind == nkSym and call[0].sym.kind == skIterator: + if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator: # createClosure() let iter = call[0].sym - assert iter.kind == skIterator + assert iter.kind == skClosureIterator env = copySym(getHiddenParam(iter)) var v = newNodeI(nkVarSection, body.info) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0e7df13cd..9c6c5e22f 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -110,6 +110,8 @@ type fNumber*: BiggestFloat # the parsed floating point literal base*: TNumericalBase # the numerical base; only valid for int # or float literals + strongSpaceA*: int8 # leading spaces of an operator + strongSpaceB*: int8 # trailing spaces of an operator literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int @@ -119,6 +121,7 @@ type indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead + strongSpaces*: bool var gLinesCompiled*: int # all lines that have been compiled @@ -183,6 +186,7 @@ proc initToken*(L: var TToken) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 + L.strongSpaceA = 0 L.literal = "" L.fNumber = 0.0 L.base = base10 @@ -192,6 +196,7 @@ proc fillToken(L: var TToken) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 + L.strongSpaceA = 0 setLen(L.literal, 0) L.fNumber = 0.0 L.base = base10 @@ -634,6 +639,14 @@ proc getOperator(L: var TLexer, tok: var TToken) = h = h !& ord(c) inc(pos) endOperator(L, tok, pos, h) + # advance pos but don't store it in L.bufpos so the next token (which might + # be an operator too) gets the preceeding spaces: + tok.strongSpaceB = 0 + while buf[pos] == ' ': + inc pos + inc tok.strongSpaceB + if buf[pos] in {CR, LF, nimlexbase.EndOfFile}: + tok.strongSpaceB = -1 proc scanComment(L: var TLexer, tok: var TToken) = var pos = L.bufpos @@ -677,10 +690,12 @@ proc scanComment(L: var TLexer, tok: var TToken) = proc skip(L: var TLexer, tok: var TToken) = var pos = L.bufpos var buf = L.buf + tok.strongSpaceA = 0 while true: case buf[pos] of ' ': inc(pos) + inc(tok.strongSpaceA) of Tabulator: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) inc(pos) @@ -691,6 +706,7 @@ proc skip(L: var TLexer, tok: var TToken) = while buf[pos] == ' ': inc(pos) inc(indent) + tok.strongSpaceA = 0 if buf[pos] > ' ': tok.indent = indent break diff --git a/compiler/lookups.nim b/compiler/lookups.nim index fcb5b6731..8239f2a47 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -109,7 +109,7 @@ type proc getSymRepr*(s: PSym): string = case s.kind - of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s) + of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s) else: result = s.name.s proc ensureNoMissingOrUnusedSymbols(scope: PScope) = diff --git a/compiler/parser.nim b/compiler/parser.nim index 5a5bfb574..cfba89f4a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -38,7 +38,6 @@ type inSemiStmtList: int proc parseAll*(p: var TParser): PNode -proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode # implements an iterator. Returns the next top-level statement or @@ -50,7 +49,6 @@ proc parseString*(s: string, filename: string = "", line: int = 0): PNode # correct error messages referring to the original source. # helpers for the other parsers -proc getPrecedence*(tok: TToken): int proc isOperator*(tok: TToken): bool proc getTok*(p: var TParser) proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "") @@ -77,14 +75,17 @@ proc parseCase(p: var TParser): PNode proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) = +proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, + strongSpaces=false) = initToken(p.tok) openLexer(p.lex, fileIdx, inputStream) getTok(p) # read the first token p.firstTok = true + p.strongSpaces = strongSpaces -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream) = - openParser(p, filename.fileInfoIdx, inputstream) +proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, + strongSpaces=false) = + openParser(p, filename.fileInfoIdx, inputstream, strongSpaces) proc closeParser(p: var TParser) = closeLexer(p.lex) @@ -193,34 +194,52 @@ proc isSigilLike(tok: TToken): bool {.inline.} = proc isLeftAssociative(tok: TToken): bool {.inline.} = result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^' -proc getPrecedence(tok: TToken): int = +proc getPrecedence(tok: TToken, strongSpaces: bool): int = + template considerStrongSpaces(x): expr = + x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0) + case tok.tokType of tkOpr: let L = tok.ident.s.len let relevantChar = relevantOprChar(tok.ident) - template considerAsgn(value: expr) = - result = if tok.ident.s[L-1] == '=': 1 else: value + template considerAsgn(value: expr) = + result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) case relevantChar of '$', '^': considerAsgn(10) of '*', '%', '/', '\\': considerAsgn(9) - of '~': result = 8 + of '~': result = considerStrongSpaces(8) of '+', '-', '|': considerAsgn(8) of '&': considerAsgn(7) - of '=', '<', '>', '!': result = 5 + of '=', '<', '>', '!': result = considerStrongSpaces(5) of '.': considerAsgn(6) - of '?': result = 2 + of '?': result = considerStrongSpaces(2) else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 - of tkDotDot: result = 6 + of tkDotDot: result = considerStrongSpaces(6) of tkAnd: result = 4 of tkOr, tkXor: result = 3 - else: result = - 10 - -proc isOperator(tok: TToken): bool = - result = getPrecedence(tok) >= 0 + else: result = -10 + +proc isOperator(tok: TToken): bool = + tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, + tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} + +proc isUnary(p: TParser): bool = + p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and + p.tok.strongSpaceB == 0 and + p.tok.strongSpaceA > 0 + +proc checkBinary(p: TParser) {.inline.} = + # we don't check '..' here as that's too annoying + if p.strongSpaces and p.tok.tokType == tkOpr: + if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB: + parMessage(p, errGenerated, "number of spaces around '$#' not consistent"% + prettyTok(p.tok)) + elif p.tok.strongSpaceA notin {0,1,2,4,8}: + parMessage(p, errGenerated, "number of spaces must be 0,1,2,4 or 8") #| module = stmt ^* (';' / IND{=}) #| @@ -650,6 +669,7 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = while p.tok.indent < 0: case p.tok.tokType of tkParLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -664,8 +684,10 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = result = dotExpr(p, result) result = parseGStrLit(p, result) of tkBracketLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast: if p.inPragma == 0: @@ -695,10 +717,11 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = primary(p, mode) # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok) + var opPrec = getPrecedence(p.tok, p.strongSpaces) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode # the operator itself must not start on a new line: - while opPrec >= limit and p.tok.indent < 0: + while opPrec >= limit and p.tok.indent < 0 and not isUnary(p): + checkBinary(p) var leftAssoc = ord(isLeftAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: @@ -710,7 +733,7 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = addSon(a, result) addSon(a, b) result = a - opPrec = getPrecedence(p.tok) + opPrec = getPrecedence(p.tok, p.strongSpaces) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = result = simpleExprAux(p, -1, mode) @@ -1933,7 +1956,9 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode = stream.lineOffset = line var parser: TParser - openParser(parser, filename, stream) + # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong + # spaces... + openParser(parser, filename, stream, false) result = parser.parseAll closeParser(parser) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bf3564016..fba4e7657 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -97,8 +97,6 @@ proc makeExternImport(s: PSym, extname: string) = incl(s.flags, sfImportc) excl(s.flags, sfForward) -const invalidIdentChars = AllChars - IdentChars - proc validateExternCName(s: PSym, info: TLineInfo) = ## Validates that the symbol name in s.loc.r is a valid C identifier. ## @@ -106,16 +104,14 @@ proc validateExternCName(s: PSym, info: TLineInfo) = ## starting with a number. If the check fails, a generic error will be ## displayed to the user. let target = ropeToStr(s.loc.r) - if target.len < 1 or (not (target[0] in IdentStartChars)) or - (not target.allCharsInSet(IdentChars)): + if target.len < 1 or target[0] notin IdentStartChars or + not target.allCharsInSet(IdentChars): localError(info, errGenerated, "invalid exported symbol") proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) - case gCmd - of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: + if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}: validateExternCName(s, info) - else: discard incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = @@ -780,6 +776,17 @@ proc implictPragmas*(c: PContext, sym: PSym, n: PNode, addToLib(lib, sym) if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) +proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = + if n == nil or n.sons == nil: + return false + + for p in n.sons: + var key = if p.kind == nkExprColonExpr: p[0] else: p + if key.kind == nkIdent and whichKeyword(key.ident) == pragma: + return true + + return false + proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0cd27a443..2e5def75a 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -64,7 +64,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, errors[errors.len - 1].add("\n " & err) if z.state == csMatch: # little hack so that iterators are preferred over everything else: - if sym.kind == skIterator: inc(z.exactMatches, 200) + if sym.kind in skIterators: inc(z.exactMatches, 200) case best.state of csEmpty, csNoMatch: best = z of csMatch: @@ -289,8 +289,9 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = newNodeI(a.kind, n.info) for i in countup(0, len(a)-1): var candidate = a.sons[i].sym - if candidate.kind in {skProc, skMethod, skConverter, skIterator}: - # if suffices that the candidate has the proper number of generic + if candidate.kind in {skProc, skMethod, skConverter, + skIterator, skClosureIterator}: + # it suffices that the candidate has the proper number of generic # type parameters: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: result.add(explicitGenericSym(c, n, candidate)) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index df58c896f..d942aa41e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -274,7 +274,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; addSonSkipIntLit(result, intType) # basetype of range proc markIndirect*(c: PContext, s: PSym) {.inline.} = - if s.kind in {skProc, skConverter, skMethod, skIterator}: + if s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator}: incl(s.flags, sfAddrTaken) # XXX add to 'c' for global analysis diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 538489490..203a51816 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -323,11 +323,6 @@ proc isOpImpl(c: PContext, n: PNode): PNode = result = newIntNode(nkIntLit, ord(t.kind == tyProc and t.callConv == ccClosure and tfIterator notin t.flags)) - of "iterator": - let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator in t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), @@ -662,11 +657,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = if flags*{efInTypeof, efWantIterator} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction - # to 'skIterator' anymore; skIterator is preferred in sigmatch already for - # typeof support. + # to 'skIterators' anymore; skIterators are preferred in sigmatch already + # for typeof support. # for ``type(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}) + {skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators) else: result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate}) @@ -679,7 +674,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if (callee.kind == skIterator) and (callee.id == c.p.owner.id): + if (callee.kind in skIterators) and (callee.id == c.p.owner.id): localError(n.info, errRecursiveDependencyX, callee.name.s) if sfNoSideEffect notin callee.flags: if {sfImportc, sfSideEffect} * callee.flags != {}: @@ -1193,7 +1188,7 @@ proc semReturn(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or - (c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): + c.p.owner.kind == skClosureIterator: if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` if c.p.resultSym != nil: @@ -1258,7 +1253,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = proc semYield(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if c.p.owner == nil or c.p.owner.kind != skIterator: + if c.p.owner == nil or c.p.owner.kind notin skIterators: localError(n.info, errYieldNotAllowedHere) elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: localError(n.info, errYieldNotAllowedInTryStmt) @@ -1266,9 +1261,11 @@ proc semYield(c: PContext, n: PNode): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var restype = c.p.owner.typ.sons[0] if restype != nil: - n.sons[0] = fitNode(c, restype, n.sons[0]) + let adjustedRes = if c.p.owner.kind == skIterator: restype.base + else: restype + n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) if n.sons[0].typ == nil: internalError(n.info, "semYield") - semYieldVarResult(c, n, restype) + semYieldVarResult(c, n, adjustedRes) else: localError(n.info, errCannotReturnExpr) elif c.p.owner.typ.sons[0] != nil: @@ -1828,7 +1825,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var s = lookUp(c, n) semCaptureSym(s, c.p.owner) result = semSym(c, n, s, flags) - if s.kind in {skProc, skMethod, skIterator, skConverter}: + if s.kind in {skProc, skMethod, skConverter}+skIterators: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -1884,7 +1881,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: - var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: @@ -1918,7 +1915,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = localError(n.info, errUseQualifier, s.name.s) elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) - of skProc, skMethod, skConverter, skIterator: + of skProc, skMethod, skConverter, skIterators: if s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) else: @@ -1942,7 +1939,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBracketExpr: checkMinSonsLen(n, 1) var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: + if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: # type parameters: partial generic specialization n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) result = explicitGenericInstantiation(c, n, s) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index b21d851c9..ffc1a43b2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -42,7 +42,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterator, skConverter: + of skProc, skMethod, skIterators, skConverter: result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpand(s): @@ -141,7 +141,7 @@ proc semGenericStmt(c: PContext, n: PNode, # symbol lookup ... of skUnknown, skParam: # Leave it as an identifier. - of skProc, skMethod, skIterator, skConverter: + of skProc, skMethod, skIterators, skConverter: result.sons[0] = symChoice(c, n.sons[0], s, scOption) first = 1 of skGenericParam: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b2b3ceb6d..a11386966 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -76,8 +76,8 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = proc semProcvarCheck(c: PContext, n: PNode) = let n = n.skipConv - if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, - skConverter}: + if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter, + skIterator, skClosureIterator}: performProcvarCheck(c, n, n.sym) proc semProc(c: PContext, n: PNode): PNode @@ -616,7 +616,8 @@ proc symForVar(c: PContext, n: PNode): PSym = proc semForVars(c: PContext, n: PNode): PNode = result = n var length = sonsLen(n) - var iter = skipTypes(n.sons[length-2].typ, {tyGenericInst}) + let iterBase = n.sons[length-2].typ.skipTypes({tyIter}) + var iter = skipTypes(iterBase, {tyGenericInst}) # length == 3 means that there is one for loop variable # and thus no tuple unpacking: if iter.kind != tyTuple or length == 3: @@ -626,7 +627,7 @@ proc semForVars(c: PContext, n: PNode): PNode = # BUGFIX: don't use `iter` here as that would strip away # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim`` # for an example: - v.typ = n.sons[length-2].typ + v.typ = iterBase n.sons[0] = newSymNode(v) if sfGenSym notin v.flags: addForVarDecl(c, v) else: @@ -664,7 +665,7 @@ proc semFor(c: PContext, n: PNode): PNode = # first class iterator: result = semForVars(c, n) elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or - call.sons[0].sym.kind != skIterator: + call.sons[0].sym.kind notin skIterators: if length == 3: n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2]) elif length == 4: @@ -997,8 +998,7 @@ proc activate(c: PContext, n: PNode) = discard proc maybeAddResult(c: PContext, s: PSym, n: PNode) = - if s.typ.sons[0] != nil and - (s.kind != skIterator or s.typ.callConv == ccClosure): + if s.typ.sons[0] != nil and s.kind != skIterator: addResult(c, s.typ.sons[0], n.info, s.kind) addResultNode(c, n) @@ -1073,12 +1073,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, rawAddSon(s.typ, nil) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) - if s.kind == skIterator: + if s.kind in skIterators: s.typ.flags.incl(tfIterator) var proto = searchForProc(c, s.scope, s) - if proto == nil: - if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure + if proto == nil: + if s.kind == skClosureIterator: s.typ.callConv = ccClosure else: s.typ.callConv = lastOptionEntry(c).defaultCC # add it here, so that recursive procs are possible: if sfGenSym in s.flags: discard @@ -1138,7 +1138,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[bodyPos] = transformBody(c.module, semBody, s) popProcCon(c) else: - if s.typ.sons[0] != nil and kind != skIterator: + if s.typ.sons[0] != nil and kind notin skIterators: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) var toBind = initIntSet() n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind) @@ -1165,7 +1165,10 @@ proc determineType(c: PContext, s: PSym) = discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType) proc semIterator(c: PContext, n: PNode): PNode = - result = semProcAux(c, n, skIterator, iteratorPragmas) + let kind = if hasPragma(n[pragmasPos], wClosure) or + n[namePos].kind == nkEmpty: skClosureIterator + else: skIterator + result = semProcAux(c, n, kind, iteratorPragmas) var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil and s.typ.callConv != ccClosure: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 5abc3ef33..31624a97f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -171,7 +171,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterator, skConverter, skTemplate, skMacro: + of OverloadableSyms: result = symChoice(c, n, s, scOpen) of skGenericParam: result = newSymNodeTypeDesc(s, n.info) @@ -348,7 +348,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkMethodDef: result = semRoutineInTemplBody(c, n, skMethod) of nkIteratorDef: - result = semRoutineInTemplBody(c, n, skIterator) + result = semRoutineInTemplBody(c, n, n[namePos].sym.kind) of nkTemplateDef: result = semRoutineInTemplBody(c, n, skTemplate) of nkMacroDef: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 809b80428..a619de7ff 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -711,6 +711,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, for i in 0 .. paramType.sonsLen - 2: result.rawAddSon newTypeS(tyAnything, c) # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) + + if paramType.lastSon.kind == tyUserTypeClass: + result.kind = tyUserTypeClassInst + result.rawAddSon paramType.lastSon + return addImplicitGeneric(result) + result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) @@ -845,8 +851,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, n.sons[0].info) if lifted != nil: r = lifted r.flags.incl tfRetType - result.sons[0] = skipIntLit(r) - res.typ = result.sons[0] + r = skipIntLit(r) + if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r]) + result.sons[0] = r + res.typ = r proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) @@ -951,6 +959,23 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = let typ = semTypeNode(c, n, nil) result.sons.safeAdd(typ) +proc semProcTypeWithScope(c: PContext, n: PNode, + prev: PType, kind: TSymKind): PType = + checkSonsLen(n, 2) + openScope(c) + result = semProcTypeNode(c, n.sons[0], nil, prev, kind) + # dummy symbol for `pragma`: + var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c) + s.typ = result + if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: + if result.callConv == ccDefault: + result.callConv = ccClosure + #Message(n.info, warnImplicitClosure, renderTree(n)) + else: + pragma(c, s, n.sons[1], procTypePragmas) + when useEffectSystem: setEffectsForProcType(result, n.sons[1]) + closeScope(c) + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -959,7 +984,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTypeOfExpr: # for ``type(countup(1,3))``, see ``tests/ttoseq``. checkSonsLen(n, 1) - result = semExprWithType(c, n.sons[0], {efInTypeof}).typ + result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter}) of nkPar: if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: @@ -1073,27 +1098,18 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n.sons[0], nil) result.rawAddSon(base) result.flags.incl tfHasStatic - of nkProcTy, nkIteratorTy: + of nkIteratorTy: if n.sonsLen == 0: - result = newConstraint(c, tyProc) + result = newConstraint(c, tyIter) else: - checkSonsLen(n, 2) - openScope(c) - result = semProcTypeNode(c, n.sons[0], nil, prev, skProc) - # dummy symbol for `pragma`: - var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c) - s.typ = result - if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: - if result.callConv == ccDefault: - result.callConv = ccClosure - #Message(n.info, warnImplicitClosure, renderTree(n)) - else: - pragma(c, s, n.sons[1], procTypePragmas) - when useEffectSystem: setEffectsForProcType(result, n.sons[1]) - closeScope(c) - if n.kind == nkIteratorTy: + result = semProcTypeWithScope(c, n, prev, skClosureIterator) result.flags.incl(tfIterator) result.callConv = ccClosure + of nkProcTy: + if n.sonsLen == 0: + result = newConstraint(c, tyProc) + else: + result = semProcTypeWithScope(c, n, prev, skProc) of nkEnumTy: result = semEnum(c, n, prev) of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f8e3459df..c0898ef26 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -410,7 +410,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone when useEffectSystem: if not compatibleEffects(f, a): return isNone - of tyNil: result = f.allowsNil + of tyNil: + result = f.allowsNil + of tyIter: + if tfIterator in f.flags: result = typeRel(c, f.base, a.base) else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = @@ -921,14 +924,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, prev.base, a.base) else: result = isNone - + + of tyIter: + if a.kind == tyIter or + (a.kind == tyProc and tfIterator in a.flags): + result = typeRel(c, f.base, a.base) + else: + result = isNone + of tyStmt: result = isGeneric of tyProxy: result = isEqual - else: internalError("typeRel: " & $f.kind) + else: internalAssert false proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate @@ -1138,7 +1148,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, z.calleeSym = m.calleeSym var best = -1 for i in countup(0, sonsLen(arg) - 1): - if arg.sons[i].sym.kind in {skProc, skIterator, skMethod, skConverter}: + if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: copyCandidate(z, m) var r = typeRel(z, f, arg.sons[i].typ) if r != isNone: diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 7c44ec0b4..478c2a837 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -17,14 +17,15 @@ type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum - skinStandard, skinBraces, skinEndX + skinStandard, skinStrongSpaces, skinBraces, skinEndX const - parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"] - filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", - "strip"] + parserNames*: array[TParserKind, string] = ["standard", "strongspaces", + "braces", "endx"] + filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", + "strip"] -type +type TParsers*{.final.} = object skin*: TParserKind parser*: TParser @@ -54,7 +55,7 @@ proc parseFile(fileIdx: int32): PNode = proc parseAll(p: var TParsers): PNode = case p.skin - of skinStandard: + of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) of skinBraces: result = pbraces.parseAll(p.parser) @@ -65,7 +66,7 @@ proc parseAll(p: var TParsers): PNode = proc parseTopLevelStmt(p: var TParsers): PNode = case p.skin - of skinStandard: + of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) of skinBraces: result = pbraces.parseTopLevelStmt(p.parser) @@ -170,7 +171,9 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = else: s = inputstream case p.skin of skinStandard, skinBraces, skinEndX: - parser.openParser(p.parser, fileIdx, s) + parser.openParser(p.parser, fileIdx, s, false) + of skinStrongSpaces: + parser.openParser(p.parser, fileIdx, s, true) -proc closeParsers(p: var TParsers) = +proc closeParsers(p: var TParsers) = parser.closeParser(p.parser) diff --git a/compiler/transf.nim b/compiler/transf.nim index 0911950d0..f4b716c5b 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -113,7 +113,7 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = result[1] = ri proc transformSymAux(c: PTransf, n: PNode): PNode = - #if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: + #if n.sym.kind == skClosureIterator: # return liftIterSym(n) var b: PNode var tc = c.transCon @@ -434,7 +434,6 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var length = sonsLen(n) var call = n.sons[length - 2] if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or - call.sons[0].typ.callConv == ccClosure or call.sons[0].sym.kind != skIterator: n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode return lambdalifting.liftForLoop(n).PTransNode @@ -454,7 +453,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var newC = newTransCon(getCurrOwner(c)) newC.forStmt = n newC.forLoopBody = loopBody - if iter.kind != skIterator: internalError(call.info, "transformFor") + internalAssert iter.kind == skIterator # generate access statements for the parameters (unless they are constant) pushTransCon(c, newC) for i in countup(1, sonsLen(call) - 1): @@ -741,7 +740,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = var c = openTransf(module, "") result = processTransf(c, n, prc) result = liftLambdas(prc, result) - #if prc.kind == skIterator and prc.typ.callConv == ccClosure: + #if prc.kind == skClosureIterator: # result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) diff --git a/compiler/types.nim b/compiler/types.nim index edf5ab47b..8cf91da8b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1247,7 +1247,7 @@ proc computeSize(typ: PType): BiggestInt = proc getReturnType*(s: PSym): PType = # Obtains the return type of a iterator/proc/macro/template - assert s.kind in {skProc, skTemplate, skMacro, skIterator} + assert s.kind in skProcKinds result = s.typ.sons[0] proc getSize(typ: PType): BiggestInt = diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 123394436..b1a751723 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1330,7 +1330,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest, flags) - of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: + of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators: # 'skTemplate' is only allowed for 'getAst' support: if sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) diff --git a/doc/idetools.txt b/doc/idetools.txt index c1eba9e5f..d4f0f077d 100644 --- a/doc/idetools.txt +++ b/doc/idetools.txt @@ -279,8 +279,8 @@ skForVar col 7: "" -skIterator ----------- +skIterator, skClosureIterator +----------------------------- The fourth column will be the empty string if the iterator is being defined, since at that point in the file the parser hasn't processed diff --git a/doc/manual.txt b/doc/manual.txt index 98219360e..8c8db7658 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -480,8 +480,8 @@ precedence and associativity; this is useful for meta programming. Associativity ------------- -All binary operators are left-associative, except binary operators whose -relevant char is ``^``. +Binary operators whose relevant character is ``^`` are right-associative, all +other binary operators are left-associative. Precedence ---------- @@ -508,7 +508,7 @@ Precedence level Operators Relevant char 7 ``+ -`` ``+ ~ |`` OP7 6 ``&`` ``&`` OP6 5 ``..`` ``.`` OP5 - 4 ``== <= < >= > != in not_in is isnot not of`` ``= < > !`` OP4 + 4 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP4 3 ``and`` OP3 2 ``or xor`` OP2 1 ``@ : ?`` OP1 @@ -516,6 +516,46 @@ Precedence level Operators Relevant char ================ =============================================== ================== =============== +Strong spaces +------------- + +The number of spaces preceeding a non-keyword operator affects precedence +if the experimental parser directive ``#!strongSpaces`` is used. Indentation +is not used to determine the number of spaces. If 2 or more operators have the +same number of preceeding spaces the precedence table applies, so ``1 + 3 * 4`` +is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: + +.. code-block:: nimrod + #! strongSpaces + if foo+4 * 4 == 8 and b&c | 9 ++ + bar: + echo "" + # is parsed as + if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo "" + + +Furthermore whether an operator is used a prefix operator is affected by the +number of spaces: + +.. code-block:: nimrod + #! strongSpaces + echo $foo + # is parsed as + echo($foo) + +This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors +or as accessors: + +.. code-block:: nimrod + #! strongSpaces + echo (1,2) + # is parsed as + echo((1,2)) + + +Grammar +------- + The grammar's start symbol is ``module``. .. include:: grammar.txt @@ -3441,7 +3481,7 @@ Declarative type classes are written in the following form: c.len is ordinal items(c) is iterator for value in c: - type(value) is T + value.type is T The type class will be matched if: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d14822974..db2bbc5a4 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -88,7 +88,7 @@ type nskUnknown, nskConditional, nskDynLib, nskParam, nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet, nskConst, nskResult, - nskProc, nskMethod, nskIterator, + nskProc, nskMethod, nskIterator, nskClosureIterator, nskConverter, nskMacro, nskTemplate, nskField, nskEnumField, nskForVar, nskLabel, nskStub diff --git a/lib/nimbase.h b/lib/nimbase.h index f73dca190..1100e084b 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -373,5 +373,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } # define GC_GUARD #endif +/* Test to see if nimrod and the C compiler agrees on the size of a pointer. + On disagreement, your C compiler will say something like: + "error: 'assert_numbits' declared as an array with a negative size" */ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; #endif diff --git a/lib/wrappers/zmq.nim b/lib/wrappers/zmq.nim index 4e658028e..9826ab813 100644 --- a/lib/wrappers/zmq.nim +++ b/lib/wrappers/zmq.nim @@ -299,12 +299,12 @@ proc open*(address: string, server: bool, mode: TConnectionMode = conDEALER, else: if connect(result.s, address) != 0'i32: zmqError() -proc close*(c: var TConnection) = +proc close*(c: TConnection) = ## closes the connection. if close(c.s) != 0'i32: zmqError() if term(c.c) != 0'i32: zmqError() -proc send*(c: var TConnection, msg: string) = +proc send*(c: TConnection, msg: string) = ## sends a message over the connection. var m: TMsg if msg_init(m, msg.len) != 0'i32: zmqError() @@ -312,7 +312,7 @@ proc send*(c: var TConnection, msg: string) = if send(c.s, m, 0'i32) != 0'i32: zmqError() discard msg_close(m) -proc receive*(c: var TConnection): string = +proc receive*(c: TConnection): string = ## receives a message from a connection. var m: TMsg if msg_init(m) != 0'i32: zmqError() @@ -320,4 +320,3 @@ proc receive*(c: var TConnection): string = result = newString(msg_size(m)) copyMem(addr(result[0]), msg_data(m), result.len) discard msg_close(m) - diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim index 578749caf..9f0d0a74b 100644 --- a/tests/iter/tanoniter1.nim +++ b/tests/iter/tanoniter1.nim @@ -8,7 +8,7 @@ discard """ """ proc factory(a, b: int): iterator (): int = - iterator foo(): int = + iterator foo(): int {.closure.} = var x = a while x <= b: yield x diff --git a/tests/metatype/udtcmanual.nim b/tests/metatype/udtcmanual.nim new file mode 100644 index 000000000..f22bd6ac6 --- /dev/null +++ b/tests/metatype/udtcmanual.nim @@ -0,0 +1,43 @@ +discard """ + output: '''1 +2 +3 +4 +5 +6 +a +b +t +e +s +t +''' +""" + +template accept(e: expr) = + static: assert compiles(e) + +template reject(e: expr) = + static: assert(not compiles(e)) + +type + Container[T] = generic C + C.len is Ordinal + items(c) is iterator + for value in C: + value.type is T + +proc takesIntContainer(c: Container[int]) = + for e in c: echo e + +takesIntContainer(@[1, 2, 3]) +reject takesIntContainer(@["x", "y"]) + +proc takesContainer(c: Container) = + for e in c: echo e + +takesContainer(@[4, 5, 6]) +takesContainer(@["a", "b"]) +takesContainer "test" +reject takesContainer(10) + diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 6d3c51749..3c2b9ee5e 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -1,8 +1,8 @@ discard """ - output: "true true false yes" + output: '''true true false yes''' """ -proc IsVoid[T](): string = +proc IsVoid[T](): string = when T is void: result = "yes" else: @@ -11,3 +11,26 @@ proc IsVoid[T](): string = const x = int is int echo x, " ", float is float, " ", float is string, " ", IsVoid[void]() +template yes(e: expr): stmt = + static: assert e + +template no(e: expr): stmt = + static: assert(not e) + +var s = @[1, 2, 3] + +yes s.items is iterator +no s.items is proc + +yes s.items is iterator: int +no s.items is iterator: float + +yes s.items is iterator: TNumber +no s.items is iterator: object + +type + Iter[T] = iterator: T + +yes s.items is Iter[TNumber] +no s.items is Iter[float] + diff --git a/todo.txt b/todo.txt index a67ff5172..51f883d1d 100644 --- a/todo.txt +++ b/todo.txt @@ -32,7 +32,6 @@ version 0.9.x - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat - stack-less GC -- implement strongSpaces:on - make '--implicitStatic:on' the default - implicit deref for parameter matching diff --git a/web/news.txt b/web/news.txt index 0001cece7..83863cdd9 100644 --- a/web/news.txt +++ b/web/news.txt @@ -82,6 +82,7 @@ News - The *command syntax* is supported in a lot more contexts. - Anonymous iterators are now supported and iterators can capture variables of an outer proc. + - The experimental ``strongSpaces`` parsing mode has been implemented. Tools improvements |