https://github.com/elcritch/cdecl
Nim helper for using C Macros
https://github.com/elcritch/cdecl
Last synced: 5 months ago
JSON representation
Nim helper for using C Macros
- Host: GitHub
- URL: https://github.com/elcritch/cdecl
- Owner: elcritch
- Created: 2022-06-05T09:36:48.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-08-12T00:25:56.000Z (about 2 years ago)
- Last Synced: 2025-05-07T13:58:13.807Z (5 months ago)
- Language: Nim
- Size: 233 KB
- Stars: 10
- Watchers: 2
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
C.D.E.C.L.: Commonly Desired Edge Case Library
See full docs at docs or source on github at elcritch/cdecl.
Small library for macros to handle various edge cases for Nim syntax. These are mostly edge case syntax handlers or tricky C Macro interfacings. The goal is to implement them as generically and well unit tested as possible.
Current macros includes:
-
cdecls: Macros to help using C macros that declare variables- cdeclmacro
-
applies: Macros that unpack arguments from various forms and calls functions-
unpackObjectArgs: macro to splat an object to position arguments -
unpackObjectArgFields: macro to splat an object to keyword arguments -
unpackLabelsAsArgs: turn labels to named arguments
-
-
bitfields: Macros for making bitfield style accessor-
bitfields: create bitfield accessors for hardware registers using any int type
-
You can see various usages in the tests folder.
Macros
unpackObjectArgs
Helper to apply all fields of an object as named paramters.
type AddObj = object
a*: int
b*: intproc add(a, b: int): int =
result = a + blet args = AddObj(a: 1, b: 2)
let res = unpackObjectArgs(add, args)
assert res == 3
unpackLabelsAsArgs
Helper to transform labels as named arguments to a function. Labels are regular Nim syntax for calling procs but are transformed to parameter names:
proc foo(name: string = "buzz", a, b: int) =
echo name, ":", " a: ", $a, " b: ", $btemplate Foo(blk: varargs[untyped]) =
## create a new template to act YAML like API
unpackLabelsAsArgs(foo, blk)Foo:
name: "buzz"
a: 11
b: 22
Will call foo(name="buzz",a=11,b=22) and print:
buzz: a: 11 b: 22
cdeclmacro
Macro helper for wrapping a C macro that declares a new C variable.
It handles emitting the appropriate C code for calling the macro. Additionally it defines a new Nim variable using importc which imports the declared variable.
Basic Example
import cdecl/cdecls
import cdecl/cdeclapi
export cdeclapi # this is needed clients to use the declared apisproc CDefineVar*(name: CToken, size: static[int]) {.
cdeclmacro: "C_MACRO_VARIABLE_DECLARER", cdeclsVar(name -> array[size, int32]).}CMacroDeclare(myVar, 128, someExternalCVariable) # creates myVar
macro cdeclmacro(name: string; def: untyped)
CRawStr Example
import macros
import cdeclproc CDefineVarStackRaw*(name: CToken, size: static[int], otherRaw: CRawStr): array[size, int32] {.
cdeclmacro: "C_DEFINE_VAR_ADDITION".}# Pass a raw string to the C macro:
proc runCDefineVarStackRaw() =
CDefineVarStackRaw(myVarStackRaw, 5, CRawStr("40+2"))
assert myVarStackRaw[0] == 42
## **macro** cdeclmacro
Macro helper for wrapping a C macro that declares a new C variable.
It handles emitting the appropriate C code for calling the macro.
It can define Nim variables using importc to wrap the generated variable. This is done using varName: CToken in the argument list and adding a cdeclsVar(varName -> varType) pragma. The cdeclsVar tells the macro which CToken argument to use and its type.
The macro will pass any extra pragmas to the variable. If the global pragma is passed in the emitted C code will be put in the /*VARSECTION*/ section.
```nim
macro cdeclmacro(name: string; def: untyped)
```
## **macro** cmacrowrapper
pragma for making a c macro wrapper
```nim
macro cmacrowrapper(name: string; def: untyped)
```
## **type** CRawStr
Represents a raw string that gets interpolated into generated C ouput
```nim
CRawStr = distinct string
```
## **type** CLabel
used to represent a C macro "label", an alias for CRawStr
```nim
CLabel = CRawStr
```
## **type** CRawToken
Represents a C token derived from a Nim expression
```nim
CRawToken = distinct static[CRawStr]
```
## **type** CToken
Represents a C token derived from a Nim expression
```nim
CToken = distinct static[CRawStr]
```
## **macro** symbolName
Get a string representation of a Nim symbol
```nim
macro symbolName(x: untyped): string
```
## **template** symbolVal
Turns a CRawStr into a normal string
```nim
template symbolVal(x: CRawStr): string
```
## **template** symbolVal
Turns a CRawStr into a normal string
```nim
template symbolVal(x: string): string
```
## **macro** unpackObjectArgs
Calls callee with fields form object args unpacked as individual arguments.
This is similar to unpackVarargs in std/macros but for call a function using the values from an object
```nim
macro unpackObjectArgs(callee: untyped; arg: typed; extras: varargs[untyped]): untyped
```
## **macro** unpackObjectArgFields
Similar to unpackObjectArgs but with named parameters based on field names.
```nim
macro unpackObjectArgFields(callee: untyped; arg: typed;
extras: varargs[untyped]): untyped
```
## **macro** unpackLabelsAsArgs
unpacks labels as named arguments.
```nim
macro unpackLabelsAsArgs(callee: typed; args: varargs[untyped]): untyped
```
## **macro** bitfields
Create a new distinct integer type with accessors for bitfields that set and get bits for each field. This is more stable than C-style bitfields (see below).
- The basic syntax for a bitfield declarations is:
- fieldname: uint8[4..5]
- - fieldName is the name of the accessors and produces both
- a getter (fieldName) and setter (fieldName=)
- - the range 4..5 is the target bit indexes. The ranges are
- inclusive meaning 6 ... 6 is 1 bit. Ranges are sorted so you can also use 5 .. 4 to match hardware documentation.
* The type uint8 is the type that the bits are converted to/from.
Signed types like int8 are supported and do signed shifts to
- properly extend the sign. For example:
- speed: int8[7..4]
The accessors generated are very simple and what you would generally produce by hand. For example:
bitfields RegConfig(uint16):
speed: int8[4..2]
- Generates code similar too:
type
RegChannel = distinct uint16
proc speed*(reg: RegChannel): uint8 =
result = uint8(bitsliced(uint16(reg), 4 .. 9))
proc speed=*(reg: var RegChannel; x: uint8) =
setBitsSlice(uint16(reg), 4 .. 9, x)
This is often preferable to C-style bitfields which Nim does support. C-style bitfields are compiler and architecture dependent and prone to breaking on field alignement, endiannes, and other issues. See https://lwn.net/Articles/478657/
```nim
macro bitfields(name, def: untyped)
```