slice support in system.nim; syntactic sugar for tables; cleanup of grammar/parser

This commit is contained in:
Araq 2011-04-23 17:11:24 +02:00
commit 4ba4999bb7
8 changed files with 234 additions and 97 deletions

View file

@ -173,49 +173,30 @@ proc parseSymbol(p: var TParser): PNode =
of tkAccent: of tkAccent:
result = newNodeP(nkAccQuoted, p) result = newNodeP(nkAccQuoted, p)
getTok(p) getTok(p)
var id = ""
while true:
case p.tok.tokType case p.tok.tokType
of tkBracketLe: of tkBracketLe:
var s = "[" id.add("[]")
getTok(p) 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) eat(p, tkBracketRi)
add(s, ']') of tkEquals:
if p.tok.tokType == tkEquals: id.add('=')
add(s, '=')
getTok(p) getTok(p)
addSon(result, newIdentNodeP(getIdent(s), p))
of tkParLe: of tkParLe:
addSon(result, newIdentNodeP(getIdent("()"), p)) id.add("()")
getTok(p) getTok(p)
eat(p, tkParRi) eat(p, tkParRi)
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot: of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
var id = p.tok.ident id.add(p.tok.ident.s)
getTok(p) getTok(p)
if p.tok.tokType == tkEquals: of tkIntLit..tkCharLit:
addSon(result, newIdentNodeP(getIdent(id.s & '='), p)) id.add(tokToStr(p.tok))
getTok(p) getTok(p)
else: else:
addSon(result, newIdentNodeP(id, p)) if id.len == 0: parMessage(p, errIdentifierExpected, tokToStr(p.tok))
else: break
parMessage(p, errIdentifierExpected, tokToStr(p.tok)) add(result, newIdentNodeP(getIdent(id), p))
result = ast.emptyNode
eat(p, tkAccent) eat(p, tkAccent)
else: else:
parMessage(p, errIdentifierExpected, tokToStr(p.tok)) parMessage(p, errIdentifierExpected, tokToStr(p.tok))
@ -247,14 +228,7 @@ proc accExpr(p: var TParser): PNode =
eat(p, tkAccent) eat(p, tkAccent)
proc indexExpr(p: var TParser): PNode = proc indexExpr(p: var TParser): PNode =
# indexExpr ::= expr ['=' expr]
result = parseExpr(p) 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 = proc indexExprList(p: var TParser, first: PNode): PNode =
result = newNodeP(nkBracketExpr, p) result = newNodeP(nkBracketExpr, p)

View file

@ -709,41 +709,11 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
GlobalError(n.Info, errUndeclaredFieldX, i.s) 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 = proc buildOverloadedSubscripts(n: PNode, inAsgn: bool): PNode =
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
add(result, ast.emptyNode) # fill with the correct node later result.add(newIdentNode(
add(result, n[0]) if inAsgn: getIdent"[]=" else: getIdent"[]", n.info))
var opr = "[" for i in 0 .. n.len-1: result.add(n[i])
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)
proc semDeref(c: PContext, n: PNode): PNode = proc semDeref(c: PContext, n: PNode): PNode =
checkSonsLen(n, 1) 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}) n.sons[i] = semExprWithType(c, n.sons[i], flags - {efAllowType})
var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt) var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt)
var arg = IndexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) var arg = IndexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1])
if arg != nil: n.sons[1] = arg if arg != nil:
else: GlobalError(n.info, errIndexTypesDoNotMatch) n.sons[1] = arg
result = n result = n
result.typ = elemType(arr) result.typ = elemType(arr)
#GlobalError(n.info, errIndexTypesDoNotMatch)
of tyTuple: of tyTuple:
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) 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]) m = fitNode(c, typ, n.sons[i])
addSon(result, m) 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 type
TParKind = enum TParKind = enum
paNone, paSingle, paTupleFields, paTuplePositions paNone, paSingle, paTupleFields, paTuplePositions
@ -967,7 +953,8 @@ proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode =
result = semTemplateExpr(c, result, s, semCheck) result = semTemplateExpr(c, result, s, semCheck)
else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s) else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
else: else:
GlobalError(n.info, errInvalidExpressionX, renderTree(a, {renderNoComments})) GlobalError(n.info, errInvalidExpressionX,
renderTree(a, {renderNoComments}))
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
@ -1090,6 +1077,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
checkSonsLen(n, 3) checkSonsLen(n, 3)
of nkCheckedFieldExpr: of nkCheckedFieldExpr:
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
of nkTableConstr:
result = semTableConstr(c, n)
of nkSymChoice: of nkSymChoice:
GlobalError(n.info, errExprXAmbiguous, renderTree(n, {renderNoComments})) GlobalError(n.info, errExprXAmbiguous, renderTree(n, {renderNoComments}))
else: else:

