Nim Metaprogramming
Use when nim's metaprogramming including macros, templates, compile-time evaluation, AST manipulation, code generation, DSL creation, and leveraging compile-time computation for performance and abstraction in systems programming.
What this skill does
# Nim Metaprogramming
## Introduction
Nim's metaprogramming system provides powerful compile-time code generation and
manipulation through templates, macros, and compile-time evaluation. This enables
zero-overhead abstractions, domain-specific languages, and optimizations performed
at compile time rather than runtime.
The system operates on Nim's abstract syntax tree (AST), allowing inspection and
transformation of code structure. Templates provide hygienic macro-like substitution,
while macros enable full AST manipulation. Compile-time evaluation executes Nim
code during compilation for constant folding and validation.
This skill covers templates for code substitution, macros for AST transformation,
compile-time evaluation with static blocks, AST inspection and manipulation, code
generation patterns, DSL creation, and metaprogramming best practices.
## Templates
Templates provide hygienic code substitution with type safety and inline expansion.
```nim
# Basic template
template max(a, b: untyped): untyped =
if a > b: a else: b
echo max(5, 10) # Expands inline
# Template with multiple statements
template withFile(filename: string, body: untyped): untyped =
let file = open(filename, fmRead)
try:
body
finally:
file.close()
withFile("data.txt"):
for line in file.lines:
echo line
# Template with inject
template defineProperty(name: untyped, typ: typedesc): untyped =
var `name Value`: typ
proc `get name`(): typ {.inject.} =
`name Value`
proc `set name`(value: typ) {.inject.} =
`name Value` = value
defineProperty(age, int)
setAge(30)
echo getAge() # 30
# Template accepting block
template benchmark(name: string, code: untyped): untyped =
let start = cpuTime()
code
let elapsed = cpuTime() - start
echo name, ": ", elapsed, " seconds"
benchmark "computation":
var sum = 0
for i in 1..1000000:
sum += i
# Template with typed parameters
template swap(a, b: typed): untyped =
let tmp = a
a = b
b = tmp
var x = 5
var y = 10
swap(x, y)
echo x, " ", y # 10 5
# Template for DSL
template html(body: untyped): string =
var result = ""
template tag(name: string, content: untyped): untyped =
result.add "<" & name & ">"
content
result.add "</" & name & ">"
body
result
let page = html:
tag "html":
tag "body":
tag "h1":
result.add "Hello World"
# Template with generic constraints
template findMax[T: Ordinal](items: openArray[T]): T =
var maxVal = items[0]
for item in items:
if item > maxVal:
maxVal = item
maxVal
echo findMax([1, 5, 3, 9, 2])
# Template forwarding
template forward(call: untyped): untyped =
echo "Before call"
call
echo "After call"
proc myProc() =
echo "Inside proc"
forward myProc()
# Template for custom operators
template `~=`(a, b: float): bool =
abs(a - b) < 0.0001
echo 1.0 ~= 1.00001 # true
# Template with immediate parameter
template log(msg: string): untyped =
when defined(debug):
echo "[LOG] ", msg
log "Debug message" # Only in debug builds
```
Templates provide compile-time code substitution with hygiene and type safety.
## Macros and AST Manipulation
Macros transform AST at compile time, enabling powerful code generation and
domain-specific languages.
```nim
import macros
# Basic macro
macro debug(n: varargs[typed]): untyped =
result = newStmtList()
for arg in n:
result.add quote do:
echo astToStr(`arg`), " = ", `arg`
let x = 5
let y = 10
debug x, y, x + y
# Macro examining AST
macro inspect(node: untyped): untyped =
echo node.treeRepr
result = node
inspect:
let x = 5 + 3
# Building AST manually
macro makeProc(name: untyped, body: untyped): untyped =
result = newProc(
name = name,
params = [newEmptyNode()],
body = body
)
makeProc greet:
echo "Hello!"
greet()
# Macro for property generation
macro property(name: untyped, typ: typedesc): untyped =
let
fieldName = ident($name & "Field")
getName = ident("get" & $name)
setName = ident("set" & $name)
result = quote do:
var `fieldName`: `typ`
proc `getName`(): `typ` =
`fieldName`
proc `setName`(value: `typ`) =
`fieldName` = value
property(count, int)
setCount(42)
echo getCount()
# Case statement macro
macro switch(value: typed, branches: varargs[untyped]): untyped =
result = nnkCaseStmt.newTree(value)
for branch in branches:
expectKind(branch, nnkCall)
let pattern = branch[0]
let body = branch[1]
result.add nnkOfBranch.newTree(pattern, body)
switch(5):
1: echo "one"
2: echo "two"
5: echo "five"
# Builder pattern macro
macro build(typ: typedesc, fields: varargs[untyped]): untyped =
result = nnkObjConstr.newTree(typ)
for field in fields:
expectKind(field, nnkExprEqExpr)
result.add field
type Person = object
name: string
age: int
let p = build(Person, name = "Alice", age = 30)
# Compile-time assertion macro
macro staticAssert(condition: bool, message: string): untyped =
if not condition.boolVal:
error(message.strVal)
result = newEmptyNode()
staticAssert sizeof(int) >= 4, "int must be at least 4 bytes"
# Unrolling loop macro
macro unroll(count: static[int], body: untyped): untyped =
result = newStmtList()
for i in 0..<count:
let iNode = newLit(i)
result.add body.replace(ident("it"), iNode)
unroll 5:
echo "Iteration: ", it
# Pattern matching macro
macro match(value: typed, patterns: varargs[untyped]): untyped =
result = nnkIfStmt.newTree()
for pattern in patterns:
expectMinLen(pattern, 2)
let condition = pattern[0]
let body = pattern[1]
let check = quote do:
`value` == `condition`
result.add nnkElifBranch.newTree(check, body)
```
Macros enable compile-time code transformation and generation through AST
manipulation.
## Compile-Time Evaluation
Nim executes code at compile time for constants, optimizations, and validations.
```nim
# Compile-time constants
const maxSize = 100
const computed = maxSize * 2 + 10 # Evaluated at compile time
# Compile-time function evaluation
proc factorial(n: int): int =
if n <= 1: 1 else: n * factorial(n - 1)
const fact10 = factorial(10) # Computed at compile time
# Static block
static:
echo "This runs at compile time"
echo "Factorial of 10 is: ", factorial(10)
# Compile-time type information
proc sizeInfo[T](x: T): string =
static:
"Size of " & $T & " is " & $sizeof(T) & " bytes"
echo sizeInfo(5)
echo sizeInfo(5.0)
# Compile-time conditional compilation
when sizeof(int) == 8:
proc printSize() = echo "64-bit platform"
else:
proc printSize() = echo "32-bit platform"
# Compile-time string operations
const
version = "1.0.0"
parts = version.split('.')
major = parts[0].parseInt
when major >= 1:
echo "Version 1.0 or later"
# Compile-time file reading
const configData = staticRead("config.txt")
proc getConfig(): string =
configData
# Compile-time HTTP requests (with stdlib)
const apiResponse = staticExec("curl -s https://api.example.com/data")
# Compile-time code generation
proc generateAccessors(fields: seq[string]): string =
result = ""
for field in fields:
result.add &"""
proc get{field.capitalizeAscii}(): int = {field}
proc set{field.capitalizeAscii}(val: int) = {field} = val
"""
const accessors = generateAccessors(@["x", "y", "z"])
# Static parameter constraints
proc processArray[T; N: static[int]](arr: array[N, T]) =
static:
echo "Array size: ", N
for item in arr:
echo item
processArray([1, 2, 3, 4, 5])
# Compile-time assertions
static:
doAssert sizeof(int) >= 4, "int too small"
doAssert sizeof(ptr) == sizeof(int), "pointer size mismatch"
# Compile-time regex compilation
import re
const emailPattern = re"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
proc validateEmail(email: string): bool =
email.match(emailPattern)
# Compile-time validation
proc validateConfig() =
static:
when not fileExists("config.txt"):
{.fatal: "confiRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.