Spec-Zone.ru › Nim

std/macros

Исходный кодИзменить

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

См. также:

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

АСД в Nim

Этот раздел описывает, как АСД моделируется с помощью системы типов Nim. АСД состоит из узлов (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-ым дочерним узлом i.

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

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

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

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

Выражение Nim Соответствующее АСД
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"

АСД:

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

Вызов с ()

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

echo("abc", "xyz")

АСД:

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

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

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

"abc" & "xyz"

АСД:

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

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

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

5 + 3 * 4

АСД:

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

Отметим, что если вы выберите использовать инфиксные операторы в префиксной форме, АСД ведет себя как вызов родительской функции с nnkAccQuoted, как показано ниже:

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

`+`(3, 4)

АСД:

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

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

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

? "xyz"

АСД:

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

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

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

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

identifier*

АСД:

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

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

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

writeLine(file=stdout, "hallo")

АСД:

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

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

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

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

echo"abc"

АСД:

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

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

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

x[]

АСД:

nnkDerefExpr(nnkIdent("x"))

Оператор addr

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

addr(x)

АСД:

nnkAddr(nnkIdent("x"))

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

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

cast[T](x)

АСД:

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

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

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

x.y

АСД:

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

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

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

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

x[y]

АСД:

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

Скобки

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

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

(a + b) * c

АСД:

nnkInfix(nnkIdent("*"),
  nnkPar(
    nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))),
  nnkIdent("c"))

Конструкторы кортежей

Узлы для построения кортежей создаются с помощью узла nnkTupleConstr.

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

(1, 2, 3)
(a: 1, b: 2, c: 3)
()

АСД:

nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
nnkTupleConstr(
  nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)),
  nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)),
  nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3)))
nnkTupleConstr()

Поскольку один кортеж был бы синтаксически идентичен скобкам с выражением внутри, анализатор ожидает заключительной запятой для них. Для конструкторов кортежей с именами полей это необязательно.

(1,)
(a: 1)

АСД:

nnkTupleConstr(nnkIntLit(1))
nnkTupleConstr(
  nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)))

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

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

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

{1, 2, 3}

АСД:

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

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

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

{a: 3, b: 5}

АСД:

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

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

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

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

[1, 2, 3]

АСД:

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

Диапазоны

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

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

1..3

АСД:

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

Пример кода:

macro genRepeatEcho() =
  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

АСД:

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

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

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

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

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

АСД:

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

Директивы

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

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

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

АСД:

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

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

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

{.pragma: cdeclRename, cdecl.}

АСД:

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

АСД:

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

Оператор when

Подобно оператору if, но корень имеет тип nnkWhenStmt.

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

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

x = 42

АСД:

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

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

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

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

stmt1
stmt2
stmt3

АСД:

nnkStmtList(stmt1, stmt2, stmt3)

Оператор case

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

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

АСД:

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

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

Оператор while

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

while expr1:
  stmt1

АСД:

nnkWhileStmt(expr1, stmt1)

Оператор for

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

for ident1, ident2 in expr1:
  stmt1

АСД:

nnkForStmt(ident1, ident2, expr1, stmt1)

Оператор try

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

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

АСД:

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

Оператор return

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

return expr1

АСД:

nnkReturnStmt(expr1)

Оператор yield

Подобно return, но с типом узла nnkYieldStmt.

nnkYieldStmt(expr1)

Оператор discard

Подобно return, но с типом узла nnkDiscardStmt.

nnkDiscardStmt(expr1)

Оператор continue

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

continue

АСД:

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 import может иметь различные варианты в зависимости от присутствующих ключевых слов. Начнём с самого простого случая.

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

import std/math

AST:

nnkImportStmt(nnkIdent("math"))

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

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

import std/math except pow

AST:

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

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

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

import std/strutils as su

AST:

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

Оператор from

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

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

from std/math import pow

AST:

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

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

Оператор export

Когда вы делаете импортированный модуль доступным для модулей, которые импортируют ваш, синтаксис 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"))

Оператор include

Как и обычный оператор 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] {.inheritable.} = object
  name: string
  case isFat: bool
  of true:
    m: array[100_000, T]
  of false:
    m: array[10, T]

AST:

# ...
nnkPragmaExpr(
  nnkIdent("Obj"),
  nnkPragma(nnkIdent("inheritable"))
),
nnkGenericParams(
nnkIdentDefs(
  nnkIdent("T"),
  nnkEmpty(),
  nnkEmpty())
),
nnkObjectTy(
  nnkEmpty(),
  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) {.nimcall.}

AST:

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

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

Версии type class этих узлов обычно имеют тот же вид узла, но без дочерних узлов. Type class tuple представлен nnkTupleClassTy, а type class proc или iterator с директивами имеет узел nnkEmpty вместо узла nnkFormalParams узла конкретного типа proc или iterator.

type TypeClass = proc {.nimcall.} | ref | tuple

AST:

nnkTypeDef(
  nnkIdent("TypeClass"),
  nnkEmpty(),
  nnkInfix(
    nnkIdent("|"),
    nnkProcTy(
      nnkEmpty(),
      nnkPragma(nnkIdent("nimcall"))
    ),
    nnkInfix(
      nnkIdent("|"),
      nnkRefTy(),
      nnkTupleClassTy()
    )
  )
)

Оператор 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 по сравнению с процедурами и итераторами. Причина в этом – макросы переписывания терминов. Заметили 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.

Скрытое стандартное преобразование

