Spec-Zone.ru › Nim 1

макросы

Этот модуль содержит интерфейс к абстрактного синтаксического дерева (AST) компилятора. Макросы работают с этим деревом.

См. также:

  • учебник по макросам
  • раздел о макросах в руководстве Nim

AST в Nim

Этот раздел описывает, как AST моделируется с помощью системы типов Nim. AST состоит из узлов (NimNode) с переменным количеством дочерних узлов. Каждый узел имеет поле, названное kind, которое описывает содержимое узла:

type
  NimNodeKind = enum     ## kind of a node; only explanatory
    nnkNone,             ## invalid node kind
    nnkEmpty,            ## empty node
    nnkIdent,            ## node contains an identifier
    nnkIntLit,           ## node contains an int literal (example: 10)
    nnkStrLit,           ## node contains a string literal (example: "abc")
    nnkNilLit,           ## node contains a nil literal (example: nil)
    nnkCaseStmt,         ## node represents a case statement
    ...                  ## many more
  
  NimNode = ref NimNodeObj
  NimNodeObj = object
    case kind: NimNodeKind           ## the node's kind
    of nnkNone, nnkEmpty, nnkNilLit:
      discard                        ## node contains no additional fields
    of nnkCharLit..nnkUInt64Lit:
      intVal: BiggestInt             ## the int literal
    of nnkFloatLit..nnkFloat64Lit:
      floatVal: BiggestFloat         ## the float literal
    of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
      strVal: string                 ## the string literal
    else:
      sons: seq[NimNode]             ## the node's sons (or children)

Для типа NimNode, оператор [] был перегружен: n[i] — это n-й дочерний узел.

Для указания AST для различных конструкций Nim используется обозначение nodekind(son1, son2, ...) или nodekind(value) или nodekind(field=value).

Некоторые дочерние узлы могут отсутствовать. Отсутствующий дочерний узел имеет тип nnkEmpty; дочерний узел никогда не может быть nil.

Листовые узлы/Атомы

Лист AST часто соответствует терминальному символу в конкретном синтаксисе. Обратите внимание, что по умолчанию float в Nim отображается как float64, так что по умолчанию AST для числа с плавающей запятой — это nnkFloat64Lit, как показано ниже.

Выражение Nim Соответствующий AST
42 nnkIntLit(intVal = 42)
42'i8 nnkInt8Lit(intVal = 42)
42'i16 nnkInt16Lit(intVal = 42)
42'i32 nnkInt32Lit(intVal = 42)
42'i64 nnkInt64Lit(intVal = 42)
42'u8 nnkUInt8Lit(intVal = 42)
42'u16 nnkUInt16Lit(intVal = 42)
42'u32 nnkUInt32Lit(intVal = 42)
42'u64 nnkUInt64Lit(intVal = 42)
42.0 nnkFloat64Lit(floatVal = 42.0)
42.0'f32 nnkFloat32Lit(floatVal = 42.0)
42.0'f64 nnkFloat64Lit(floatVal = 42.0)
"abc" nnkStrLit(strVal = "abc")
r"abc" nnkRStrLit(strVal = "abc")
"""abc""" nnkTripleStrLit(strVal = "abc")
' ' nnkCharLit(intVal = 32)
nil nnkNilLit()
myIdentifier nnkIdent(strVal = "myIdentifier")
myIdentifier после прохода поиска: nnkSym(strVal = "myIdentifier", ...)

Идентификаторы — это узлы nnkIdent. После прохода поиска имён эти узлы преобразуются в узлы nnkSym.

Вызовы/выражения

Вызов команды

Конкретный синтаксис:

echo "abc", "xyz"

AST:

nnkCommand(
  nnkIdent("echo"),
  nnkStrLit("abc"),
  nnkStrLit("xyz")
)

Вызов с ()

Конкретный синтаксис:

echo("abc", "xyz")

AST:

nnkCall(
  nnkIdent("echo"),
  nnkStrLit("abc"),
  nnkStrLit("xyz")
)

Вызов инфиксного оператора

Конкретный синтаксис:

"abc" & "xyz"

AST:

nnkInfix(
  nnkIdent("&"),
  nnkStrLit("abc"),
  nnkStrLit("xyz")
)

Обратите внимание, что при использовании нескольких инфиксных операторов команда анализируется с учётом приоритета операторов.

Конкретный синтаксис:

5 + 3 * 4

AST:

nnkInfix(
  nnkIdent("+"),
  nnkIntLit(5),
  nnkInfix(
    nnkIdent("*"),
    nnkIntLit(3),
    nnkIntLit(4)
  )
)

Если вы решите использовать инфиксные операторы в префиксной форме, AST будет вести себя как вызов родительской функции с nnkAccQuoted, как показано ниже:

Конкретный синтаксис:

`+`(3, 4)

AST:

nnkCall(
  nnkAccQuoted(
    nnkIdent("+")
  ),
  nnkIntLit(3),
  nnkIntLit(4)
)

Вызов префиксного оператора

Конкретный синтаксис:

? "xyz"

AST:

nnkPrefix(
  nnkIdent("?"),
  nnkStrLit("abc")
)

Вызов постфиксного оператора

Примечание: В Nim нет постфиксных операторов. Однако узел nnkPostfix используется для маркера экспорта звёздочкой *.

Конкретный синтаксис:

identifier*

AST:

nnkPostfix(
  nnkIdent("*"),
  nnkIdent("identifier")
)

Вызов с именованными аргументами

Конкретный синтаксис:

writeLine(file=stdout, "hallo")

AST:

nnkCall(
  nnkIdent("writeLine"),
  nnkExprEqExpr(
    nnkIdent("file"),
    nnkIdent("stdout")
  ),
  nnkStrLit("hallo")
)

Вызов с литералом необработанной строки

Это используется, например, в примерах bindSym здесь и с re"some regexp" в модуле регулярных выражений.

Конкретный синтаксис:

echo"abc"

AST:

nnkCallStrLit(
  nnkIdent("echo"),
  nnkRStrLit("hello")
)

Оператор разыменования []

Конкретный синтаксис:

x[]

AST:

nnkDerefExpr(nnkIdent("x"))

Оператор addr

Конкретный синтаксис:

addr(x)

AST:

nnkAddr(nnkIdent("x"))

Оператор приведения типа

Конкретный синтаксис:

cast[T](x)

AST:

nnkCast(nnkIdent("T"), nnkIdent("x"))

Оператор доступа к объекту .

Конкретный синтаксис:

x.y

AST:

nnkDotExpr(nnkIdent("x"), nnkIdent("y"))

Если вы используете гибкий синтаксис вызова Nim (как в x.len()), результат будет таким же, но обернут в nnkCall.

Оператор доступа к массиву []

Конкретный синтаксис:

x[y]

AST:

nnkBracketExpr(nnkIdent("x"), nnkIdent("y"))

Скобки

Скобки для влияния на приоритет операторов или построения кортежей создаются с помощью узла nnkPar.

Конкретный синтаксис:

(1, 2, (3))

AST:

nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))

Фигурные скобки

Фигурные скобки используются как конструктор множества.

Конкретный синтаксис:

{1, 2, 3}

AST:

nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))

При использовании как конструктора таблицы синтаксис отличается.

Конкретный синтаксис:

{a: 3, b: 5}

AST:

nnkTableConstr(
  nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)),
  nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5))
)

Квадратные скобки

Квадратные скобки используются как конструктор массива.

Конкретный синтаксис:

[1, 2, 3]

AST:

nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))

Диапазоны

Диапазоны встречаются в конструкторах множеств, ветвях оператора case или срезах массивов. Внутренне используется тип узла nnkRange, но при построении AST вместо этого следует использовать построение с .. как инфиксным оператором.

Конкретный синтаксис:

1..3

AST:

nnkInfix(
  nnkIdent(".."),
  nnkIntLit(1),
  nnkIntLit(3)
)

Пример кода:

macro genRepeatEcho(): stmt =
  result = newNimNode(nnkStmtList)
  
  var forStmt = newNimNode(nnkForStmt) # generate a for statement
  forStmt.add(ident("i")) # use the variable `i` for iteration
  
  var rangeDef = newNimNode(nnkInfix).add(
    ident("..")).add(
    newIntLitNode(3),newIntLitNode(5)) # iterate over the range 3..5
  
  forStmt.add(rangeDef)
  forStmt.add(newCall(ident("echo"), newIntLitNode(3))) # meat of the loop
  result.add(forStmt)

genRepeatEcho() # gives:
                # 3
                # 3
                # 3

Выражение if

Представление выражения if тонкое, но легко просматривается.

Конкретный синтаксис:

if cond1: expr1 elif cond2: expr2 else: expr3

AST:

nnkIfExpr(
  nnkElifExpr(cond1, expr1),
  nnkElifExpr(cond2, expr2),
  nnkElseExpr(expr3)
)

Комментарии документации

