Skip to content

Instantly share code, notes, and snippets.

@mjendrusch
Created October 21, 2016 00:17
Show Gist options
  • Save mjendrusch/60cc4648d87448727b290faf0a6ef06c to your computer and use it in GitHub Desktop.
Save mjendrusch/60cc4648d87448727b290faf0a6ef06c to your computer and use it in GitHub Desktop.
Current state of type-checked JsObject using a compile-time heap.
import tables
# A template to generate all needed types and procs to emulate
# variable compile-time fields for objects.
template initHeap*(ptrName, ptrType: untyped): untyped =
type
ptrName* = distinct int
`ptrType Heap`* = Table[ptrName, ptrType]
proc `==`*(a, b: ptrName): bool =
int(a) == int(b)
var
counter* {. gensym, compileTime .}: int = 0
heap* {. gensym, compileTime .}: `ptrType Heap` =
initTable[ptrName, ptrType]()
template `[]`*(pt: ptrName): ptrType =
heap[pt]
template `[]=`*(pt: ptrName; val: ptrType) =
static:
heap[pt] = val
template `new ptrName`*(): ptrName =
static:
inc counter
ptrName(counter)
import ctJsObject
# Demo of static (flow-) typing together with structural types
# (using concepts and when), for JsObjects.
# A structural type
type someStructure = concept c
c is JsObject
c.a is int
c.b is string
# Quick and dirty import of console
var console {. importc, nodecl .}: JsObject[newPtrTab]
var x = newJsObject()
var y = newJsObject()
x.a = "22"
x.d = "test"
y.a = 42
y.b = "foo"
console.log(x, y)
# A branching `proc` to demonstrate flow typing
# plus structural types.
proc doSomething(x: JsObject) =
when x is someStructure:
x.a = "surprise"
x.b = 42
else:
x.a = 42
x.c = 99
block:
var x = clone(x)
x.a = "foo"
# x is not someStructure
doSomething(x)
# y is someStructure
doSomething(y)
# both take the right branches
var b: int = y.b
# Though all this looks very dynamic, it is still fully
# statically checked, and the compiler will return an error,
# if the types do not fit.
var a: int = x.c
import macros
import strutils
import typetraits
import tables
import strutils
import sequtils
import compileTimeHeap
{.experimental.}
initHeap(ptrTab, Table[string, cstring])
type
JsObject*[pt: static[ptrTab]] = object
template newTab*(): ptrTab =
const res = newPtrTab()
res[] = initTable[string, cstring]()
res
macro stringifyIdent*(x: untyped): string =
if x.kind == nnkHiddenDeref:
return $(x[0])
$x
macro stringToIdent*(x: static[cstring]): untyped =
result = newIdentNode(!($x))
template identOrName*(x: auto): static[string] =
"`" & stringifyIdent(x) & "`"
proc setImpl*[T; pt: static[ptrTab]](obj: JsObject[pt]; field: static[string]; val: T) = #{. importcpp: "#[#] = #" .}
{. emit: "`obj`[\"" & field & "\"]" & " = `val`;" .}
proc getImpl*[pt: static[ptrTab]](obj: JsObject[pt]; name: static[string]): JsObject[ptrTab(-1)] = #{. importcpp: "(#[#])" .}
{. emit: "`result` = `obj`[\"" & name & "\"];".}
proc callImpl*[pt: static[ptrTab]](obj: JsObject[pt]; nam: static[string];
args: static[string]; qt: static[ptrTab]): JsObject[qt] {. discardable .} =
{. emit: "`result` = `obj`[\"" & nam & "\"](" & args & ");" .}
template `.=`*[T; pt: static[ptrTab]](obj: JsObject[pt]; nam: string; val: T) =
static:
pt[][stringifyIdent(nam)] = T.name
setImpl(obj, nam, val)
template `.`*[pt: static[ptrTab]](obj: JsObject[pt]; nam: untyped): auto =
when compiles(stringToIdent(pt[][stringifyIdent(nam)])):
cast[stringToIdent(pt[][stringifyIdent(nam)])](getImpl(obj, stringifyIdent(nam)))
else:
cast[JsObject[newTab()]](getImpl(obj, stringifyIdent(nam)))
template `.()`*[pt: static[ptrTab]](obj: JsObject[pt]; nam: static[string]; args: varargs[string, identOrName]): auto =
callImpl(obj, nam, join(args, ","), newTab())
template newJsObject*(): auto =
const res = newTab()
proc ret(): JsObject[res] {. gensym .} =
{. emit: "`result` = {};" .}
ret()
template clone*[procPt: static[ptrTab]](obj: JsObject[procPt]): auto =
var res = newJsObject()
static:
if not compiles(res.pt[]):
res.pt[] = initTable[string, cstring]()
for k, v in procPt[].pairs:
res.pt[][k] = v
res
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment