Add initial proof of concept.
This commit is contained in:
parent
c8a87911b5
commit
53f7dd4008
2 changed files with 850 additions and 0 deletions
425
butterfly.nim
Normal file
425
butterfly.nim
Normal file
|
|
@ -0,0 +1,425 @@
|
||||||
|
import macros
|
||||||
|
import tables
|
||||||
|
import strutils
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
proc `$`*[T](ar: openarray[T]): string =
|
||||||
|
## Converts an array into a string
|
||||||
|
result = "["
|
||||||
|
if ar.len() > 0:
|
||||||
|
result &= $ar[0]
|
||||||
|
for i in 1..ar.len()-1:
|
||||||
|
result &= ", " & $ar[i]
|
||||||
|
result &= "]"
|
||||||
|
|
||||||
|
proc typeToStr*[T](some:typedesc[T]): string = name(T)
|
||||||
|
|
||||||
|
template tupleObjToStr(obj): string {.dirty.} =
|
||||||
|
var res = typeToStr(type(obj))
|
||||||
|
template helper(n) {.gensym.} =
|
||||||
|
res.add("(")
|
||||||
|
var firstElement = true
|
||||||
|
for name, value in n.fieldPairs():
|
||||||
|
when compiles(value):
|
||||||
|
if not firstElement:
|
||||||
|
res.add(", ")
|
||||||
|
res.add(name)
|
||||||
|
res.add(": ")
|
||||||
|
when (value is object or value is tuple):
|
||||||
|
when (value is tuple):
|
||||||
|
res.add("tuple " & typeToStr(type(value)))
|
||||||
|
else:
|
||||||
|
res.add(typeToStr(type(value)))
|
||||||
|
helper(value)
|
||||||
|
elif (value is string):
|
||||||
|
res.add("\"" & $value & "\"")
|
||||||
|
else:
|
||||||
|
res.add($value)
|
||||||
|
firstElement = false
|
||||||
|
res.add(")")
|
||||||
|
helper(obj)
|
||||||
|
res
|
||||||
|
|
||||||
|
proc `$`*(s: ref object): string =
|
||||||
|
result = "ref " & tupleObjToStr(s[]).replace(":ObjectType", "")
|
||||||
|
|
||||||
|
proc objToStr*[T: object](obj: var T): string =
|
||||||
|
tupleObjToStr(obj)
|
||||||
|
|
||||||
|
proc objToStr*[T: tuple](obj: T): string =
|
||||||
|
result = "tuple " & tupleObjToStr(obj)
|
||||||
|
|
||||||
|
macro toString*(obj: typed): untyped =
|
||||||
|
## this macro is to work around not being
|
||||||
|
## able to override system.`$`
|
||||||
|
##
|
||||||
|
## Basically, I want to use my proc to print
|
||||||
|
## objects and tuples, but the regular $ for
|
||||||
|
## everything else
|
||||||
|
let kind = obj.getType().typeKind
|
||||||
|
case kind:
|
||||||
|
of ntyTuple, ntyObject:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
einheit.objToStr(obj)
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
of ntyString:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
"\"" & $(obj) & "\""
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
else:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
$(obj)
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
LitKind = enum
|
||||||
|
lkBoolLit,
|
||||||
|
lkCharLit,
|
||||||
|
lkIntLit, lkInt8Lit,
|
||||||
|
lkInt16Lit, lkInt32Lit, lkInt64Lit,
|
||||||
|
lkUIntLit, lkUInt8Lit,
|
||||||
|
lkUInt16Lit, lkUInt32Lit, lkUInt64Lit,
|
||||||
|
lkFloatLit, lkFloat32Lit, lkFloat64Lit,
|
||||||
|
lkStrLit
|
||||||
|
|
||||||
|
Literal = ref object
|
||||||
|
case kind: LitKind
|
||||||
|
of lkBoolLit:
|
||||||
|
boolVal: bool
|
||||||
|
of lkCharLit:
|
||||||
|
charVal: char
|
||||||
|
of lkIntLit:
|
||||||
|
intVal: int
|
||||||
|
of lkInt8Lit:
|
||||||
|
int8Val: int8
|
||||||
|
of lkInt16Lit:
|
||||||
|
int16Val: int16
|
||||||
|
of lkInt32Lit:
|
||||||
|
int32Val: int32
|
||||||
|
of lkInt64Lit:
|
||||||
|
int64Val: int64
|
||||||
|
of lkUIntLit:
|
||||||
|
uintVal: uint
|
||||||
|
of lkUInt8Lit:
|
||||||
|
uint8Val: uint8
|
||||||
|
of lkUInt16Lit:
|
||||||
|
uint16Val: uint16
|
||||||
|
of lkUInt32Lit:
|
||||||
|
uint32Val: uint32
|
||||||
|
of lkUInt64Lit:
|
||||||
|
uint64Val: uint64
|
||||||
|
of lkFloatLit:
|
||||||
|
floatVal: float
|
||||||
|
of lkFloat32Lit:
|
||||||
|
float32Val: float32
|
||||||
|
of lkFloat64Lit:
|
||||||
|
float64Val: float64
|
||||||
|
of lkStrLit:
|
||||||
|
strVal: string
|
||||||
|
|
||||||
|
FieldType = enum
|
||||||
|
ftObject,
|
||||||
|
ftLiteral
|
||||||
|
|
||||||
|
Field = ref object
|
||||||
|
case kind: FieldType
|
||||||
|
of ftObject:
|
||||||
|
objVal: Object
|
||||||
|
of ftLiteral:
|
||||||
|
litVal: Literal
|
||||||
|
|
||||||
|
Object = ref object
|
||||||
|
name: string
|
||||||
|
fields: TableRef[string, Field]
|
||||||
|
|
||||||
|
ObjectType = ref object
|
||||||
|
name: string
|
||||||
|
fields: TableRef[string, string]
|
||||||
|
|
||||||
|
TypeType = enum
|
||||||
|
ttObject,
|
||||||
|
ttLiteral
|
||||||
|
|
||||||
|
Type = ref object
|
||||||
|
case kind: TypeType
|
||||||
|
of ttObject:
|
||||||
|
objType: ObjectType
|
||||||
|
of ttLiteral:
|
||||||
|
litType: string
|
||||||
|
|
||||||
|
Context = ref object
|
||||||
|
locals: TableRef[string, Context]
|
||||||
|
stmts: seq[string]
|
||||||
|
types: TableRef[string, Type]
|
||||||
|
parent: Context
|
||||||
|
|
||||||
|
proc value(lit: Literal): string =
|
||||||
|
case lit.kind:
|
||||||
|
of lkBoolLit:
|
||||||
|
result = $lit.boolVal
|
||||||
|
of lkCharLit:
|
||||||
|
result = "'" & $lit.charVal & "'"
|
||||||
|
of lkIntLit:
|
||||||
|
result = $lit.intVal
|
||||||
|
of lkInt8Lit:
|
||||||
|
result = $lit.int8Val
|
||||||
|
of lkInt16Lit:
|
||||||
|
result = $lit.int16Val
|
||||||
|
of lkInt32Lit:
|
||||||
|
result = $lit.int32Val
|
||||||
|
of lkInt64Lit:
|
||||||
|
result = $lit.int64Val
|
||||||
|
of lkUIntLit:
|
||||||
|
result = $lit.uintVal
|
||||||
|
of lkUInt8Lit:
|
||||||
|
result = $lit.uint8Val
|
||||||
|
of lkUInt16Lit:
|
||||||
|
result = $lit.uint16Val
|
||||||
|
of lkUInt32Lit:
|
||||||
|
result = $lit.uint32Val
|
||||||
|
of lkUInt64Lit:
|
||||||
|
result = $lit.uint64Val
|
||||||
|
of lkFloatLit:
|
||||||
|
result = $lit.floatVal
|
||||||
|
of lkFloat32Lit:
|
||||||
|
result = $lit.float32Val
|
||||||
|
of lkFloat64Lit:
|
||||||
|
result = $lit.float64Val
|
||||||
|
of lkStrLit:
|
||||||
|
result = "\"" & $lit.strVal & "\""
|
||||||
|
|
||||||
|
|
||||||
|
proc type(lit: Literal): string =
|
||||||
|
case lit.kind:
|
||||||
|
of lkBoolLit:
|
||||||
|
result = "bool"
|
||||||
|
of lkCharLit:
|
||||||
|
result = "char"
|
||||||
|
of lkIntLit:
|
||||||
|
result = "int"
|
||||||
|
of lkInt8Lit:
|
||||||
|
result = "int8"
|
||||||
|
of lkInt16Lit:
|
||||||
|
result = "int16"
|
||||||
|
of lkInt32Lit:
|
||||||
|
result = "int32"
|
||||||
|
of lkInt64Lit:
|
||||||
|
result = "int64"
|
||||||
|
of lkUIntLit:
|
||||||
|
result = "uint"
|
||||||
|
of lkUInt8Lit:
|
||||||
|
result = "uint8"
|
||||||
|
of lkUInt16Lit:
|
||||||
|
result = "uint16"
|
||||||
|
of lkUInt32Lit:
|
||||||
|
result = "uint32"
|
||||||
|
of lkUInt64Lit:
|
||||||
|
result = "uint64"
|
||||||
|
of lkFloatLit:
|
||||||
|
result = "float"
|
||||||
|
of lkFloat32Lit:
|
||||||
|
result = "float32"
|
||||||
|
of lkFloat64Lit:
|
||||||
|
result = "float64"
|
||||||
|
of lkStrLit:
|
||||||
|
result = "string"
|
||||||
|
|
||||||
|
proc type*(obj: Object): string
|
||||||
|
proc value*(obj: Object): string
|
||||||
|
|
||||||
|
proc type*(field: Field): string =
|
||||||
|
case field.kind:
|
||||||
|
of ftObject:
|
||||||
|
result = butterfly.type(field.objVal)
|
||||||
|
of ftLiteral:
|
||||||
|
result = butterfly.type(field.litVal)
|
||||||
|
|
||||||
|
proc value*(field: Field): string =
|
||||||
|
case field.kind:
|
||||||
|
of ftObject:
|
||||||
|
result = butterfly.value(field.objVal)
|
||||||
|
of ftLiteral:
|
||||||
|
result = butterfly.value(field.litVal)
|
||||||
|
|
||||||
|
proc type*(obj: Object): string =
|
||||||
|
result = obj.name & "("
|
||||||
|
|
||||||
|
var fieldData: seq[string] = @[]
|
||||||
|
|
||||||
|
for fname in obj.fields.keys():
|
||||||
|
let value = obj.fields[fname]
|
||||||
|
let ftype = butterfly.type(value)
|
||||||
|
fieldData.add(fname & ": " & ftype)
|
||||||
|
|
||||||
|
result &= fieldData.join(", ")
|
||||||
|
result &= ")"
|
||||||
|
|
||||||
|
proc value*(obj: Object): string =
|
||||||
|
result = obj.name & "("
|
||||||
|
|
||||||
|
var fieldData: seq[string] = @[]
|
||||||
|
|
||||||
|
for fname in obj.fields.keys():
|
||||||
|
let value = obj.fields[fname]
|
||||||
|
let fval = butterfly.value(value)
|
||||||
|
fieldData.add(fname & ": " & fval)
|
||||||
|
|
||||||
|
result &= fieldData.join(", ")
|
||||||
|
result &= ")"
|
||||||
|
|
||||||
|
proc `value=`*[T](lit: var Literal, value: T) =
|
||||||
|
when T is char:
|
||||||
|
lit.charVal = value
|
||||||
|
elif T is bool:
|
||||||
|
lit.boolVal = value
|
||||||
|
elif T is int:
|
||||||
|
lit.intVal = value
|
||||||
|
elif T is int8:
|
||||||
|
lit.int8Val = value
|
||||||
|
elif T is int16:
|
||||||
|
lit.int16Val = value
|
||||||
|
elif T is int32:
|
||||||
|
lit.int32Val = value
|
||||||
|
elif T is int64:
|
||||||
|
lit.int64Val = value
|
||||||
|
elif T is uint:
|
||||||
|
lit.intVal = value
|
||||||
|
elif T is uint8:
|
||||||
|
lit.int8Val = value
|
||||||
|
elif T is uint16:
|
||||||
|
lit.int16Val = value
|
||||||
|
elif T is uint32:
|
||||||
|
lit.int32Val = value
|
||||||
|
elif T is uint64:
|
||||||
|
lit.int64Val = value
|
||||||
|
elif T is float:
|
||||||
|
lit.floatVal = value
|
||||||
|
elif T is float32:
|
||||||
|
lit.float32Val = value
|
||||||
|
elif T is float64:
|
||||||
|
lit.float64Val = value
|
||||||
|
elif T is string:
|
||||||
|
lit.strVal = value
|
||||||
|
|
||||||
|
var procTable = newTable[string, Context]()
|
||||||
|
|
||||||
|
proc debugProc() =
|
||||||
|
echo "Debug"
|
||||||
|
|
||||||
|
proc updateProcTable(procName: string, params: string) =
|
||||||
|
let
|
||||||
|
key = procName & params
|
||||||
|
|
||||||
|
if not procTable.hasKey(key):
|
||||||
|
procTable[key] = new(Context)
|
||||||
|
|
||||||
|
template doNodes(nodeKind: NimNodeKind, node: NimNode, code: untyped) {.dirty.}=
|
||||||
|
var stackN: seq[NimNode] = @[node]
|
||||||
|
|
||||||
|
while stackN.len() > 0:
|
||||||
|
let newNode = stackN.pop()
|
||||||
|
for i in 0 ..< newNode.len():
|
||||||
|
let n = newNode[i]
|
||||||
|
if n.kind == nodeKind:
|
||||||
|
code
|
||||||
|
else:
|
||||||
|
stackN.add(n)
|
||||||
|
|
||||||
|
|
||||||
|
iterator getNode(nodeKind: NimNodeKind, node: NimNode): NimNode =
|
||||||
|
var stack: seq[NimNode] = @[node]
|
||||||
|
|
||||||
|
while stack.len() > 0:
|
||||||
|
let newNode = stack.pop()
|
||||||
|
for i in 0 ..< newNode.len():
|
||||||
|
let child = newNode[i]
|
||||||
|
if child.kind == nodeKind:
|
||||||
|
yield child
|
||||||
|
else:
|
||||||
|
stack.add(child)
|
||||||
|
|
||||||
|
template addStmtTree(parentx: NimNode, nodex: NimNode, indexx: int): untyped {.dirty.} =
|
||||||
|
proc helper(parent: NimNode, node: NimNode, index: int): NimNode =
|
||||||
|
var newList = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
for child in node.children:
|
||||||
|
|
||||||
|
if child.kind == nnkIfStmt:
|
||||||
|
for subch in child.children:
|
||||||
|
if subch.kind == nnkElifBranch:
|
||||||
|
discard helper(subch, subch[1], 1)
|
||||||
|
else:
|
||||||
|
discard helper(subch, subch[0], 0)
|
||||||
|
|
||||||
|
if child.kind == nnkBlockStmt:
|
||||||
|
discard helper(child, child[1], 1)
|
||||||
|
|
||||||
|
|
||||||
|
let line = child.lineInfoObj()
|
||||||
|
echo "Inserting debug on line: " & $line.line
|
||||||
|
newList.add(newCall(bindSym("debugProc")))
|
||||||
|
newList.add(child)
|
||||||
|
for n in getNode(nnkCall, child):
|
||||||
|
if n.kind != nnkEmpty:
|
||||||
|
let
|
||||||
|
impl = n[0].getImpl()
|
||||||
|
procName = $impl[0].toStrLit()
|
||||||
|
params = $impl[3].toStrLit()
|
||||||
|
key = procName & params
|
||||||
|
|
||||||
|
if not visitedProcs.hasKey(key):
|
||||||
|
# this can be added once a compiler bug is fixed
|
||||||
|
#[newList.add(
|
||||||
|
newCall(
|
||||||
|
bindSym("updateProcTable"),
|
||||||
|
newLit(procName),
|
||||||
|
newLit(params)
|
||||||
|
)
|
||||||
|
)]#
|
||||||
|
visitedProcs[key] = true
|
||||||
|
# add the next stmt list onto the stack
|
||||||
|
# of the newly discovered proc
|
||||||
|
if impl[6].kind == nnkStmtList:
|
||||||
|
discard helper(impl, impl[6], 6)
|
||||||
|
echo impl.toStrLit
|
||||||
|
|
||||||
|
newList.add(newCall(bindSym("debugProc")))
|
||||||
|
parent[index] = newList
|
||||||
|
return parent
|
||||||
|
helper(parentx, nodex, indexx)
|
||||||
|
|
||||||
|
|
||||||
|
macro debug(code: typed): untyped =
|
||||||
|
var visitedProcs = newTable[string, bool]()
|
||||||
|
|
||||||
|
code[0] = addStmtTree(code[0], code[0][6], 6)
|
||||||
|
|
||||||
|
result = code
|
||||||
|
echo code.treeRepr
|
||||||
|
echo code.toStrLit()
|
||||||
|
|
||||||
|
# This proc now has it's stmts replaced!
|
||||||
|
proc otherProc(stuff: string): string =
|
||||||
|
let a = "beans"
|
||||||
|
echo stuff & a
|
||||||
|
result = stuff & a
|
||||||
|
|
||||||
|
debug:
|
||||||
|
proc main() =
|
||||||
|
let x = "balls".otherProc()
|
||||||
|
let y = otherProc("balls2")
|
||||||
|
block:
|
||||||
|
echo "Beans"
|
||||||
|
echo "Beans"
|
||||||
|
if x == "ballsbeans":
|
||||||
|
echo "Yaya"
|
||||||
|
echo "who"
|
||||||
|
else:
|
||||||
|
echo "balls"
|
||||||
|
echo "multi"
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
425
src/butterfly.nim
Normal file
425
src/butterfly.nim
Normal file
|
|
@ -0,0 +1,425 @@
|
||||||
|
import macros
|
||||||
|
import tables
|
||||||
|
import strutils
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
proc `$`*[T](ar: openarray[T]): string =
|
||||||
|
## Converts an array into a string
|
||||||
|
result = "["
|
||||||
|
if ar.len() > 0:
|
||||||
|
result &= $ar[0]
|
||||||
|
for i in 1..ar.len()-1:
|
||||||
|
result &= ", " & $ar[i]
|
||||||
|
result &= "]"
|
||||||
|
|
||||||
|
proc typeToStr*[T](some:typedesc[T]): string = name(T)
|
||||||
|
|
||||||
|
template tupleObjToStr(obj): string {.dirty.} =
|
||||||
|
var res = typeToStr(type(obj))
|
||||||
|
template helper(n) {.gensym.} =
|
||||||
|
res.add("(")
|
||||||
|
var firstElement = true
|
||||||
|
for name, value in n.fieldPairs():
|
||||||
|
when compiles(value):
|
||||||
|
if not firstElement:
|
||||||
|
res.add(", ")
|
||||||
|
res.add(name)
|
||||||
|
res.add(": ")
|
||||||
|
when (value is object or value is tuple):
|
||||||
|
when (value is tuple):
|
||||||
|
res.add("tuple " & typeToStr(type(value)))
|
||||||
|
else:
|
||||||
|
res.add(typeToStr(type(value)))
|
||||||
|
helper(value)
|
||||||
|
elif (value is string):
|
||||||
|
res.add("\"" & $value & "\"")
|
||||||
|
else:
|
||||||
|
res.add($value)
|
||||||
|
firstElement = false
|
||||||
|
res.add(")")
|
||||||
|
helper(obj)
|
||||||
|
res
|
||||||
|
|
||||||
|
proc `$`*(s: ref object): string =
|
||||||
|
result = "ref " & tupleObjToStr(s[]).replace(":ObjectType", "")
|
||||||
|
|
||||||
|
proc objToStr*[T: object](obj: var T): string =
|
||||||
|
tupleObjToStr(obj)
|
||||||
|
|
||||||
|
proc objToStr*[T: tuple](obj: T): string =
|
||||||
|
result = "tuple " & tupleObjToStr(obj)
|
||||||
|
|
||||||
|
macro toString*(obj: typed): untyped =
|
||||||
|
## this macro is to work around not being
|
||||||
|
## able to override system.`$`
|
||||||
|
##
|
||||||
|
## Basically, I want to use my proc to print
|
||||||
|
## objects and tuples, but the regular $ for
|
||||||
|
## everything else
|
||||||
|
let kind = obj.getType().typeKind
|
||||||
|
case kind:
|
||||||
|
of ntyTuple, ntyObject:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
einheit.objToStr(obj)
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
of ntyString:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
"\"" & $(obj) & "\""
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
else:
|
||||||
|
template toStrAst(obj): string =
|
||||||
|
$(obj)
|
||||||
|
result = getAst(toStrAst(obj))
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
LitKind = enum
|
||||||
|
lkBoolLit,
|
||||||
|
lkCharLit,
|
||||||
|
lkIntLit, lkInt8Lit,
|
||||||
|
lkInt16Lit, lkInt32Lit, lkInt64Lit,
|
||||||
|
lkUIntLit, lkUInt8Lit,
|
||||||
|
lkUInt16Lit, lkUInt32Lit, lkUInt64Lit,
|
||||||
|
lkFloatLit, lkFloat32Lit, lkFloat64Lit,
|
||||||
|
lkStrLit
|
||||||
|
|
||||||
|
Literal = ref object
|
||||||
|
case kind: LitKind
|
||||||
|
of lkBoolLit:
|
||||||
|
boolVal: bool
|
||||||
|
of lkCharLit:
|
||||||
|
charVal: char
|
||||||
|
of lkIntLit:
|
||||||
|
intVal: int
|
||||||
|
of lkInt8Lit:
|
||||||
|
int8Val: int8
|
||||||
|
of lkInt16Lit:
|
||||||
|
int16Val: int16
|
||||||
|
of lkInt32Lit:
|
||||||
|
int32Val: int32
|
||||||
|
of lkInt64Lit:
|
||||||
|
int64Val: int64
|
||||||
|
of lkUIntLit:
|
||||||
|
uintVal: uint
|
||||||
|
of lkUInt8Lit:
|
||||||
|
uint8Val: uint8
|
||||||
|
of lkUInt16Lit:
|
||||||
|
uint16Val: uint16
|
||||||
|
of lkUInt32Lit:
|
||||||
|
uint32Val: uint32
|
||||||
|
of lkUInt64Lit:
|
||||||
|
uint64Val: uint64
|
||||||
|
of lkFloatLit:
|
||||||
|
floatVal: float
|
||||||
|
of lkFloat32Lit:
|
||||||
|
float32Val: float32
|
||||||
|
of lkFloat64Lit:
|
||||||
|
float64Val: float64
|
||||||
|
of lkStrLit:
|
||||||
|
strVal: string
|
||||||
|
|
||||||
|
FieldType = enum
|
||||||
|
ftObject,
|
||||||
|
ftLiteral
|
||||||
|
|
||||||
|
Field = ref object
|
||||||
|
case kind: FieldType
|
||||||
|
of ftObject:
|
||||||
|
objVal: Object
|
||||||
|
of ftLiteral:
|
||||||
|
litVal: Literal
|
||||||
|
|
||||||
|
Object = ref object
|
||||||
|
name: string
|
||||||
|
fields: TableRef[string, Field]
|
||||||
|
|
||||||
|
ObjectType = ref object
|
||||||
|
name: string
|
||||||
|
fields: TableRef[string, string]
|
||||||
|
|
||||||
|
TypeType = enum
|
||||||
|
ttObject,
|
||||||
|
ttLiteral
|
||||||
|
|
||||||
|
Type = ref object
|
||||||
|
case kind: TypeType
|
||||||
|
of ttObject:
|
||||||
|
objType: ObjectType
|
||||||
|
of ttLiteral:
|
||||||
|
litType: string
|
||||||
|
|
||||||
|
Context = ref object
|
||||||
|
locals: TableRef[string, Context]
|
||||||
|
stmts: seq[string]
|
||||||
|
types: TableRef[string, Type]
|
||||||
|
parent: Context
|
||||||
|
|
||||||
|
proc value(lit: Literal): string =
|
||||||
|
case lit.kind:
|
||||||
|
of lkBoolLit:
|
||||||
|
result = $lit.boolVal
|
||||||
|
of lkCharLit:
|
||||||
|
result = "'" & $lit.charVal & "'"
|
||||||
|
of lkIntLit:
|
||||||
|
result = $lit.intVal
|
||||||
|
of lkInt8Lit:
|
||||||
|
result = $lit.int8Val
|
||||||
|
of lkInt16Lit:
|
||||||
|
result = $lit.int16Val
|
||||||
|
of lkInt32Lit:
|
||||||
|
result = $lit.int32Val
|
||||||
|
of lkInt64Lit:
|
||||||
|
result = $lit.int64Val
|
||||||
|
of lkUIntLit:
|
||||||
|
result = $lit.uintVal
|
||||||
|
of lkUInt8Lit:
|
||||||
|
result = $lit.uint8Val
|
||||||
|
of lkUInt16Lit:
|
||||||
|
result = $lit.uint16Val
|
||||||
|
of lkUInt32Lit:
|
||||||
|
result = $lit.uint32Val
|
||||||
|
of lkUInt64Lit:
|
||||||
|
result = $lit.uint64Val
|
||||||
|
of lkFloatLit:
|
||||||
|
result = $lit.floatVal
|
||||||
|
of lkFloat32Lit:
|
||||||
|
result = $lit.float32Val
|
||||||
|
of lkFloat64Lit:
|
||||||
|
result = $lit.float64Val
|
||||||
|
of lkStrLit:
|
||||||
|
result = "\"" & $lit.strVal & "\""
|
||||||
|
|
||||||
|
|
||||||
|
proc type(lit: Literal): string =
|
||||||
|
case lit.kind:
|
||||||
|
of lkBoolLit:
|
||||||
|
result = "bool"
|
||||||
|
of lkCharLit:
|
||||||
|
result = "char"
|
||||||
|
of lkIntLit:
|
||||||
|
result = "int"
|
||||||
|
of lkInt8Lit:
|
||||||
|
result = "int8"
|
||||||
|
of lkInt16Lit:
|
||||||
|
result = "int16"
|
||||||
|
of lkInt32Lit:
|
||||||
|
result = "int32"
|
||||||
|
of lkInt64Lit:
|
||||||
|
result = "int64"
|
||||||
|
of lkUIntLit:
|
||||||
|
result = "uint"
|
||||||
|
of lkUInt8Lit:
|
||||||
|
result = "uint8"
|
||||||
|
of lkUInt16Lit:
|
||||||
|
result = "uint16"
|
||||||
|
of lkUInt32Lit:
|
||||||
|
result = "uint32"
|
||||||
|
of lkUInt64Lit:
|
||||||
|
result = "uint64"
|
||||||
|
of lkFloatLit:
|
||||||
|
result = "float"
|
||||||
|
of lkFloat32Lit:
|
||||||
|
result = "float32"
|
||||||
|
of lkFloat64Lit:
|
||||||
|
result = "float64"
|
||||||
|
of lkStrLit:
|
||||||
|
result = "string"
|
||||||
|
|
||||||
|
proc type*(obj: Object): string
|
||||||
|
proc value*(obj: Object): string
|
||||||
|
|
||||||
|
proc type*(field: Field): string =
|
||||||
|
case field.kind:
|
||||||
|
of ftObject:
|
||||||
|
result = butterfly.type(field.objVal)
|
||||||
|
of ftLiteral:
|
||||||
|
result = butterfly.type(field.litVal)
|
||||||
|
|
||||||
|
proc value*(field: Field): string =
|
||||||
|
case field.kind:
|
||||||
|
of ftObject:
|
||||||
|
result = butterfly.value(field.objVal)
|
||||||
|
of ftLiteral:
|
||||||
|
result = butterfly.value(field.litVal)
|
||||||
|
|
||||||
|
proc type*(obj: Object): string =
|
||||||
|
result = obj.name & "("
|
||||||
|
|
||||||
|
var fieldData: seq[string] = @[]
|
||||||
|
|
||||||
|
for fname in obj.fields.keys():
|
||||||
|
let value = obj.fields[fname]
|
||||||
|
let ftype = butterfly.type(value)
|
||||||
|
fieldData.add(fname & ": " & ftype)
|
||||||
|
|
||||||
|
result &= fieldData.join(", ")
|
||||||
|
result &= ")"
|
||||||
|
|
||||||
|
proc value*(obj: Object): string =
|
||||||
|
result = obj.name & "("
|
||||||
|
|
||||||
|
var fieldData: seq[string] = @[]
|
||||||
|
|
||||||
|
for fname in obj.fields.keys():
|
||||||
|
let value = obj.fields[fname]
|
||||||
|
let fval = butterfly.value(value)
|
||||||
|
fieldData.add(fname & ": " & fval)
|
||||||
|
|
||||||
|
result &= fieldData.join(", ")
|
||||||
|
result &= ")"
|
||||||
|
|
||||||
|
proc `value=`*[T](lit: var Literal, value: T) =
|
||||||
|
when T is char:
|
||||||
|
lit.charVal = value
|
||||||
|
elif T is bool:
|
||||||
|
lit.boolVal = value
|
||||||
|
elif T is int:
|
||||||
|
lit.intVal = value
|
||||||
|
elif T is int8:
|
||||||
|
lit.int8Val = value
|
||||||
|
elif T is int16:
|
||||||
|
lit.int16Val = value
|
||||||
|
elif T is int32:
|
||||||
|
lit.int32Val = value
|
||||||
|
elif T is int64:
|
||||||
|
lit.int64Val = value
|
||||||
|
elif T is uint:
|
||||||
|
lit.intVal = value
|
||||||
|
elif T is uint8:
|
||||||
|
lit.int8Val = value
|
||||||
|
elif T is uint16:
|
||||||
|
lit.int16Val = value
|
||||||
|
elif T is uint32:
|
||||||
|
lit.int32Val = value
|
||||||
|
elif T is uint64:
|
||||||
|
lit.int64Val = value
|
||||||
|
elif T is float:
|
||||||
|
lit.floatVal = value
|
||||||
|
elif T is float32:
|
||||||
|
lit.float32Val = value
|
||||||
|
elif T is float64:
|
||||||
|
lit.float64Val = value
|
||||||
|
elif T is string:
|
||||||
|
lit.strVal = value
|
||||||
|
|
||||||
|
var procTable = newTable[string, Context]()
|
||||||
|
|
||||||
|
proc debugProc() =
|
||||||
|
echo "Debug"
|
||||||
|
|
||||||
|
proc updateProcTable(procName: string, params: string) =
|
||||||
|
let
|
||||||
|
key = procName & params
|
||||||
|
|
||||||
|
if not procTable.hasKey(key):
|
||||||
|
procTable[key] = new(Context)
|
||||||
|
|
||||||
|
template doNodes(nodeKind: NimNodeKind, node: NimNode, code: untyped) {.dirty.}=
|
||||||
|
var stackN: seq[NimNode] = @[node]
|
||||||
|
|
||||||
|
while stackN.len() > 0:
|
||||||
|
let newNode = stackN.pop()
|
||||||
|
for i in 0 ..< newNode.len():
|
||||||
|
let n = newNode[i]
|
||||||
|
if n.kind == nodeKind:
|
||||||
|
code
|
||||||
|
else:
|
||||||
|
stackN.add(n)
|
||||||
|
|
||||||
|
|
||||||
|
iterator getNode(nodeKind: NimNodeKind, node: NimNode): NimNode =
|
||||||
|
var stack: seq[NimNode] = @[node]
|
||||||
|
|
||||||
|
while stack.len() > 0:
|
||||||
|
let newNode = stack.pop()
|
||||||
|
for i in 0 ..< newNode.len():
|
||||||
|
let child = newNode[i]
|
||||||
|
if child.kind == nodeKind:
|
||||||
|
yield child
|
||||||
|
else:
|
||||||
|
stack.add(child)
|
||||||
|
|
||||||
|
template addStmtTree(parentx: NimNode, nodex: NimNode, indexx: int): untyped {.dirty.} =
|
||||||
|
proc helper(parent: NimNode, node: NimNode, index: int): NimNode =
|
||||||
|
var newList = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
for child in node.children:
|
||||||
|
|
||||||
|
if child.kind == nnkIfStmt:
|
||||||
|
for subch in child.children:
|
||||||
|
if subch.kind == nnkElifBranch:
|
||||||
|
discard helper(subch, subch[1], 1)
|
||||||
|
else:
|
||||||
|
discard helper(subch, subch[0], 0)
|
||||||
|
|
||||||
|
if child.kind == nnkBlockStmt:
|
||||||
|
discard helper(child, child[1], 1)
|
||||||
|
|
||||||
|
|
||||||
|
let line = child.lineInfoObj()
|
||||||
|
echo "Inserting debug on line: " & $line.line
|
||||||
|
newList.add(newCall(bindSym("debugProc")))
|
||||||
|
newList.add(child)
|
||||||
|
for n in getNode(nnkCall, child):
|
||||||
|
if n.kind != nnkEmpty:
|
||||||
|
let
|
||||||
|
impl = n[0].getImpl()
|
||||||
|
procName = $impl[0].toStrLit()
|
||||||
|
params = $impl[3].toStrLit()
|
||||||
|
key = procName & params
|
||||||
|
|
||||||
|
if not visitedProcs.hasKey(key):
|
||||||
|
# this can be added once a compiler bug is fixed
|
||||||
|
#[newList.add(
|
||||||
|
newCall(
|
||||||
|
bindSym("updateProcTable"),
|
||||||
|
newLit(procName),
|
||||||
|
newLit(params)
|
||||||
|
)
|
||||||
|
)]#
|
||||||
|
visitedProcs[key] = true
|
||||||
|
# add the next stmt list onto the stack
|
||||||
|
# of the newly discovered proc
|
||||||
|
if impl[6].kind == nnkStmtList:
|
||||||
|
discard helper(impl, impl[6], 6)
|
||||||
|
echo impl.toStrLit
|
||||||
|
|
||||||
|
newList.add(newCall(bindSym("debugProc")))
|
||||||
|
parent[index] = newList
|
||||||
|
return parent
|
||||||
|
helper(parentx, nodex, indexx)
|
||||||
|
|
||||||
|
|
||||||
|
macro debug(code: typed): untyped =
|
||||||
|
var visitedProcs = newTable[string, bool]()
|
||||||
|
|
||||||
|
code[0] = addStmtTree(code[0], code[0][6], 6)
|
||||||
|
|
||||||
|
result = code
|
||||||
|
echo code.treeRepr
|
||||||
|
echo code.toStrLit()
|
||||||
|
|
||||||
|
# This proc now has it's stmts replaced!
|
||||||
|
proc otherProc(stuff: string): string =
|
||||||
|
let a = "beans"
|
||||||
|
echo stuff & a
|
||||||
|
result = stuff & a
|
||||||
|
|
||||||
|
debug:
|
||||||
|
proc main() =
|
||||||
|
let x = "balls".otherProc()
|
||||||
|
let y = otherProc("balls2")
|
||||||
|
block:
|
||||||
|
echo "Beans"
|
||||||
|
echo "Beans"
|
||||||
|
if x == "ballsbeans":
|
||||||
|
echo "Yaya"
|
||||||
|
echo "who"
|
||||||
|
else:
|
||||||
|
echo "balls"
|
||||||
|
echo "multi"
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue