summary refs log tree commit diff stats
path: root/lib/core/macrocache.nim
blob: e376ad87fe3a65a496adb65cbde2a6f6387a6a39 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#
#
#            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