Code Monkey home page Code Monkey logo

Comments (1)

zetashift avatar zetashift commented on August 14, 2024

If I do source[idx..source.len-1] in every clause of the case, it compiles and to get it working for numbers higher than 10 I changed the decoding ints to:

proc decodeString(this: Decoder, s: string): (BencodeType, int) =
  let 
    colonPos = s.find(':')
    strLen = s[0..<colonPos].parseInt
    str = s[colonPos+1..colonPos+strLen]

  result = (BencodeType(kind: btString, s: str), 
            colonPos+strLen)

But now I can't decode lists anymore :(. Getting the following error:

Got it working here is the full working code, compiles all the examples:

# A simple Bencode parser following Nim Days by xmonader
import strformat, tables, json, strutils, hashes

type 
  BencodeKind* = enum
    btString, btInt, btList, btDict
  BencodeType* = ref object
    case kind*: BencodeKind
    of BencodeKind.btString: s* : string
    of BencodeKind.btInt:    i* : int
    of BencodeKind.btList:   l* : seq[BencodeType]
    of BencodeKind.btDict:   d* : OrderedTable[BencodeType, BencodeType]
  
  Encoder* = ref object
  Decoder* = ref object

# Forward declaration because of recursion
proc encode(this: Encoder, obj: BencodeType): string 
proc decode(this: Decoder, source: string): (BencodeType, int)

# Constructors
proc newEncoder*(): Encoder = new Encoder
proc newDecoder*(): Decoder = new Decoder

# Dispatch API for BencodeType
proc encodeObject*(this: Encoder, obj: BencodeType): string = this.encode(obj)
proc decodeObject*(this: Decoder, source: string): BencodeType =
  result = this.decode(source)[0]

# For object variants like BencodeType we need to define custom `==` and `hash` procs
proc hash*(obj: BencodeType): Hash =
  case obj.kind
  of btString: !$(hash(obj.s))
  of btInt: !$(hash(obj.i))
  of btList: !$(hash(obj.l))
  of btDict:
    var h = 0
    for key, value in obj.d.pairs:
      h = h !& hash(key) !& hash(value)
    !$(h)
  
proc `==`*(a, b: BencodeType): bool =
  if a.isNil:
    if b.isNil: return true
    return false
  elif b.isNil or a.kind != b.kind:
    return false
  else:
    case a.kind
    of btString:
      result = a.s == b.s
    of btInt:
      result = a.i == b.i
    of btList:
      result = a.l == b.l
    of btDict:
      if a.d.len != b.d.len: return false
      for key, value in a.d:
        if not b.d.hasKey(key): return false
        if b.d[key] != value: return false
      result = true

# Define a simple string representation for BencodeType 
proc `$`*(a: BencodeType): string =
  case a.kind
  of btString: fmt("<Bencode {a.s}>")
  of btInt: fmt("<Bencode {a.i}>")
  of btList: fmt("<Bencode {a.l}>")
  of btDict: fmt("<Bencode {a.d}>")

# Encoding procs
proc encodeString(this: Encoder, s: string): string =
  if s.len == 0: return ""
  return $s.len & ":" & s

proc encodeInt(this: Encoder, i: int): string =
  return fmt("i{i}e")

proc encodeList(this: Encoder, l: seq[BencodeType]): string =
  result = "l"
  for el in l:
    result &= this.encode(el)
  result &= "e"
  
proc encodeDict(this: Encoder, d: OrderedTable[BencodeType, BencodeType]): string =
  result = "d"
  for key, value in d.pairs():
    # Bencode dicts must have String keys
    assert key.kind == BencodeKind.btString
    result &= this.encode(key) & this.encode(value)
  result &= "e"

proc encode(this: Encoder, obj: BencodeType): string =
  case obj.kind
  of BencodeKind.btString: result = this.encodeString(obj.s)
  of BencodeKind.btInt: result = this.encodeInt(obj.i)
  of BencodeKind.btList: result = this.encodeList(obj.l)
  of BencodeKind.btDict: result = this.encodeDict(obj.d)

# Procs for decoding
proc decodeString(this: Decoder, s: string): (BencodeType, int) =
  let 
    colonPos = s.find(':')
    strLen = s[0..<colonPos].parseInt
    str = s[colonPos+1..colonPos+strLen]

  result = (BencodeType(kind: btString, s: str), 
            colonPos+strLen+1)

proc decodeInt(this: Decoder, s: string): (BencodeType, int) =
  let epos = s.find('e')
  let i = parseInt(s[1..<epos])
  result = (BencodeType(kind: btInt, i:i), epos+1)

proc decodeList(this: Decoder, s: string): (BencodeType, int) =
  var 
    resultList = newSeq[BencodeType]()
    currentChar = s[1]
    idx = 1

  while idx < s.len:
    currentChar = s[idx]
    if currentChar == 'e':
      idx += 1
      break

    let (obj, nextObjPos) = this.decode(s[idx..<s.len])
    echo obj
    resultList.add(obj)
    idx += nextObjPos
  
  result = (BencodeType(kind: btList, l: resultList), idx)

proc decodeDict(this: Decoder, s: string): (BencodeType, int) =
  var 
    resultDict = initOrderedTable[BencodeType, BencodeType]()
    currentChar = s[1]
    idx = 1
    readingKey = true
    currentKey: BencodeType
    
  while idx < s.len:
    currentChar = s[idx]
    if currentChar == 'e': break
    let (obj, nextObjPos) = this.decode(s[idx..<s.len])
    
    if readingKey:
      currentKey = obj
      readingKey = false
    else:
      resultDict[currentKey] = obj
      readingKey = true
    idx += nextObjPos
  
  result = (BencodeType(kind: btDict, d: resultDict), idx)

proc decode(this: Decoder, source: string): (BencodeType, int) =
  var
    currentChar = source[0]
    idx = 0
  
  while idx < source.len:
    currentChar = source[idx]
    case currentChar
    of 'i':
      let (obj, nextObjPos) = this.decodeInt(source[idx..source.len-1])
      idx += nextObjPos
      return (obj, idx)
    of 'l':
      let (obj, nextObjPos) = this.decodeList(source[idx..source.len-1])
      idx += nextObjPos
      return (obj, idx)
    of 'd':
      let (obj, nextObjPos) = this.decodeDict(source[idx..source.len-1])
      idx += nextObjPos
      return (obj, idx)
    else:
      let (obj, nextObjPos) = this.decodeString(source[idx..source.len-1])
      idx += nextObjPos
      return (obj, idx)

from nimdays.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.