nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.63k stars 1.47k forks source link

Confusing error with static parameters #12345

Open Clyybber opened 5 years ago

Clyybber commented 5 years ago

The following snippet errors out with a confusing error.

Example

import strutils

type
  Tile[n: static int] = object
    a: array[n, int]
  Coord = tuple[x, y: int]
  Rule[n: static int] = tuple[c: Coord, t: Tile[n]]
  Discriminator[n: static int] = seq[Rule[n]]
  Tileset[n: static int] = array[n, Discriminator[n]]

proc parseMapHeader(content: static string): seq[string] {.compileTime.} =
  var cols = 0 #When 3 then it is a tilename in the form of ":::NAME"
  for c in content:
    if cols == 3:
      if c == '\n':
        cols = 0
      else:
        result[^1].add c
    elif c == ':':
      inc cols
      if cols == 3:
        result.add ""
    else:
      cols = 0
  for s in mitems(result):
    s = s.strip

#works fine with `tiles: static seq[string]` -> `tiles: seq[string]`
proc parseProb(tiles: static seq[string], tileslen: static int, p: string): Tile[tileslen] =
  discard "TODO"

proc parseMap(tiles: static seq[string], tileslen: static int, content: static string): Tileset[tileslen] =
  var i = 0
  template c: char = content[i]
  for id, name in tiles:
    assert content[i..i+2] == ":::"
    while c != '\n': inc i #Skip one line ':::`name`'
    inc i
    var asciiDefs: array[char, Tile[tileslen]]
    block parseDefs:
      while true:
        block parseSomeDef:
          if c in Letters and content[i+1..i+3] == " = ":
            block parseAsciiDef:
              let oldC = c
              assert content[i+1..i+3] == " = "
              inc i, 4 #Skip "_ = "
              var pStr: string
              while c != '\n':
                pStr.add c
                inc i
              asciiDefs[oldC] = parseProb(tiles, tileslen, pStr)
              inc i #Skip '\n'
              break parseSomeDef
          else:
            break parseDefs

when isMainModule:
  const content = """
:::PillarRoom
A = [Pillar: 1]
"""
  const names = parseMapHeader(content)
  const parsedTileset = parseMap(names, names.len, content)

Current Output

stack trace: (most recent call last)
/home/clyybber/projects/tests/ttt.nim(63, 33)
/home/clyybber/projects/tests/ttt.nim(11, 6) parseMap
/home/clyybber/projects/tests/ttt.nim(11, 6) Error: index out of bounds, the container is empty

Expected Output

Should compile or spit out a more appropriate error message.

Possible Solution

See the comment in the snippet

Additional Information

$ nim -v
Nim Compiler Version 1.0.99
metagn commented 2 months ago

A bit simplified:

type
  Tile[n: static int] = object
    a: array[n, int]
  Coord = tuple[x, y: int]
  Rule[n: static int] = tuple[c: Coord, t: Tile[n]]
  Discriminator[n: static int] = seq[Rule[n]]
  Tileset[n: static int] = array[n, Discriminator[n]]

#works fine with `tiles: static seq[string]` -> `tiles: seq[string]`
proc parseProb(tiles: static seq[string], tileslen: static int, p: string): Tile[tileslen] =
  discard "TODO"

proc parseMap(tiles: static seq[string], tileslen: static int, content: static string): Tileset[tileslen] =
  for id, name in tiles:
    var pStr: string = ""
    discard parseProb(tiles, tileslen, pStr)

when isMainModule:
  const parsedTileset = parseMap(@["a", "b", "c"], 3, "abc")