Комментарии с двойным диез (##) в коде фактически имеют собственный формат, используя strVal для получения и установки текста комментария. Комментарии с одиночным диез (#) игнорируются.

Конкретный синтаксис:

## This is a comment
## This is part of the first comment
stmt1
## Yet another

AST:

nnkCommentStmt() # only appears once for the first two lines!
stmt1
nnkCommentStmt() # another nnkCommentStmt because there is another comment
                 # (separate from the first)

Директивы

Одной из интересных особенностей Nim являются директивы, позволяющие тонко настраивать различные аспекты языка. Они бывают разных типов, например, украшают процедуры и объекты, но отдельная директива emit демонстрирует основы с AST.

Конкретный синтаксис:

{.emit: "#include <stdio.h>".}

AST:

nnkPragma(
  nnkExprColonExpr(
    nnkIdent("emit"),
    nnkStrLit("#include <stdio.h>") # the "argument"
  )
)

Появляется столько nnkIdent , сколько директив находится между {..}. Обратите внимание, что объявление новых директив по сути то же самое:

Конкретный синтаксис:

{.pragma: cdeclRename, cdecl.}

AST:

nnkPragma(
  nnkExprColonExpr(
    nnkIdent("pragma"), # this is always first when declaring a new pragma
    nnkIdent("cdeclRename") # the name of the pragma
  ),
  nnkIdent("cdecl")
)

Операторы

Оператор if

Представление оператора if тонкое, но легко просматривается. Если нет ветви else, дочерний узел nnkElse отсутствует.

Конкретный синтаксис:

if cond1:
  stmt1
elif cond2:
  stmt2
elif cond3:
  stmt3
else:
  stmt4

AST:

nnkIfStmt(
  nnkElifBranch(cond1, stmt1),
  nnkElifBranch(cond2, stmt2),
  nnkElifBranch(cond3, stmt3),
  nnkElse(stmt4)
)

Оператор when

Как оператор if, но корень имеет тип nnkWhenStmt.

Присваивание

Конкретный синтаксис:

x = 42

AST:

nnkAsgn(nnkIdent("x"), nnkIntLit(42))

Это не синтаксис присваивания, когда он комбинируется с var, let, или const.

Список операторов

Конкретный синтаксис:

stmt1
stmt2
stmt3

AST:

nnkStmtList(stmt1, stmt2, stmt3)

Оператор case

Конкретный синтаксис:

case expr1
of expr2, expr3..expr4:
  stmt1
of expr5:
  stmt2
elif cond1:
  stmt3
else:
  stmt4

AST:

nnkCaseStmt(
  expr1,
  nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1),
  nnkOfBranch(expr5, stmt2),
  nnkElifBranch(cond1, stmt3),
  nnkElse(stmt4)
)

Части nnkElifBranch и nnkElse могут отсутствовать.

Оператор while

Конкретный синтаксис:

while expr1:
  stmt1

AST:

nnkWhileStmt(expr1, stmt1)

Оператор for

Конкретный синтаксис:

for ident1, ident2 in expr1:
  stmt1

AST:

nnkForStmt(ident1, ident2, expr1, stmt1)

Оператор try

Конкретный синтаксис:

try:
  stmt1
except e1, e2:
  stmt2
except e3:
  stmt3
except:
  stmt4
finally:
  stmt5

AST:

nnkTryStmt(
  stmt1,
  nnkExceptBranch(e1, e2, stmt2),
  nnkExceptBranch(e3, stmt3),
  nnkExceptBranch(stmt4),
  nnkFinally(stmt5)
)

Оператор return

Конкретный синтаксис:

return expr1

AST:

nnkReturnStmt(expr1)

Оператор yield

Как return, но с типом узла nnkYieldStmt.

nnkYieldStmt(expr1)

Оператор discard

Как return, но с типом узла nnkDiscardStmt.

nnkDiscardStmt(expr1)

Оператор continue

Конкретный синтаксис:

continue

AST:

nnkContinueStmt()

Оператор break

Конкретный синтаксис:

break otherLocation

AST:

nnkBreakStmt(nnkIdent("otherLocation"))

Если break используется без места перехода, nnkEmpty заменяет nnkIdent.

Оператор блока

Конкретный синтаксис:

block name:

AST:

nnkBlockStmt(nnkIdent("name"), nnkStmtList(...))

Блок block не нуждается в имени, в этом случае используется nnkEmpty.

Оператор asm

Конкретный синтаксис:

asm """
  some asm
"""

AST:

nnkAsmStmt(
  nnkEmpty(), # for pragmas
  nnkTripleStrLit("some asm"),
)

Раздел импорта

Заявление Nim's import фактически принимает различные варианты в зависимости от присутствующих ключевых слов. Начнем с самого простого варианта.

Конкретная синтаксическая запись:

import math

AST:

nnkImportStmt(nnkIdent("math"))

С except, мы получаем nnkImportExceptStmt.

Конкретная синтаксическая запись:

import math except pow

AST:

nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow"))

Обратите внимание, что import math as m не использует другой узел; вместо этого мы используем nnkImportStmt с as в качестве инфиксного оператора.

Конкретная синтаксическая запись:

import strutils as su

AST:

nnkImportStmt(
  nnkInfix(
    nnkIdent("as"),
    nnkIdent("strutils"),
    nnkIdent("su")
  )
)

Из заявления

Если мы используем from ... import, результат также отличается.

Конкретная синтаксическая запись:

from math import pow

AST:

nnkFromStmt(nnkIdent("math"), nnkIdent("pow"))

Использование from math as m import pow идентично модификатору as со import заявлением, но заключено в nnkFromStmt.

Заявление о выводе

Когда вы делаете импортированный модуль доступным для модулей, которые импортируют ваш модуль, синтаксис export довольно простой.

Конкретная синтаксическая запись:

export unsigned

AST:

nnkExportStmt(nnkIdent("unsigned"))

Подобно заявлению import, AST отличается для export ... except.

Конкретная синтаксическая запись:

export math except pow # we're going to implement our own exponentiation

AST:

nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow"))

Заявление об включении

Как и простое import заявление, но с nnkIncludeStmt.

Конкретная синтаксическая запись:

include blocks

AST:

nnkIncludeStmt(nnkIdent("blocks"))

Раздел Var

Конкретная синтаксическая запись:

var a = 3

AST:

nnkVarSection(
  nnkIdentDefs(
    nnkIdent("a"),
    nnkEmpty(), # or nnkIdent(...) if the variable declares the type
    nnkIntLit(3),
  )
)

Обратите внимание, что второй или третий (или оба) параметра выше должны существовать, так как компилятор должен каким-то образом узнать тип (который он может вывести из заданного присваивания).

Это не тот же AST для всех случаев использования var. Подробности см. в разделе Объявление процедуры.

Раздел Let

Это эквивалентно var, но с nnkLetSection вместо nnkVarSection.

Конкретная синтаксическая запись:

let a = 3

AST:

nnkLetSection(
  nnkIdentDefs(
    nnkIdent("a"),
    nnkEmpty(), # or nnkIdent(...) for the type
    nnkIntLit(3),
  )
)

Раздел Const

Конкретная синтаксическая запись:

const a = 3

AST:

nnkConstSection(
  nnkConstDef( # not nnkConstDefs!
    nnkIdent("a"),
    nnkEmpty(), # or nnkIdent(...) if the variable declares the type
    nnkIntLit(3), # required in a const declaration!
  )
)

Раздел Type

Начиная с простого случая, раздел type выглядит так же, как var и const.

Конкретная синтаксическая запись:

type A = int

AST:

nnkTypeSection(
  nnkTypeDef(
    nnkIdent("A"),
    nnkEmpty(),
    nnkIdent("int")
  )
)

Объявление типов distinct аналогично, при этом последнее nnkIdent заключено в nnkDistinctTy.

Конкретная синтаксическая запись:

type MyInt = distinct int

AST:

# ...
nnkTypeDef(
  nnkIdent("MyInt"),
  nnkEmpty(),
  nnkDistinctTy(
    nnkIdent("int")
  )
)

Если раздел type использует параметры обобщения, они обрабатываются здесь:

Конкретная синтаксическая запись:

type A[T] = expr1

AST:

nnkTypeSection(
  nnkTypeDef(
    nnkIdent("A"),
    nnkGenericParams(
      nnkIdentDefs(
        nnkIdent("T"),
        nnkEmpty(), # if the type is declared with options, like
                    # ``[T: SomeInteger]``, they are given here
        nnkEmpty(),
      )
    )
    expr1,
  )
)

Обратите внимание, что не все nnkTypeDef используют nnkIdent в качестве параметра. Одно из наиболее распространенных применений объявлений типов — работа с объектами.

Конкретная синтаксическая запись:

type IO = object of RootObj

AST:

# ...
nnkTypeDef(
  nnkIdent("IO"),
  nnkEmpty(),
  nnkObjectTy(
    nnkEmpty(), # no pragmas here
    nnkOfInherit(
      nnkIdent("RootObj") # inherits from RootObj
    )
    nnkEmpty()
  )
)