var f: float = 1

Тип "f" – это float, но тип "1" на самом деле int. Вставка int в float – ошибка типа. Nim вставляет узел nnkHiddenStdConv вокруг узла nnkIntLit, чтобы новый узел имел правильный тип float. Это работает для всех автоматически преобразуемых узлов и делает преобразование явным.

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

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

Подлежит написанию.

Импорты

since

Типы

BindSymRule = enum
  brClosed,                 ## only the symbols in current scope are bound
  brOpen,                   ## open for 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. Разница между открытыми и закрытыми символами описана в manual.html#symbol-lookup-in-generics-open-and-closed-symbols Источник Изменить
LineInfo = object
  filename*: string
  line*, column*: int
Источник Изменить
NimIdent {....deprecated.} = object of RootObj
Устарело
Представляет идентификатор Nim в AST. Примечание: Это редко используется, для построения идентификатора из строки используйте ident"abc". Источник Изменить
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,
  nnkOutTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSinkAsgn, nnkEnumTy,
  nnkEnumFieldDef, nnkArgList, nnkPattern, nnkHiddenTryStmt, nnkClosure,
  nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr, nnkError, ## erroneous AST node
  nnkModuleRef, nnkReplayAction, nnkNilRodNode, ## internal IC nodes
  nnkOpenSym
Источник Изменить
NimNodeKinds = set[NimNodeKind]
Источник Изменить
NimSym {....deprecated.} = ref NimSymObj
Устарело
Представляет символ Nim в компиляторе; символ - это найденный идентификатор. Источник Изменить
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
Источник Изменить
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
Источник Изменить
TNimSymKinds {....deprecated.} = set[NimSymKind]
Устарело
Источник Изменить
TNimTypeKinds {....deprecated.} = set[NimTypeKind]
Устарело
Источник Изменить

Константы

AtomicNodes = {nnkNone..nnkNilLit}
Источник Изменить
CallNodes = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
             nnkCallStrLit, nnkHiddenCallConv}
Источник Изменить
nnkCallKinds = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
                nnkCallStrLit, nnkHiddenCallConv}
Источник Изменить
nnkLiterals = {nnkCharLit..nnkNilLit}
Источник Изменить
nnkMutableTy {....deprecated.} = nnkOutTy
Источник Изменить
nnkSharedTy {....deprecated.} = nnkSinkAsgn
Источник Изменить
RoutineNodes = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
                nnkIteratorDef, nnkTemplateDef, nnkConverterDef, nnkMacroDef}
Источник Изменить

Процедуры

proc `$`(arg: LineInfo): string {....raises: [], tags: [], forbids: [].}
Возвращает строковое представление в формате filepath(line, column). Исходный код Изменить
proc `$`(i: NimIdent): string {.magic: "NStrVal", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Use \'strVal\' instead.",
                                raises: [], tags: [], forbids: [].}
Устаревшее: Устаревшее с версии 0.18.1; Используйте 'strVal' вместо этого.
Преобразует идентификатор Nim в строку. Исходный код Изменить
proc `$`(node: NimNode): string {....raises: [], tags: [], forbids: [].}
Получить строку узла идентификатора. Исходный код Изменить
proc `$`(s: NimSym): string {.magic: "NStrVal", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Use \'strVal\' instead.",
                              raises: [], tags: [], forbids: [].}
Устаревшее: Устаревшее с версии 0.18.1; Используйте 'strVal' вместо этого.
Преобразует символ Nim в строку. Исходный код Изменить
proc `==`(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Use \'==\' on \'NimNode\' instead.",
                                  raises: [], tags: [], forbids: [].}
Устаревшее: Устаревшее с версии 0.18.1; Используйте '==' на 'NimNode' вместо этого.
Сравнивает два идентификатора Nim. Исходный код Изменить
proc `==`(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect,
                                 ...raises: [], tags: [], forbids: [].}
Сравнивает два узла Nim. Возвращает true, если узлы структурно эквивалентны. Это означает, что два независимо созданных узла могут быть равны. Исходный код Изменить
proc `==`(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Use \'==(NimNode, NimNode)\' instead.",
                                raises: [], tags: [], forbids: [].}
Устаревшее: Устаревшее с версии 0.18.1; Используйте '==(NimNode, NimNode)' вместо этого.
Сравнивает два символа Nim. Исходный код Изменить
proc `[]`(n: NimNode; i: BackwardsIndex): NimNode {....raises: [], tags: [],
    forbids: [].}
