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
parent 05fee773ec
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:
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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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")

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!
- 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