Skip to content

Commit

Permalink
Merge pull request #1 from guzba/master
Browse files Browse the repository at this point in the history
internal generation
  • Loading branch information
treeform authored Aug 25, 2021
2 parents 97db3ac + 360d5bf commit 89ef461
Show file tree
Hide file tree
Showing 12 changed files with 499 additions and 24 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2021 Author
Copyright (c) 2021 Andre von Houck and Ryan Oldenburg

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
# You can use this nim template to jump start your nim library or project.

This template includes:
* MIT licence
* src directory and a private common.nim
* test directory
* GitHub Actions to run the tests on GitHub
8 changes: 8 additions & 0 deletions bindy.nimble
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version = "0.1.0"
author = "Andre von Houck and Ryan Oldenburg"
description = "Generate a shared library and bindings for many languages."
license = "MIT"

srcDir = "src"

requires "nim >= 1.4.8"
1 change: 0 additions & 1 deletion examples/example.nim

This file was deleted.

8 changes: 0 additions & 8 deletions nimtemplate.nimble

This file was deleted.

183 changes: 183 additions & 0 deletions src/bindy.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import bindy/internal, bindy/common, macros, strformat, tables

proc toggleBasicOnly*() =
discard

macro exportEnums*(body: typed) =
for statement in body:
for sym in statement:
if sym.getImpl()[2].kind != nnkEnumTy:
error(
&"Enum export entry of unexpected kind {sym.getImpl()[2].kind}",
sym
)

exportEnumInternal(sym)

macro exportProcs*(body: typed) =
for statement in body:
let exportName = statement[0].repr

var exported: int
for procedure in statement:
let procType = procedure.getTypeInst()
if procType.kind != nnkProcTy:
error(
&"Proc exports statement of unexpected kind {procType.kind}",
statement
)

if procType[0].len > 1:
# Filter out overloads that are owned by objects
let firstParam = procType[0][1][1]
if firstParam.kind == nnkBracketExpr:
continue
let firstParamImpl = firstParam.getImpl()
if firstParamImpl.kind == nnkTypeDef and
firstParamImpl[2].kind != nnkEnumTy:
continue

exportProcInternal(procedure)
inc exported

if exported == 0:
error(
&"Proc export statement {exportName} does not export anything",
statement
)

macro exportObjects*(body: typed) =
for statement in body:
for sym in statement:
let objImpl = sym.getImpl()[2]
if objImpl.kind != nnkObjectTy:
error(&"Unexpected export object impl kind {objImpl.kind}", statement)

let objType = sym.getType()
for property in objType[2]:
if not property.isExported:
error(&"Unexported property on {sym.repr}", objType)

let propertyTypeImpl = property.getTypeImpl()
if propertyTypeImpl.repr notin basicTypes:
if propertyTypeImpl.kind notin {nnkEnumTy, nnkObjectTy}:
error(
&"Object cannot export property of type {property[^2].repr}",
propertyTypeImpl
)

exportObjectInternal(sym)

macro exportRefObject*(
sym: typed, whitelist: static[openarray[string]], body: typed
) =
let refImpl = sym.getImpl()[2]
if refImpl.kind != nnkRefTy:
error(
&"Unexpected export ref object impl kind {refImpl.kind}",
sym
)

var
exportProcs: seq[NimNode]
basicOnly = false
for statement in body:
if statement.kind == nnkDiscardStmt:
continue

if statement.kind == nnkCall:
if statement[0].repr == "toggleBasicOnly":
basicOnly = not basicOnly
continue

for procedure in statement:
let procType = procedure.getTypeInst()
if procType.kind != nnkProcTy:
error(
&"Ref object exports statement of unexpected kind {procType.kind}",
procType
)

if procType[0].len <= 1:
continue

if procType[0][1][1].repr != sym.repr:
var found = false

let procImpl = procedure.getImpl()
if procImpl[3][1][1].kind == nnkInfix:
for choice in procImpl[3][1][1][1 .. ^1]:
if choice.repr == sym.repr:
found = true

if not found:
continue

if basicOnly:
var skip = false
for paramType in procType[0][2 .. ^1]:
if paramType[1].repr == "bool":
continue
if paramType[1].getImpl().kind == nnkNilLit:
continue
if paramType[1].getImpl().kind == nnkTypeDef:
if paramType[1].getImpl()[2].kind == nnkEnumTy:
continue
skip = true
break
if skip:
continue

exportProcs.add(procedure)

if exportProcs.len == 0:
error(
&"Ref object export statement {statement[0].repr} does not export anything",
statement
)

var entries: CountTable[string]
for exportProc in exportProcs:
entries.inc(exportProc.repr)

exportRefObjectInternal(sym, whitelist)

for procedure in exportProcs:
var prefixes = @[sym]
if entries[procedure.repr] > 1:
# If there are more than one procs with this name, add a second prefix
let procType = procedure.getTypeInst()
if procType[0].len > 2:
prefixes.add(procType[0][2][1])
exportProcInternal(procedure, prefixes)

macro exportSeq*(sym: typed, body: typed) =
var exportProcs: seq[NimNode]
for statement in body:
if statement.kind == nnkDiscardStmt:
continue

for procedure in statement:
let procType = procedure.getTypeInst()
if procType.kind != nnkProcTy:
error(
&"Ref object exports statement of unexpected kind {procType.kind}",
procType
)

if procType[0].len <= 1:
continue

if procType[0][1][1].kind != nnkBracketExpr:
continue

if procType[0][1][1][1].getSeqName() == sym.getSeqName():
exportProcs.add(procedure)

exportSeqInternal(sym)

for procedure in exportProcs:
exportProcInternal(procedure, [sym])

macro writeFiles*(dir, lib: static[string]) =
writeInternal(dir, lib)
74 changes: 74 additions & 0 deletions src/bindy/common.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import macros, strformat, strutils

const basicTypes* = [
"bool",
"int8",
"uint8",
"int16",
"uint16",
"int32",
"uint32",
"int64",
"uint64",
"int",
"uint",
"float32",
"float64",
"float"
]

proc toSnakeCase*(s: string): string =
## Converts NimType to nim_type.
var prevCap = false
for i, c in s:
if c in {'A' .. 'Z'}:
if result.len > 0 and result[^1] != '_' and not prevCap:
result.add '_'
prevCap = true
result.add c.toLowerAscii()
else:
prevCap = false
result.add c

proc toCapSnakeCase*(s: string): string =
## Converts NimType to NIM_TYPE.
var prevCap = false
for i, c in s:
if c in {'A' .. 'Z'}:
if result.len > 0 and result[^1] != '_' and not prevCap:
result.add '_'
prevCap = true
else:
prevCap = false
result.add c.toUpperAscii()

proc toVarCase*(s: string): string =
## Converts NimType to nimType.
var i = 0
while i < s.len:
if s[i] notin {'A' .. 'Z'}:
break

result.add s[i].toLowerAscii()
inc i

if i < s.len:
result.add s[i .. ^1]

proc getSeqName*(sym: NimNode): string =
if sym.kind == nnkBracketExpr:
result = &"Seq{sym[1]}"
else:
result = &"Seq{sym}"
result[3] = toUpperAscii(result[3])

proc getName*(sym: NimNode): string =
if sym.kind == nnkBracketExpr:
sym.getSeqName()
else:
sym.repr

proc raises*(procSym: NimNode): bool =
for pragma in procSym.getImpl()[4]:
if pragma.kind == nnkExprColonExpr and pragma[0].repr == "raises":
return pragma[1].len > 0
Loading

0 comments on commit 89ef461

Please sign in to comment.