181 lines
4.6 KiB
Nim
Executable file
181 lines
4.6 KiB
Nim
Executable file
#
|
|
#
|
|
# Nimrod's Runtime Library
|
|
# (c) Copyright 2010 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, ?"hello"]
|
|
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 =
|
|
|
|
|