mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-25 12:25:08 +00:00
slice support in system.nim; syntactic sugar for tables; cleanup of grammar/parser
This commit is contained in:
@@ -173,49 +173,30 @@ proc parseSymbol(p: var TParser): PNode =
|
||||
of tkAccent:
|
||||
result = newNodeP(nkAccQuoted, p)
|
||||
getTok(p)
|
||||
case p.tok.tokType
|
||||
of tkBracketLe:
|
||||
var s = "["
|
||||
getTok(p)
|
||||
while true:
|
||||
if p.tok.tokType == tkComma:
|
||||
add(s, ",")
|
||||
getTok(p)
|
||||
elif (p.tok.tokType == tkOpr) and (p.tok.ident.s == "$"):
|
||||
add(s, "$..")
|
||||
getTok(p)
|
||||
eat(p, tkDotDot)
|
||||
if (p.tok.tokType == tkOpr) and (p.tok.ident.s == "$"):
|
||||
add(s, '$')
|
||||
getTok(p)
|
||||
elif p.tok.tokType == tkDotDot:
|
||||
add(s, "..")
|
||||
getTok(p)
|
||||
if (p.tok.tokType == tkOpr) and (p.tok.ident.s == "$"):
|
||||
add(s, '$')
|
||||
getTok(p)
|
||||
else: break
|
||||
eat(p, tkBracketRi)
|
||||
add(s, ']')
|
||||
if p.tok.tokType == tkEquals:
|
||||
add(s, '=')
|
||||
var id = ""
|
||||
while true:
|
||||
case p.tok.tokType
|
||||
of tkBracketLe:
|
||||
id.add("[]")
|
||||
getTok(p)
|
||||
addSon(result, newIdentNodeP(getIdent(s), p))
|
||||
of tkParLe:
|
||||
addSon(result, newIdentNodeP(getIdent("()"), p))
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
|
||||
var id = p.tok.ident
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkEquals:
|
||||
addSon(result, newIdentNodeP(getIdent(id.s & '='), p))
|
||||
eat(p, tkBracketRi)
|
||||
of tkEquals:
|
||||
id.add('=')
|
||||
getTok(p)
|
||||
else:
|
||||
addSon(result, newIdentNodeP(id, p))
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, tokToStr(p.tok))
|
||||
result = ast.emptyNode
|
||||
of tkParLe:
|
||||
id.add("()")
|
||||
getTok(p)
|
||||
eat(p, tkParRi)
|
||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
|
||||
id.add(p.tok.ident.s)
|
||||
getTok(p)
|
||||
of tkIntLit..tkCharLit:
|
||||
id.add(tokToStr(p.tok))
|
||||
getTok(p)
|
||||
else:
|
||||
if id.len == 0: parMessage(p, errIdentifierExpected, tokToStr(p.tok))
|
||||
break
|
||||
add(result, newIdentNodeP(getIdent(id), p))
|
||||
eat(p, tkAccent)
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, tokToStr(p.tok))
|
||||
@@ -247,15 +228,8 @@ proc accExpr(p: var TParser): PNode =
|
||||
eat(p, tkAccent)
|
||||
|
||||
proc indexExpr(p: var TParser): PNode =
|
||||
# indexExpr ::= expr ['=' expr]
|
||||
result = parseExpr(p)
|
||||
if p.tok.tokType == tkEquals:
|
||||
var a = result
|
||||
result = newNodeP(nkExprEqExpr, p)
|
||||
addSon(result, a)
|
||||
getTok(p)
|
||||
addSon(result, parseExpr(p))
|
||||
|
||||
|
||||
proc indexExprList(p: var TParser, first: PNode): PNode =
|
||||
result = newNodeP(nkBracketExpr, p)
|
||||
addSon(result, first)
|
||||
|
||||
@@ -709,41 +709,11 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
else:
|
||||
GlobalError(n.Info, errUndeclaredFieldX, i.s)
|
||||
|
||||
proc whichSliceOpr(n: PNode): string =
|
||||
if n.sons[0].kind == nkEmpty:
|
||||
if n.sons[1].kind == nkEmpty: result = "[..]"
|
||||
else: result = "[..$]"
|
||||
elif n.sons[1].kind == nkEmpty:
|
||||
result = "[$..]"
|
||||
else:
|
||||
result = "[$..$]"
|
||||
|
||||
proc addSliceOpr(result: var string, n: PNode) =
|
||||
if n[0].kind == nkEmpty:
|
||||
if n[1].kind == nkEmpty: result.add("..")
|
||||
else: result.add("..$")
|
||||
elif n[1].kind == nkEmpty: result.add("$..")
|
||||
else: result.add("$..$")
|
||||
|
||||
proc buildOverloadedSubscripts(n: PNode, inAsgn: bool): PNode =
|
||||
result = newNodeI(nkCall, n.info)
|
||||
add(result, ast.emptyNode) # fill with the correct node later
|
||||
add(result, n[0])
|
||||
var opr = "["
|
||||
for i in 1..n.len-1:
|
||||
if i > 1: add(opr, ",")
|
||||
if n[i].kind == nkRange:
|
||||
# we have a slice argument
|
||||
checkSonsLen(n[i], 2)
|
||||
addSliceOpr(opr, n[i])
|
||||
addSon(result, n[i][0])
|
||||
addSon(result, n[i][1])
|
||||
else:
|
||||
add(result, n[i])
|
||||
if inAsgn: add(opr, "]=")
|
||||
else: add(opr, "]")
|
||||
# now we know the operator
|
||||
result.sons[0] = newIdentNode(getIdent(opr), n.info)
|
||||
result.add(newIdentNode(
|
||||
if inAsgn: getIdent"[]=" else: getIdent"[]", n.info))
|
||||
for i in 0 .. n.len-1: result.add(n[i])
|
||||
|
||||
proc semDeref(c: PContext, n: PNode): PNode =
|
||||
checkSonsLen(n, 1)
|
||||
@@ -773,10 +743,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n.sons[i] = semExprWithType(c, n.sons[i], flags - {efAllowType})
|
||||
var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt)
|
||||
var arg = IndexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1])
|
||||
if arg != nil: n.sons[1] = arg
|
||||
else: GlobalError(n.info, errIndexTypesDoNotMatch)
|
||||
result = n
|
||||
result.typ = elemType(arr)
|
||||
if arg != nil:
|
||||
n.sons[1] = arg
|
||||
result = n
|
||||
result.typ = elemType(arr)
|
||||
#GlobalError(n.info, errIndexTypesDoNotMatch)
|
||||
of tyTuple:
|
||||
checkSonsLen(n, 2)
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
@@ -862,6 +833,21 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
|
||||
m = fitNode(c, typ, n.sons[i])
|
||||
addSon(result, m)
|
||||
|
||||
proc semTableConstr(c: PContext, n: PNode): PNode =
|
||||
# we simply transform ``{key: value, key2: value}`` to
|
||||
# ``[(key, value), (key2, value2)]``
|
||||
result = newNodeI(nkBracket, n.info)
|
||||
for i in 0..n.len-1:
|
||||
var x = n.sons[i]
|
||||
if x.kind == nkExprColonExpr and sonsLen(x) == 2:
|
||||
var pair = newNodeI(nkPar, x.info)
|
||||
pair.add(x[0])
|
||||
pair.add(x[1])
|
||||
result.add(pair)
|
||||
else:
|
||||
illFormedAst(x)
|
||||
result = semExpr(c, result)
|
||||
|
||||
type
|
||||
TParKind = enum
|
||||
paNone, paSingle, paTupleFields, paTuplePositions
|
||||
@@ -967,7 +953,8 @@ proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
result = semTemplateExpr(c, result, s, semCheck)
|
||||
else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
|
||||
else:
|
||||
GlobalError(n.info, errInvalidExpressionX, renderTree(a, {renderNoComments}))
|
||||
GlobalError(n.info, errInvalidExpressionX,
|
||||
renderTree(a, {renderNoComments}))
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result = n
|
||||
@@ -1090,7 +1077,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
checkSonsLen(n, 3)
|
||||
of nkCheckedFieldExpr:
|
||||
checkMinSonsLen(n, 2)
|
||||
of nkSymChoice:
|
||||
of nkTableConstr:
|
||||
result = semTableConstr(c, n)
|
||||
of nkSymChoice:
|
||||
GlobalError(n.info, errExprXAmbiguous, renderTree(n, {renderNoComments}))
|
||||
else:
|
||||
GlobalError(n.info, errInvalidExpressionX,
|
||||
|
||||
@@ -21,12 +21,11 @@ plusExpr ::= mulExpr (OP7 optInd mulExpr)*
|
||||
mulExpr ::= dollarExpr (OP8 optInd dollarExpr)*
|
||||
dollarExpr ::= primary (OP9 optInd primary)*
|
||||
|
||||
indexExpr ::= expr ['=' expr]
|
||||
indexExpr ::= expr
|
||||
|
||||
castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
|
||||
addrExpr ::= 'addr' '(' optInd expr optPar ')'
|
||||
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
|
||||
| '[' (',' | ['$'] '..' ['$'])* ']'
|
||||
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']'
|
||||
| '=' | literal)+ '`'
|
||||
| IDENT
|
||||
|
||||
|
||||
@@ -1815,6 +1815,34 @@ Example:
|
||||
An if expression always results in a value, so the ``else`` part is
|
||||
required. ``Elif`` parts are also allowed (but unlikely to be good
|
||||
style).
|
||||
|
||||
|
||||
Table constructor
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
A `table constructor`:idx: is syntactic sugar for an array constructor:
|
||||
|
||||
.. code-block:: nimrod
|
||||
{"key1": "value1", "key2": "value2"}
|
||||
|
||||
# is the same as:
|
||||
[("key1", "value1"), ("key2", "value2")]
|
||||
|
||||
|
||||
The empty table can be written ``{:}`` (in contrast to the empty set
|
||||
which is ``{}``) which is thus another way to write as the empty array
|
||||
constructor ``[]``. This slightly unusal way of supporting tables
|
||||
has lots of advantages:
|
||||
|
||||
* The order of the (key,value)-pairs is preserved, thus it is easy to
|
||||
support ordered dicts with for example ``{key: val}.newOrderedTable``.
|
||||
* A table literal can be put into a ``const`` section and the compiler
|
||||
can easily put it into the executable's data section just like it can
|
||||
for arrays and the generated data section requires a minimal amount
|
||||
of memory.
|
||||
* Every table implementation is treated equal syntactically.
|
||||
* Apart from the minimal syntactic sugar the language core does not need to
|
||||
know about tables.
|
||||
|
||||
|
||||
Type conversions
|
||||
|
||||
@@ -155,6 +155,16 @@ proc newStringTable*(keyValuePairs: openarray[string],
|
||||
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
||||
inc(i, 2)
|
||||
|
||||
proc newStringTable*(keyValuePairs: openarray[tuple[key, val: string]],
|
||||
mode: TStringTableMode = modeCaseSensitive): PStringTable {.
|
||||
rtl, extern: "nst$1WithTableConstr".} =
|
||||
## creates a new string table with given key value pairs.
|
||||
## Example::
|
||||
## var mytab = newStringTable({"key1": "val1", "key2": "val2"},
|
||||
## modeCaseInsensitive)
|
||||
result = newStringTable(mode)
|
||||
for key, val in items(keyvaluePairs): result[key] = val
|
||||
|
||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {.
|
||||
rtl, extern: "nstFormat".} =
|
||||
## The `%` operator for string tables.
|
||||
@@ -198,3 +208,9 @@ proc `$`*(t: PStringTable): string {.rtl, extern: "nstDollar".} =
|
||||
result.add(val)
|
||||
result.add("}")
|
||||
|
||||
when isMainModule:
|
||||
var x = {"k": "v", "11": "22", "565": "67"}.newStringTable
|
||||
assert x["k"] == "v"
|
||||
assert x["11"] == "22"
|
||||
assert x["565"] == "67"
|
||||
|
||||
|
||||
@@ -129,7 +129,8 @@ proc `..`*[T](a, b: T): TSlice[T] {.noSideEffect, inline.} =
|
||||
|
||||
proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
|
||||
## `slice`:idx: operator that constructs an interval ``[low(T), b]``
|
||||
result.a = low(b)
|
||||
when int(low(b)) == low(int): result.a = 0
|
||||
else: result.a = low(b)
|
||||
result.b = b
|
||||
|
||||
proc contains*[T](s: TSlice[T], value: T): bool {.noSideEffect, inline.} =
|
||||
@@ -1006,7 +1007,7 @@ proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
|
||||
|
||||
proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
|
||||
## The stingify operator for an enumeration argument. This works for
|
||||
## any enumeration type thanks to compiler magic. If a
|
||||
## any enumeration type thanks to compiler magic. If
|
||||
## a ``$`` operator for a concrete enumeration is provided, this is
|
||||
## used instead. (In other words: *Overwriting* is possible.)
|
||||
|
||||
@@ -1268,7 +1269,11 @@ proc `<`*[T: tuple](x, y: T): bool =
|
||||
|
||||
proc `$`*[T: tuple](x: T): string =
|
||||
## generic ``$`` operator for tuples that is lifted from the components
|
||||
## of `x`.
|
||||
## of `x`. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## $(23, 45) == "(23, 45)"
|
||||
## $() == "()"
|
||||
result = "("
|
||||
for name, value in fieldPairs(x):
|
||||
if result.len > 1: result.add(", ")
|
||||
@@ -1277,6 +1282,19 @@ proc `$`*[T: tuple](x: T): string =
|
||||
result.add($value)
|
||||
result.add(")")
|
||||
|
||||
when false:
|
||||
proc `$`*[T](a: openArray[T]): string =
|
||||
## generic ``$`` operator for open arrays that is lifted from the elements
|
||||
## of `a`. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## $[23, 45] == "[23, 45]"
|
||||
result = "["
|
||||
for x in items(a):
|
||||
if result.len > 1: result.add(", ")
|
||||
result.add($x)
|
||||
result.add("]")
|
||||
|
||||
# ----------------- GC interface ---------------------------------------------
|
||||
|
||||
proc GC_disable*() {.rtl, inl.}
|
||||
@@ -1751,3 +1769,74 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
||||
|
||||
{.pop.} # checks
|
||||
{.pop.} # hints
|
||||
|
||||
template `-|`(b, s: expr): expr =
|
||||
(if b >= 0: b else: s.len + b)
|
||||
|
||||
proc `[]`*(s: string, x: TSlice[int]): string {.inline.} =
|
||||
## slice operation for strings. Negative indexes are supported.
|
||||
result = s.copy(x.a-|s, x.b-|s)
|
||||
|
||||
proc `[]=`*(s: var string, x: TSlice[int], b: string) =
|
||||
## slice assignment for strings. Negative indexes are supported.
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
if L == b.len:
|
||||
for i in 0 .. <L: s[i+a] = b[i]
|
||||
else:
|
||||
raise newException(EOutOfRange, "differing lengths for slice assignment")
|
||||
|
||||
proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] =
|
||||
## slice operation for arrays. Negative indexes are NOT supported because
|
||||
## the array might have negative bounds.
|
||||
var L = x.b - x.a + 1
|
||||
newSeq(result, L)
|
||||
for i in 0.. <L: result[i] = a[i + x.a]
|
||||
|
||||
proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[int], b: openArray[T]) =
|
||||
## slice assignment for arrays. Negative indexes are NOT supported because
|
||||
## the array might have negative bounds.
|
||||
var L = x.b - x.a + 1
|
||||
if L == b.len:
|
||||
for i in 0 .. <L: a[i+x.a] = b[i]
|
||||
else:
|
||||
raise newException(EOutOfRange, "differing lengths for slice assignment")
|
||||
|
||||
proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[Idx]): seq[T] =
|
||||
## slice operation for arrays. Negative indexes are NOT supported because
|
||||
## the array might have negative bounds.
|
||||
var L = ord(x.b) - ord(x.a) + 1
|
||||
newSeq(result, L)
|
||||
var j = x.a
|
||||
for i in 0.. <L:
|
||||
result[i] = a[j]
|
||||
inc(j)
|
||||
|
||||
proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[Idx], b: openArray[T]) =
|
||||
## slice assignment for arrays. Negative indexes are NOT supported because
|
||||
## the array might have negative bounds.
|
||||
var L = ord(x.b) - ord(x.a) + 1
|
||||
if L == b.len:
|
||||
var j = x.a
|
||||
for i in 0 .. <L:
|
||||
a[j] = b[i]
|
||||
inc(j)
|
||||
else:
|
||||
raise newException(EOutOfRange, "differing lengths for slice assignment")
|
||||
|
||||
proc `[]`*[T](s: seq[T], x: TSlice[int]): seq[T] =
|
||||
## slice operation for sequences. Negative indexes are supported.
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
newSeq(result, L)
|
||||
for i in 0.. <L: result[i] = s[i + a]
|
||||
|
||||
proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) =
|
||||
## slice assignment for sequences. Negative indexes are supported.
|
||||
var a = x.a-|s
|
||||
var L = x.b-|s - a + 1
|
||||
if L == b.len:
|
||||
for i in 0 .. <L: s[i+a] = b[i]
|
||||
else:
|
||||
raise newException(EOutOfRange, "differing lengths for slice assignment")
|
||||
|
||||
|
||||
43
tests/accept/run/tslices.nim
Normal file
43
tests/accept/run/tslices.nim
Normal file
@@ -0,0 +1,43 @@
|
||||
discard """
|
||||
file: "tslices.nim"
|
||||
output: '''456456
|
||||
456456
|
||||
456456
|
||||
Zugr5nd
|
||||
'''
|
||||
"""
|
||||
|
||||
# Test the new slices.
|
||||
|
||||
import strutils
|
||||
|
||||
var mystr = "Abgrund"
|
||||
mystr[..1] = "Zu"
|
||||
|
||||
mystr[4..4] = "5"
|
||||
|
||||
type
|
||||
TEnum = enum e1, e2, e3, e4, e5, e6
|
||||
|
||||
var myarr: array[TEnum, int] = [1, 2, 3, 4, 5, 6]
|
||||
myarr[e1..e3] = myarr[e4..e6]
|
||||
myarr[..e3] = myarr[e4..e6]
|
||||
|
||||
for x in items(myarr): stdout.write(x)
|
||||
echo()
|
||||
|
||||
var myarr2: array[0..5, int] = [1, 2, 3, 4, 5, 6]
|
||||
myarr2[0..2] = myarr2[3..5]
|
||||
|
||||
for x in items(myarr2): stdout.write(x)
|
||||
echo()
|
||||
|
||||
|
||||
var myseq = @[1, 2, 3, 4, 5, 6]
|
||||
myseq[0..2] = myseq[-3.. -1]
|
||||
|
||||
for x in items(myseq): stdout.write(x)
|
||||
echo()
|
||||
|
||||
echo mystr
|
||||
|
||||
5
todo.txt
5
todo.txt
@@ -1,5 +1,5 @@
|
||||
- clean up the tests!
|
||||
- fake-consts and semantic checking for table constructor
|
||||
- fake-consts, implict ref/ptr->var conversion
|
||||
|
||||
|
||||
High priority (version 0.9.0)
|
||||
@@ -10,7 +10,7 @@ High priority (version 0.9.0)
|
||||
- fix overloading resolution
|
||||
- wrong co-/contravariance
|
||||
- make ^ available as operator
|
||||
|
||||
- iterators should not always be destructive
|
||||
|
||||
Bugs
|
||||
----
|
||||
@@ -59,7 +59,6 @@ Low priority
|
||||
- 'nimrod def': does not always work
|
||||
- test branch coverage
|
||||
- checked exceptions
|
||||
- slicing
|
||||
|
||||
|
||||
Library
|
||||
|
||||
Reference in New Issue
Block a user