diff options
author | Araq <rumpf_a@web.de> | 2011-04-23 17:11:24 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-04-23 17:11:24 +0200 |
commit | 4ba4999bb7b172b683cf7b8d574adbf04afa7527 (patch) | |
tree | f1216df68dba7abb0e9324afa11464db885800dd | |
parent | 05fee773ec48b5b45cdb7469a6c8410b59fcb542 (diff) | |
download | Nim-4ba4999bb7b172b683cf7b8d574adbf04afa7527.tar.gz |
slice support in system.nim; syntactic sugar for tables; cleanup of grammar/parser
-rwxr-xr-x | compiler/parser.nim | 72 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 67 | ||||
-rwxr-xr-x | doc/grammar.txt | 5 | ||||
-rwxr-xr-x | doc/manual.txt | 28 | ||||
-rwxr-xr-x | lib/pure/strtabs.nim | 16 | ||||
-rwxr-xr-x | lib/system.nim | 95 | ||||
-rw-r--r-- | tests/accept/run/tslices.nim | 43 | ||||
-rwxr-xr-x | todo.txt | 5 |
8 files changed, 234 insertions, 97 deletions
diff --git a/compiler/parser.nim b/compiler/parser.nim index 2289bb98f..1b8f2f259 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 56de998f5..35fb793c4 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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, diff --git a/doc/grammar.txt b/doc/grammar.txt index 837a93003..2348a1a7c 100755 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -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 diff --git a/doc/manual.txt b/doc/manual.txt index e230861fb..9067946ac 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index d11ff31c8..fda0740d3 100755 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -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" + diff --git a/lib/system.nim b/lib/system.nim index f5757d475..cfe97eab9 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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") + diff --git a/tests/accept/run/tslices.nim b/tests/accept/run/tslices.nim new file mode 100644 index 000000000..1061b4245 --- /dev/null +++ b/tests/accept/run/tslices.nim @@ -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 + diff --git a/todo.txt b/todo.txt index 96818da7d..beb0e385e 100755 --- a/todo.txt +++ b/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 |