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

@ -94,7 +94,7 @@ proc writeIntSet(a: IntSet, s: var string) =
encodeVInt(x, s) encodeVInt(x, s)
inc i inc i
s.add('}') s.add('}')
proc genMergeInfo*(m: BModule): PRope = proc genMergeInfo*(m: BModule): PRope =
if optSymbolFiles notin gGlobalOptions: return nil if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:" var s = "/*\tNIM_merge_INFO:"
@ -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
@ -132,7 +132,7 @@ proc skipUntilCmd(L: var TBaseLexer) =
of CR: pos = nimlexbase.handleCR(L, pos) of CR: pos = nimlexbase.handleCR(L, pos)
of LF: pos = nimlexbase.handleLF(L, pos) of LF: pos = nimlexbase.handleLF(L, pos)
of '\0': break of '\0': break
of '/': of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t': if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3 inc pos, 3
break break
@ -145,7 +145,7 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len result = s == NimMergeEndMark.len
proc readVerbatimSection(L: var TBaseLexer): PRope = proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
var r = newStringOfCap(30_000) var r = newStringOfCap(30_000)
@ -162,7 +162,7 @@ proc readVerbatimSection(L: var TBaseLexer): PRope =
of '\0': of '\0':
internalError("ccgmerge: expected: " & NimMergeEndMark) internalError("ccgmerge: expected: " & NimMergeEndMark)
break break
else: else:
if atEndMark(buf, pos): if atEndMark(buf, pos):
inc pos, NimMergeEndMark.len inc pos, NimMergeEndMark.len
break break
@ -181,7 +181,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
if buf[pos] != ':': internalError("ccgmerge: ':' expected") if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':' L.bufpos = pos + 1 # skip ':'
proc newFakeType(id: int): PType = proc newFakeType(id: int): PType =
new(result) new(result)
result.id = id result.id = id
@ -227,8 +227,8 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template withCFile(cfilename: string, body: stmt) {.immediate.} = template withCFile(cfilename: string, body: stmt) {.immediate.} =
var s = llStreamOpen(cfilename, fmRead) var s = llStreamOpen(cfilename, fmRead)
if s == nil: return if s == nil: return
var L {.inject.}: TBaseLexer var L {.inject.}: TBaseLexer
@ -239,7 +239,7 @@ template withCFile(cfilename: string, body: stmt) {.immediate.} =
if ^L.bufpos == '\0': break if ^L.bufpos == '\0': break
body body
closeBaseLexer(L) closeBaseLexer(L)
proc readMergeInfo*(cfilename: string, m: BModule) = proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge meta information into `m`. ## reads the merge meta information into `m`.
withCFile(cfilename): withCFile(cfilename):
@ -257,7 +257,7 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
## reads the merge sections into `m`. ## reads the merge sections into `m`.
withCFile(cfilename): withCFile(cfilename):
readKey(L, k) readKey(L, k)
if k == "NIM_merge_INFO": if k == "NIM_merge_INFO":
discard discard
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/': elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2) inc(L.bufpos, 2)
@ -283,7 +283,7 @@ proc mergeRequired*(m: BModule): bool =
#echo "not empty: ", i, " ", ropeToStr(m.s[i]) #echo "not empty: ", i, " ", ropeToStr(m.s[i])
return true return true
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):
if m.initProc.s(i) != nil: if m.initProc.s(i) != nil:
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i]) #echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
return true return true
@ -291,7 +291,7 @@ proc mergeFiles*(cfilename: string, m: BModule) =
## merges the C file with the old version on hard disc. ## merges the C file with the old version on hard disc.
var old: TMergeSections var old: TMergeSections
readMergeSections(cfilename, old) readMergeSections(cfilename, old)
# do the merge; old section before new section: # do the merge; old section before new section:
for i in low(TCFileSection)..high(TCFileSection): for i in low(TCFileSection)..high(TCFileSection):
m.s[i] = con(old.f[i], m.s[i]) m.s[i] = con(old.f[i], m.s[i])
for i in low(TCProcSection)..high(TCProcSection): for i in low(TCProcSection)..high(TCProcSection):

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

@ -9,13 +9,13 @@
## This module contains the data structures for the semantic checking phase. ## This module contains the data structures for the semantic checking phase.
import import
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab, strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
wordrecg, wordrecg,
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef
type type
TOptionEntry* = object of lists.TListEntry # entries to put on a TOptionEntry* = object of lists.TListEntry # entries to put on a
# stack for pragma parsing # stack for pragma parsing
options*: TOptions options*: TOptions
@ -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,12 +36,13 @@ 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
inst*: PInstantiation inst*: PInstantiation
TExprFlag* = enum TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck efAllowDestructor, efWantValue, efOperand, efNoSemCheck
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
@ -57,7 +58,7 @@ type
# this is used so that generic instantiations # this is used so that generic instantiations
# can access private object fields # can access private object fields
instCounter*: int # to prevent endless instantiations instCounter*: int # to prevent endless instantiations
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!) # store this info in the syms themselves!)
inTypeClass*: int # > 0 if we are in a user-defined type class inTypeClass*: int # > 0 if we are in a user-defined type class
@ -95,7 +96,7 @@ type
instDeepCopy*: proc (c: PContext; dc: PSym; t: PType; instDeepCopy*: proc (c: PContext; dc: PSym; t: PType;
info: TLineInfo): PSym {.nimcall.} info: TLineInfo): PSym {.nimcall.}
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s result.genericSym = s
result.inst = inst result.inst = inst
@ -127,7 +128,7 @@ proc popOwner*()
var gOwners*: seq[PSym] = @[] var gOwners*: seq[PSym] = @[]
proc getCurrOwner(): PSym = proc getCurrOwner(): PSym =
# owner stack (used for initializing the # owner stack (used for initializing the
# owner field of syms) # owner field of syms)
# the documentation comment always gets # the documentation comment always gets
@ -135,19 +136,19 @@ proc getCurrOwner(): PSym =
# BUGFIX: global array is needed! # BUGFIX: global array is needed!
result = gOwners[high(gOwners)] result = gOwners[high(gOwners)]
proc pushOwner(owner: PSym) = proc pushOwner(owner: PSym) =
add(gOwners, owner) add(gOwners, owner)
proc popOwner() = proc popOwner() =
var length = len(gOwners) var length = len(gOwners)
if length > 0: setLen(gOwners, length - 1) if length > 0: setLen(gOwners, length - 1)
else: internalError("popOwner") else: internalError("popOwner")
proc lastOptionEntry(c: PContext): POptionEntry = proc lastOptionEntry(c: PContext): POptionEntry =
result = POptionEntry(c.optionStack.tail) result = POptionEntry(c.optionStack.tail)
proc pushProcCon*(c: PContext, owner: PSym) {.inline.} = proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
if owner == nil: if owner == nil:
internalError("owner is nil") internalError("owner is nil")
return return
var x: PProcCon var x: PProcCon
@ -158,7 +159,7 @@ proc pushProcCon*(c: PContext, owner: PSym) {.inline.} =
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc newOptionEntry(): POptionEntry = proc newOptionEntry(): POptionEntry =
new(result) new(result)
result.options = gOptions result.options = gOptions
result.defaultCC = ccDefault result.defaultCC = ccDefault
@ -182,8 +183,8 @@ proc newContext(module: PSym): PContext =
proc inclSym(sq: var TSymSeq, s: PSym) = proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq) var L = len(sq)
for i in countup(0, L - 1): for i in countup(0, L - 1):
if sq[i].id == s.id: return if sq[i].id == s.id: return
setLen(sq, L + 1) setLen(sq, L + 1)
sq[L] = s sq[L] = s
@ -193,20 +194,20 @@ proc addConverter*(c: PContext, conv: PSym) =
proc addPattern*(c: PContext, p: PSym) = proc addPattern*(c: PContext, p: PSym) =
inclSym(c.patterns, p) inclSym(c.patterns, p)
proc newLib(kind: TLibKind): PLib = proc newLib(kind: TLibKind): PLib =
new(result) new(result)
result.kind = kind #initObjectSet(result.syms) result.kind = kind #initObjectSet(result.syms)
proc addToLib(lib: PLib, sym: PSym) = proc addToLib(lib: PLib, sym: PSym) =
#if sym.annex != nil and not isGenericRoutine(sym): #if sym.annex != nil and not isGenericRoutine(sym):
# LocalError(sym.info, errInvalidPragma) # LocalError(sym.info, errInvalidPragma)
sym.annex = lib sym.annex = lib
proc makePtrType(c: PContext, baseType: PType): PType = proc makePtrType(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c) result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType(c: PContext, baseType: PType): PType = proc makeVarType(c: PContext, baseType: PType): PType =
result = newTypeS(tyVar, c) result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
@ -286,7 +287,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info) result = newNodeI(nkEmpty, n.info)
result.typ = errorType(c) result.typ = errorType(c)
proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) = proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind dest.kind = kind
dest.owner = getCurrOwner() dest.owner = getCurrOwner()
dest.size = - 1 dest.size = - 1
@ -311,13 +312,13 @@ proc illFormedAst*(n: PNode) =
proc illFormedAstLocal*(n: PNode) = proc illFormedAstLocal*(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) = proc checkSonsLen*(n: PNode, length: int) =
if sonsLen(n) != length: illFormedAst(n) if sonsLen(n) != length: illFormedAst(n)
proc checkMinSonsLen*(n: PNode, length: int) = proc checkMinSonsLen*(n: PNode, length: int) =
if sonsLen(n) < length: illFormedAst(n) if sonsLen(n) < length: illFormedAst(n)
proc isTopLevel*(c: PContext): bool {.inline.} = proc isTopLevel*(c: PContext): bool {.inline.} =
result = c.currentScope.depthLevel <= 2 result = c.currentScope.depthLevel <= 2
proc experimentalMode*(c: PContext): bool {.inline.} = proc experimentalMode*(c: PContext): bool {.inline.} =

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,8 +222,8 @@ 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):
{.deprecated: [TSlice: Slice].} {.deprecated: [TSlice: Slice].}
@ -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
----------------- -----------------