Получить n's i-ый дочерний узел. Исходный код Изменить
proc `[]`(n: NimNode; i: int): NimNode {.magic: "NChild", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Получить n's i-ый дочерний узел. Исходный код Изменить
proc `[]`[T, U: Ordinal](n: NimNode; x: HSlice[T, U]): seq[NimNode]
Операция среза для NimNode. Возвращает последовательность дочерних узлов n в диапазоне [n[x.a], n[x.b]]. Исходный код Изменить
proc `[]=`(n: NimNode; i: BackwardsIndex; child: NimNode) {....raises: [],
    tags: [], forbids: [].}
Установить n's i-ый дочерний узел в child. Исходный код Изменить
proc `[]=`(n: NimNode; i: int; child: NimNode) {.magic: "NSetChild",
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Установить n's i-ый дочерний узел в child. Исходный код Изменить
proc add(father, child: NimNode): NimNode {.magic: "NAdd", discardable,
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Добавляет child к узлу father. Возвращает родительский узел для вложенных вызовов. Исходный код Изменить
proc add(father: NimNode; children: varargs[NimNode]): NimNode {.
    magic: "NAddMultiple", discardable, noSideEffect, ...raises: [], tags: [],
    forbids: [].}
Добавляет каждый дочерний узел из children к узлу father. Возвращает узел father для вложенных вызовов. Исходный код Изменить
proc addIdentIfAbsent(dest: NimNode; ident: string) {....raises: [], tags: [],
    forbids: [].}
Добавить ident к dest, если он отсутствует. Предназначено для использования с директивами. Исходный код Изменить
proc addPragma(someProc, pragma: NimNode) {....raises: [], tags: [], forbids: [].}
Добавляет директиву к определению процедуры. Исходный код Изменить
proc astGenRepr(n: NimNode): string {....gcsafe, raises: [], tags: [], forbids: [].}

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

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

Исходный код Изменить
proc basename(a: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Извлечь идентификатор из префиксных/постфиксных выражений. Исходный код Изменить
proc basename=(a: NimNode; val: string) {....raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc bindSym(ident: string | NimNode; rule: BindSymRule = brClosed): NimNode {.
    magic: "NBindSym", noSideEffect, ...raises: [], tags: [], forbids: [].}

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

См. справочник для получения дополнительной информации.

Исходный код Изменить
proc body(someProc: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc body=(someProc: NimNode; val: NimNode) {....raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc boolVal(n: NimNode): bool {.noSideEffect, ...raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc callsite(): NimNode {.magic: "NCallSite", ...gcsafe, deprecated: "Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead",
                           raises: [], tags: [], forbids: [].}
Устаревшее: Устаревшее с v0.18.1; используйте `varargs[untyped]` в прототипе макроса вместо этого
Возвращает AST вызова выражения, вызвавшего этот макрос. Исходный код Изменить
proc copy(node: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Псевдоним для copyNimTree. Исходный код Изменить
proc copyChildrenTo(src, dest: NimNode) {....raises: [], tags: [], forbids: [].}
Скопировать все дочерние узлы из src в dest. Исходный код Изменить
proc copyLineInfo(arg: NimNode; info: NimNode) {.magic: "NLineInfo",
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Копирует информацию о строке из info. Исходный код Изменить
proc copyNimNode(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}
Создаёт новый узел AST, копируя узел n. В отличие от copyNimTree, дочерние узлы n не копируются.

Пример:

macro foo(x: typed) =
  var s = copyNimNode(x)
  doAssert s.len == 0
  doAssert s.kind == nnkStmtList

foo:
  let x = 12
  echo x
Исходный код Изменить
proc copyNimTree(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}
Создаёт новый узел AST, рекурсивно копируя узел n. В отличие от copyNimNode, это копирует n, дочерние узлы n и т.д.

Пример:

macro foo(x: typed) =
  var s = copyNimTree(x)
  doAssert s.len == 2
  doAssert s.kind == nnkStmtList

foo:
  let x = 12
  echo x
Исходный код Изменить
proc del(father: NimNode; idx = 0; n = 1) {.magic: "NDel", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Удаляет n дочерних узлов father, начиная с индекса idx. Исходный код Изменить
proc eqIdent(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Сравнение без учёта регистра. a и b могут быть идентификатором или символом. Оба могут быть обернуты в маркер экспорта (nnkPostfix) или заключены в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Изменить
proc eqIdent(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Сравнение без учёта регистра. a может быть идентификатором или символом. a может быть обернут в маркер экспорта (nnkPostfix) или заключён в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Изменить
proc eqIdent(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Сравнение без учёта регистра. b может быть идентификатором или символом. b может быть обернут в маркер экспорта (nnkPostfix) или заключён в обратные кавычки (nnkAccQuoted), эти узлы будут распакованы. Исходный код Изменить
proc eqIdent(a: string; b: string): bool {.magic: "EqIdent", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Сравнение без учёта регистра. Исходный код Изменить
proc error(msg: string; n: NimNode = nil) {.magic: "NError", ...gcsafe, noreturn,
    ...raises: [], tags: [], forbids: [].}
Выводит сообщение об ошибке во время компиляции. Необязательный параметр n: NimNode используется в качестве источника информации о файле и строке в сообщении об ошибке компиляции. Исходный код Изменить
proc expectIdent(n: NimNode; name: string) {....raises: [], tags: [], forbids: [].}
Проверяет, что eqIdent(n,name) истинно. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют AST, передаваемый им. Исходный код Изменить
proc expectKind(n: NimNode; k: NimNodeKind) {....raises: [], tags: [], forbids: [].}
Проверяет, что n имеет тип k. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют AST, передаваемый им. Исходный код Изменить
proc expectKind(n: NimNode; k: set[NimNodeKind]) {....raises: [], tags: [],
    forbids: [].}
Проверяет, что n имеет тип k. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют AST, передаваемый им. Исходный код Изменить
proc expectLen(n: NimNode; len: int) {....raises: [], tags: [], forbids: [].}
Проверяет, что у n ровно len дочерних узлов. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Изменить
proc expectLen(n: NimNode; min, max: int) {....raises: [], tags: [], forbids: [].}
Проверяет, что у n количество дочерних узлов находится в диапазоне min..max. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Изменить
proc expectMinLen(n: NimNode; min: int) {....raises: [], tags: [], forbids: [].}
Проверяет, что у n не менее min дочерних узлов. Если это не так, компиляция прерывается с сообщением об ошибке. Это полезно для написания макросов, которые проверяют количество аргументов. Исходный код Изменить
proc extractDocCommentsAndRunnables(n: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}

Возвращает nnkStmtList, содержащий верхнеуровневые комментарии к документации и runnableExamples в a, останавливаясь на первом дочернем узле, который не является ни тем, ни другим. Пример:

import std/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
Исходный код Изменить
proc floatVal(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Возвращает значение float из любого числового литерала с плавающей точкой. Исходный код Изменить
proc floatVal=(n: NimNode; val: BiggestFloat) {.magic: "NSetFloatVal",
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc genSym(kind: NimSymKind = nskLet; ident = ""): NimNode {.magic: "NGenSym",
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Генерирует новый уникальный символ. Символ должен быть в контексте объявления. Исходный код Изменить
proc getAlign(arg: NimNode): int {.magic: "NSizeOf", noSideEffect, ...raises: [],
                                   tags: [], forbids: [].}
Возвращает тот же результат, что и system.alignof, если выравнивание известно компилятору Nim. Работает с NimNode для использования в контексте макросов. Возвращает отрицательное значение, если компилятор Nim не знает выравнивание. Исходный код Изменить
proc getAst(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst",
    noSideEffect, ...raises: [], tags: [], forbids: [].}

Получает узлы AST, возвращаемые вызовом макроса или шаблона. Смотрите также genasts.genAst. Пример:

macro FooMacro() =
  var ast = getAst(BarTemplate())
Исходный код Изменить
proc getImpl(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, ...deprecated: "use `getImpl: NimNode -> NimNode` instead",
                                   raises: [], tags: [], forbids: [].}
Устаревшее: используйте `getImpl: NimNode -> NimNode` вместо этого
Исходный код Изменить
proc getImpl(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Возвращает копию объявления символа или nil. Исходный код Изменить
proc getImplTransformed(symbol: NimNode): NimNode {.magic: "GetImplTransf",
    noSideEffect, ...raises: [], tags: [], forbids: [].}
Для типизированной процедуры возвращает АСТ после этапа преобразования; это полезно для отладки того, как компилятор преобразует код (например: defer, for), но обратите внимание, что преобразования кода зависят от реализации и могут изменяться. См. пример в tests/macros/tmacros_various.nim. Исходный код Редактировать
proc getOffset(arg: NimNode): int {.magic: "NSizeOf", noSideEffect, ...raises: [],
                                    tags: [], forbids: [].}
Возвращает тот же результат, что и system.offsetof, если смещение известно Nim-компилятору. Ожидает узел разрешенного символа из поля типа. Поэтому требует только один аргумент вместо двух. Возвращает отрицательное значение, если Nim-компилятор не знает смещение. Исходный код Редактировать
proc getProjectPath(): string {....raises: [], tags: [], forbids: [].}

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

Не следует путать с 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: [], forbids: [].}
Возвращает тот же результат, что и system.sizeof, если размер известен Nim-компилятору. Возвращает отрицательное значение, если Nim-компилятор не знает размер. Исходный код Редактировать
proc getType(n: NimNode): NimNode {.magic: "NGetType", noSideEffect, ...raises: [],
                                    tags: [], forbids: [].}
С помощью 'getType' вы можете получить доступ к типу узла. Тип Nim сопоставляется с АСТ Nim, поэтому это немного запутанно, но это означает, что для обхода типов можно использовать тот же API. Рекурсивные типы сплющиваются для вас, поэтому нет опасности бесконечной рекурсии во время обхода. Чтобы разрешить рекурсивные типы, нужно вызвать 'getType' снова. Чтобы увидеть, какой тип это, вызовите typeKind на результате getType. Исходный код Редактировать
proc getType(n: typedesc): NimNode {.magic: "NGetType", noSideEffect,
                                     ...raises: [], tags: [], forbids: [].}
Версия getType, которая принимает typedesc. Исходный код Редактировать
proc getTypeImpl(n: NimNode): NimNode {.magic: "NGetType", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}
Возвращает тип узла в форме, соответствующей реализации типа. Любые промежуточные алиасы расширяются, чтобы получить окончательную реализацию типа. Вы можете вместо этого использовать 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 getTypeImpl(n: typedesc): NimNode {.magic: "NGetType", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Версия getTypeImpl, которая принимает typedesc. Исходный код Редактировать
proc getTypeInst(n: NimNode): NimNode {.magic: "NGetType", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}
Возвращает тип узла в форме, соответствующей способу объявления экземпляра типа в коде.

Пример:

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,
    ...raises: [], tags: [], forbids: [].}
Версия getTypeInst, которая принимает typedesc. Исходный код Редактировать
proc hasArgOfName(params: NimNode; name: string): bool {....raises: [], tags: [],
    forbids: [].}
Поиск nnkFormalParams по аргументу. Исходный код Редактировать
proc hint(msg: string; n: NimNode = nil) {.magic: "NHint", ...gcsafe, raises: [],
    tags: [], forbids: [].}
Выводит сообщение-подсказку во время компиляции. Исходный код Редактировать
proc ident(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; All functionality is defined on \'NimNode\'.",
                                   raises: [], tags: [], forbids: [].}
Устаревшее: Устарело начиная с версии 0.18.1; Вся функциональность определена в 'NimNode'.
Исходный код Редактировать
proc ident(name: string): NimNode {.magic: "StrToIdent", noSideEffect,
                                    ...raises: [], tags: [], forbids: [].}
Создаёт новый узел ident из строки. Исходный код Редактировать
proc ident=(n: NimNode; val: NimIdent) {.magic: "NSetIdent", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Generate a new \'NimNode\' with \'ident(string)\' instead.",
    raises: [], tags: [], forbids: [].}
Устаревшее: Устарело начиная с версии 0.18.1; Создавайте новый 'NimNode' с 'ident(string)' вместо этого.
Исходный код Редактировать
proc infix(a: NimNode; op: string; b: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Исходный код Редактировать
proc insert(a: NimNode; pos: int; b: NimNode) {....raises: [], tags: [],
    forbids: [].}
Вставить узел b в узел a на позицию pos. Исходный код Редактировать
proc internalErrorFlag(): string {.magic: "NError", noSideEffect, ...raises: [],
                                   tags: [], forbids: [].}
Некоторые встроенные функции устанавливают флаг ошибки. Затем он преобразуется в правильное исключение. Примечание: обычный код приложения не должен вызывать эту функцию. Исходный код Редактировать
proc intVal(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect,
                                      ...raises: [], tags: [], forbids: [].}
Возвращает целое значение из любого целочисленного литерала или символа поля перечисления. Исходный код Редактировать
proc intVal=(n: NimNode; val: BiggestInt) {.magic: "NSetIntVal", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Исходный код Редактировать
proc isExported(n: NimNode): bool {.noSideEffect, ...raises: [], tags: [],
                                    forbids: [].}
Возвращает, экспортирован ли символ. Исходный код Редактировать
proc isInstantiationOf(instanceProcSym, genProcSym: NimNode): bool {.
    magic: "SymIsInstantiationOf", noSideEffect, ...raises: [], tags: [],
    forbids: [].}
Проверяет, является ли символ процедуры экземпляром обобщенного символа процедуры. Полезно для проверки символов процедур на соответствие обобщенным символам, возвращаемым bindSym. Исходный код Редактировать
proc kind(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect, ...raises: [],
                                     tags: [], forbids: [].}
Возвращает kind узла n. Исходный код Редактировать
proc last(node: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Возвращает последний элемент в узлах-потомках. То же, что и node[^1]. Исходный код Изменить
proc len(n: NimNode): int {.magic: "NLen", noSideEffect, ...raises: [], tags: [],
                            forbids: [].}
Возвращает количество дочерних элементов n. Исходный код Изменить
proc lineInfo(arg: NimNode): string {....raises: [], tags: [], forbids: [].}
Возвращает информацию о строке в формате filepath(line, column). Исходный код Изменить
proc lineInfoObj(n: NimNode): LineInfo {....raises: [], tags: [], forbids: [].}
Возвращает LineInfo узла n, используя абсолютный путь для filename. Исходный код Изменить
proc lispRepr(n: NimNode; indented = false): string {....gcsafe, raises: [],
    tags: [], forbids: [].}

Преобразует AST-дерево n в удобочитаемую строку, похожую на Lisp.

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

Исходный код Изменить
proc name(someProc: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc name=(someProc: NimNode; val: NimNode) {....raises: [], tags: [], forbids: [].}
Исходный код Изменить
proc nestList(op: NimNode; pack: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Вложенный список pack в дерево выражений вызовов: [a, b, c] преобразуется в op(a, op(c, d)). Это также известно как выражение свертки. Исходный код Изменить
proc nestList(op: NimNode; pack: NimNode; init: NimNode): NimNode {....raises: [],
    tags: [], forbids: [].}
Вложенный список pack в дерево выражений вызовов: [a, b, c] преобразуется в op(a, op(c, d)). Это также известно как выражение свертки. Исходный код Изменить
proc newAssignment(lhs, rhs: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Исходный код Изменить
proc newBlockStmt(body: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Создать новый блок: stmt. Исходный код Изменить
proc newBlockStmt(label, body: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Создать новое оператор блока с меткой. Исходный код Изменить
proc newCall(theProc: NimIdent; args: varargs[NimNode]): NimNode {....deprecated: "Deprecated since v0.18.1; use \'newCall(string, ...)\' or \'newCall(NimNode, ...)\' instead",
    raises: [], tags: [], forbids: [].}
Устарело: Устарело начиная с версии v0.18.1; используйте 'newCall(string, ...)' или 'newCall(NimNode, ...)' вместо этого
Создает новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Изменить
proc newCall(theProc: NimNode; args: varargs[NimNode]): NimNode {....raises: [],
    tags: [], forbids: [].}
Создает новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Изменить
proc newCall(theProc: string; args: varargs[NimNode]): NimNode {....raises: [],
    tags: [], forbids: [].}
Создает новый узел вызова. theProc — это процедура, которая вызывается с аргументами args[0..]. Исходный код Изменить
proc newColonExpr(a, b: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Создать новое выражение двоеточия. newColonExpr(a, b) -> a: b Исходный код Изменить
proc newCommentStmtNode(s: string): NimNode {.noSideEffect, ...raises: [],
    tags: [], forbids: [].}
Создает узел оператора комментария. Исходный код Изменить
proc newConstStmt(name, value: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Создать новый оператор const. Исходный код Изменить
proc newDotExpr(a, b: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Создать новое выражение точки. a.dot(b) -> a.b Исходный код Изменить
proc newEmptyNode(): NimNode {.noSideEffect, ...raises: [], tags: [], forbids: [].}
Создать новый пустой узел. Исходный код Изменить
proc newEnum(name: NimNode; fields: openArray[NimNode]; public, pure: bool): NimNode {.
    ...raises: [], tags: [], forbids: [].}

Создает новый перечисление. name должен быть идентификатором. Поля могут быть либо идентификаторами, либо EnumFieldDef:

newEnum(
  name    = ident("Colors"),
  fields  = [ident("Blue"), ident("Red")],
  public  = true, pure = false)

# type Colors* = Blue Red
Исходный код Изменить
proc newFloatLitNode(f: BiggestFloat): NimNode {....raises: [], tags: [],
    forbids: [].}
Создает узел с плавающей запятой из f. Исходный код Изменить
proc newIdentDefs(name, kind: NimNode; default = newEmptyNode()): NimNode {.
    ...raises: [], tags: [], forbids: [].}

Создает новый узел 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 newIdentNode(i: NimIdent): NimNode {....deprecated: "use ident(string)",
    raises: [], tags: [], forbids: [].}
Устарело: используйте ident(string)
Создаёт узел идентификатора из i. Исходный код Изменить
proc newIdentNode(i: string): NimNode {.magic: "StrToIdent", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}
Создаёт узел идентификатора из i. Это просто псевдоним для ident(string). Используйте его, он короче. Исходный код Изменить
proc newIfStmt(branches: varargs[tuple[cond, body: NimNode]]): NimNode {.
    ...raises: [], tags: [], forbids: [].}

Конструктор для операторов if.

newIfStmt(
  (Ident, StmtList),
  ...
)
Исходный код Редактировать
proc newIntLitNode(i: BiggestInt): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт узел целочисленной литеральной константы из i. Исходный код Редактировать
proc newLetStmt(name, value: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Создаёт новый оператор let. Исходный код Редактировать
proc newLit(arg: enum): NimNode
Исходный код Редактировать
proc newLit(arg: object): NimNode
Исходный код Редактировать
proc newLit(arg: ref object): NimNode
генерирует новый узел литерала типа ref. Исходный код Редактировать
proc newLit(b: bool): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел булевого литерала. Исходный код Редактировать
proc newLit(c: char): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел символьного литерала. Исходный код Редактировать
proc newLit(f: float32): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел литерала с плавающей точкой. Исходный код Редактировать
proc newLit(f: float64): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел литерала с плавающей точкой. Исходный код Редактировать
proc newLit(i: int): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел целочисленного литерала. Исходный код Редактировать
proc newLit(i: int8): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел целочисленного литерала. Исходный код Редактировать
proc newLit(i: int16): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел целочисленного литерала. Исходный код Редактировать
proc newLit(i: int32): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел целочисленного литерала. Исходный код Редактировать
proc newLit(i: int64): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел целочисленного литерала. Исходный код Редактировать
proc newLit(i: uint): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел беззнакового целочисленного литерала. Исходный код Редактировать
proc newLit(i: uint8): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел беззнакового целочисленного литерала. Исходный код Редактировать
proc newLit(i: uint16): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел беззнакового целочисленного литерала. Исходный код Редактировать
proc newLit(i: uint32): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел беззнакового целочисленного литерала. Исходный код Редактировать
proc newLit(i: uint64): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел беззнакового целочисленного литерала. Исходный код Редактировать
proc newLit(s: string): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новый узел строкового литерала. Исходный код Редактировать
proc newLit[N, T](arg: array[N, T]): NimNode
Исходный код Редактировать
proc newLit[T: tuple](arg: T): NimNode
Исходный код Редактировать
proc newLit[T](arg: seq[T]): NimNode
Исходный код Редактировать
proc newLit[T](s: set[T]): NimNode
Исходный код Редактировать
proc newNilLit(): NimNode {....raises: [], tags: [], forbids: [].}
Сокращение для создания узла nil. Исходный код Редактировать
proc newNimNode(kind: NimNodeKind; lineInfoFrom: NimNode = nil): NimNode {.
    magic: "NNewNimNode", noSideEffect, ...raises: [], tags: [], forbids: [].}

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

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

Исходный код Редактировать
proc newPar(exprs: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Создаёт новое выражение в скобках. Исходный код Редактировать
proc newPar(exprs: varargs[NimNode]): NimNode {....deprecated: "don\'t use newPar/nnkPar to construct tuple expressions; use nnkTupleConstr instead",
    raises: [], tags: [], forbids: [].}
Устарело: не используйте newPar/nnkPar для создания кортежей; используйте nnkTupleConstr вместо этого
Создаёт новое выражение в скобках. Исходный код Редактировать
proc newProc(name = newEmptyNode();
             params: openArray[NimNode] = [newEmptyNode()];
             body: NimNode = newStmtList(); procType = nnkProcDef;
             pragmas: NimNode = newEmptyNode()): NimNode {....raises: [], tags: [],
    forbids: [].}

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

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

Исходный код Редактировать
proc newStmtList(stmts: varargs[NimNode]): NimNode {....raises: [], tags: [],
    forbids: [].}
Создаёт новый список операторов. Исходный код Редактировать
proc newStrLitNode(s: string): NimNode {.noSideEffect, ...raises: [], tags: [],
    forbids: [].}
Создаёт узел строкового литерала из s. Исходный код Редактировать
proc newTree(kind: NimNodeKind; children: varargs[NimNode]): NimNode {.
    ...raises: [], tags: [], forbids: [].}
Создаёт новый узел с дочерними узлами. Исходный код Редактировать
proc newVarStmt(name, value: NimNode): NimNode {....raises: [], tags: [],
    forbids: [].}
Создать новый оператор var. Источник Изменить
proc nodeID(n: NimNode): int {.magic: "NodeId", ...raises: [], tags: [],
                               forbids: [].}
Возвращает идентификатор n, когда компилятор был скомпилирован с флагом -d:useNodeids, в противном случае возвращает -1. Эта процедура предназначена только для отладки компилятора. Источник Изменить
proc owner(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect, ...deprecated,
                                    raises: [], tags: [], forbids: [].}
Устарело

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

См. также:

  • процедура symKind для получения типа символа
  • процедура getImpl для получения объявления символа
Источник Изменить
proc params(someProc: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Источник Изменить
proc params=(someProc: NimNode; params: NimNode) {....raises: [], tags: [],
    forbids: [].}
Источник Изменить
proc parseExpr(s: string; filename: string = ""): NimNode {.noSideEffect,
    ...raises: [ValueError], tags: [], forbids: [].}
Компилирует переданную строку в её представление AST. Ожидает единственное выражение. Вызывает исключение ValueError при ошибках синтаксического анализа. Для более информативных сообщений об ошибках можно указать имя файла. Источник Изменить
proc parseStmt(s: string; filename: string = ""): NimNode {.noSideEffect,
    ...raises: [ValueError], tags: [], forbids: [].}
Компилирует переданную строку в её представление AST. Ожидает одно или несколько инструкций. Вызывает исключение ValueError при ошибках синтаксического анализа. Для более информативных сообщений об ошибках можно указать имя файла. Источник Изменить
proc postfix(node: NimNode; op: string): NimNode {....raises: [], tags: [],
    forbids: [].}
Источник Изменить
proc pragma(someProc: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Получить прагму типа процедуры. Они будут расширены. Источник Изменить
proc pragma=(someProc: NimNode; val: NimNode) {....raises: [], tags: [],
    forbids: [].}
Установить прагму типа процедуры. Источник Изменить
proc prefix(node: NimNode; op: string): NimNode {....raises: [], tags: [],
    forbids: [].}
Источник Изменить
proc quote(bl: typed; op = "``"): NimNode {.magic: "QuoteAst", noSideEffect,
    ...raises: [], tags: [], forbids: [].}

Оператор квази-цитирования. Принимает выражение или блок и возвращает представляющее его AST. Внутри цитируемого AST можно интерполировать выражения NimNode из окружающего контекста. Если оператор не указан, цитирование выполняется с помощью обратных кавычек. В противном случае необходимо использовать данный оператор в качестве префиксного оператора для любого интерполированного выражения. Первоначальное значение оператора интерполяции можно получить, экранировав его (добавив его в качестве префикса) при использовании в качестве унарного оператора: например, @ экранируется как @@, &% экранируется как &%&% и так далее; см. примеры.

Интерполяция пользовательского оператора требует акцентного цитирования (``), когда она разрешается в символ.

См. также genasts, который позволяет избежать некоторых проблем с quote.

Пример:

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`
check 1 + 1 == 2

Пример:

# example showing how to define a symbol that requires backtick without
# quoting it.
var destroyCalled = false
macro bar() =
  let s = newTree(nnkAccQuoted, ident"=destroy")
  # let s = ident"`=destroy`" # this would not work
  result = quote do:
    type Foo = object
    # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
    proc `s`(a: var Foo) = destroyCalled = true
    block:
      let a = Foo()
bar()
doAssert destroyCalled

Пример:

# custom `op`
var destroyCalled = false
macro bar(ident) =
  var x = 1.5
  result = quote("@") do:
    type Foo = object
    let `@ident` = 0 # custom op interpolated symbols need quoted (``)
    proc `=destroy`(a: var Foo) =
      doAssert @x == 1.5
      doAssert compiles(@x == 1.5)
      let b1 = @[1,2]
      let b2 = @@[1,2]
      doAssert $b1 == "[1, 2]"
      doAssert $b2 == "@[1, 2]"
      destroyCalled = true
    block:
      let a = Foo()
bar(someident)
doAssert destroyCalled

proc `&%`(x: int): int = 1
proc `&%`(x, y: int): int = 2

macro bar2() =
  var x = 3
  result = quote("&%") do:
    var y = &%x # quoting operator
    doAssert &%&%y == 1 # unary operator => need to escape
    doAssert y &% y == 2 # binary operator => no need to escape
    doAssert y == 3
bar2()
Источник Изменить
proc sameType(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect,
                                     ...raises: [], tags: [], forbids: [].}
Сравнивает типы двух узлов Nim. Возвращает true, если типы одинаковы, например, true при сравнении псевдонима с исходным типом. Источник Изменить
proc setLineInfo(arg: NimNode; file: string; line: int; column: int) {.
    ...raises: [], tags: [], forbids: [].}
Устанавливает информацию о строке в NimNode. Файл должен существовать, но может быть относительным путем. Если вы хотите добавить информацию о строке в блок, используя quote, вам нужно добавить информацию о строке после блока цитирования. Источник Изменить
proc setLineInfo(arg: NimNode; lineInfo: LineInfo) {....raises: [], tags: [],
    forbids: [].}
См. процедуру setLineInfo Источник Изменить
proc signatureHash(n: NimNode): string {.magic: "NSigHash", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Возвращает стабильный идентификатор, полученный из сигнатуры символа. Сигнатура сочетает в себе множество факторов, таких как тип символа, модуль владения символа и другие. Тот же идентификатор используется в back-end для создания искаженного имени символа. Источник Изменить
proc strVal(n: NimNode): string {.magic: "NStrVal", noSideEffect, ...raises: [],
                                  tags: [], forbids: [].}

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

См. также:

  • процедура strVal= для установки строкового значения.
Источник Изменить
proc strVal=(n: NimNode; val: string) {.magic: "NSetStrVal", noSideEffect,
                                        ...raises: [], tags: [], forbids: [].}

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

См. также:

  • процедура strVal для получения строкового значения.
  • процедура ident для создания идентификатора.
  • процедура bindSym для привязки символа.
Источник Изменить
proc symBodyHash(s: NimNode): string {.noSideEffect, ...raises: [], tags: [],
                                       forbids: [].}
Возвращает стабильный дайджест для символов, полученных не только из сигнатуры типа и модуля владения, но и из тела реализации. Все процедуры/переменные, используемые в реализации этого символа, также хэшируются рекурсивно, включая магические константы из системного модуля. Источник Изменить
proc symbol(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; All functionality is defined on \'NimNode\'.",
                                  raises: [], tags: [], forbids: [].}
Устарело: Устарело начиная с версии 0.18.1; Вся функциональность определена в 'NimNode'.
Источник Изменить
proc symbol=(n: NimNode; val: NimSym) {.magic: "NSetSymbol", noSideEffect, ...deprecated: "Deprecated since version 0.18.1; Generate a new \'NimNode\' with \'genSym\' instead.",
                                        raises: [], tags: [], forbids: [].}
Устарело: Устарело начиная с версии 0.18.1; Вместо этого создайте новый 'NimNode' с помощью 'genSym'.
Источник Изменить
proc symKind(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Источник Изменить
proc toNimIdent(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, ...deprecated: "Deprecated since version 0.18.0: Use \'ident\' or \'newIdentNode\' instead.",
                                       raises: [], tags: [], forbids: [].}
Устаревшее: Устарело начиная с версии 0.18.0: Используйте 'ident' или 'newIdentNode' вместо этого.
Создаёт идентификатор из строки s. Исходный код Редактировать
proc toStrLit(n: NimNode): NimNode {....raises: [], tags: [], forbids: [].}
Преобразует АСТ n в конкретный код Nim и оборачивает его в узел строковой литералы. Исходный код Редактировать
proc treeRepr(n: NimNode): string {....gcsafe, raises: [], tags: [], forbids: [].}

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

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

Исходный код Редактировать
proc typeKind(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect,
    ...raises: [], tags: [], forbids: [].}
Возвращает тип узла 'n', который должен представлять тип, то есть узел должен быть получен с помощью getType. Исходный код Редактировать
proc unpackInfix(node: NimNode): tuple[left: NimNode, op: string, right: NimNode] {.
    ...raises: [], tags: [], forbids: [].}
Исходный код Редактировать
proc unpackPostfix(node: NimNode): tuple[node: NimNode, op: string] {.
    ...raises: [], tags: [], forbids: [].}
Исходный код Редактировать
proc unpackPrefix(node: NimNode): tuple[node: NimNode, op: string] {....raises: [],
    tags: [], forbids: [].}
Исходный код Редактировать
proc warning(msg: string; n: NimNode = nil) {.magic: "NWarning", ...gcsafe,
    raises: [], tags: [], forbids: [].}
Выводит сообщение об ошибке во время компиляции. Исходный код Редактировать

Итераторы

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

Макросы

macro dumpAstGen(s: untyped): untyped

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

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

Например:

dumpAstGen:
  echo "Hello, World!"

Вывод:

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

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

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

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

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

Например:

dumpLisp:
  echo "Hello, World!"

Вывод:

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

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

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

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

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

Например:

dumpTree:
  echo "Hello, World!"

Вывод:

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

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

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

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

Например,

import std/[sugar, macros]

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

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

Исходный код Редактировать
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 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 unpackVarargs(callee: untyped; args: varargs[untyped]): untyped
Вызывает callee с args, распакованными как отдельные аргументы. Это полезно в 2 случаях:
  • при передаче varargs[T] для какого-то типизированного T
  • при передаче varargs[untyped], когда args может быть пустым из-за ограничения компилятора

Пример:

template call1(fun: typed; args: varargs[untyped]): untyped =
  unpackVarargs(fun, args)
  # when varargsLen(args) > 0: fun(args) else: fun() # this would also work
template call2(fun: typed; args: varargs[typed]): untyped =
  unpackVarargs(fun, args)
proc fn1(a = 0, b = 1) = discard (a, b)
call1(fn1, 10, 11)
call1(fn1) # `args` is empty in this case
if false: call2(echo, 10, 11) # would print 1011
Исходный код Редактировать

Шаблоны

template findChild(n: NimNode; cond: untyped): NimNode {.dirty.}

Находит первый узел-потомок, удовлетворяющий условию (или nil).

var res = findChild(n, it.kind == nnkPostfix and
                       it.basename.ident == ident"foo")
Исходный код Редактировать
template `or`(x, y: NimNode): NimNode

Вычисляет x и, если это не пустой узел, возвращает его. В противном случае вычисляет y. Может быть использован для объединения нескольких выражений для получения первого непустого выражения.

let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode
Исходный код Редактировать

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

Spec-Zone.ru

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