View file

@ -21,12 +21,11 @@ plusExpr ::= mulExpr (OP7 optInd mulExpr)*
mulExpr ::= dollarExpr (OP8 optInd dollarExpr)* mulExpr ::= dollarExpr (OP8 optInd dollarExpr)*
dollarExpr ::= primary (OP9 optInd primary)* dollarExpr ::= primary (OP9 optInd primary)*
indexExpr ::= expr ['=' expr] indexExpr ::= expr
castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' castExpr ::= 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
addrExpr ::= 'addr' '(' optInd expr optPar ')' addrExpr ::= 'addr' '(' optInd expr optPar ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' | '[' ']'
| '[' (',' | ['$'] '..' ['$'])* ']'
| '=' | literal)+ '`' | '=' | literal)+ '`'
| IDENT | IDENT

View file

@ -1817,6 +1817,34 @@ required. ``Elif`` parts are also allowed (but unlikely to be good
style). 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 Type conversions
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Syntactically a `type conversion` is like a procedure call, but a Syntactically a `type conversion` is like a procedure call, but a

View file

@ -155,6 +155,16 @@ proc newStringTable*(keyValuePairs: openarray[string],
result[keyValuePairs[i]] = keyValuePairs[i + 1] result[keyValuePairs[i]] = keyValuePairs[i + 1]
inc(i, 2) 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 {. proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {.
rtl, extern: "nstFormat".} = rtl, extern: "nstFormat".} =
## The `%` operator for string tables. ## The `%` operator for string tables.
@ -198,3 +208,9 @@ proc `$`*(t: PStringTable): string {.rtl, extern: "nstDollar".} =
result.add(val) result.add(val)
result.add("}") 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"

View file

@ -129,7 +129,8 @@ proc `..`*[T](a, b: T): TSlice[T] {.noSideEffect, inline.} =
proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
## `slice`:idx: operator that constructs an interval ``[low(T), b]`` ## `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 result.b = b
proc contains*[T](s: TSlice[T], value: T): bool {.noSideEffect, inline.} = 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.} proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
## The stingify operator for an enumeration argument. This works for ## 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 ## a ``$`` operator for a concrete enumeration is provided, this is
## used instead. (In other words: *Overwriting* is possible.) ## 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 = proc `$`*[T: tuple](x: T): string =
## generic ``$`` operator for tuples that is lifted from the components ## generic ``$`` operator for tuples that is lifted from the components
## of `x`. ## of `x`. Example:
##
## .. code-block:: nimrod
## $(23, 45) == "(23, 45)"
## $() == "()"
result = "(" result = "("
for name, value in fieldPairs(x): for name, value in fieldPairs(x):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
@ -1277,6 +1282,19 @@ proc `$`*[T: tuple](x: T): string =
result.add($value) result.add($value)
result.add(")") 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 --------------------------------------------- # ----------------- GC interface ---------------------------------------------
proc GC_disable*() {.rtl, inl.} proc GC_disable*() {.rtl, inl.}
@ -1751,3 +1769,74 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
{.pop.} # checks {.pop.} # checks
{.pop.} # hints {.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")

View 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

View file

@ -1,5 +1,5 @@
- clean up the tests! - 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) High priority (version 0.9.0)
@ -10,7 +10,7 @@ High priority (version 0.9.0)
- fix overloading resolution - fix overloading resolution
- wrong co-/contravariance - wrong co-/contravariance
- make ^ available as operator - make ^ available as operator
- iterators should not always be destructive
Bugs Bugs
---- ----
@ -59,7 +59,6 @@ Low priority
- 'nimrod def': does not always work - 'nimrod def': does not always work
- test branch coverage - test branch coverage
- checked exceptions - checked exceptions
- slicing
Library Library