Синтаксис объектов Nim богат. Давайте рассмотрим сложный пример целиком, чтобы увидеть некоторые из сложностей.

Конкретная синтаксическая запись:

type Obj[T] = object {.inheritable.}
  name: string
  case isFat: bool
  of true:
    m: array[100_000, T]
  of false:
    m: array[10, T]

AST:

# ...
nnkObjectTy(
  nnkPragma(
    nnkIdent("inheritable")
  ),
  nnkEmpty(),
  nnkRecList( # list of object parameters
    nnkIdentDefs(
      nnkIdent("name"),
      nnkIdent("string"),
      nnkEmpty()
    ),
    nnkRecCase( # case statement within object (not nnkCaseStmt)
      nnkIdentDefs(
        nnkIdent("isFat"),
        nnkIdent("bool"),
        nnkEmpty()
      ),
      nnkOfBranch(
        nnkIdent("true"),
        nnkRecList( # again, a list of object parameters
          nnkIdentDefs(
            nnkIdent("m"),
            nnkBracketExpr(
              nnkIdent("array"),
              nnkIntLit(100000),
              nnkIdent("T")
            ),
            nnkEmpty()
        )
      ),
      nnkOfBranch(
        nnkIdent("false"),
        nnkRecList(
          nnkIdentDefs(
            nnkIdent("m"),
            nnkBracketExpr(
              nnkIdent("array"),
              nnkIntLit(10),
              nnkIdent("T")
            ),
            nnkEmpty()
          )
        )
      )
    )
  )
)

Использование enum аналогично использованию object.

Конкретная синтаксическая запись:

type X = enum
  First

AST:

# ...
nnkEnumTy(
  nnkEmpty(),
  nnkIdent("First") # you need at least one nnkIdent or the compiler complains
)

Использование concept (экспериментальная) аналогично объектам.

Конкретная синтаксическая запись:

type Con = concept x,y,z
  (x & y & z) is string

AST:

# ...
nnkTypeClassTy( # note this isn't nnkConceptTy!
  nnkArglist(
    # ... idents for x, y, z
  )
  # ...
)

Статические типы, такие как static[int], используют nnkIdent в nnkStaticTy.

Конкретная синтаксическая запись:

type A[T: static[int]] = object

AST:

# ... within nnkGenericParams
nnkIdentDefs(
  nnkIdent("T"),
  nnkStaticTy(
    nnkIdent("int")
  ),
  nnkEmpty()
)
# ...

В общем случае объявление типов отражает этот синтаксис (например, nnkStaticTy для static, и т. д.). Далее следуют примеры (исключения помечены как *):

Тип Nim Соответствующий AST
static nnkStaticTy
tuple nnkTupleTy
var nnkVarTy
ptr nnkPtrTy
ref nnkRefTy
distinct nnkDistinctTy
enum nnkEnumTy
concept nnkTypeClassTy*
array nnkBracketExpr(nnkIdent("array"),...*
proc nnkProcTy
iterator nnkIteratorTy
object nnkObjectTy

Обращайте особое внимание при объявлении типов как proc. Поведение аналогично Procedure declaration, ниже, но nnkGenericParams не обрабатывается. Параметры обобщения обрабатываются в типе, а не в самом proc.

Конкретная синтаксическая запись:

type MyProc[T] = proc(x: T)

AST:

# ...
nnkTypeDef(
  nnkIdent("MyProc"),
  nnkGenericParams( # here, not with the proc
    # ...
  )
  nnkProcTy( # behaves like a procedure declaration from here on
    nnkFormalParams(
      # ...
    )
  )
)

Тот же синтаксис применяется к iterator (с nnkIteratorTy), но не применяется к converter или template.

Заявление Mixin

Конкретная синтаксическая запись:

mixin x

AST:

nnkMixinStmt(nnkIdent("x"))

Заявление Bind

Конкретная синтаксическая запись:

bind x

AST:

nnkBindStmt(nnkIdent("x"))

Объявление процедуры

Давайте рассмотрим процедуру со многими интересными аспектами, чтобы понять, как разбиваются вызовы процедур.

Конкретная синтаксическая запись:

proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard

AST:

nnkProcDef(
  nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name
  nnkEmpty(), # patterns for term rewriting in templates and macros (not procs)
  nnkGenericParams( # generic type parameters, like with type declaration
    nnkIdentDefs(
      nnkIdent("T"),
      nnkIdent("SomeInteger"),
      nnkEmpty()
    )
  ),
  nnkFormalParams(
    nnkIdent("int"), # the first FormalParam is the return type. nnkEmpty() if there is none
    nnkIdentDefs(
      nnkIdent("x"),
      nnkIdent("int"), # type type (required for procs, not for templates)
      nnkIntLit(3) # a default value
    ),
    nnkIdentDefs(
      nnkIdent("y"),
      nnkIdent("float32"),
      nnkEmpty()
    )
  ),
  nnkPragma(nnkIdent("inline")),
  nnkEmpty(), # reserved slot for future use
  nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
)

Есть еще один момент. Nim имеет гибкую идентификацию типов для своих процедур. Несмотря на то, что proc(a: int, b: int) и proc(a, b: int) эквивалентны в коде, AST немного отличается для последнего.

Конкретная синтаксическая запись:

proc(a, b: int)

AST:

# ...AST as above...
nnkFormalParams(
  nnkEmpty(), # no return here
  nnkIdentDefs(
    nnkIdent("a"), # the first parameter
    nnkIdent("b"), # directly to the second parameter
    nnkIdent("int"), # their shared type identifier
    nnkEmpty(), # default value would go here
  )
),
# ...

Когда процедура использует специальную переменную типа возврата var, результат отличается от результата раздела var.

Конкретная синтаксическая запись:

proc hello(): var int

AST:

# ...
nnkFormalParams(
  nnkVarTy(
    nnkIdent("int")
  )
)

Объявление итератора

Синтаксис итераторов похож на процедуры, но nnkIteratorDef заменяет nnkProcDef.

Конкретная синтаксическая запись:

iterator nonsense[T](x: seq[T]): float {.closure.} = ...

AST:

nnkIteratorDef(
  nnkIdent("nonsense"),
  nnkEmpty(),
  ...
)

Объявление преобразователя

Преобразователь похож на процедуру.

Конкретная синтаксическая запись:

converter toBool(x: float): bool

AST:

nnkConverterDef(
  nnkIdent("toBool"),
  # ...
)

Объявление шаблона

Шаблоны (а также макросы, как мы увидим) имеют немного расширенный AST по сравнению с процедурами и итераторами. Причина в этом [макросы переписывания терминов](manual.html#term-rewriting-macros). Вы видите nnkEmpty() в качестве второго аргумента к nnkProcDef и nnkIteratorDef выше? Именно туда попадают макросы переписывания терминов.

Конкретная синтаксическая запись:

template optOpt{expr1}(a: int): int

AST:

nnkTemplateDef(
  nnkIdent("optOpt"),
  nnkStmtList( # instead of nnkEmpty()
    expr1
  ),
  # follows like a proc or iterator
)

Если шаблон не имеет типов для своих параметров, идентификаторы типов внутри nnkFormalParams просто становятся nnkEmpty.

Объявление макроса

Макросы ведут себя как шаблоны, но nnkTemplateDef заменяется на nnkMacroDef.

Особые виды узлов

Существует несколько видов узлов, используемых для семантической проверки или генерации кода. Они доступны из этого модуля, но не должны использоваться. Другие виды узлов специально разработаны для упрощения манипулирования AST. Они описаны здесь.

Будет написано позже.

Импорты

since

Типы

NimNodeKind = enum
  nnkNone, nnkEmpty, nnkIdent, nnkSym, nnkType, nnkCharLit, nnkIntLit,
  nnkInt8Lit, nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
  nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, nnkFloat32Lit,
  nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, nnkTripleStrLit,
  nnkNilLit, nnkComesFrom, nnkDotCall, nnkCommand, nnkCall, nnkCallStrLit,
  nnkInfix, nnkPrefix, nnkPostfix, nnkHiddenCallConv, nnkExprEqExpr,
  nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkPar, nnkObjConstr, nnkCurly,
  nnkCurlyExpr, nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, nnkDotExpr,
  nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, nnkElifExpr, nnkElseExpr,
  nnkLambda, nnkDo, nnkAccQuoted, nnkTableConstr, nnkBind, nnkClosedSymChoice,
  nnkOpenSymChoice, nnkHiddenStdConv, nnkHiddenSubConv, nnkConv, nnkCast,
  nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
  nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString,
  nnkCStringToString, nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams,
  nnkOfInherit, nnkImportAs, nnkProcDef, nnkMethodDef, nnkConverterDef,
  nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch,
  nnkExceptBranch, nnkElse, nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt,
  nnkWhenStmt, nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
  nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, nnkConstDef,
  nnkTypeDef, nnkYieldStmt, nnkDefer, nnkTryStmt, nnkFinally, nnkRaiseStmt,
  nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
  nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkImportExceptStmt,
  nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkBindStmt,
  nnkMixinStmt, nnkUsingStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
  nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr,
  nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
  nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, nnkConstTy,
  nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy,
  nnkEnumFieldDef, nnkArglist, nnkPattern, nnkHiddenTryStmt, nnkClosure,
  nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr
Исходный код Редактировать
NimNodeKinds = set[NimNodeKind]
Исходный код Редактировать
NimTypeKind = enum
  ntyNone, ntyBool, ntyChar, ntyEmpty, ntyAlias, ntyNil, ntyExpr, ntyStmt,
  ntyTypeDesc, ntyGenericInvocation, ntyGenericBody, ntyGenericInst,
  ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal, ntyArray, ntyObject,
  ntyTuple, ntySet, ntyRange, ntyPtr, ntyRef, ntyVar, ntySequence, ntyProc,
  ntyPointer, ntyOpenArray, ntyString, ntyCString, ntyForward, ntyInt, ntyInt8,
  ntyInt16, ntyInt32, ntyInt64, ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128,
  ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64, ntyUnused0, ntyUnused1,
  ntyUnused2, ntyVarargs, ntyUncheckedArray, ntyError, ntyBuiltinTypeClass,
  ntyUserTypeClass, ntyUserTypeClassInst, ntyCompositeTypeClass, ntyInferred,
  ntyAnd, ntyOr, ntyNot, ntyAnything, ntyStatic, ntyFromExpr, ntyOptDeprecated,
  ntyVoid
Исходный код Редактировать
TNimTypeKinds {...}{.deprecated.} = set[NimTypeKind]
Исходный код Редактировать
NimSymKind = enum
  nskUnknown, nskConditional, nskDynLib, nskParam, nskGenericParam, nskTemp,
  nskModule, nskType, nskVar, nskLet, nskConst, nskResult, nskProc, nskFunc,
  nskMethod, nskIterator, nskConverter, nskMacro, nskTemplate, nskField,
  nskEnumField, nskForVar, nskLabel, nskStub
Исходный код Редактировать
TNimSymKinds {...}{.deprecated.} = set[NimSymKind]
Исходный код Редактировать
NimIdent {...}{.deprecated.} = object of RootObj
Представляет идентификатор Nim в AST. Примечание: Это редко используется, для создания идентификатора из строки используйте ident"abc". Исходный код Редактировать
NimSym {...}{.deprecated.} = ref NimSymObj
Представляет символ Nim в компиляторе; символ — это идентификатор, по которому производится поиск. Исходный код Редактировать
BindSymRule = enum
  brClosed,                 ## only the symbols in current scope are bound
  brOpen,                   ## open wrt overloaded symbols, but may be a single
                             ## symbol if not ambiguous (the rules match that of
                             ## binding in generics)
  brForceOpen                ## same as brOpen, but it will always be open even
                             ## if not ambiguous (this cannot be achieved with
                             ## any other means in the language currently)
определяет, как ведет себя bindSym Исходный код Редактировать
LineInfo = object
  filename*: string
  line*, column*: int
Исходный код Редактировать

Константы

nnkLiterals = {nnkCharLit..nnkNilLit}
Исходный код Редактировать
nnkCallKinds = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
                nnkCallStrLit}
Исходный код Редактировать
RoutineNodes = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
                nnkIteratorDef, nnkTemplateDef, nnkConverterDef, nnkMacroDef}
Исходный код Редактировать
AtomicNodes = {nnkNone..nnkNilLit}
Исходный код Редактировать
CallNodes = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
             nnkCallStrLit, nnkHiddenCallConv}
