implemented a[^1] notation

This commit is contained in:
Araq 2015-03-26 02:12:26 +01:00
commit 5d63ecb3a4
13 changed files with 209 additions and 50 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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"{}=")

View file

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

View file

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

View file

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

View file

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