From 86e6042cfba9acfb44b8c2440b7a95db63bc5653 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 02:07:09 +0300 Subject: [PATCH 1/8] Add docs to macrocache --- lib/core/macrocache.nim | 191 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 176 insertions(+), 15 deletions(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index 8fe1fa603f7f..db26f05a1ffa 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -7,38 +7,199 @@ # distribution, for details about the copyright. # -## This module provides an API for macros that need to collect compile -## time information across module boundaries in global variables. -## Starting with version 0.19 of Nim this is not directly supported anymore -## as it breaks incremental compilations. -## Instead the API here needs to be used. +## 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 `val` + let val = newLit("hello ic") + mcTable["val"] = val + assert mcTable["val"].kind == nnkStrLit + + # add `val` to `mcSeq` + mcSeq.add(val) + assert mcSeq.len == 1 + assert mcSeq[0] == val + + # 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 + assert counter.value == 1 + + 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`. + ## + ## This doesn't do anything if `value` is already in `s`. + runnableExamples: + import std/macros + const mySeq = CacheSeq"inclTest" + + static: + mySeq.add(newLit(5)) + mySeq.incl(newLit(5)) -proc value*(c: CacheCounter): int {.magic: "NccValue".} -proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} + # still one element + assert mySeq.len == 1 -proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} -proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} -proc len*(s: CacheSeq): int {.magic: "NcsLen".} -proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} +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(newLit("helper")) + assert mySeq.len == 1 + for _ in 0..<5: + mySeq.add(val) + + assert mySeq.len == 6 + +proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = + ## Returns the `i`th value from `s`. + runnableExamples: + import std/macros + + const mySeq = CacheSeq"subTest" + static: + # add 42 values + for i in 0..41: + mySeq.add(newLit(i + 1)) + + assert mySeq[41].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".} - ## 'key' has to be unique! +proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = + ## Inserts a `(key, value)` pair into `t`. + ## + ## .. warning:: `key` has to be unique! + 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 -proc len*(t: CacheTable): int {.magic: "NctLen".} -proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} + const dataTable = CacheTable"lenTest" + static: + dataTable["key"] = newLit(5) + dataTable["key2"] = newLit(false) + assert dataTable.len == 2 + +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) From 829a38cf81652434fe154810f314a869f76af96b Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 02:11:41 +0300 Subject: [PATCH 2/8] use hint --- lib/core/macrocache.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index db26f05a1ffa..40c759d965b9 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -87,7 +87,7 @@ proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = ## Adds `value` to `s`. ## - ## This doesn't do anything if `value` is already in `s`. + ## .. hint:: This doesn't do anything if `value` is already in `s`. runnableExamples: import std/macros const mySeq = CacheSeq"inclTest" From c83aa5e7b24f87f03c88a204d2d4623576c61bc7 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 02:15:43 +0300 Subject: [PATCH 3/8] Use incl in the incl example --- lib/core/macrocache.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index 40c759d965b9..1dd410dde87e 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -93,7 +93,7 @@ proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = const mySeq = CacheSeq"inclTest" static: - mySeq.add(newLit(5)) + mySeq.incl(newLit(5)) mySeq.incl(newLit(5)) # still one element From d37d1e68a154e04cd98ecb054bc427c8f7cc57f5 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 02:17:14 +0300 Subject: [PATCH 4/8] add macrocache to lib --- doc/lib.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/lib.rst b/doc/lib.rst index 3202c5a53a60..0c8b843491a2 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -67,6 +67,9 @@ Core * `macros `_ Contains the AST API and documentation of Nim for writing macros. +* `macrocache `_ + Provides an API for macros to collect compile-time information across modules. + * `rlocks `_ Reentrant locks for Nim. From d79dd945ff3a2da4f4146767b980560490e8ca76 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 02:24:14 +0300 Subject: [PATCH 5/8] consistency --- lib/core/macrocache.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index 1dd410dde87e..15f880601119 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -107,7 +107,7 @@ proc len*(s: CacheSeq): int {.magic: "NcsLen".} = const mySeq = CacheSeq"lenTest" static: let val = newLit("helper") - mySeq.add(newLit("helper")) + mySeq.add(val) assert mySeq.len == 1 for _ in 0..<5: mySeq.add(val) From 641949022f522fe5efb69a35f72a659b299c36c2 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 04:19:02 +0300 Subject: [PATCH 6/8] Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/lib.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 0c8b843491a2..9d715b1c7c09 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -64,11 +64,11 @@ Core * `locks `_ Locks and condition variables for Nim. -* `macros `_ - Contains the AST API and documentation of Nim for writing macros. - * `macrocache `_ Provides an API for macros to collect compile-time information across modules. + +* `macros `_ + Contains the AST API and documentation of Nim for writing macros. * `rlocks `_ Reentrant locks for Nim. From 9125ad10c0f882179f954c2afd94199c01bbc910 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 08:42:01 +0300 Subject: [PATCH 7/8] apply suggestions --- lib/core/macrocache.nim | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index 15f880601119..cef89fda222b 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -24,15 +24,20 @@ runnableExamples: const mcCounter = CacheCounter"myCounter" static: - # add new key "val" with the value `val` - let val = newLit("hello ic") - mcTable["val"] = val + # add new key "val" with the value `myval` + let myval = newLit("hello ic") + mcTable["val"] = myval assert mcTable["val"].kind == nnkStrLit - - # add `val` to `mcSeq` - mcSeq.add(val) + + # 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 - assert mcSeq[0] == val + + static: + assert mcSeq[0].strVal == "hello ic" # increase `mcCounter` by 3 mcCounter.inc(3) @@ -66,9 +71,8 @@ proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} = static: let counter = CacheCounter"incTest" inc counter - assert counter.value == 1 - inc counter, 5 + assert counter.value == 6 proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = @@ -109,10 +113,9 @@ proc len*(s: CacheSeq): int {.magic: "NcsLen".} = let val = newLit("helper") mySeq.add(val) assert mySeq.len == 1 - for _ in 0..<5: - mySeq.add(val) - assert mySeq.len == 6 + mySeq.add(val) + assert mySeq.len == 2 proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = ## Returns the `i`th value from `s`. @@ -121,11 +124,8 @@ proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = const mySeq = CacheSeq"subTest" static: - # add 42 values - for i in 0..41: - mySeq.add(newLit(i + 1)) - - assert mySeq[41].intVal == 42 + mySeq.add(newLit(42)) + assert mySeq[0].intVal == 42 iterator items*(s: CacheSeq): NimNode = ## Iterates over each item in `s`. @@ -166,8 +166,7 @@ proc len*(t: CacheTable): int {.magic: "NctLen".} = const dataTable = CacheTable"lenTest" static: dataTable["key"] = newLit(5) - dataTable["key2"] = newLit(false) - assert dataTable.len == 2 + assert dataTable.len == 1 proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = ## Retrieves the `NimNode` value at `t[key]`. From c866efd28b1ff1be7f4598d6332f856199a412a1 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 08:45:35 +0300 Subject: [PATCH 8/8] clarify the warning --- lib/core/macrocache.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index cef89fda222b..e376ad87fe3a 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -146,7 +146,8 @@ iterator items*(s: CacheSeq): NimNode = proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = ## Inserts a `(key, value)` pair into `t`. ## - ## .. warning:: `key` has to be unique! + ## .. 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