Исходный код Редактировать

Процедуры

proc toNimIdent(s: string): NimIdent {...}{.magic: "StrToIdent", noSideEffect, deprecated: "Deprecated since version 0.18.0: Use \'ident\' or \'newIdentNode\' instead.".}
Устаревшее: Устарело начиная с версии 0.18.0: Используйте 'ident' или 'newIdentNode' вместо этого.
Создаёт идентификатор из строки s. Исходный код Редактировать
proc `==`(a, b: NimIdent): bool {...}{.magic: "EqIdent", noSideEffect, deprecated: "Deprecated since version 0.18.1; Use \'==\' on \'NimNode\' instead.".}
Устаревшее: Устарело начиная с версии 0.18.1; Используйте '==' на 'NimNode' вместо этого.
Сравнивает два идентификатора Nim. Исходный код Редактировать
proc `==`(a, b: NimNode): bool {...}{.magic: "EqNimrodNode", noSideEffect.}
Сравнивает два узла Nim. Возвращает true, если узлы структурно эквивалентны. Это означает, что два независимо созданных узла могут быть равны. Исходный код Редактировать
proc `==`(a, b: NimSym): bool {...}{.magic: "EqNimrodNode", noSideEffect, deprecated: "Deprecated since version 0.18.1; Use \'==(NimNode, NimNode)\' instead.".}
Устаревшее: Устарело начиная с версии 0.18.1; Используйте '==(NimNode, NimNode)' вместо этого.
Сравнивает два символа Nim. Исходный код Редактировать
proc sameType(a, b: NimNode): bool {...}{.magic: "SameNodeType", noSideEffect,
                                     raises: [], tags: [].}
Сравнивает типы двух узлов Nim. Возвращает true, если типы одинаковы, например, при сравнении алиаса с исходным типом. Исходный код Редактировать
proc len(n: NimNode): int {...}{.magic: "NLen", noSideEffect.}
Возвращает количество дочерних элементов n. Исходный код Редактировать
proc `[]`(n: NimNode; i: int): NimNode {...}{.magic: "NChild", noSideEffect.}
Получить n's i-й дочерний элемент. Исходный код Редактировать
proc `[]`(n: NimNode; i: BackwardsIndex): NimNode {...}{.raises: [], tags: [].}
Получить n's i-й дочерний элемент. Исходный код Редактировать
proc `[]`[T, U](n: NimNode; x: HSlice[T, U]): seq[NimNode]
Операция среза для NimNode. Возвращает последовательность дочерних элементов n в пределах диапазона [n[x.a], n[x.b]]. Исходный код Редактировать
proc `[]=`(n: NimNode; i: int; child: NimNode) {...}{.magic: "NSetChild",
    noSideEffect.}
Установить n's i-й дочерний элемент в child. Исходный код Редактировать
proc `[]=`(n: NimNode; i: BackwardsIndex; child: NimNode) {...}{.raises: [], tags: [].}
Установить n's i-й дочерний элемент в child. Исходный код Редактировать
proc add(father, child: NimNode): NimNode {...}{.magic: "NAdd", discardable,
    noSideEffect, locks: 0.}
Добавляет child к узлу father. Возвращает родительский узел для вложенных вызовов. Исходный код Редактировать
proc add(father: NimNode; children: varargs[NimNode]): NimNode {...}{.
    magic: "NAddMultiple", discardable, noSideEffect, locks: 0.}
