diff options
-rw-r--r-- | changelog.md | 6 | ||||
-rw-r--r-- | compiler/ast.nim | 13 | ||||
-rw-r--r-- | compiler/cgendata.nim | 2 | ||||
-rw-r--r-- | compiler/parser.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/semobjconstr.nim | 15 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/semtypes.nim | 4 | ||||
-rw-r--r-- | lib/core/macros.nim | 23 | ||||
-rw-r--r-- | lib/pure/asyncmacro.nim | 14 | ||||
-rw-r--r-- | lib/pure/random.nim | 2 | ||||
-rw-r--r-- | lib/system.nim | 99 | ||||
-rw-r--r-- | todo.txt | 1 |
13 files changed, 96 insertions, 89 deletions
diff --git a/changelog.md b/changelog.md index 6dea3ee63..5740f8589 100644 --- a/changelog.md +++ b/changelog.md @@ -22,3 +22,9 @@ for more information. - The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages use the ``pred`` proc. +- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are + implemented. These are now implemented purely in ``system.nim`` without compiler + support. ``system.Slice`` now takes 2 generic parameters so that it can + take ``BackwardsIndex`` indices. ``BackwardsIndex`` is produced by ``system.^``. + This means if you overload ``[]`` or ``[]=`` you need to ensure they also work + with ``system.BackwardsIndex`` (if applicable for the accessors). diff --git a/compiler/ast.nim b/compiler/ast.nim index 43aa3e484..e6d967dde 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1017,16 +1017,11 @@ proc add*(father, son: PNode) = type Indexable = PNode | PType -template `[]`*(n: Indexable, i: int): Indexable = - n.sons[i] +template `[]`*(n: Indexable, i: int): Indexable = n.sons[i] +template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x -template `-|`*(b, s: untyped): untyped = - (if b >= 0: b else: s.len + b) - -# son access operators with support for negative indices -template `{}`*(n: Indexable, i: int): untyped = n[i -| n] -template `{}=`*(n: Indexable, i: int, s: Indexable) = - n.sons[i -| n] = s +template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] +template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x when defined(useNodeIds): const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879 diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index be087095f..19ab2fe50 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -154,7 +154,7 @@ proc includeHeader*(this: BModule; header: string) = proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} = # section in the current block - result = p.blocks[^1].sections[s] + result = p.blocks[p.blocks.len-1].sections[s] proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} = # top level proc sections diff --git a/compiler/parser.nim b/compiler/parser.nim index e0885c388..113922189 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1905,7 +1905,7 @@ proc parseVariable(p: var TParser): PNode = #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: result = parseIdentColonEquals(p, {withPragma, withDot}) - result{-1} = postExprBlocks(p, result{-1}) + result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) proc parseBind(p: var TParser, k: TNodeKind): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3cba0abd8..195625489 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1665,7 +1665,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = # We transform the do block into a template with a param for # each interpolation. We'll pass this template to getAst. var - quotedBlock = n{-1} + quotedBlock = n[^1] op = if n.len == 3: expectString(c, n[1]) else: "``" quotes = newSeq[PNode](1) # the quotes will be added to a nkCall statement diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index c73b042fe..56d160aa4 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -78,13 +78,13 @@ proc caseBranchMatchesExpr(branch, matched: PNode): bool = proc pickCaseBranch(caseExpr, matched: PNode): PNode = # XXX: Perhaps this proc already exists somewhere - let endsWithElse = caseExpr{-1}.kind == nkElse + let endsWithElse = caseExpr[^1].kind == nkElse for i in 1 .. caseExpr.len - 1 - int(endsWithElse): if caseExpr[i].caseBranchMatchesExpr(matched): return caseExpr[i] if endsWithElse: - return caseExpr{-1} + return caseExpr[^1] iterator directFieldsInRecList(recList: PNode): PNode = # XXX: We can remove this case by making all nkOfBranch nodes @@ -136,17 +136,20 @@ proc semConstructFields(c: PContext, recNode: PNode, of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = - fieldsPresentInInitExpr(recNode[branchIdx]{-1}, initExpr) + let branch = recNode[branchIdx] + let fields = branch[branch.len - 1] + fieldsPresentInInitExpr(fields, initExpr) template checkMissingFields(branchNode: PNode) = - checkForMissingFields(branchNode{-1}, initExpr) + let fields = branchNode[branchNode.len - 1] + checkForMissingFields(fields, initExpr) let discriminator = recNode.sons[0]; internalAssert discriminator.kind == nkSym var selectedBranch = -1 for i in 1 ..< recNode.len: - let innerRecords = recNode[i]{-1} + let innerRecords = recNode[i][^1] let status = semConstructFields(c, innerRecords, initExpr, flags) if status notin {initNone, initUnknown}: mergeInitStatus(result, status) @@ -250,7 +253,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) result = newNodeIT(nkObjConstr, n.info, t) for child in n: result.add child - + t = skipTypes(t, {tyGenericInst, tyAlias}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) if t.kind != tyObject: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 55d84c6fc..540ef4c07 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -823,7 +823,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = a.sons[0] = newSymNode(s) proc checkCovariantParamsUsages(genericType: PType) = - var body = genericType{-1} + var body = genericType[^1] proc traverseSubTypes(t: PType): bool = template error(msg) = localError(genericType.sym.info, msg) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index cebbbad47..18ea87341 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1618,8 +1618,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = var a = n.sons[i] if a.kind != nkIdentDefs: illFormedAst(n) let L = a.len - var def = a{-1} - let constraint = a{-2} + var def = a[^1] + let constraint = a[^2] var typ: PType if constraint.kind != nkEmpty: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d3f541153..fc5b5bfb7 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -129,13 +129,6 @@ const nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} -proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} - ## get `n`'s `i`'th child. - -proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", - noSideEffect.} - ## set `n`'s `i`'th child to `child`. - proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} ## constructs an identifier from the string `s` @@ -162,6 +155,20 @@ proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.} ## returns the number of children of `n`. +proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} + ## get `n`'s `i`'th child. + +proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int] + ## get `n`'s `i`'th child. + +proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", + noSideEffect.} + ## set `n`'s `i`'th child to `child`. + +proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) = + ## set `n`'s `i`'th child to `child`. + n[n.len - i.int] = child + proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable, noSideEffect, locks: 0.} ## Adds the `child` to the `father` node. Returns the @@ -1104,7 +1111,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = else: result = false -proc hasArgOfName* (params: NimNode; name: string): bool {.compiletime.}= +proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}= ## Search nnkFormalParams for an argument. assert params.kind == nnkFormalParams for i in 1 ..< params.len: diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 6e7d7993f..981190211 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -61,14 +61,14 @@ proc generateExceptionCheck(futSym, else: var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[] let errorNode = newDotExpr(futSym, newIdentNode("error")) - for i in 1 .. <tryStmt.len: + for i in 1 ..< tryStmt.len: let exceptBranch = tryStmt[i] if exceptBranch[0].kind == nnkStmtList: exceptionChecks.add((newIdentNode("true"), exceptBranch[0])) else: var exceptIdentCount = 0 var ifCond: NimNode - for i in 0 .. <exceptBranch.len: + for i in 0 ..< exceptBranch.len: let child = exceptBranch[i] if child.kind == nnkIdent: let cond = infix(errorNode, "of", child) @@ -270,7 +270,7 @@ proc processBody(node, retFutureSym: NimNode, return else: discard - for i in 0 .. <result.len: + for i in 0 ..< result.len: result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, futureVarIdents, nil) @@ -287,7 +287,7 @@ proc getName(node: NimNode): string {.compileTime.} = proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} = result = @[] - for i in 1 .. <len(params): + for i in 1 ..< len(params): expectKind(params[i], nnkIdentDefs) if params[i][1].kind == nnkBracketExpr and ($params[i][1][0].ident).normalize == "futurevar": @@ -466,7 +466,7 @@ proc stripAwait(node: NimNode): NimNode = node[0][0] = emptyNoopSym else: discard - for i in 0 .. <result.len: + for i in 0 ..< result.len: result[i] = stripAwait(result[i]) proc splitParamType(paramType: NimNode, async: bool): NimNode = @@ -512,7 +512,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) = # Retrieve the `T` inside `Future[T]`. let returnType = stripReturnType(result[0][3][0]) result[0][3][0] = splitParamType(returnType, async=false) - for i in 1 .. <result[0][3].len: + for i in 1 ..< result[0][3].len: # Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false) @@ -521,7 +521,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) = result[1] = prc.copyNimTree() if result[1][3][0].kind == nnkBracketExpr: result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true) - for i in 1 .. <result[1][3].len: + for i in 1 ..< result[1][3].len: # Async proc (1) -> FormalParams (3) -> IdentDefs, the parameter (i) -> # parameter type (1). result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 27fbfad45..2c406faa1 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -93,7 +93,7 @@ proc random*(max: float): float {.benign.} = let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max -proc random*[T](x: Slice[T]): T = +proc random*[T](x: Slice[T, T]): T = ## For a slice `a .. b` returns a value in the range `a .. b-1`. result = T(random(x.b - x.a)) + x.a diff --git a/lib/system.nim b/lib/system.nim index 3b7a93eca..3ea2d8ef8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -308,13 +308,14 @@ when defined(nimArrIdx): shallowCopy(x, y) type - Slice*[T] = object ## builtin slice type - a*, b*: T ## the bounds + Slice*[T, U] = object ## builtin slice type + a*: T ## the lower bound (inclusive) + b*: U ## the upper bound (inclusive) when defined(nimalias): {.deprecated: [TSlice: Slice].} -proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = +proc `..`*[T, U](a: T, b: U): Slice[T, U] {.noSideEffect, inline, magic: "DotDot".} = ## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a` ## and `b` are inclusive. Slices can also be used in the set constructor ## and in ordinal case statements, but then they are special-cased by the @@ -322,7 +323,7 @@ proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = result.a = a result.b = b -proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} = +proc `..`*[T](b: T): Slice[T, T] {.noSideEffect, inline, magic: "DotDot".} = ## `slice`:idx: operator that constructs an interval ``[default(T), b]`` result.b = b @@ -1168,7 +1169,7 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} ## is achieved by reversing the parameters for ``contains``; ``in`` then ## passes its arguments in reverse order. -proc contains*[T](s: Slice[T], value: T): bool {.noSideEffect, inline.} = +proc contains*[T](s: Slice[T, T], value: T): bool {.noSideEffect, inline.} = ## Checks if `value` is within the range of `s`; returns true iff ## `value >= s.a and value <= s.b` ## @@ -2087,7 +2088,7 @@ proc clamp*[T](x, a, b: T): T = if x > b: return b return x -proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} = +proc len*[T: Ordinal](x: Slice[T, T]): int {.noSideEffect, inline.} = ## length of ordinal slice, when x.b < x.a returns zero length ## ## .. code-block:: Nim @@ -2155,7 +2156,7 @@ iterator items*(E: typedesc[enum]): E = for v in low(E)..high(E): yield v -iterator items*[T](s: Slice[T]): T = +iterator items*[T](s: Slice[T, T]): T = ## iterates over the slice `s`, yielding each value between `s.a` and `s.b` ## (inclusively). for x in s.a..s.b: @@ -3414,14 +3415,13 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## integer division that results in a float. result = toFloat(x) / toFloat(y) -proc `^`*[T](x: int; y: openArray[T]): int {.noSideEffect, magic: "Roof".} -proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} = +type + BackwardsIndex* = distinct int ## type that is constructed by ``^`` for + ## reversed array accesses. + +template `^`*(x: int): BackwardsIndex = BackwardsIndex(x) ## builtin `roof`:idx: operator that can be used for convenient array access. - ## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a`` - ## expression must not have side effects for this to compile. Note that since - ## this is a builtin, it automatically works for all kinds of - ## overloaded ``[]`` or ``[]=`` accessors. - discard + ## ``a[^x]`` is a shortcut for ``a[a.len-x]``. template `..^`*(a, b: untyped): untyped = ## a shortcut for '.. ^' to avoid the common gotcha that a space between @@ -3453,17 +3453,20 @@ template spliceImpl(s, a, L, b: untyped): untyped = # fill the hole: for i in 0 ..< b.len: s[a+i] = b[i] +template `^^`(s, i: untyped): untyped = + (when i is BackwardsIndex: s.len - int(i) else: int(i)) + when hasAlloc or defined(nimscript): - proc `[]`*(s: string, x: Slice[int]): string {.inline.} = + proc `[]`*[T, U](s: string, x: Slice[T, U]): string {.inline.} = ## slice operation for strings. ## returns the inclusive range [s[x.a], s[x.b]]: ## ## .. code-block:: nim ## var s = "abcdef" ## assert s[1..3] == "bcd" - result = s.substr(x.a, x.b) + result = s.substr(s ^^ x.a, s ^^ x.b) - proc `[]=`*(s: var string, x: Slice[int], b: string) = + proc `[]=`*[T, U](s: var string, x: Slice[T, U], b: string) = ## slice assignment for strings. If ## ``b.len`` is not exactly the number of elements that are referred to ## by `x`, a `splice`:idx: is performed: @@ -3472,75 +3475,69 @@ when hasAlloc or defined(nimscript): ## var s = "abcdef" ## s[1 .. ^2] = "xyz" ## assert s == "axyzf" - var a = x.a - var L = x.b - a + 1 + var a = s ^^ x.a + var L = (s ^^ x.b) - a + 1 if L == b.len: for i in 0..<L: s[i+a] = b[i] else: spliceImpl(s, a, L, b) -proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] = +proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: Slice[U, V]): seq[T] = ## slice operation for arrays. ## returns the inclusive range [a[x.a], a[x.b]]: ## ## .. code-block:: nim ## var a = [1,2,3,4] ## assert a[0..2] == @[1,2,3] - when low(a) < 0: - {.error: "Slicing for arrays with negative indices is unsupported.".} - var L = x.b - x.a + 1 + let xa = a ^^ x.a + let L = (a ^^ x.b) - xa + 1 result = newSeq[T](L) - for i in 0..<L: result[i] = a[i + x.a] + for i in 0..<L: result[i] = a[Idx(i + xa + int low(a))] -proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) = +proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: Slice[U, V], b: openArray[T]) = ## slice assignment for arrays. - when low(a) < 0: - {.error: "Slicing for arrays with negative indices is unsupported.".} - var L = x.b - x.a + 1 + let xa = a ^^ x.a + let L = (a ^^ x.b) - xa + 1 if L == b.len: - for i in 0..<L: a[i+x.a] = b[i] + for i in 0..<L: a[Idx(i + xa + int low(a))] = b[i] else: sysFatal(RangeError, "different lengths for slice assignment") -proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] = - ## slice operation for arrays. - var L = ord(x.b) - ord(x.a) + 1 - newSeq(result, L) - for i in 0..<L: - result[i] = a[Idx(ord(x.a) + i)] - -proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) = - ## slice assignment for arrays. - var L = ord(x.b) - ord(x.a) + 1 - if L == b.len: - for i in 0..<L: - a[Idx(ord(x.a) + i)] = b[i] - else: - sysFatal(RangeError, "different lengths for slice assignment") - -proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = +proc `[]`*[T, U, V](s: seq[T], x: Slice[U, V]): seq[T] = ## slice operation for sequences. ## returns the inclusive range [s[x.a], s[x.b]]: ## ## .. code-block:: nim ## var s = @[1,2,3,4] ## assert s[0..2] == @[1,2,3] - var a = x.a - var L = x.b - a + 1 + let a = s ^^ x.a + let L = (s ^^ x.b) - a + 1 newSeq(result, L) for i in 0 ..< L: result[i] = s[i + a] -proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) = +proc `[]=`*[T, U, V](s: var seq[T], x: Slice[U, V], b: openArray[T]) = ## slice assignment for sequences. If ## ``b.len`` is not exactly the number of elements that are referred to ## by `x`, a `splice`:idx: is performed. - var a = x.a - var L = x.b - a + 1 + let a = s ^^ x.a + let L = (s ^^ x.b) - a + 1 if L == b.len: for i in 0 ..< L: s[i+a] = b[i] else: spliceImpl(s, a, L, b) +proc `[]`*[T](s: seq[T]; i: BackwardsIndex): T = s[s.len - int(i)] +proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T = + a[Idx(a.len - int(i) + int low(a))] +proc `[]`*(s: string; i: BackwardsIndex): char = s[s.len - int(i)] + +proc `[]`*[T](s: var seq[T]; i: BackwardsIndex; x: T) = + s[s.len - int(i)] = x +proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) = + a[Idx(a.len - int(i) + int low(a))] = x +proc `[]`*(s: var string; i: BackwardsIndex; x: char) = + s[s.len - int(i)] = x + proc slurp*(filename: string): string {.magic: "Slurp".} ## This is an alias for `staticRead <#staticRead>`_. diff --git a/todo.txt b/todo.txt index 7c2a9088d..c9f64bd8b 100644 --- a/todo.txt +++ b/todo.txt @@ -3,7 +3,6 @@ version 1.0 battle plan - make FlowVar compatible to Futures - remove 'mod x' type rule -- implement x[^1] differently, no compiler magic - fix "high priority" bugs - try to fix as many compiler crashes as reasonable |