Skip to content

Commit

Permalink
Prep for JS backend (not functional yet)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yu-Vitaqua-fer-Chronos committed Feb 13, 2024
1 parent b1ddc58 commit 4545a31
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 44 deletions.
85 changes: 61 additions & 24 deletions src/pulse.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,59 @@ runnableExamples:
eventHandler.fire(meta, Event(typ: "test B"))

import std/[
asyncdispatch,
strformat,
tables,
macros
]

when not defined(js):
import std/asyncdispatch

type
UnregisteredEventDefect* = object of Defect
AlreadyRegisteredEventDefect* = object of Defect

EventHandlerBase[T] = object of RootObj
tbl: TableRef[pointer, seq[pointer]]
tbl: TableRef[TypeInfo, seq[pointer]]

EventHandler*[T] = object of EventHandlerBase[T]
AsyncEventHandler*[T] = object of EventHandlerBase[T]

EventHandlers[T] = AsyncEventHandler[T] | EventHandler[T]

ListenerProc[T, R] = proc(o: T, p: R) {.nimcall.}
AsyncListenerProc[T, R] = proc(o: T, p: R) {.async, nimcall.}

ListenerProcs[T, R] = AsyncListenerProc[T, R] | ListenerProc[T, R]
TypeInfo = distinct int

func `==`*(a, b: TypeInfo): bool {.borrow.}

proc newEventHandler*[T](): EventHandler[T] = EventHandler[T](tbl: newTable[TypeInfo, seq[pointer]]())

when not defined(js):
type
AsyncEventHandler*[T] = object of EventHandlerBase[T]

EventHandlers[T] = AsyncEventHandler[T] | EventHandler[T]

AsyncListenerProc[T, R] = proc(o: T, p: R) {.async, nimcall.}

ListenerProcs[T, R] = AsyncListenerProc[T, R] | ListenerProc[T, R]

proc newEventHandler*[T](): EventHandler[T] = EventHandler[T](tbl: newTable[pointer, seq[pointer]]())
proc newAsyncEventHandler*[T](): AsyncEventHandler[T] = AsyncEventHandler[T](tbl: newTable[TypeInfo, seq[pointer]]())

template getTInfo(t: typedesc): TypeInfo =
cast[TypeInfo](default(t).getTypeInfo)

else:
type
EventHandlers[T] = EventHandler[T]

proc newAsyncEventHandler*[T](): AsyncEventHandler[T] = AsyncEventHandler[T](tbl: newTable[pointer, seq[pointer]]())
ListenerProcs[T, R] = ListenerProc[T, R]

var counter = 0

proc getTInfo(_: typedesc): TypeInfo =
let id {.global.} = counter
once:
inc counter
TypeInfo(id)

macro validateProc[T](t: typedesc[T], r: typed) =
## Macro that validates inputted procs so that they're guaranteed to work.
Expand Down Expand Up @@ -76,28 +104,37 @@ macro validateProc[T](t: typedesc[T], r: typed) =

error(&"`{r.repr}` must have no return type!", n[3][0])

func registerEventType*[T](eh: var EventHandlers, t: typedesc[T]) =
eh.tbl[default(t).getTypeInfo()] = newSeq[pointer]()

func internal_registerListener[T, R](eh: var EventHandlers[T], t: typedesc[R], l: ListenerProcs[T, R]) =
eh.tbl[default(t).getTypeInfo()].add(cast[pointer](l))
proc registerEventType*[T](eh: var EventHandlers, t: typedesc[T]) =
if eh.tbl.hasKey(getTInfo(t)):
raise newException(AlreadyRegisteredEventDefect, "Event already registered!")

template registerListener*[T, R](eh: EventHandler[T], t: typedesc[R], l: ListenerProc[T, R]) =
validateProc(t, l)
eh.tbl[getTInfo(t)] = newSeq[pointer]()

if not eh.tbl.hasKey(default(t).getTypeInfo()):
proc internal_registerListener[T, R](eh: var EventHandlers[T], t: typedesc[R], l: ListenerProcs[T, R]) =
if not eh.tbl.hasKey(getTInfo(t)):
raise newException(UnregisteredEventDefect, "Event type not registered!")

eh.internal_registerListener(t, l)
eh.tbl[getTInfo(t)].add(cast[pointer](l))

template registerListener*[T, R](eh: AsyncEventHandler[T], t: typedesc[R], l: AsyncListenerProc[T, R]) =
template registerListener*[T, R](eh: EventHandler[T], t: typedesc[R], l: ListenerProc[T, R]) =
validateProc(t, l)

eh.internal_registerListener(t, l)

proc fire*[T, R](eh: EventHandler[T], o: T, p: R) =
for handler in eh.tbl[p.getTypeInfo]:
cast[ListenerProc[T, R]](handler)(o, p)
for handler in eh.tbl[typeof(p).getTInfo()]:
when not defined(js):
cast[ListenerProc[T, R]](handler)(o, p)

else:
{.error: "JS backend is not functional! Do not use!".}

when not defined(js):
template registerListener*[T, R](eh: AsyncEventHandler[T], t: typedesc[R], l: AsyncListenerProc[T, R]) =
validateProc(t, l)

eh.internal_registerListener(t, l)

proc fire*[T, R](eh: AsyncEventHandler[T], o: T, p: R) {.async.} =
for handler in eh.tbl[p.getTypeInfo]:
asyncCheck cast[AsyncListenerProc[T, R]](handler)(o, p)
proc fire*[T, R](eh: AsyncEventHandler[T], o: T, p: R) {.async.} =
for handler in eh.tbl[typeof(p).getTInfo()]:
asyncCheck cast[AsyncListenerProc[T, R]](handler)(o, p)
42 changes: 22 additions & 20 deletions tests/test1.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
#
# To run these tests, simply execute `nimble test`.

import asyncdispatch
import unittest
import std/unittest

import pulse

Expand Down Expand Up @@ -34,27 +33,30 @@ test "Sync Events":
a.fire(A(), B())
a.fire(A(), "Hello, World!")

test "Async Events":
var a = newAsyncEventHandler[A]()
when not defined(js):
import std/asyncdispatch

a.registerEventType(A)
a.registerEventType(B)
a.registerEventType(string)
test "Async Events":
var a = newAsyncEventHandler[A]()

a.registerListener(A) do (a: A, b: A) {.async.}:
await sleepAsync(100)
echo "A"
a.registerEventType(A)
a.registerEventType(B)
a.registerEventType(string)

a.registerListener(B) do (a: A, b: B) {.async.}:
echo "B"
a.registerListener(A) do (a: A, b: A) {.async.}:
await sleepAsync(100)
echo "A"

a.registerListener(string) do (a: A, b: string) {.async.}:
echo b
a.registerListener(B) do (a: A, b: B) {.async.}:
echo "B"

a.registerListener(string) do (a: A, b: string) {.async.}:
echo b

proc main() {.async.} =
asyncCheck a.fire(A(), A())
asyncCheck a.fire(A(), B())
asyncCheck a.fire(A(), "Hello, World!")
await sleepAsync(110)
proc main() {.async.} =
asyncCheck a.fire(A(), A())
asyncCheck a.fire(A(), B())
asyncCheck a.fire(A(), "Hello, World!")
await sleepAsync(110)

waitFor main()
waitFor main()

0 comments on commit 4545a31

Please sign in to comment.