-
Notifications
You must be signed in to change notification settings - Fork 1
Code snippets
Jason Beetham edited this page Dec 30, 2024
·
29 revisions
A simple way of doing 24 bit integers.
import std/strutils
type int24 {.packed.} = object
data {.bitsize: 24.}: int32
proc `+`*(a, b: int24): int24 = int24(data: a.data + b.data)
proc `-`*(a, b: int24): int24 = int24(data: a.data - b.data)
proc `div`*(a, b: int24): int24 = int24(data: a.data div b.data)
proc `*`*(a, b: int24): int24 = int24(data: a.data * b.data)
proc `>`*(a, b: int24): bool = a.data > b.data
proc `$`(i: int24): string = $i.data
proc low(_: typedesc[int24]): int = -8388608
proc high(_: typedesc[int24]): int = 8388607
proc toInt24(i: SomeNumber): int24 =
let u32 = int32(i)
if u32 notin int32(int24.low)..int32(int24.high):
raise (ref RangeDefect)(msg: $u32 & " not in int24 range.")
int24(data: u32)
proc `'i24`(s: static string): int24 =
when parseInt(s) notin int24.low .. int24.high:
{.error: $i & " not in int24 range.".}
int24(data: int32 parseInt(s))
var a = 300'i24
assert sizeof(a) == 3
echo a + 3'i24
import std/macros
macro offsetOfDot(t: typed): untyped = newLit t[^1].getOffset
proc isAligned(t: typedesc[object]): bool =
for x in t().fields:
when x.offsetOfDot() mod 64 != 0:
return false
true
type Aligned64 = concept type A
A is object
isAligned(A)
proc doThing(a: Aligned64) =
echo a
type MyType = object
a: int
b{.align: 64.}: int
doThing(MyType())
import std/[tables, strutils]
var todos{.compileTime.}: Table[string, seq[string]]
template todo*(msg: static string) =
static:
const info = instantiationInfo()
discard todos.hasKeyOrPut("unlabelled", newSeq[string]())
todos["unlabelled"].add "$#($#,$#): $#" % [info.fileName, $info.line, $info.column, msg]
{.warning: "Todo: " & msg.}
template todo*(label, msg: static string) =
static:
const info = instantiationInfo()
discard todos.hasKeyOrPut(label, newSeq[string]())
todos[label].add "$#($#,$#): $#" % [info.fileName, $info.line, $info.column, msg]
{.warning: "Todo: " & msg.}
proc emitTodos(): string {.compileTime.} =
for entry, msgs in todos:
result.add entry
result.add "\n"
for msg in msgs:
result.add " "
result.add msg
result.add "\n"
proc test() = todo("unimplemented"): "implement this"
proc test2() = todo("Meeeeh"): "implement this"
proc test3() = todo("Meeeeh"): "Yea really"
todo "Do not forget the whole logic"
echo emitTodos()
type
Base = object of RootObj
ChildA = object of Base
x, y: int
ChildB = object of ChildA
s: string
ChildC = object of Base
z: float
proc doThing[T: Base](val: T) = echo val
Base().doThing()
ChildA().doThing()
ChildB().doThing()
ChildC().doThing()
proc doThing(c: ChildA) = echo c.x * c.y
proc doThing(c: ChildB) = echo c.s
proc doThing(c: ChildC) =
doThing(Base(c)) # call the original procedure
echo c.z
Base().doThing()
ChildA().doThing()
ChildB().doThing()
ChildC().doThing()
type Inputable = concept type I
getInputDefault(I) is I
proc promptInput[T: Inputable](msg: string, default: T): T =
# Check for input and if none return default
echo default
proc promptInput[T: Inputable](msg: string): T =
mixin getInputDefault
promptInput(msg, getInputDefault(T))
proc getInputDefault(typ: typedesc[int or float]): typ = default(typ)
proc getInputDefault(typ: typedesc[string]): typ = "hello"
discard promptInput[string]("hello")
# Super boiled down example.
type
UserPluginBase = ref object of RootObj
# Base fields used by the implementation
apiPlugin: ApiPlugin
someField: int
ApiPlugin = ref object # ptr object for you
userData: pointer
process: proc(apiPlugin: ApiPlugin) {.cdecl.}
ApiEntry = object
createPlugin: proc(): ApiPlugin {.cdecl.}
# Api destroy function
proc destroy[T](plugin: ApiPlugin) {.cdecl.} =
mixin destroy
let userPlugin = cast[T](plugin.userData)
userPlugin.destroy() # User defined destroy function
GcUnref(userPlugin)
# Api process function
proc process[T](plugin: ApiPlugin) {.cdecl.} =
mixin process
let userPlugin = cast[T](plugin.userData)
userPlugin.process() # User defined process function
proc createPlugin[T](): ApiPlugin{.cdecl.} =
mixin setupPlugin
var userPlugin = T()
GcRef(userPlugin)
setUpPlugin(userPlugin)
userPlugin.apiPlugin = ApiPlugin(
userData: cast[pointer](userPlugin),
process: process[T],
)
userPlugin.apiPlugin
proc init(_: typedesc[ApiEntry], T: typedesc): ApiEntry =
ApiEntry(createPlugin: createPlugin[T])
type
UserPlugin = ref object of UserPluginBase
# Any other fields the user wants
someUserField: int
proc setupPlugin(up: UserPlugin) =
up.someUserField = 300
proc process(up: UserPlugin) = echo up.someUserField
var entry = ApiEntry.init(UserPlugin)
let plug = entry.createPlugin()
plug.process(plug)
type MyType = object
case kind: bool
of true:
aVal: int
modifier: int
of false:
bVal: int
name: string
proc `a`(myType: MyType): int =
case myType.kind
of true:
myType.aVal
of false:
myType.bVal
proc `a`(myType: var MyType): var int =
case myType.kind
of true:
result = myType.aVal
of false:
result = myType.bVal
proc `a=`(myType: var MyType, val: int) =
case myType.kind
of true:
myType.aVal = val
of false:
myType.bVal = val
import std/typetraits
proc lastType[T](a: openarray[T]): auto =
when T is (seq or array):
default(T).lastType()
else:
default(T)
type
BoxAtom = string or int64 or float
BoxConcept = concept bc
lastType(bc) is BoxAtom
Boxable = BoxAtom or BoxConcept
proc flatten[T](box: Boxable, result: var seq[T]) =
for entry in box.items:
when typeof(box[0]) is BoxAtom:
result.add entry
else:
entry.flatten(result)
proc flatten(box: Boxable): auto =
result = newSeq[typeof(box.lastType)]()
box.flatten(result)
var a = @[@[@[10i64, 20, 30], @[1i64, 2, 2]], @[@[1000i64, 3000, 4, 5]]]
echo a.flatten()
proc seqDepth[T: seq](n: T, val: var int) =
when n[0] is seq:
inc val
seqDepth(default(typeof(n[0])), val)
proc seqDepth[T: seq](n: typedesc[T]): int =
inc result
when (default(T))[0] is seq:
seqDepth(default(T), result)
type
NSeq[Size: static int, T] {.explain.} = concept n, type N
seqDepth(N) == Size
proc get[n: static int; T](sequence: NSeq[n, T], indices: array[n, int]): auto = # Cannot use T must use auto, probably compiler bug
discard
@[10].get([1])
@[@[10]].get([1, 1])
@[@[@[10]]].get([1, 1, 1])
proc rootType[T: not seq](_: typedesc[T]): T = discard
proc rootType[T](_: typedesc[seq[T]]): auto =
rootType T
proc flatten[T, Y](s: seq[T], result: var seq[Y]) =
when T is Y:
result.add s
else:
for val in s:
flatten(val, result)
proc flatten[T](s: seq[T]): auto =
result = newSeq[typeof rootType(seq[T])]()
flatten(s, result)
var a = @[@[@[10, 20, 30], @[4, 5], @[300, 40, 50]], @[@[3, 9, 12, 300]]]
echo a.flatten()
import std/macros
macro getOwner(o: typed): untyped =
let prc = o.owner
if not prc.owner.eqIdent "unknown":
nnkPar.newTree(newLit $prc.owner, newLit $prc)
else:
nnkPar.newTree(newLit $prc, newLit"")
template moduleProcName(): untyped =
type A = distinct void
getOwner(A)
proc doThing() =
echo moduleProcName
doThing()
echo moduleProcName
import std/macros
macro isTopLevelImpl(o: typed): untyped =
newLit o.owner.symKind == nskModule
template isTopLevel(): bool =
type A = distinct void
isTopLevelImpl(A)
type MyType = object
when isTopLevel():
export MyType
import std/macros
macro joinIters(body: ForLoopStmt) =
result = newStmtList()
for x in body[^2][1..^1]:
result.add nnkForStmt.newTree(body[0..^3] & @[x] & body[^1])
result = newBlockStmt(ident"joinLoop", result)
iterator myIter: int = yield 132
for x in joinIters([10, 20, 30], 0..4, myIter()):
echo x
type MyEnum = enum A, B, C
var a: array[MyEnum, int]
a[A] = 300
a[B] = 400
a[C] = 500
template hasProc(module, name: untyped, typ: typedesc[proc]): untyped = compiles(typ(module.name))
import std/strutils
static:
assert hasProc(strutils, contains, proc(a, b: string): bool)
assert not hasProc(strutils, contains, proc(a, b: int))
# module b
import a
proc doThing*(myType: MyType) = echo myType.x * myType.y
# module a
type MyType* = object
x*, y*: int
import b
doThing(MyType(x: 300, y: 200))
import std/macros
proc hello() =
## prints a hello world
proc doThing[T]() =
## Does other stuff
## Maybe even flies
macro getHelp(t: proc): untyped =
var comments: string
let impl = t.getImpl
if impl[^1].kind == nnkCommentStmt:
comments = impl[^1].strVal
else:
for x in t.getImpl[^1]:
if x.kind == nnkCommentStmt:
comments.add x.strVal & "\n"
else:
break
newCall("echo", newLit(comments))
getHelp(hello)
getHelp(doThing[int])
import std/macros
macro makeTypedesc(s: static seq[string]): untyped =
result = nnkTupleConstr.newTree()
for x in s:
result.add parseExpr(x)
proc getUnpackTypedescs(val: typedesc[object], fieldsToUnpack: static openarray[string]): seq[string] {.compileTime.} =
for name, field in val().fieldPairs:
if name in fieldsToUnpack:
result.add $typeof(field)
proc getUnpackTypedescs(val: typedesc[object]): seq[string] {.compileTime.} =
for name, field in val().fieldPairs:
result.add $typeof(field)
var added {.compileTime.}: int
proc unpack(val: object, fieldsToUnpack: static openarray[string] = @[]): auto =
result =
when fieldsToUnpack.len > 0:
default makeTypeDesc getUnpackTypedescs(typeof(val), fieldsToUnpack)
else:
default makeTypeDesc static(getUnpackTypeDescs(typeof(val)))
for name, field in val.fieldPairs:
when fieldsToUnpack.len == 0:
result[added] = field
static: inc added
elif name in fieldsToUnpack:
result[added] = field
static: inc added
static: added = 0
import std/tables
type Note = object
title, msg: string
b: seq[string]
c: Table[float, seq[(int, float)]]
var a = Note(title: "Hello", msg: "World")
echo a.unpack()
echo a.unpack(["msg"])
echo a.unpack(["msg", "b", "c"])
{.warningAsError[CaseTransition]: on.}
proc selfAssign[T](v: var T) = v = v
proc isVariant(Obj: typedesc[object]): bool =
var obj = default Obj
for name, field in obj.fieldpairs:
when not compiles(selfAssign field):
return true
type Variant = concept type V
isVariant(V)
type
MyType = object
case x: bool
of true:
a: int
else:
b: float
MyType2 = object
x, y, z: int
assert MyType is Variant
assert MyType2 isnot Variant
import std/json
assert JsonNodeObj is Variant
import std/macros
type Error[T, Y] = object
case isErr: bool
of true:
errVal: T
of false:
val: Y
type UnhandledError = object of CatchableError
proc isSuccess[T, Y](err: Error[T, Y]): bool = not err.isErr
macro ownedByProc(sym: typed): untyped =
newLit sym.owner.kind == nnkSym and sym.owner.symKind == nskProc
template error[T, Y](result: var Error[T, Y], err: T) =
result = Error[T, Y](isErr: true, errVal: err)
proc dummy = discard
when ownedByProc(dummy):
return
template success[T, Y](result: var Error[T, Y], goodVal: Y) =
result = Error[T, Y](isErr: false, val: goodVal)
proc dummy = discard
when ownedByProc(dummy):
return
import std/random
proc doThing(): Error[string, int] =
let val = rand(100)
if val > 50:
result.error "Got random chance: " & $val
echo "Not Printed"
else:
result.success val
echo "Not Printed"
randomize()
for x in 0..10:
let a = doThing()
if a.isSuccess:
echo "Got a val: ", $a.val
else:
echo a.errVal
import std/[macros, genasts]
macro procSym(call: typed): untyped = call[0]
macro procOf*(theProcCall: untyped): untyped =
result = newCall(theProcCall[0])
for x in theProcCall[1..^1]:
if x.kind == nnkVarTy:
let name = genSym(nskVar, "argument")
result.add:
genast(typ = x[^1]):
(
var argument = default(typ)
typ
)
else:
result.add newCall("default", x)
result = newCall(bindSym"procSym", result)
import std/strutils
var myProc = procOf contains(string, string)
assert myProc("a", "a")
import std/[macros, genasts]
macro anyOf(t: typedesc[tuple]): untyped =
let impl =
if t.kind == nnkSym:
t.getImpl
else:
t
result = impl[0]
for x in impl[1..^1]:
result = infix(newCall("typeof", result), "or", newCall("typeof", x))
result = newCall("typeof", result)
proc doThing(a: anyOf (int, float, string, bool)) = echo a
doThing(100)
doThing(100d)
doThing("hello")
doThing(false)
import std/macros
proc `><`[T](val: T): T = T val
macro spread(prc: proc, args: varargs[typed]) =
result = newCall(prc)
var foundSpreaded = false
let paramCount = prc.getTypeInst[0].len - 1 # return is a parameter
for arg in args:
if arg.kind == nnkPrefix and arg[0].eqIdent "><":
if foundSpreaded:
error("Cannot spread twice presently", arg)
foundSpreaded = true
for i in 0..(paramCount - result.len):
result.add newCall("[]", arg[1], newLit(i))
else:
result.add arg
proc doThing(a: string, b: float, c, d, e: int) =
echo locals()
var a = @[10, 20, 30, 40, 50]
doThing.spread("hello", 35f, ><a)
import std/[macros, genasts]
type Dispatch[T: static[enum]] = distinct T.typeof
proc findDelim(obj: NimNode): (NimNode, NimNode) =
for x in obj:
if x.kind == nnkRecCase:
return (x[0][0], x[0][1])
else:
result = findDelim(x)
if result[0].kind != nnkNilLit:
return
macro variantDispatch(obj: object, call: untyped): untyped =
let (delimName, delimKind) = obj.getTypeImpl.findDelim()
result = newStmtList()
let obj =
if obj.kind != nnkSym:
let name = genSym(nskLet)
result.add newLetStmt(name, obj)
name
else:
obj
let caseStmt = nnkCaseStmt.newTree(nnkDotExpr.newTree(obj, delimName))
for x in delimKind.getTypeImpl[1..^1]:
let call = call.copyNimTree()
call.insert(1, obj)
call.add:
genast(x, disp = bindSym"Dispatch", enm = delimKind):
Dispatch[x](x)
caseStmt.add nnkOfBranch.newTree(x, call)
result.add caseStmt
echo result.repr
type
Color = enum Red, Green, Blue
MyType = object
case kind: Color
of Red:
discard
else:
discard
proc doThing(typ: MyType, _: Dispatch[Red]) = echo "Red"
proc doThing(typ: MyType, _: Dispatch[Green]) = echo "Green"
proc doThing(typ: MyType, _: Dispatch[Blue]) = echo "Blue"
variantDispatch(MyType(kind: Red), doThing())
variantDispatch(MyType(kind: Green), doThing())
variantDispatch(MyType(kind: Blue), doThing())