summary refs log blame commit diff stats
path: root/nimlib/pure/variants.nim
blob: f661f81a6dbc8f964d710a593addb5805e541908 (plain) (tree)




















































































































































































                                                                            
#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2009 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements Nimrod's support for the ``variant`` datatype.
## `TVariant` shows how the flexibility of dynamic typing is achieved
## within a static type system. 

type
  TVarType* = enum
    vtNone,
    vtBool, 
    vtChar,
    vtEnum,
    vtInt,
    vtFloat,
    vtString,
    vtSet,
    vtSeq,
    vtDict
  TVariant* {.final.} = object of TObject
    case vtype: TVarType
    of vtNone: nil
    of vtBool, vtChar, vtEnum, vtInt: vint: int64
    of vtFloat: vfloat: float64
    of vtString: vstring: string
    of vtSet, vtSeq: q: seq[TVariant]
    of vtDict: d: seq[tuple[key, val: TVariant]]
    
iterator objectFields*[T](x: T, skipInherited: bool): tuple[
  key: string, val: TVariant] {.magic: "ObjectFields"}

proc `<>`*(x: ordinal): TVariant =
  result.kind = vtEnum
  result.vint = x

proc `<>`*(x: biggestInt): TVariant =
  result.kind = vtInt
  result.vint = x

proc `<>`*(x: char): TVariant =
  result.kind = vtChar
  result.vint = ord(x)

proc `<>`*(x: bool): TVariant =
  result.kind = vtBool
  result.vint = ord(x)

proc `<>`*(x: biggestFloat): TVariant =
  result.kind = vtFloat
  result.vfloat = x

proc `<>`*(x: string): TVariant =
  result.kind = vtString
  result.vstring = x

proc `<>`*[T](x: openArray[T]): TVariant =
  result.kind = vtSeq
  newSeq(result.q, x.len)
  for i in 0..x.len-1: result.q[i] = <>x[i]

proc `<>`*[T](x: set[T]): TVariant =
  result.kind = vtSet
  result.q = @[]
  for a in items(x): result.q.add(<>a)

proc `<>`* [T: object](x: T): TVariant {.magic: "ToVariant".}
  ## this converts a value to a variant ("boxing")

proc `><`*[T](v: TVariant, typ: T): T {.magic: "FromVariant".}

[<>5, <>67, <>"hallo"]
myVar><int

  
proc `==`* (x, y: TVariant): bool =
  if x.vtype == y.vtype:
    case x.vtype
    of vtNone: result = true
    of vtBool, vtChar, vtEnum, vtInt: result = x.vint == y.vint
    of vtFloat: result = x.vfloat == y.vfloat
    of vtString: result = x.vstring == y.vstring
    of vtSet:
      # complicated! We check that each a in x also occurs in y and that the
      # counts are identical:
      if x.q.len == y.q.len:
        for a in items(x.q):
          block inner:
            for b in items(y.q):
              if a == b: break inner
            return false
        result = true
    of vtSeq:
      if x.q.len == y.q.len:
        for i in 0..x.q.len-1:
          if x.q[i] != y.q[i]: return false
        result = true
    of vtDict:
      # it is an ordered dict:
      if x.d.len == y.d.len:
        for i in 0..x.d.len-1:
          if x.d[i].key != y.d[i].key: return false
          if x.d[i].val != y.d[i].val: return false
        result = true

proc `[]`* (a, b: TVariant): TVariant =
  case a.vtype
  of vtSeq:
    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
      result = a.q[b.vint]
    else:
      variantError()
  of vtDict:
    for i in 0..a.d.len-1:
      if a.d[i].key == b: return a.d[i].val
    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
      result = a.d[b.vint].val
    variantError()
  else: variantError()

proc `[]=`* (a, b, c: TVariant) =
  case a.vtype
  of vtSeq:
    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
      a.q[b.vint] = b
    else:
      variantError()
  of vtDict:
    for i in 0..a.d.len-1:
      if a.d[i].key == b:
        a.d[i].val = c
        return
    if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
      a.d[b.vint].val = c
    variantError()
  else: variantError()
  
proc `[]`* (a: TVariant, b: int): TVariant {.inline} = return a[<>b]
proc `[]`* (a: TVariant, b: string): TVariant {.inline} = return a[<>b]
proc `[]=`* (a: TVariant, b: int, c: TVariant) {.inline} = a[<>b] = c
proc `[]=`* (a: TVariant, b: string, c: TVariant) {.inline} = a[<>b] = c

proc `+`* (x, y: TVariant): TVariant =
  case x.vtype
  of vtBool, vtChar, vtEnum, vtInt:
    if y.vtype == x.vtype:
      result.vtype = x.vtype
      result.vint = x.vint + y.vint
    else:
      case y.vtype
      of vtBool, vtChar, vtEnum, vtInt:
        
    
    
    vint: int64
  of vtFloat: vfloat: float64
  of vtString: vstring: string
  of vtSet, vtSeq: q: seq[TVariant]
  of vtDict: d: seq[tuple[key, val: TVariant]]

proc `-`* (x, y: TVariant): TVariant
proc `*`* (x, y: TVariant): TVariant
proc `/`* (x, y: TVariant): TVariant
proc `div`* (x, y: TVariant): TVariant
proc `mod`* (x, y: TVariant): TVariant
proc `&`* (x, y: TVariant): TVariant
proc `$`* (x: TVariant): string =
  # uses JS notation
  
proc parseVariant*(s: string): TVariant
proc `<`* (x, y: TVariant): bool
proc `<=`* (x, y: TVariant): bool

proc hash*(x: TVariant): int =