implemented a[^1] notation
This commit is contained in:
parent
36b6bfaf78
commit
5d63ecb3a4
13 changed files with 209 additions and 50 deletions
|
|
@ -529,7 +529,7 @@ type
|
||||||
TMagic* = enum # symbols that require compiler magic:
|
TMagic* = enum # symbols that require compiler magic:
|
||||||
mNone,
|
mNone,
|
||||||
mDefined, mDefinedInScope, mCompiles,
|
mDefined, mDefinedInScope, mCompiles,
|
||||||
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf,
|
mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof,
|
||||||
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
mEcho, mShallowCopy, mSlurp, mStaticExec,
|
||||||
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
|
||||||
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ proc genMergeInfo*(m: BModule): PRope =
|
||||||
s.add("*/")
|
s.add("*/")
|
||||||
result = s.toRope
|
result = s.toRope
|
||||||
|
|
||||||
template `^`(pos: expr): expr = L.buf[pos]
|
template `^`(pos: int): expr = L.buf[pos]
|
||||||
|
|
||||||
proc skipWhite(L: var TBaseLexer) =
|
proc skipWhite(L: var TBaseLexer) =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
|
|
|
||||||
|
|
@ -131,11 +131,10 @@ proc semNodeKindConstraints*(p: PNode): PNode =
|
||||||
result.strVal.add(ppEof)
|
result.strVal.add(ppEof)
|
||||||
|
|
||||||
type
|
type
|
||||||
TSideEffectAnalysis = enum
|
TSideEffectAnalysis* = enum
|
||||||
seUnknown, seSideEffect, seNoSideEffect
|
seUnknown, seSideEffect, seNoSideEffect
|
||||||
|
|
||||||
proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
|
||||||
# XXX is 'raise' a side effect?
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
# only calls can produce side effects:
|
# only calls can produce side effects:
|
||||||
|
|
@ -162,6 +161,8 @@ proc checkForSideEffects(n: PNode): TSideEffectAnalysis =
|
||||||
# an atom cannot produce a side effect:
|
# an atom cannot produce a side effect:
|
||||||
result = seNoSideEffect
|
result = seNoSideEffect
|
||||||
else:
|
else:
|
||||||
|
# assume no side effect:
|
||||||
|
result = seNoSideEffect
|
||||||
for i in 0 .. <n.len:
|
for i in 0 .. <n.len:
|
||||||
let ret = checkForSideEffects(n.sons[i])
|
let ret = checkForSideEffects(n.sons[i])
|
||||||
if ret == seSideEffect: return ret
|
if ret == seSideEffect: return ret
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type
|
||||||
|
|
||||||
POptionEntry* = ref TOptionEntry
|
POptionEntry* = ref TOptionEntry
|
||||||
PProcCon* = ref TProcCon
|
PProcCon* = ref TProcCon
|
||||||
TProcCon*{.final.} = object # procedure context; also used for top-level
|
TProcCon* = object # procedure context; also used for top-level
|
||||||
# statements
|
# statements
|
||||||
owner*: PSym # the symbol this context belongs to
|
owner*: PSym # the symbol this context belongs to
|
||||||
resultSym*: PSym # the result symbol (if we are in a proc)
|
resultSym*: PSym # the result symbol (if we are in a proc)
|
||||||
|
|
@ -36,6 +36,7 @@ type
|
||||||
# in standalone ``except`` and ``finally``
|
# in standalone ``except`` and ``finally``
|
||||||
next*: PProcCon # used for stacking procedure contexts
|
next*: PProcCon # used for stacking procedure contexts
|
||||||
wasForwarded*: bool # whether the current proc has a separate header
|
wasForwarded*: bool # whether the current proc has a separate header
|
||||||
|
bracketExpr*: PNode # current bracket expression (for ^ support)
|
||||||
|
|
||||||
TInstantiationPair* = object
|
TInstantiationPair* = object
|
||||||
genericSym*: PSym
|
genericSym*: PSym
|
||||||
|
|
|
||||||
|
|
@ -1133,19 +1133,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
## returns nil if not a built-in subscript operator; also called for the
|
## returns nil if not a built-in subscript operator; also called for the
|
||||||
## checking of assignments
|
## checking of assignments
|
||||||
if sonsLen(n) == 1:
|
if sonsLen(n) == 1:
|
||||||
var x = semDeref(c, n)
|
let x = semDeref(c, n)
|
||||||
if x == nil: return nil
|
if x == nil: return nil
|
||||||
result = newNodeIT(nkDerefExpr, x.info, x.typ)
|
result = newNodeIT(nkDerefExpr, x.info, x.typ)
|
||||||
result.add(x[0])
|
result.add(x[0])
|
||||||
return
|
return
|
||||||
checkMinSonsLen(n, 2)
|
checkMinSonsLen(n, 2)
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||||
var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||||
case arr.kind
|
case arr.kind
|
||||||
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
|
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
|
||||||
tyCString:
|
tyCString:
|
||||||
if n.len != 2: return nil
|
if n.len != 2: return nil
|
||||||
n.sons[0] = makeDeref(n.sons[0])
|
n.sons[0] = makeDeref(n.sons[0])
|
||||||
|
c.p.bracketExpr = n.sons[0]
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
n.sons[i] = semExprWithType(c, n.sons[i],
|
n.sons[i] = semExprWithType(c, n.sons[i],
|
||||||
flags*{efInTypeof, efDetermineType})
|
flags*{efInTypeof, efDetermineType})
|
||||||
|
|
@ -1166,6 +1167,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
n.sons[0] = makeDeref(n.sons[0])
|
n.sons[0] = makeDeref(n.sons[0])
|
||||||
|
c.p.bracketExpr = n.sons[0]
|
||||||
# [] operator for tuples requires constant expression:
|
# [] operator for tuples requires constant expression:
|
||||||
n.sons[1] = semConstExpr(c, n.sons[1])
|
n.sons[1] = semConstExpr(c, n.sons[1])
|
||||||
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
|
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
|
||||||
|
|
@ -1176,13 +1178,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.info, errIndexTypesDoNotMatch)
|
localError(n.info, errIndexTypesDoNotMatch)
|
||||||
result = n
|
result = n
|
||||||
else: discard
|
else:
|
||||||
|
c.p.bracketExpr = n.sons[0]
|
||||||
|
|
||||||
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
let oldBracketExpr = c.p.bracketExpr
|
||||||
result = semSubscript(c, n, flags)
|
result = semSubscript(c, n, flags)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
# overloaded [] operator:
|
# overloaded [] operator:
|
||||||
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
|
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
|
||||||
|
c.p.bracketExpr = oldBracketExpr
|
||||||
|
|
||||||
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||||
var id = considerQuotedIdent(a[1])
|
var id = considerQuotedIdent(a[1])
|
||||||
|
|
@ -1249,11 +1254,15 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
# a[i] = x
|
# a[i] = x
|
||||||
# --> `[]=`(a, i, x)
|
# --> `[]=`(a, i, x)
|
||||||
|
let oldBracketExpr = c.p.bracketExpr
|
||||||
a = semSubscript(c, a, {efLValue})
|
a = semSubscript(c, a, {efLValue})
|
||||||
if a == nil:
|
if a == nil:
|
||||||
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
|
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
|
||||||
add(result, n[1])
|
add(result, n[1])
|
||||||
return semExprNoType(c, result)
|
result = semExprNoType(c, result)
|
||||||
|
c.p.bracketExpr = oldBracketExpr
|
||||||
|
return result
|
||||||
|
c.p.bracketExpr = oldBracketExpr
|
||||||
of nkCurlyExpr:
|
of nkCurlyExpr:
|
||||||
# a{i} = x --> `{}=`(a, i, x)
|
# a{i} = x --> `{}=`(a, i, x)
|
||||||
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
|
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,11 @@ proc semLocals(c: PContext, n: PNode): PNode =
|
||||||
result.add(a)
|
result.add(a)
|
||||||
|
|
||||||
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
|
|
||||||
|
proc isStrangeArray(t: PType): bool =
|
||||||
|
let t = t.skipTypes(abstractInst)
|
||||||
|
result = t.kind == tyArray and t.firstOrd != 0
|
||||||
|
|
||||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
case n[0].sym.magic
|
case n[0].sym.magic
|
||||||
|
|
@ -153,4 +158,29 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
of mProcCall:
|
of mProcCall:
|
||||||
result = n
|
result = n
|
||||||
result.typ = n[1].typ
|
result.typ = n[1].typ
|
||||||
|
of mRoof:
|
||||||
|
# error correction:
|
||||||
|
result = n.sons[1]
|
||||||
|
if c.p.bracketExpr.isNil:
|
||||||
|
localError(n.info, "no surrounding array access context for '^'")
|
||||||
|
elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
|
||||||
|
localError(n.info, "invalid context for '^' as '$#' has side effects" %
|
||||||
|
renderTree(c.p.bracketExpr))
|
||||||
|
elif c.p.bracketExpr.typ.isStrangeArray:
|
||||||
|
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
|
||||||
|
renderTree(c.p.bracketExpr))
|
||||||
|
else:
|
||||||
|
# ^x is rewritten to: len(a)-x
|
||||||
|
let lenExpr = newNodeI(nkCall, n.info)
|
||||||
|
lenExpr.add newIdentNode(getIdent"len", n.info)
|
||||||
|
lenExpr.add c.p.bracketExpr
|
||||||
|
let lenExprB = semExprWithType(c, lenExpr)
|
||||||
|
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
|
||||||
|
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
|
||||||
|
renderTree(lenExpr))
|
||||||
|
else:
|
||||||
|
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
|
||||||
|
result.add newSymNode(createMagic("-", mSubI), n.info)
|
||||||
|
result.add lenExprB
|
||||||
|
result.add n.sons[1]
|
||||||
else: result = n
|
else: result = n
|
||||||
|
|
|
||||||
|
|
@ -590,7 +590,7 @@ proc firstOrd(t: PType): BiggestInt =
|
||||||
of tyUInt..tyUInt64: result = 0
|
of tyUInt..tyUInt64: result = 0
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
# if basetype <> nil then return firstOrd of basetype
|
# if basetype <> nil then return firstOrd of basetype
|
||||||
if (sonsLen(t) > 0) and (t.sons[0] != nil):
|
if sonsLen(t) > 0 and t.sons[0] != nil:
|
||||||
result = firstOrd(t.sons[0])
|
result = firstOrd(t.sons[0])
|
||||||
else:
|
else:
|
||||||
assert(t.n.sons[0].kind == nkSym)
|
assert(t.n.sons[0].kind == nkSym)
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ type
|
||||||
set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
|
set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
|
||||||
|
|
||||||
type
|
type
|
||||||
Slice* {.final, pure.}[T] = object ## builtin slice type
|
Slice*[T] = object ## builtin slice type
|
||||||
a*, b*: T ## the bounds
|
a*, b*: T ## the bounds
|
||||||
|
|
||||||
when defined(nimalias):
|
when defined(nimalias):
|
||||||
|
|
@ -3247,4 +3247,12 @@ proc procCall*(x: expr) {.magic: "ProcCall".} =
|
||||||
## procCall someMethod(a, b)
|
## procCall someMethod(a, b)
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} =
|
||||||
|
## 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
|
||||||
|
|
||||||
{.pop.} #{.push warning[GcMem]: off.}
|
{.pop.} #{.push warning[GcMem]: off.}
|
||||||
|
|
|
||||||
36
tests/array/troof1.nim
Normal file
36
tests/array/troof1.nim
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
discard """
|
||||||
|
output: '''@[2, 3, 4]321
|
||||||
|
9.0 4.0
|
||||||
|
(a: 1.0, b: 2.0, c: 8.0)2.0'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc foo[T](x, y: T): T = x
|
||||||
|
|
||||||
|
var a = @[1, 2, 3, 4]
|
||||||
|
var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
|
||||||
|
echo a[1.. ^1], a[^2], a[^3], a[^4]
|
||||||
|
echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
|
||||||
|
|
||||||
|
type
|
||||||
|
MyArray = object
|
||||||
|
a, b, c: float
|
||||||
|
|
||||||
|
var
|
||||||
|
ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
|
||||||
|
|
||||||
|
proc len(x: MyArray): int = 3
|
||||||
|
|
||||||
|
proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
|
||||||
|
case idx
|
||||||
|
of 0: x.a = val
|
||||||
|
of 1: x.b = val
|
||||||
|
of 2: x.c = val
|
||||||
|
|
||||||
|
proc `[]`(x: var MyArray; idx: range[0..2]): float =
|
||||||
|
case idx
|
||||||
|
of 0: result = x.a
|
||||||
|
of 1: result = x.b
|
||||||
|
of 2: result = x.c
|
||||||
|
|
||||||
|
ma[^1] = 8.0
|
||||||
|
echo ma, ma[^2]
|
||||||
10
tests/array/troof2.nim
Normal file
10
tests/array/troof2.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "invalid context for '^' as 'foo()' has side effects"
|
||||||
|
line: "9"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc foo(): seq[int] =
|
||||||
|
echo "ha"
|
||||||
|
|
||||||
|
let f = foo()[^1]
|
||||||
|
|
||||||
8
tests/array/troof3.nim
Normal file
8
tests/array/troof3.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "invalid context for '^' as len!=high+1 for 'a'"
|
||||||
|
line: "8"
|
||||||
|
"""
|
||||||
|
|
||||||
|
var a: array[1..3, string]
|
||||||
|
|
||||||
|
echo a[^1]
|
||||||
37
tests/array/troof4.nim
Normal file
37
tests/array/troof4.nim
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "no surrounding array access context for '^'"
|
||||||
|
line: "37"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc foo[T](x, y: T): T = x
|
||||||
|
|
||||||
|
var a = @[1, 2, 3, 4]
|
||||||
|
var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
|
||||||
|
echo a[1.. ^1], a[^2], a[^3], a[^4]
|
||||||
|
echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
|
||||||
|
|
||||||
|
type
|
||||||
|
MyArray = object
|
||||||
|
a, b, c: float
|
||||||
|
|
||||||
|
var
|
||||||
|
ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
|
||||||
|
|
||||||
|
proc len(x: MyArray): int = 3
|
||||||
|
|
||||||
|
proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
|
||||||
|
case idx
|
||||||
|
of 0: x.a = val
|
||||||
|
of 1: x.b = val
|
||||||
|
of 2: x.c = val
|
||||||
|
|
||||||
|
proc `[]`(x: var MyArray; idx: range[0..2]): float =
|
||||||
|
case idx
|
||||||
|
of 0: result = x.a
|
||||||
|
of 1: result = x.b
|
||||||
|
of 2: result = x.c
|
||||||
|
|
||||||
|
ma[^1] = 8.0
|
||||||
|
echo ma, ma[^2]
|
||||||
|
|
||||||
|
echo(^1)
|
||||||
19
web/news.txt
19
web/news.txt
|
|
@ -42,6 +42,22 @@ News
|
||||||
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
|
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
|
||||||
produced instead of ``nkAddr('x')``.
|
produced instead of ``nkAddr('x')``.
|
||||||
- ``concept`` is now a keyword and is used instead of ``generic``.
|
- ``concept`` is now a keyword and is used instead of ``generic``.
|
||||||
|
- The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produces OverflowError
|
||||||
|
exceptions. This means code like the following:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
var x = low(T)
|
||||||
|
while x <= high(T):
|
||||||
|
echo x
|
||||||
|
inc x
|
||||||
|
|
||||||
|
Needs to be replaced by something like this:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
var x = low(T).int
|
||||||
|
while x <= high(T).int:
|
||||||
|
echo x.T
|
||||||
|
inc x
|
||||||
|
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
|
|
@ -84,6 +100,9 @@ News
|
||||||
varOrConst(x) # "var"
|
varOrConst(x) # "var"
|
||||||
varOrConst(45) # "const"
|
varOrConst(45) # "const"
|
||||||
|
|
||||||
|
- Array and seq indexing can now use the builtin ``^`` operator to access
|
||||||
|
things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
|
||||||
|
|
||||||
|
|
||||||
Library additions
|
Library additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue