287 lines
9.3 KiB
Nim
287 lines
9.3 KiB
Nim
import macros, macro_dsl, estreams
|
|
from strutils import format
|
|
|
|
template newLenName() =
|
|
let lenName {.inject.} = ^("len"& $lenNames)
|
|
inc(lenNames)
|
|
|
|
template defPacketImports*() {.dirty.} =
|
|
import macros, macro_dsl, estreams
|
|
from strutils import format
|
|
|
|
macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
|
|
result = newNimNode(nnkStmtList)
|
|
let
|
|
typeName = quoted2ident(typeNameN)
|
|
packetID = ^"p"
|
|
streamID = ^"s"
|
|
var
|
|
constructorParams = newNimNode(nnkFormalParams).und(typeName)
|
|
constructor = newNimNode(nnkProcDef).und(
|
|
postfix(^("new"& $typeName.ident), "*"),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
constructorParams,
|
|
emptyNode(),
|
|
emptyNode())
|
|
pack = newNimNode(nnkProcDef).und(
|
|
postfix(^"pack", "*"),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkFormalParams).und(
|
|
emptyNode(), # : void
|
|
newNimNode(nnkIdentDefs).und(
|
|
streamID, # s: PBuffer
|
|
^"PBuffer",
|
|
newNimNode(nnkNilLit)),
|
|
newNimNode(nnkIdentDefs).und(
|
|
packetID, # p: var typeName
|
|
newNimNode(nnkVarTy).und(typeName),
|
|
emptyNode())),
|
|
emptyNode(),
|
|
emptyNode())
|
|
read = newNimNode(nnkProcDef).und(
|
|
newIdentNode("read"& $typeName.ident).postfix("*"),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkFormalParams).und(
|
|
typeName, #result type
|
|
newNimNode(nnkIdentDefs).und(
|
|
streamID, # s: PBuffer = nil
|
|
^"PBuffer",
|
|
newNimNode(nnkNilLit))),
|
|
emptyNode(),
|
|
emptyNode())
|
|
constructorBody = newNimNode(nnkStmtList)
|
|
packBody = newNimNode(nnkStmtList)
|
|
readBody = newNimNode(nnkStmtList)
|
|
lenNames = 0
|
|
for i in 0.. typeFields.len - 1:
|
|
let
|
|
name = typeFields[i][0]
|
|
dotName = packetID.dot(name)
|
|
resName = newIdentNode("result").dot(name)
|
|
case typeFields[i][1].kind
|
|
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
|
|
case $typeFields[i][1][0].ident
|
|
of "seq":
|
|
## let lenX = readInt16(s)
|
|
newLenName()
|
|
let
|
|
item = ^"item" ## item name in our iterators
|
|
seqType = typeFields[i][1][1] ## type of seq
|
|
readName = newIdentNode("read"& $seqType.ident)
|
|
readBody.add(newNimNode(nnkLetSection).und(
|
|
newNimNode(nnkIdentDefs).und(
|
|
lenName,
|
|
newNimNode(nnkEmpty),
|
|
newCall("readInt16", streamID))))
|
|
readBody.add( ## result.name = @[]
|
|
resName := ("@".prefix(newNimNode(nnkBracket))),
|
|
newNimNode(nnkForStmt).und( ## for item in 1..len:
|
|
item,
|
|
infix(1.lit, "..", lenName),
|
|
newNimNode(nnkStmtList).und(
|
|
newCall( ## add(result.name, unpack[seqType](stream))
|
|
"add", resName, newNimNode(nnkCall).und(readName, streamID)
|
|
) ) ) )
|
|
packbody.add(
|
|
newNimNode(nnkVarSection).und(newNimNode(nnkIdentDefs).und(
|
|
lenName, ## var lenName = int16(len(p.name))
|
|
newIdentNode("int16"),
|
|
newCall("int16", newCall("len", dotName)))),
|
|
newCall("writeBE", streamID, lenName),
|
|
newNimNode(nnkForStmt).und( ## for item in 0..length - 1: pack(p.name[item], stream)
|
|
item,
|
|
infix(0.lit, "..", infix(lenName, "-", 1.lit)),
|
|
newNimNode(nnkStmtList).und(
|
|
newCall("echo", item, ": ".lit),
|
|
newCall("pack", streamID, dotName[item]))))
|
|
#set the default value to @[] (new sequence)
|
|
typeFields[i][2] = "@".prefix(newNimNode(nnkBracket))
|
|
else:
|
|
error("Unknown type: "& treeRepr(typeFields[i]))
|
|
of nnkIdent: ##normal type
|
|
case $typeFields[i][1].ident
|
|
of "string": # length encoded string
|
|
packBody.add(newCall("write", streamID, dotName))
|
|
readBody.add(resName := newCall("readStr", streamID))
|
|
of "int8", "int16", "int32", "float32", "float64", "char", "bool":
|
|
packBody.add(newCall(
|
|
"writeBE", streamID, dotName))
|
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
|
else: ## hopefully the type you specified was another defpacket() type
|
|
packBody.add(newCall("pack", streamID, dotName))
|
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
|
else:
|
|
error("I dont know what to do with: "& treerepr(typeFields[i]))
|
|
|
|
var
|
|
toStringFunc = newNimNode(nnkProcDef).und(
|
|
newNimNode(nnkPostfix).und(
|
|
^"*",
|
|
newNimNode(nnkAccQuoted).und(^"$")),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkFormalParams).und(
|
|
^"string",
|
|
newNimNode(nnkIdentDefs).und(
|
|
packetID, # p: typeName
|
|
typeName,
|
|
emptyNode())),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkStmtList).und(# [6]
|
|
newNimNode(nnkAsgn).und(
|
|
^"result", ## result =
|
|
newNimNode(nnkCall).und(# [6][0][1]
|
|
^"format", ## format
|
|
emptyNode())))) ## "[TypeName $1 $2]"
|
|
formatStr = "["& $typeName.ident
|
|
|
|
const emptyFields = {nnkEmpty, nnkNilLit}
|
|
var objFields = newNimNode(nnkRecList)
|
|
for i in 0 ..< len(typeFields):
|
|
let fname = typeFields[i][0]
|
|
constructorParams.add(newNimNode(nnkIdentDefs).und(
|
|
fname,
|
|
typeFields[i][1],
|
|
typeFields[i][2]))
|
|
constructorBody.add((^"result").dot(fname) := fname)
|
|
#export the name
|
|
typeFields[i][0] = fname.postfix("*")
|
|
if not(typeFields[i][2].kind in emptyFields):
|
|
## empty the type default for the type def
|
|
typeFields[i][2] = newNimNode(nnkEmpty)
|
|
objFields.add(typeFields[i])
|
|
toStringFunc[6][0][1].add(
|
|
prefix("$", packetID.dot(fname)))
|
|
formatStr.add " $"
|
|
formatStr.add($(i + 1))
|
|
|
|
formatStr.add ']'
|
|
toStringFunc[6][0][1][1] = formatStr.lit()
|
|
|
|
result.add(
|
|
newNimNode(nnkTypeSection).und(
|
|
newNimNode(nnkTypeDef).und(
|
|
typeName.postfix("*"),
|
|
newNimNode(nnkEmpty),
|
|
newNimNode(nnkObjectTy).und(
|
|
newNimNode(nnkEmpty), #not sure what this is
|
|
newNimNode(nnkEmpty), #parent: OfInherit(Ident(!"SomeObj"))
|
|
objFields))))
|
|
result.add(constructor.und(constructorBody))
|
|
result.add(pack.und(packBody))
|
|
result.add(read.und(readBody))
|
|
result.add(toStringFunc)
|
|
when defined(GenPacketShowOutput):
|
|
echo(repr(result))
|
|
|
|
proc newProc*(name: NimNode; params: varargs[NimNode]; resultType: NimNode): NimNode {.compileTime.} =
|
|
result = newNimNode(nnkProcDef).und(
|
|
name,
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkFormalParams).und(resultType),
|
|
emptyNode(),
|
|
emptyNode(),
|
|
newNimNode(nnkStmtList))
|
|
result[3].add(params)
|
|
|
|
proc body*(procNode: NimNode): NimNode {.compileTime.} =
|
|
assert procNode.kind == nnkProcDef and procNode[6].kind == nnkStmtList
|
|
result = procNode[6]
|
|
|
|
proc iddefs*(a, b: string; c: NimNode): NimNode {.compileTime.} =
|
|
result = newNimNode(nnkIdentDefs).und(^a, ^b, c)
|
|
proc iddefs*(a: string; b: NimNode): NimNode {.compileTime.} =
|
|
result = newNimNode(nnkIdentDefs).und(^a, b, emptyNode())
|
|
proc varTy*(a: NimNode): NimNode {.compileTime.} =
|
|
result = newNimNode(nnkVarTy).und(a)
|
|
|
|
macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
|
|
var
|
|
packetID = ^"p"
|
|
streamID = ^"s"
|
|
result = newNimNode(nnkStmtList).und(
|
|
newProc(
|
|
(^("read"& $typeName.ident)).postfix("*"),
|
|
[ iddefs("s", "PBuffer", newNimNode(nnkNilLit)) ],
|
|
typeName),
|
|
newProc(
|
|
(^"pack").postfix("*"),
|
|
[ iddefs("s", "PBuffer", newNimNode(nnkNilLit)),
|
|
iddefs("p", varTy(typeName)) ],
|
|
emptyNode()))
|
|
var
|
|
readBody = result[0][6]
|
|
packBody = result[1][6]
|
|
resName = ^"result"
|
|
|
|
case underlyingType.kind
|
|
of nnkBracketExpr:
|
|
case $underlyingType[0].ident
|
|
of "array":
|
|
for i in underlyingType[1][1].intval.int .. underlyingType[1][2].intval.int:
|
|
readBody.add(
|
|
newCall("read", ^"s", resName[lit(i)]))
|
|
packBody.add(
|
|
newCall("writeBE", ^"s", packetID[lit(i)]))
|
|
else:
|
|
echo "Unknown type: ", repr(underlyingtype)
|
|
else:
|
|
echo "unknown type:", repr(underlyingtype)
|
|
echo(repr(result))
|
|
|
|
template forwardPacketT*(typeName: untyped; underlyingType: untyped) {.dirty.} =
|
|
proc `read typeName`*(buffer: PBuffer): typeName =
|
|
#discard readData(s, addr result, sizeof(result))
|
|
var res: underlyingType
|
|
buffer.read(res)
|
|
result = typeName(res)
|
|
proc `pack`*(buffer: PBuffer; ord: var typeName) =
|
|
#writeData(s, addr p, sizeof(p))
|
|
buffer.write(underlyingType(ord))
|
|
|
|
when isMainModule:
|
|
type
|
|
SomeEnum = enum
|
|
A = 0'i8,
|
|
B, C
|
|
forwardPacket(SomeEnum, int8)
|
|
|
|
|
|
defPacket(Foo, tuple[x: array[0..4, int8]])
|
|
var f = newFoo([4'i8, 3'i8, 2'i8, 1'i8, 0'i8])
|
|
var s2 = newStringStream("")
|
|
f.pack(s2)
|
|
assert s2.data == "\4\3\2\1\0"
|
|
|
|
var s = newStringStream()
|
|
s.flushImpl = proc(s: PStream) =
|
|
var z = PStringStream(s)
|
|
z.setPosition(0)
|
|
z.data.setLen(0)
|
|
|
|
|
|
s.setPosition(0)
|
|
s.data.setLen(0)
|
|
var o = B
|
|
o.pack(s)
|
|
o = A
|
|
o.pack(s)
|
|
o = C
|
|
o.pack(s)
|
|
assert s.data == "\1\0\2"
|
|
s.flush
|
|
|
|
defPacket(Y, tuple[z: int8])
|
|
proc `$`(z: Y): string = result = "Y(" & $z.z & ")"
|
|
defPacket(TestPkt, tuple[x: seq[Y]])
|
|
var test = newTestPkt()
|
|
test.x.add([newY(5), newY(4), newY(3), newY(2), newY(1)])
|
|
for itm in test.x:
|
|
echo(itm)
|
|
test.pack(s)
|
|
echo(repr(s.data))
|