Добавляет каждый дочерний элемент children к узлу father. Возвращает узел father для вложенных вызовов. Исходный код Редактировать
proc del(father: NimNode; idx = 0; n = 1) {...}{.magic: "NDel", noSideEffect.}
Удаляет n дочерних элементов father, начиная с индекса idx. Исходный код Редактировать
proc kind(n: NimNode): NimNodeKind {...}{.magic: "NKind", noSideEffect.}
Возвращает kind узла n. Исходный код Редактировать
proc intVal(n: NimNode): BiggestInt {...}{.magic: "NIntVal", noSideEffect.}
Возвращает целое значение из любого целочисленного литерала или символа поля перечисления. Исходный код Редактировать
proc floatVal(n: NimNode): BiggestFloat {...}{.magic: "NFloatVal", noSideEffect.}
Возвращает число с плавающей точкой из любого литерала с плавающей точкой. Исходный код Редактировать
proc ident(n: NimNode): NimIdent {...}{.magic: "NIdent", noSideEffect, deprecated: "Deprecated since version 0.18.1; All functionality is defined on \'NimNode\'.".}
Устаревшее: Устарело начиная с версии 0.18.1; Вся функциональность определена в 'NimNode'.
Исходный код Редактировать
proc symbol(n: NimNode): NimSym {...}{.magic: "NSymbol", noSideEffect, deprecated: "Deprecated since version 0.18.1; All functionality is defined on \'NimNode\'.".}
Устаревшее: Устарело начиная с версии 0.18.1; Вся функциональность определена в 'NimNode'.
Исходный код Редактировать
proc getImpl(s: NimSym): NimNode {...}{.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
Устаревшее: используйте `getImpl: NimNode -> NimNode` вместо этого
Исходный код Редактировать
proc symKind(symbol: NimNode): NimSymKind {...}{.magic: "NSymKind", noSideEffect.}
Исходный код Редактировать
proc getImpl(symbol: NimNode): NimNode {...}{.magic: "GetImpl", noSideEffect.}
Возвращает копию объявления символа или nil. Исходный код Редактировать
proc strVal(n: NimNode): string {...}{.magic: "NStrVal", noSideEffect.}

Возвращает строковое значение идентификатора, символа, комментария или строкового литерала.

См. также:

  • strVal= proc для установки строкового значения.
Исходный код Редактировать
proc `$`(i: NimIdent): string {...}{.magic: "NStrVal", noSideEffect, deprecated: "Deprecated since version 0.18.1; Use \'strVal\' instead.".}
Устаревшее: Устарело начиная с версии 0.18.1; Используйте 'strVal' вместо этого.
Преобразует идентификатор Nim в строку. Исходный код Редактировать
proc `$`(s: NimSym): string {...}{.magic: "NStrVal", noSideEffect, deprecated: "Deprecated since version 0.18.1; Use \'strVal\' instead.".}
Устаревшее: Устарело начиная с версии 0.18.1; Используйте 'strVal' вместо этого.
Преобразует символ Nim в строку. Исходный код Редактировать
proc getImplTransformed(symbol: NimNode): NimNode {...}{.magic: "GetImplTransf",
    noSideEffect.}
Для типизированных proc возвращает AST после прохода трансформации; это полезно для отладки того, как компилятор преобразует код (например: defer, for), но обратите внимание, что преобразования кода зависят от реализации и могут быть изменены. См. пример в tests/macros/tmacros_various.nim. Исходный код Редактировать
proc owner(sym: NimNode): NimNode {...}{.magic: "SymOwner", noSideEffect.}

Принимает узел типа nnkSym и возвращает символ владельца. Значение 'владелец' зависит от sym's NimSymKind и контекста объявления. Для объявления верхнего уровня это символ nskModule, для локальных переменных proc - nskProc символ, для полей enum/object - nskType символ и т.д. Для символов без владельца возвращается nil.

См. также:

  • symKind proc для получения типа символа
  • getImpl proc для получения объявления символа
Исходный код Редактировать
proc isInstantiationOf(instanceProcSym, genProcSym: NimNode): bool {...}{.
    magic: "SymIsInstantiationOf", noSideEffect.}
Проверяет, является ли символ proc экземпляром символа универсального proc. Полезно для проверки символов proc по отношению к универсальным символам, возвращаемым bindSym. Исходный код Редактировать
proc getType(n: NimNode): NimNode {...}{.magic: "NGetType", noSideEffect.}
С помощью 'getType' вы можете получить тип узла. Тип Nim отображается в Nim AST, поэтому это немного запутанно, но это означает, что один и тот же API можно использовать для обхода типов. Рекурсивные типы сглаживаются для вас, поэтому нет опасности бесконечной рекурсии во время обхода. Чтобы разрешить рекурсивные типы, нужно снова вызвать 'getType'. Чтобы узнать, какой тип это, вызовите typeKind на результате getType. Исходный код Изменить
proc getType(n: typedesc): NimNode {...}{.magic: "NGetType", noSideEffect.}
Версия getType, которая принимает typedesc. Исходный код Изменить
proc typeKind(n: NimNode): NimTypeKind {...}{.magic: "NGetType", noSideEffect.}
Возвращает тип узла 'n', который должен представлять тип, то есть узел должен быть получен с помощью getType. Исходный код Изменить
proc getTypeInst(n: NimNode): NimNode {...}{.magic: "NGetType", noSideEffect,
                                        raises: [], tags: [].}
Возвращает тип узла в форме, соответствующей тому, как экземпляр типа был объявлен в коде.

Пример:

type
  Vec[N: static[int], T] = object
    arr: array[N, T]
  Vec4[T] = Vec[4, T]
  Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeInst(x: typed): untyped =
  newLit(x.getTypeInst.repr)
doAssert(dumpTypeInst(a) == "Vec4f")
doAssert(dumpTypeInst(b) == "Vec4[float32]")
doAssert(dumpTypeInst(c) == "Vec[4, float32]")
Исходный код Изменить
proc getTypeInst(n: typedesc): NimNode {...}{.magic: "NGetType", noSideEffect.}
Версия getTypeInst, которая принимает typedesc. Исходный код Изменить
proc getTypeImpl(n: NimNode): NimNode {...}{.magic: "NGetType", noSideEffect,
                                        raises: [], tags: [].}
Возвращает тип узла в форме, соответствующей реализации типа. Любые промежуточные псевдонимы расширяются, чтобы получить окончательную реализацию типа. Вместо этого вы можете использовать getImpl для символа, если хотите найти промежуточные псевдонимы.

Пример:

type
  Vec[N: static[int], T] = object
    arr: array[N, T]
  Vec4[T] = Vec[4, T]
  Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeImpl(x: typed): untyped =
  newLit(x.getTypeImpl.repr)
let t = """
object
  arr: array[0 .. 3, float32]
"""
doAssert(dumpTypeImpl(a) == t)
doAssert(dumpTypeImpl(b) == t)
doAssert(dumpTypeImpl(c) == t)
Исходный код Изменить
proc signatureHash(n: NimNode): string {...}{.magic: "NSigHash", noSideEffect.}
Возвращает стабильный идентификатор, полученный из сигнатуры символа. Сигнатура объединяет множество факторов, таких как тип символа, модуль владения символом и другие. Тот же идентификатор используется в бэкенде для создания искажённого имени символа. Исходный код Изменить
proc symBodyHash(s: NimNode): string {...}{.noSideEffect, raises: [], tags: [].}
Возвращает стабильный дайджест для символов, полученный не только из сигнатуры типа и владеющего модуля, но также и из тела реализации. Все процедуры/переменные, используемые в реализации этого символа, также хешируются рекурсивно, включая магические значения из модуля системы. Исходный код Изменить
proc getTypeImpl(n: typedesc): NimNode {...}{.magic: "NGetType", noSideEffect.}
Версия getTypeImpl, которая принимает typedesc. Исходный код Изменить
proc intVal=(n: NimNode; val: BiggestInt) {...}{.magic: "NSetIntVal", noSideEffect.}
Исходный код Изменить
proc floatVal=(n: NimNode; val: BiggestFloat) {...}{.magic: "NSetFloatVal",
    noSideEffect.}
Исходный код Изменить
proc symbol=(n: NimNode; val: NimSym) {...}{.magic: "NSetSymbol", noSideEffect, deprecated: "Deprecated since version 0.18.1; Generate a new \'NimNode\' with \'genSym\' instead.".}
Устарело: Устарело с версии 0.18.1; Создайте новый 'NimNode' с помощью 'genSym' вместо этого.
Исходный код Изменить
proc ident=(n: NimNode; val: NimIdent) {...}{.magic: "NSetIdent", noSideEffect, deprecated: "Deprecated since version 0.18.1; Generate a new \'NimNode\' with \'ident(string)\' instead.".}
Устарело: Устарело с версии 0.18.1; Создайте новый 'NimNode' с помощью 'ident(string)' вместо этого.
Исходный код Изменить
proc strVal=(n: NimNode; val: string) {...}{.magic: "NSetStrVal", noSideEffect.}

Устанавливает строковое значение строковой литерали или комментария. Установка strVal запрещена для узлов nnkIdent и nnkSym; необходимо создать новый узел с помощью ident или bindSym.

См. также:

  • процедуру strVal для получения строкового значения.
  • процедуру ident для создания идентификатора.
  • процедуру bindSym для связывания символа.
Исходный код Изменить
proc newNimNode(kind: NimNodeKind; lineInfoFrom: NimNode = nil): NimNode {...}{.
    magic: "NNewNimNode", noSideEffect.}

Создаёт новый узел AST указанного типа.

Параметр lineInfoFrom используется для информации о строках, когда сгенерированный код вызывает ошибку. Вы должны убедиться, что он установлен на узел, который вы преобразуете.

Исходный код Изменить
proc copyNimNode(n: NimNode): NimNode {...}{.magic: "NCopyNimNode", noSideEffect.}
Исходный код Изменить
proc copyNimTree(n: NimNode): NimNode {...}{.magic: "NCopyNimTree", noSideEffect.}
Исходный код Изменить
proc error(msg: string; n: NimNode = nil) {...}{.magic: "NError", gcsafe, locks: 0.}
Выводит сообщение об ошибке во время компиляции. Необязательный параметр n: NimNode используется в качестве источника для информации о файле и строке в сообщении об ошибке компиляции. Исходный код Изменить
proc warning(msg: string; n: NimNode = nil) {...}{.magic: "NWarning", gcsafe,
    locks: 0.}
Выводит сообщение об ошибке во время компиляции. Исходный код Изменить
proc hint(msg: string; n: NimNode = nil) {...}{.magic: "NHint", gcsafe, locks: 0.}
Выводит сообщение с подсказкой во время компиляции. Исходный код Изменить
proc newStrLitNode(s: string): NimNode {...}{.compileTime, noSideEffect, raises: [],
    tags: [].}
Создаёт узел строковой литерали из s. Исходный код Изменить
proc newCommentStmtNode(s: string): NimNode {...}{.compileTime, noSideEffect,
    raises: [], tags: [].}
Создаёт узел комментария. Исходный код Изменить
proc newIntLitNode(i: BiggestInt): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт узел целочисленной литерали из i. Исходный код Изменить
proc newFloatLitNode(f: BiggestFloat): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт узел с плавающей точкой из f. Исходный код Изменить
proc newIdentNode(i: NimIdent): NimNode {...}{.compileTime, deprecated, raises: [],
    tags: [].}
Устаревшее
Создаёт узел идентификатора из i. Исходный код Изменить
proc newIdentNode(i: string): NimNode {...}{.magic: "StrToIdent", noSideEffect.}
Создаёт узел идентификатора из i. Это просто псевдоним для ident(string). Используйте его, он короче. Исходный код Изменить
proc ident(name: string): NimNode {...}{.magic: "StrToIdent", noSideEffect.}
Создаёт новый узел ident из строки. Исходный код Изменить
proc bindSym(ident: string | NimNode; rule: BindSymRule = brClosed): NimNode {...}{.
    magic: "NBindSym", noSideEffect.}

Создаёт узел, который связывает ident с узлом символа. Связанный символ может быть перегруженным символом. Если ident является узлом NimNode, у него должен быть вид nnkIdent. Если rule == brClosed является либо деревом nnkClosedSymChoice, либо возвращается nnkSym, если символ не является неоднозначным. Если rule == brOpen является либо деревом nnkOpenSymChoice, либо возвращается nnkSym, если символ не является неоднозначным. Если rule == brForceOpen всегда возвращается дерево nnkOpenSymChoice, даже если символ не является неоднозначным.

Экспериментальная функция: для активации используйте {.experimental: "dynamicBindSym"}. Если вызов происходит из шаблона/обычного кода, то ident и rule должны быть константным выражением/литеральным значением. Если вызов происходит из макросов/времени компиляции/статических блоков, то ident и rule могут быть вычисляемыми значениями ВМ.

Исходный код Редактировать
proc genSym(kind: NimSymKind = nskLet; ident = ""): NimNode {...}{.magic: "NGenSym",
    noSideEffect.}
Генерирует свежий символ, гарантированно уникальный. Символ должен встречаться в контексте объявления. Исходный код Редактировать
proc callsite(): NimNode {...}{.magic: "NCallSite", gcsafe, locks: 0, deprecated: "Deprecated since v0.18.1; use varargs[untyped] in the macro prototype instead".}
Устарело: Устарело начиная с v0.18.1; используйте varargs[untyped] в макропрототипе вместо этого
Возвращает АСТ узла вызова выражения, вызвавшего этот макрос. Исходный код Редактировать
proc toStrLit(n: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Преобразует АСТ n в конкретный код Nim и оборачивает его в узел строки. Исходный код Редактировать
proc `$`(arg: LineInfo): string {...}{.raises: [], tags: [].}
Возвращает строковое представление в формате filepath(line, column). Исходный код Редактировать
proc copyLineInfo(arg: NimNode; info: NimNode) {...}{.magic: "NLineInfo",
    noSideEffect.}
Копирует lineinfo из info. Исходный код Редактировать
proc lineInfoObj(n: NimNode): LineInfo {...}{.compileTime, raises: [], tags: [].}
Возвращает LineInfo объекта n, используя абсолютный путь для filename. Исходный код Редактировать
proc lineInfo(arg: NimNode): string {...}{.compileTime, raises: [], tags: [].}
Возвращает информацию о строке в формате filepath(line, column). Исходный код Редактировать
proc internalErrorFlag(): string {...}{.magic: "NError", noSideEffect.}
Некоторые встроенные функции устанавливают флаг ошибки. Затем это преобразуется в соответствующее исключение. Примечание: Обычный код приложения не должен вызывать это. Исходный код Редактировать
proc parseExpr(s: string): NimNode {...}{.noSideEffect, compileTime,
                                     raises: [ValueError], tags: [].}
Компилирует переданную строку в её представление АСТ. Ожидается одно выражение. Выбрасывает ValueError для ошибок разбора. Исходный код Редактировать
proc parseStmt(s: string): NimNode {...}{.noSideEffect, compileTime,
                                     raises: [ValueError], tags: [].}
Компилирует переданную строку в её представление АСТ. Ожидается одна или несколько инструкций. Выбрасывает ValueError для ошибок разбора. Исходный код Редактировать
proc getAst(macroOrTemplate: untyped): NimNode {...}{.magic: "ExpandToAst",
    noSideEffect.}
Получает узлы АСТ, возвращённые из вызова макроса или шаблона. Пример:
macro FooMacro() =
  var ast = getAst(BarTemplate())
Исходный код Редактировать
proc quote(bl: typed; op = "``"): NimNode {...}{.magic: "QuoteAst", noSideEffect.}

Оператор квази-цитирования. Принимает выражение или блок и возвращает АСТ, который его представляет. Внутри процитированной АСТ вы можете интерполировать выражения NimNode из окружающего контекста. Если оператор не указан, цитирование выполняется с помощью обратных кавычек. В противном случае, указанный оператор должен использоваться как префиксный оператор для любого интерполируемого выражения.

Пример:

macro check(ex: untyped) =
  # this is a simplified version of the check macro from the
  # unittest module.
  
  # If there is a failed check, we want to make it easy for
  # the user to jump to the faulty line in the code, so we
  # get the line info here:
  var info = ex.lineinfo
  
  # We will also display the code string of the failed check:
  var expString = ex.toStrLit
  
  # Finally we compose the code to implement the check:
  result = quote do:
    if not `ex`:
      echo `info` & ": Check failed: " & `expString`
Исходный код Редактировать
proc expectKind(n: NimNode; k: NimNodeKind) {...}{.compileTime, raises: [], tags: [].}
Проверяет, что n имеет вид k. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют АСТ, передаваемый им. Исходный код Редактировать
proc expectMinLen(n: NimNode; min: int) {...}{.compileTime, raises: [], tags: [].}
Проверяет, что n имеет как минимум min дочерних узлов. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Редактировать
proc expectLen(n: NimNode; len: int) {...}{.compileTime, raises: [], tags: [].}
Проверяет, что n имеет ровно len дочерних узлов. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Редактировать
proc expectLen(n: NimNode; min, max: int) {...}{.compileTime, raises: [], tags: [].}
Проверяет, что n имеет количество дочерних узлов в диапазоне min..max. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Редактировать
proc newTree(kind: NimNodeKind; children: varargs[NimNode]): NimNode {...}{.
    compileTime, raises: [], tags: [].}
Создаёт новый узел с дочерними узлами. Исходный код Редактировать
proc newCall(theProc: NimNode; args: varargs[NimNode]): NimNode {...}{.compileTime,
    raises: [], tags: [].}
Создаёт новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Редактировать
proc newCall(theProc: NimIdent; args: varargs[NimNode]): NimNode {...}{.compileTime, deprecated: "Deprecated since v0.18.1; use \'newCall(string, ...)\' or \'newCall(NimNode, ...)\' instead",
    raises: [], tags: [].}
Устарело: Устарело начиная с v0.18.1; используйте 'newCall(string, ...)' или 'newCall(NimNode, ...)' вместо этого
Создаёт новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Редактировать
proc newCall(theProc: string; args: varargs[NimNode]): NimNode {...}{.compileTime,
    raises: [], tags: [].}
Создаёт новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Редактировать
proc newLit(c: char): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел символа-литерала. Исходный код Редактировать
proc newLit(i: int): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы. Исходный код Редактировать
proc newLit(i: int8): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы. Исходный код Редактировать
proc newLit(i: int16): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы. Исходный код Редактировать
proc newLit(i: int32): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы. Исходный код Редактировать
proc newLit(i: int64): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы. Исходный код Редактировать
proc newLit(i: uint): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целой беззнаковой литералы. Исходный код Редактировать
proc newLit(i: uint8): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целой беззнаковой литералы. Исходный код Редактировать
proc newLit(i: uint16): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы без знака. Исходный код Редактировать
proc newLit(i: uint32): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы без знака. Исходный код Редактировать
proc newLit(i: uint64): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел целочисленной литералы без знака. Исходный код Редактировать
proc newLit(b: bool): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел булевой литералы. Исходный код Редактировать
proc newLit(s: string): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел строковой литералы. Исходный код Редактировать
proc newLit(f: float32): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел вещественной литералы. Исходный код Редактировать
proc newLit(f: float64): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый узел вещественной литералы. Исходный код Редактировать
proc newLit(arg: enum): NimNode {...}{.compileTime.}
Исходный код Редактировать
proc newLit(arg: object): NimNode {...}{.compileTime.}
Исходный код Редактировать
proc newLit(arg: ref object): NimNode {...}{.compileTime.}
Создаёт новый узел литералы типа ref. Исходный код Редактировать
proc newLit[N, T](arg: array[N, T]): NimNode {...}{.compileTime.}
Исходный код Редактировать
proc newLit[T](arg: seq[T]): NimNode {...}{.compileTime.}
Исходный код Редактировать
proc newLit[T](s: set[T]): NimNode {...}{.compileTime.}
Исходный код Редактировать
proc newLit[T: tuple](arg: T): NimNode {...}{.compileTime.}
Используйте -d:nimHasWorkaround14720, чтобы восстановить поведение до внесения исправления, принудительно создавая именованную кортеж, даже когда arg является безымянным. Исходный код Редактировать
proc nestList(op: NimNode; pack: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Вложенный список pack в дерево выражений вызова: [a, b, c] преобразуется в op(a, op(c, d)). Это также известно как выражение свертки. Исходный код Редактировать
proc nestList(op: NimNode; pack: NimNode; init: NimNode): NimNode {...}{.compileTime,
    raises: [], tags: [].}
Вложенный список pack в дерево выражений вызова: [a, b, c] преобразуется в op(a, op(c, d)). Это также известно как выражение свертки. Исходный код Редактировать
proc treeRepr(n: NimNode): string {...}{.compileTime, gcsafe, locks: 0, raises: [],
                                    tags: [].}

Преобразует AST n в удобочитаемую строку в виде дерева.

См. также repr, lispRepr, и astGenRepr.

Исходный код Редактировать
proc lispRepr(n: NimNode; indented = false): string {...}{.compileTime, gcsafe,
    locks: 0, raises: [], tags: [].}

Преобразует AST n в удобочитаемую строку в стиле Lisp.

См. также repr, treeRepr, и astGenRepr.

Исходный код Редактировать
proc astGenRepr(n: NimNode): string {...}{.compileTime, gcsafe, locks: 0, raises: [],
                                      tags: [].}

Преобразует AST n в код, необходимый для генерации этого AST.

См. также repr, treeRepr, и lispRepr.

Исходный код Редактировать
proc newEmptyNode(): NimNode {...}{.compileTime, noSideEffect, raises: [], tags: [].}
Создаёт новый пустой узел. Исходный код Редактировать
proc newStmtList(stmts: varargs[NimNode]): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новый список операторов. Исходный код Редактировать
proc newPar(exprs: varargs[NimNode]): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новое выражение в скобках. Исходный код Редактировать
proc newBlockStmt(label, body: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новый оператор блока с меткой. Исходный код Редактировать
proc newBlockStmt(body: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новый блок: stmt. Исходный код Редактировать
proc newVarStmt(name, value: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новый оператор var. Исходный код Редактировать
proc newLetStmt(name, value: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новый оператор let. Исходный код Редактировать
proc newConstStmt(name, value: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Создаёт новый оператор const. Исходный код Редактировать
proc newAssignment(lhs, rhs: NimNode): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Исходный код Редактировать
proc newDotExpr(a, b: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новое выражение с точкой. a.dot(b) -> a.b Исходный код Редактировать
proc newColonExpr(a, b: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Создаёт новое выражение с двоеточием. newColonExpr(a, b) -> a: b Исходный код Редактировать
proc newIdentDefs(name, kind: NimNode; default = newEmptyNode()): NimNode {...}{.
    compileTime, raises: [], tags: [].}

Создаёт новый узел nnkIdentDefs определённого типа и значения.

nnkIdentDefs должны иметь как минимум три дочерних узла, но могут иметь больше: сначала идёт список идентификаторов, затем тип и узел значения. Эта вспомогательная процедура создаёт поддерево из трёх узлов, первым из которых является имя единственного идентификатора. Узлы kind и default (значение) могут быть пустыми в зависимости от того, где появляется nnkIdentDefs: определения кортежей или объектов будут иметь пустой узел default, блоки let или var могут иметь пустой узел kind в случае, если идентификатору присваивается значение. Пример:

var varSection = newNimNode(nnkVarSection).add(
  newIdentDefs(ident("a"), ident("string")),
  newIdentDefs(ident("b"), newEmptyNode(), newLit(3)))
# --> var
#       a: string
#       b = 3

Если вам нужно создать несколько идентификаторов, вам необходимо использовать более низкоуровневую функцию newNimNode:

result = newNimNode(nnkIdentDefs).add(
  ident("a"), ident("b"), ident("c"), ident("string"),
    newStrLitNode("Hello"))
Исходный код Редактировать
proc newNilLit(): NimNode {...}{.compileTime, raises: [], tags: [].}
Новая сокращенная литерала nil. Исходный код Редактировать
proc last(node: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Возвращает последний элемент в дочерних элементах узла. То же самое, что и node[^1]. Исходный код Редактировать
proc expectKind(n: NimNode; k: set[NimNodeKind]) {...}{.compileTime, raises: [],
    tags: [].}
Проверяет, что n имеет тип k. В противном случае компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют AST, переданный им. Исходный код Редактировать
proc newProc(name = newEmptyNode();
             params: openArray[NimNode] = [newEmptyNode()];
             body: NimNode = newStmtList(); procType = nnkProcDef;
             pragmas: NimNode = newEmptyNode()): NimNode {...}{.compileTime,
    raises: [], tags: [].}

Сокращение для создания новой процедуры.

Массив params должен начинаться с возвращаемого типа процедуры, за которым следует список IdentDefs, определяющих параметры.

Исходный код Редактировать
proc newIfStmt(branches: varargs[tuple[cond, body: NimNode]]): NimNode {...}{.
    compileTime, raises: [], tags: [].}
Конструктор для if операторов.
newIfStmt(
  (Ident, StmtList),
  ...
)
Исходный код Редактировать
proc newEnum(name: NimNode; fields: openArray[NimNode]; public, pure: bool): NimNode {...}{.
    compileTime, raises: [], tags: [].}
Создаёт новый перечисление. name должен быть идентификатором. Поля могут быть либо идентификаторами, либо EnumFieldDef
newEnum(
  name    = ident("Colors"),
  fields  = [ident("Blue"), ident("Red")],
  public  = true, pure = false)

# type Colors* = Blue Red
Исходный код Редактировать
proc copyChildrenTo(src, dest: NimNode) {...}{.compileTime, raises: [], tags: [].}
Копирует все дочерние элементы из src в dest. Исходный код Редактировать
proc name(someProc: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc name=(someProc: NimNode; val: NimNode) {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc params(someProc: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc params=(someProc: NimNode; params: NimNode) {...}{.compileTime, raises: [],
    tags: [].}
Исходный код Редактировать
proc pragma(someProc: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Получить пragma типа процедуры. Они будут расширены. Исходный код Редактировать
proc pragma=(someProc: NimNode; val: NimNode) {...}{.compileTime, raises: [],
    tags: [].}
Установить пragma типа процедуры. Исходный код Редактировать
proc addPragma(someProc, pragma: NimNode) {...}{.compileTime, raises: [], tags: [].}
Добавляет пragma к определению процедуры. Исходный код Редактировать
proc body(someProc: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc body=(someProc: NimNode; val: NimNode) {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc basename(a: NimNode): NimNode {...}{.raises: [], tags: [].}
Извлечь идентификатор из префиксных/постфиксных выражений. Исходный код Редактировать
proc `$`(node: NimNode): string {...}{.compileTime, raises: [], tags: [].}
Получить строку узла идентификатора. Исходный код Редактировать
proc insert(a: NimNode; pos: int; b: NimNode) {...}{.compileTime, raises: [],
    tags: [].}
Вставить узел b в узел a в pos. Исходный код Редактировать
proc basename=(a: NimNode; val: string) {...}{.compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc postfix(node: NimNode; op: string): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Исходный код Редактировать
proc prefix(node: NimNode; op: string): NimNode {...}{.compileTime, raises: [],
    tags: [].}
Исходный код Редактировать
proc infix(a: NimNode; op: string; b: NimNode): NimNode {...}{.compileTime,
    raises: [], tags: [].}
Исходный код Редактировать
proc unpackPostfix(node: NimNode): tuple[node: NimNode, op: string] {...}{.
    compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc unpackPrefix(node: NimNode): tuple[node: NimNode, op: string] {...}{.
    compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc unpackInfix(node: NimNode): tuple[left: NimNode, op: string, right: NimNode] {...}{.
    compileTime, raises: [], tags: [].}
Исходный код Редактировать
proc copy(node: NimNode): NimNode {...}{.compileTime, raises: [], tags: [].}
Псевдоним для copyNimTree. Исходный код Редактировать
proc eqIdent(a: string; b: string): bool {...}{.magic: "EqIdent", noSideEffect.}
Сравнение, нечувствительное к стилю. Исходный код Редактировать
proc eqIdent(a: NimNode; b: string): bool {...}{.magic: "EqIdent", noSideEffect.}
Сравнение, нечувствительное к стилю. a может быть идентификатором или символом. a может быть заключено в маркер экспорта (nnkPostfix) или заключено в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Редактировать
proc eqIdent(a: string; b: NimNode): bool {...}{.magic: "EqIdent", noSideEffect.}
Сравнение, нечувствительное к стилю. b может быть идентификатором или символом. b может быть заключено в маркер экспорта (nnkPostfix) или заключено в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Редактировать
proc eqIdent(a: NimNode; b: NimNode): bool {...}{.magic: "EqIdent", noSideEffect.}
Сравнение, нечувствительное к стилю. a и b могут быть идентификатором или символом. Оба могут быть заключены в маркер экспорта (nnkPostfix) или заключены в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Редактировать
proc expectIdent(n: NimNode; name: string) {...}{.compileTime, raises: [], tags: [].}
Проверить, что eqIdent(n,name) верно. В противном случае компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют AST, передаваемый им. Исходный код Редактировать
proc hasArgOfName(params: NimNode; name: string): bool {...}{.compileTime,
    raises: [], tags: [].}
Поиск в nnkFormalParams по аргументу. Исходный код Редактировать
proc addIdentIfAbsent(dest: NimNode; ident: string) {...}{.compileTime, raises: [],
    tags: [].}
Добавить ident к dest, если его нет. Это предназначено для использования с pragmas. Исходный код Редактировать
proc boolVal(n: NimNode): bool {...}{.compileTime, noSideEffect, raises: [], tags: [].}
Исходный код Редактировать
proc nodeID(n: NimNode): int {...}{.magic: "NodeId".}
Возвращает id n, если компилятор был скомпилирован со флагом -d:useNodeids, в противном случае возвращает -1. Эта процедура предназначена только для отладки компилятора. Исходный код Редактировать
proc getProjectPath(): string {...}{.raises: [], tags: [].}

Возвращает путь к текущему компилируемому проекту.

Не следует путать с system.currentSourcePath, который возвращает путь к исходному файлу, содержащему вызов шаблона.

Например, предположим dir1/foo.nim, что импортирует dir2/bar.nim, имейте bar.nim вывести как getProjectPath, так и currentSourcePath выходы.

Теперь, когда foo.nim компилируется, getProjectPath из bar.nim вернёт путь к dir1/ , в то время как currentSourcePath вернёт путь к файлу исходного кода bar.nim.

Теперь, когда bar.nim компилируется напрямую, getProjectPath теперь вернёт путь к dir2/ , а currentSourcePath по-прежнему вернёт тот же путь, путь к исходному файлу bar.nim.

Путь, возвращённый этой процедурой, устанавливается во время компиляции.

См. также:

  • процедура getCurrentDir
Исходный код Редактировать
proc getSize(arg: NimNode): int {...}{.magic: "NSizeOf", noSideEffect, raises: [],
                                  tags: [].}
Возвращает тот же результат, что и system.sizeof, если размер известен компилятору Nim. Возвращает отрицательное значение, если компилятор Nim не знает размер. Исходный код Редактировать
proc getAlign(arg: NimNode): int {...}{.magic: "NSizeOf", noSideEffect, raises: [],
                                   tags: [].}
Возвращает тот же результат, что и system.alignof, если выравнивание известно компилятору Nim. Работает с NimNode для использования в контексте макроса. Возвращает отрицательное значение, если компилятор Nim не знает выравнивание. Исходный код Редактировать
proc getOffset(arg: NimNode): int {...}{.magic: "NSizeOf", noSideEffect, raises: [],
                                    tags: [].}
Возвращает, экспортируется ли символ или нет. Исходный код Редактировать
proc extractDocCommentsAndRunnables(n: NimNode): NimNode {...}{.raises: [], tags: [].}
возвращает nnkStmtList , содержащий комментарии документации верхнего уровня и runnableExamples в a, останавливаясь на первом дочернем элементе, который не является ни тем, ни другим. Пример:
import macros
macro transf(a): untyped =
  result = quote do:
    proc fun2*() = discard
  let header = extractDocCommentsAndRunnables(a.body)
  # correct usage: rest is appended
  result.body = header
  result.body.add quote do: discard # just an example
  # incorrect usage: nesting inside a nnkStmtList:
  # result.body = quote do: (`header`; discard)

proc fun*() {.transf.} =
  ## first comment
  runnableExamples: discard
  runnableExamples: discard
  ## last comment
  discard # first statement after doc comments + runnableExamples
  ## not docgen'd
Исходный код Редактировать

Итераторы

iterator items(n: NimNode): NimNode {...}{.inline, raises: [], tags: [].}
Перебирает дочерние элементы узла NimNode n. Исходный код Редактировать
iterator pairs(n: NimNode): (int, NimNode) {...}{.inline, raises: [], tags: [].}
Перебирает дочерние элементы узла NimNode n и их индексы. Исходный код Редактировать
iterator children(n: NimNode): NimNode {...}{.inline, raises: [], tags: [].}
Перебирает дочерние элементы узла NimNode n. Исходный код Редактировать

Макросы

macro dumpTree(s: untyped): untyped

Принимает блок кода Nim и выводит разбор дерева абстрактного синтаксиса с использованием процедуры treeRepr. Вывод происходит во время компиляции.

Вы можете использовать это как инструмент для изучения дерева абстрактного синтаксиса Nim и для выявления типов узлов, которые должны быть созданы для представления определённого выражения/высказывания.

Например:

dumpTree:
  echo "Hello, World!"

Выводит:

StmtList
  Command
    Ident "echo"
    StrLit "Hello, World!"

Также см. dumpAstGen и dumpLisp.

Исходный код Редактировать
macro dumpLisp(s: untyped): untyped

Принимает блок кода Nim и выводит разбор дерева абстрактного синтаксиса с использованием процедуры lispRepr. Вывод происходит во время компиляции.

Вы можете использовать это как инструмент для изучения дерева абстрактного синтаксиса Nim и для выявления типов узлов, которые должны быть созданы для представления определённого выражения/высказывания.

Например:

dumpLisp:
  echo "Hello, World!"

Выводит:

(StmtList
 (Command
  (Ident "echo")
  (StrLit "Hello, World!")))

Также см. dumpAstGen и dumpTree.

Исходный код Редактировать
macro dumpAstGen(s: untyped): untyped

Принимает блок кода Nim и выводит разбор дерева абстрактного синтаксиса с использованием процедуры astGenRepr. Вывод происходит во время компиляции.

Вы можете использовать это как инструмент для написания макросов быстрее, написав примеры вывода, а затем скопировав фрагменты в макрос для модификации.

Например:

dumpAstGen:
  echo "Hello, World!"

Выводит:

nnkStmtList.newTree(
  nnkCommand.newTree(
    newIdentNode("echo"),
    newLit("Hello, World!")
  )
)

Также см. dumpTree и dumpLisp.

Исходный код Редактировать
macro expandMacros(body: typed): untyped

Расширяет один уровень макроса — полезно для отладки. Может использоваться для проверки того, что происходит при расширении вызова макроса, не изменяя его результат.

Например,

import sugar, macros

let
  x = 10
  y = 20
expandMacros:
  dump(x + y)

фактически отобразит x + y, но в то же время выведет во время компиляции расширение макроса dump, которое в данном случае равно debugEcho ["x + y", " = ", x + y].

Исходный код Редактировать
macro hasCustomPragma(n: typed; cp: typed{nkSym}): untyped

Расширяется до true , если выражение n, которое ожидается как nnkDotExpr (при проверке поля), процедура или тип имеют пользовательский псевдоним cp.

См. также getCustomPragmaVal.

template myAttr() {.pragma.}
type
  MyObj = object
    myField {.myAttr.}: int

proc myProc() {.myAttr.} = discard

var o: MyObj
assert(o.myField.hasCustomPragma(myAttr))
assert(myProc.hasCustomPragma(myAttr))
Исходный код Редактировать
macro getCustomPragmaVal(n: typed; cp: typed{nkSym}): untyped

Расширяется до значения пользовательского псевдонима cp выражения n, которое ожидается как nnkDotExpr, процедура или тип.

См. также hasCustomPragma

template serializationKey(key: string) {.pragma.}
type
  MyObj {.serializationKey: "mo".} = object
    myField {.serializationKey: "mf".}: int
var o: MyObj
assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
assert(o.getCustomPragmaVal(serializationKey) == "mo")
assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
Исходный код Редактировать
macro unpackVarargs(callee: untyped; args: varargs[untyped]): untyped
Исходный код Редактировать

Шаблоны

template `or`(x, y: NimNode): NimNode
Оцените x и, если это не пустой узел, верните его. В противном случае оцените до y. Может использоваться для цепочки нескольких выражений, чтобы получить первое непустое выражение.
let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode
Исходный код Редактировать
template findChild(n: NimNode; cond: untyped): NimNode {...}{.dirty.}
Найдите первый дочерний узел, соответствующий условию (или nil).
var res = findChild(n, it.kind == nnkPostfix and
                       it.basename.ident == toNimIdent"foo")
Исходный код Редактировать

© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/macros.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API