core/macrocache

  Source   Edit

This module provides an API for macros to collect compile-time information across module boundaries. It should be used instead of global {.compileTime.} variables as those break incremental compilation.

The main feature of this module is that if you create CacheTables or any other Cache types with the same name in different modules, their content will be shared, meaning that you can fill a CacheTable in one module, and iterate over its contents in another.

Example:

import core/macrocache
import std/macros

const mcTable = CacheTable"myTable"
const mcSeq = CacheSeq"mySeq"
const mcCounter = CacheCounter"myCounter"

static:
  # add new key "val" with the value `myval`
  let myval = newLit("hello ic")
  mcTable["val"] = myval
  assert mcTable["val"].kind == nnkStrLit

# Can access the same cache from different static contexts
# All the information is retained
static:
  # get value from `mcTable` and add it to `mcSeq`
  mcSeq.add(mcTable["val"])
  assert mcSeq.len == 1

static:
  assert mcSeq[0].strVal == "hello ic"

  # increase `mcCounter` by 3
  mcCounter.inc(3)
  assert mcCounter.value == 3

Types

CacheCounter = distinct string
Compile-time counter, uses int for storing the count.   Source   Edit
CacheSeq = distinct string
Compile-time sequence of NimNodes.   Source   Edit
CacheTable = distinct string

Compile-time table of key-value pairs.

Keys are strings and values are NimNodes.

  Source   Edit

Procs

proc `[]=`(t: CacheTable; key: string; value: NimNode) {.magic: "NctPut",
    ...raises: [], tags: [].}
Inserts a (key, value) pair into t.
Warning: key has to be unique! Assigning value to a key that is already in the table will result in a compiler error.

Example:

import std/macros

const mcTable = CacheTable"subTest"
static:
  # assign newLit(5) to the key "value"
  mcTable["value"] = newLit(5)

  # check that we can get the value back
  assert mcTable["value"].kind == nnkIntLit
  Source   Edit
proc `[]`(s: CacheSeq; i: int): NimNode {.magic: "NcsAt", ...raises: [], tags: [].}
Returns the ith value from s.

Example:

import std/macros

const mySeq = CacheSeq"subTest"
static:
  mySeq.add(newLit(42))
  assert mySeq[0].intVal == 42
  Source   Edit
proc `[]`(t: CacheTable; key: string): NimNode {.magic: "NctGet", ...raises: [],
    tags: [].}
Retrieves the NimNode value at t[key].

Example:

import std/macros

const mcTable = CacheTable"subTest"
static:
  mcTable["toAdd"] = newStmtList()

  # get the NimNode back
  assert mcTable["toAdd"].kind == nnkStmtList
  Source   Edit
proc add(s: CacheSeq; value: NimNode) {.magic: "NcsAdd", ...raises: [], tags: [].}
Adds value to s.

Example:

import std/macros
const mySeq = CacheSeq"addTest"

static:
  mySeq.add(newLit(5))
  mySeq.add(newLit("hello ic"))

  assert mySeq.len == 2
  assert mySeq[1].strVal == "hello ic"
  Source   Edit
proc inc(c: CacheCounter; by = 1) {.magic: "NccInc", ...raises: [], tags: [].}
Increments the counter c with the value by.

Example:

static:
  let counter = CacheCounter"incTest"
  inc counter
  inc counter, 5

  assert counter.value == 6
  Source   Edit
proc incl(s: CacheSeq; value: NimNode) {.magic: "NcsIncl", ...raises: [], tags: [].}
Adds value to s.
Hint: This doesn't do anything if value is already in s.

Example:

import std/macros
const mySeq = CacheSeq"inclTest"

static:
  mySeq.incl(newLit(5))
  mySeq.incl(newLit(5))

  # still one element
  assert mySeq.len == 1
  Source   Edit
proc len(s: CacheSeq): int {.magic: "NcsLen", ...raises: [], tags: [].}
Returns the length of s.

Example:

import std/macros

const mySeq = CacheSeq"lenTest"
static:
  let val = newLit("helper")
  mySeq.add(val)
  assert mySeq.len == 1

  mySeq.add(val)
  assert mySeq.len == 2
  Source   Edit
proc len(t: CacheTable): int {.magic: "NctLen", ...raises: [], tags: [].}
Returns the number of elements in t.

Example:

import std/macros

const dataTable = CacheTable"lenTest"
static:
  dataTable["key"] = newLit(5)
  assert dataTable.len == 1
  Source   Edit
proc value(c: CacheCounter): int {.magic: "NccValue", ...raises: [], tags: [].}
Returns the value of a counter c.

Example:

static:
  let counter = CacheCounter"valTest"
  # default value is 0
  assert counter.value == 0

  inc counter
  assert counter.value == 1
  Source   Edit

Iterators

iterator items(s: CacheSeq): NimNode {....raises: [], tags: [].}
Iterates over each item in s.

Example:

import std/macros
const myseq = CacheSeq"itemsTest"

static:
  myseq.add(newLit(5))
  myseq.add(newLit(42))

  for val in myseq:
    # check that all values in `myseq` are int literals
    assert val.kind == nnkIntLit
  Source   Edit
iterator pairs(t: CacheTable): (string, NimNode) {....raises: [], tags: [].}
Iterates over all (key, value) pairs in t.

Example:

import std/macros
const mytabl = CacheTable"values"

static:
  mytabl["intVal"] = newLit(5)
  mytabl["otherVal"] = newLit(6)
  for key, val in mytabl:
    # make sure that we actually get the same keys
    assert key in ["intVal", "otherVal"]

    # all vals are int literals
    assert val.kind == nnkIntLit
  Source   Edit