# # # Nim's Runtime Library # (c) Copyright 2018 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## 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 `CacheTable`s 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. runnableExamples: 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 type CacheSeq* = distinct string ## Compile-time sequence of `NimNode`s. CacheTable* = distinct string ## Compile-time table of key-value pairs. ## ## Keys are `string`s and values are `NimNode`s. CacheCounter* = distinct string ## Compile-time counter, uses `int` for storing the count. proc value*(c: CacheCounter): int {.magic: "NccValue".} = ## Returns the value of a counter `c`. runnableExamples: static: let counter = CacheCounter"valTest" # default value is 0 assert counter.value == 0 inc counter assert counter.value == 1 proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} = ## Increments the counter `c` with the value `by`. runnableExamples: static: let counter = CacheCounter"incTest" inc counter inc counter, 5 assert counter.value == 6 proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = ## Adds `value` to `s`. runnableExamples: 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" proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = ## Adds `value` to `s`. ## ## .. hint:: This doesn't do anything if `value` is already in `s`. runnableExamples: import std/macros const mySeq = CacheSeq"inclTest" static: mySeq.incl(newLit(5)) mySeq.incl(newLit(5)) # still one element assert mySeq.len == 1 proc len*(s: CacheSeq): int {.magic: "NcsLen".} = ## Returns the length of `s`. runnableExamples: 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 proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = ## Returns the `i`th value from `s`. runnableExamples: import std/macros const mySeq = CacheSeq"subTest" static: mySeq.add(newLit(42)) assert mySeq[0].intVal == 42 iterator items*(s: CacheSeq): NimNode = ## Iterates over each item in `s`. runnableExamples: 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 for i in 0 ..< len(s): yield s[i] proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = ## 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. runnableExamples: 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 proc len*(t: CacheTable): int {.magic: "NctLen".} = ## Returns the number of elements in `t`. runnableExamples: import std/macros const dataTable = CacheTable"lenTest" static: dataTable["key"] = newLit(5) assert dataTable.len == 1 proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = ## Retrieves the `NimNode` value at `t[key]`. runnableExamples: import std/macros const mcTable = CacheTable"subTest" static: mcTable["toAdd"] = newStmtList() # get the NimNode back assert mcTable["toAdd"].kind == nnkStmtList proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} iterator pairs*(t: CacheTable): (string, NimNode) = ## Iterates over all `(key, value)` pairs in `t`. runnableExamples: 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 var h = 0 while hasNext(t, h): let (a, b, h2) = next(t, h) yield (a, b) h = h2