summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2011-04-23 17:11:24 +0200
committerAraq <rumpf_a@web.de>2011-04-23 17:11:24 +0200
commit4ba4999bb7b172b683cf7b8d574adbf04afa7527 (patch)
treef1216df68dba7abb0e9324afa11464db885800dd
parent05fee773ec48b5b45cdb7469a6c8410b59fcb542 (diff)
downloadNim-4ba4999bb7b172b683cf7b8d574adbf04afa7527.tar.gz
slice support in system.nim; syntactic sugar for tables; cleanup of grammar/parser
-rwxr-xr-xcompiler/parser.nim72
-rwxr-xr-xcompiler/semexprs.nim67
-rwxr-xr-xdoc/grammar.txt5
-rwxr-xr-xdoc/manual.txt28
-rwxr-xr-xlib/pure/strtabs.nim16
-rwxr-xr-xlib/system.nim95
-rw-r--r--tests/accept/run/tslices.nim43
-rwxr-xr-xtodo.txt5
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