fixes merge conflicts
This commit is contained in:
commit
f7d1902043
887 changed files with 48670 additions and 42944 deletions
|
|
@ -8,28 +8,41 @@
|
|||
#
|
||||
|
||||
type
|
||||
AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator
|
||||
ThreadLocal ## the allocator is thread local only.
|
||||
ZerosMem ## the allocator always zeros the memory on an allocation
|
||||
Allocator* = ptr object {.inheritable.}
|
||||
alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
|
||||
dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
|
||||
realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
|
||||
deallocAll*: proc (a: Allocator) {.nimcall.}
|
||||
flags*: set[AllocatorFlag]
|
||||
|
||||
var
|
||||
currentAllocator {.threadvar.}: Allocator
|
||||
localAllocator {.threadvar.}: Allocator
|
||||
sharedAllocator: Allocator
|
||||
|
||||
proc getCurrentAllocator*(): Allocator =
|
||||
result = currentAllocator
|
||||
proc getLocalAllocator*(): Allocator =
|
||||
result = localAllocator
|
||||
|
||||
proc setCurrentAllocator*(a: Allocator) =
|
||||
currentAllocator = a
|
||||
proc setLocalAllocator*(a: Allocator) =
|
||||
localAllocator = a
|
||||
|
||||
proc alloc*(size: int; alignment: int = 8): pointer =
|
||||
let a = getCurrentAllocator()
|
||||
result = a.alloc(a, size, alignment)
|
||||
proc getSharedAllocator*(): Allocator =
|
||||
result = sharedAllocator
|
||||
|
||||
proc dealloc*(p: pointer; size: int) =
|
||||
let a = getCurrentAllocator()
|
||||
a.dealloc(a, p, size)
|
||||
proc setSharedAllocator*(a: Allocator) =
|
||||
sharedAllocator = a
|
||||
|
||||
proc realloc*(p: pointer; oldSize, newSize: int): pointer =
|
||||
let a = getCurrentAllocator()
|
||||
result = a.realloc(a, p, oldSize, newSize)
|
||||
when false:
|
||||
proc alloc*(size: int; alignment: int = 8): pointer =
|
||||
let a = getCurrentAllocator()
|
||||
result = a.alloc(a, size, alignment)
|
||||
|
||||
proc dealloc*(p: pointer; size: int) =
|
||||
let a = getCurrentAllocator()
|
||||
a.dealloc(a, p, size)
|
||||
|
||||
proc realloc*(p: pointer; oldSize, newSize: int): pointer =
|
||||
let a = getCurrentAllocator()
|
||||
result = a.realloc(a, p, oldSize, newSize)
|
||||
|
|
|
|||
47
lib/core/macrocache.nim
Normal file
47
lib/core/macrocache.nim
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2018 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides an API for macros that need to collect compile
|
||||
## time information across module boundaries in global variables.
|
||||
## Starting with version 0.19 of Nim this is not directly supported anymore
|
||||
## as it breaks incremental compilations.
|
||||
## Instead the API here needs to be used. See XXX (wikipedia page) for a
|
||||
## theoretical foundation behind this.
|
||||
|
||||
type
|
||||
CacheSeq* = distinct string
|
||||
CacheTable* = distinct string
|
||||
CacheCounter* = distinct string
|
||||
|
||||
proc value*(c: CacheCounter): int {.magic: "NccValue".}
|
||||
proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
|
||||
|
||||
proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
|
||||
proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
|
||||
proc len*(s: CacheSeq): int {.magic: "NcsLen".}
|
||||
proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
|
||||
|
||||
iterator items*(s: CacheSeq): NimNode =
|
||||
for i in 0 ..< len(s): yield s[i]
|
||||
|
||||
proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
|
||||
## 'key' has to be unique!
|
||||
|
||||
proc len*(t: CacheTable): int {.magic: "NctLen".}
|
||||
proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
|
||||
|
||||
proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
|
||||
proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
|
||||
|
||||
iterator pairs*(t: CacheTable): (string, NimNode) =
|
||||
var h = 0
|
||||
while hasNext(t, h):
|
||||
let (a, b, h2) = next(t, h)
|
||||
yield (a, b)
|
||||
h = h2
|
||||
|
|
@ -8,12 +8,16 @@
|
|||
#
|
||||
|
||||
include "system/inclrtl"
|
||||
include "system/helpers"
|
||||
|
||||
## This module contains the interface to the compiler's abstract syntax
|
||||
## tree (`AST`:idx:). Macros operate on this tree.
|
||||
|
||||
## .. include:: ../../doc/astspec.txt
|
||||
|
||||
# If you look for the implementation of the magic symbol
|
||||
# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
|
||||
|
||||
type
|
||||
NimNodeKind* = enum
|
||||
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
||||
|
|
@ -76,7 +80,8 @@ type
|
|||
nnkGotoState,
|
||||
nnkState,
|
||||
nnkBreakState,
|
||||
nnkFuncDef
|
||||
nnkFuncDef,
|
||||
nnkTupleConstr
|
||||
|
||||
NimNodeKinds* = set[NimNodeKind]
|
||||
NimTypeKind* = enum # some types are no longer used, see ast.nim
|
||||
|
|
@ -118,13 +123,10 @@ type
|
|||
## use ``ident"abc"``.
|
||||
|
||||
NimSymObj = object # hidden
|
||||
NimSym* = ref NimSymObj
|
||||
NimSym* {.deprecated.} = ref NimSymObj
|
||||
## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
|
||||
## *ident*.
|
||||
|
||||
{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
|
||||
TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
|
||||
TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
|
||||
|
||||
const
|
||||
nnkLiterals* = {nnkCharLit..nnkNilLit}
|
||||
|
|
@ -134,25 +136,23 @@ const
|
|||
|
||||
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
|
||||
## constructs an identifier from the string `s`
|
||||
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
|
||||
## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead.
|
||||
|
||||
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
|
||||
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
|
||||
## constructs an identifier from the string `s`
|
||||
## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead.
|
||||
|
||||
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
|
||||
## converts a Nim identifier to a string
|
||||
|
||||
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
|
||||
## converts a Nim symbol to a string
|
||||
|
||||
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
|
||||
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.}
|
||||
## compares two Nim identifiers
|
||||
## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead.
|
||||
|
||||
proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||
## compares two Nim nodes
|
||||
|
||||
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
|
||||
## compares two Nim symbols
|
||||
## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
|
||||
|
||||
|
||||
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
|
||||
## compares two Nim nodes' types. Return true if the types are the same,
|
||||
|
|
@ -195,8 +195,53 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
|
|||
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
|
||||
|
||||
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
|
||||
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
|
||||
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
|
||||
|
||||
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} =
|
||||
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
|
||||
|
||||
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.}
|
||||
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
|
||||
|
||||
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
|
||||
|
||||
when defined(nimSymKind):
|
||||
proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
|
||||
proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
|
||||
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
|
||||
## retrieve the implementation of `symbol`. `symbol` can be a
|
||||
## routine or a const.
|
||||
|
||||
proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.}
|
||||
## converts a Nim identifier to a string
|
||||
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
|
||||
|
||||
proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.}
|
||||
## converts a Nim symbol to a string
|
||||
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
|
||||
|
||||
else: # bootstrapping substitute
|
||||
proc getImpl*(symbol: NimNode): NimNode =
|
||||
symbol.symbol.getImpl
|
||||
|
||||
proc strValOld(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
|
||||
|
||||
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
|
||||
|
||||
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
|
||||
|
||||
proc strVal*(n: NimNode): string =
|
||||
if n.kind == nnkIdent:
|
||||
$n.ident
|
||||
elif n.kind == nnkSym:
|
||||
$n.symbol
|
||||
else:
|
||||
n.strValOld
|
||||
|
||||
when defined(nimHasSymOwnerInMacro):
|
||||
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
|
||||
## accepts node of kind nnkSym and returns its owner's symbol.
|
||||
## result is also mnde of kind nnkSym if owner exists otherwise
|
||||
## nnkNilLit is returned
|
||||
|
||||
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## with 'getType' you can access the node's `type`:idx:. A Nim type is
|
||||
|
|
@ -214,26 +259,65 @@ proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
|
|||
|
||||
proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
|
||||
## Returns the type kind of the node 'n' that should represent a type, that
|
||||
## means the node should have been obtained via `getType`.
|
||||
## means the node should have been obtained via ``getType``.
|
||||
|
||||
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## Like getType except it includes generic parameters for a specific instance
|
||||
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
|
||||
## Returns the `type`:idx: of a node in a form matching the way the
|
||||
## type instance was declared in the code.
|
||||
runnableExamples:
|
||||
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.}
|
||||
## Like getType except it includes generic parameters for a specific instance
|
||||
## Version of ``getTypeInst`` which takes a ``typedesc``.
|
||||
|
||||
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## Like getType except it includes generic parameters for the implementation
|
||||
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
|
||||
## Returns the `type`:idx: of a node in a form matching the implementation
|
||||
## of the type. Any intermediate aliases are expanded to arrive at the final
|
||||
## type implementation. You can instead use ``getImpl`` on a symbol if you
|
||||
## want to find the intermediate aliases.
|
||||
runnableExamples:
|
||||
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.}
|
||||
## Like getType except it includes generic parameters for the implementation
|
||||
|
||||
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
|
||||
## Version of ``getTypeImpl`` which takes a ``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.}
|
||||
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
|
||||
|
||||
proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.}
|
||||
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead.
|
||||
|
||||
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.}
|
||||
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead.
|
||||
|
||||
#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
|
||||
# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
|
||||
# can easily do:
|
||||
|
|
@ -255,18 +339,13 @@ proc newNimNode*(kind: NimNodeKind,
|
|||
proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
|
||||
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
|
||||
|
||||
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect.} =
|
||||
## retrieve the implementation of a symbol `s`. `s` can be a routine or a
|
||||
## const.
|
||||
discard
|
||||
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
|
||||
## writes an error message at compile time
|
||||
|
||||
proc warning*(msg: string) {.magic: "NWarning", benign.}
|
||||
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
|
||||
## writes a warning message at compile time
|
||||
|
||||
proc hint*(msg: string) {.magic: "NHint", benign.}
|
||||
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
|
||||
## writes a hint message at compile time
|
||||
|
||||
proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
|
||||
|
|
@ -294,11 +373,9 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
|
|||
result = newNimNode(nnkIdent)
|
||||
result.ident = i
|
||||
|
||||
proc newIdentNode*(i: string): NimNode {.compileTime.} =
|
||||
## creates an identifier node from `i`
|
||||
result = newNimNode(nnkIdent)
|
||||
result.ident = toNimIdent i
|
||||
|
||||
proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.}
|
||||
## creates an identifier node from `i`. It is simply an alias for
|
||||
## ``ident(string)``. Use that, it's shorter.
|
||||
|
||||
type
|
||||
BindSymRule* = enum ## specifies how ``bindSym`` behaves
|
||||
|
|
@ -312,24 +389,34 @@ type
|
|||
|
||||
{.deprecated: [TBindSymRule: BindSymRule].}
|
||||
|
||||
proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
|
||||
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
|
||||
magic: "NBindSym", noSideEffect.}
|
||||
## creates a node that binds `ident` to a symbol node. The bound symbol
|
||||
## may be an overloaded symbol.
|
||||
## if `ident` is a NimNode, it must have nkIdent kind.
|
||||
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
|
||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
|
||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
||||
## returned even if the symbol is not ambiguous.
|
||||
##
|
||||
## experimental feature:
|
||||
## use {.experimental: "dynamicBindSym".} to activate it
|
||||
## if called from template / regular code, `ident` and `rule` must be
|
||||
## constant expression / literal value.
|
||||
## if called from macros / compile time procs / static blocks,
|
||||
## `ident` and `rule` can be VM computed value.
|
||||
|
||||
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
||||
magic: "NGenSym", noSideEffect.}
|
||||
## generates a fresh symbol that is guaranteed to be unique. The symbol
|
||||
## needs to occur in a declaration context.
|
||||
|
||||
proc callsite*(): NimNode {.magic: "NCallSite", benign.}
|
||||
proc callsite*(): NimNode {.magic: "NCallSite", benign,
|
||||
deprecated: "use varargs[untyped] in the macro prototype instead".}
|
||||
## returns the AST of the invocation expression that invoked this macro.
|
||||
## **Deprecated since version 0.18.1**.
|
||||
|
||||
proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
|
||||
## converts the AST `n` to the concrete Nim code and wraps that
|
||||
|
|
@ -342,7 +429,8 @@ type
|
|||
line*,column*: int
|
||||
|
||||
proc `$`*(arg: Lineinfo): string =
|
||||
result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
|
||||
# BUG: without `result = `, gives compile error
|
||||
result = lineInfoToString(arg.filename, arg.line, arg.column)
|
||||
|
||||
#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
|
||||
## returns the position the node appears in the original source file
|
||||
|
|
@ -352,7 +440,11 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
|
|||
proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
|
||||
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
|
||||
|
||||
proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
|
||||
## copy lineinfo from info node
|
||||
|
||||
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
|
||||
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
|
||||
result.filename = n.getFile
|
||||
result.line = n.getLine
|
||||
result.column = n.getColumn
|
||||
|
|
@ -464,9 +556,11 @@ proc newCall*(theProc: NimNode,
|
|||
result.add(args)
|
||||
|
||||
proc newCall*(theProc: NimIdent,
|
||||
args: varargs[NimNode]): NimNode {.compileTime.} =
|
||||
args: varargs[NimNode]): NimNode {.compileTime, deprecated.} =
|
||||
## produces a new call node. `theProc` is the proc that is called with
|
||||
## the arguments ``args[0..]``.
|
||||
## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``,
|
||||
## or ``newCall(NimNode, ...)`` instead.
|
||||
result = newNimNode(nnkCall)
|
||||
result.add(newIdentNode(theProc))
|
||||
result.add(args)
|
||||
|
|
@ -594,17 +688,30 @@ proc newLit*(s: string): NimNode {.compileTime.} =
|
|||
result = newNimNode(nnkStrLit)
|
||||
result.strVal = s
|
||||
|
||||
proc nestList*(theProc: NimIdent,
|
||||
x: NimNode): NimNode {.compileTime.} =
|
||||
## nests the list `x` into a tree of call expressions:
|
||||
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
|
||||
proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
|
||||
## nests the list `pack` into a tree of call expressions:
|
||||
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
|
||||
## This is also known as fold expression.
|
||||
if pack.len < 1:
|
||||
error("`nestList` expects a node with at least 1 child")
|
||||
result = pack[^1]
|
||||
for i in countdown(pack.len - 2, 0):
|
||||
result = newCall(op, pack[i], result)
|
||||
|
||||
proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} =
|
||||
## nests the list `pack` into a tree of call expressions:
|
||||
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
|
||||
## This is also known as fold expression.
|
||||
result = init
|
||||
for i in countdown(pack.len - 1, 0):
|
||||
result = newCall(op, pack[i], result)
|
||||
|
||||
proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} =
|
||||
## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead.
|
||||
var L = x.len
|
||||
result = newCall(theProc, x[L-2], x[L-1])
|
||||
for i in countdown(L-3, 0):
|
||||
# XXX the 'copyNimTree' here is necessary due to a bug in the evaluation
|
||||
# engine that would otherwise create an endless loop here. :-(
|
||||
# This could easily user code and so should be fixed in evals.nim somehow.
|
||||
result = newCall(theProc, x[i], copyNimTree(result))
|
||||
result = newCall(theProc, x[i], result)
|
||||
|
||||
proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||
## Convert the AST `n` to a human-readable tree-like string.
|
||||
|
|
@ -616,13 +723,11 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
|
|||
res.add(($n.kind).substr(3))
|
||||
|
||||
case n.kind
|
||||
of nnkEmpty: discard # same as nil node in this representation
|
||||
of nnkNilLit: res.add(" nil")
|
||||
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
|
||||
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
|
||||
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
|
||||
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
|
||||
of nnkIdent: res.add(" ident\"" & $n.ident & '"')
|
||||
of nnkSym: res.add(" \"" & $n.symbol & '"')
|
||||
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
|
||||
res.add(" " & $n.strVal.newLit.repr)
|
||||
of nnkNone: assert false
|
||||
else:
|
||||
for j in 0..n.len-1:
|
||||
|
|
@ -641,13 +746,11 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
|
|||
add(result, "(")
|
||||
|
||||
case n.kind
|
||||
of nnkEmpty: discard # same as nil node in this representation
|
||||
of nnkNilLit: add(result, "nil")
|
||||
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
|
||||
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
||||
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
||||
of nnkIdent: add(result, "ident\"" & $n.ident & '"')
|
||||
of nnkSym: add(result, $n.symbol)
|
||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
|
||||
add(result, n.strVal.newLit.repr)
|
||||
of nnkNone: assert false
|
||||
else:
|
||||
if n.len > 0:
|
||||
|
|
@ -678,54 +781,27 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
|||
## See also `repr`, `treeRepr`, and `lispRepr`.
|
||||
|
||||
const
|
||||
NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone}
|
||||
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
|
||||
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
|
||||
|
||||
proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} =
|
||||
## Functions copied from strutils
|
||||
proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} =
|
||||
const
|
||||
HexChars = "0123456789ABCDEF"
|
||||
var
|
||||
t = x
|
||||
result = newString(len)
|
||||
for j in countdown(len-1, 0):
|
||||
result[j] = HexChars[int(t and 0xF)]
|
||||
t = t shr 4
|
||||
# handle negative overflow
|
||||
if t == 0 and x < 0: t = -1
|
||||
|
||||
result = newStringOfCap(s.len + s.len shr 2)
|
||||
result.add(prefix)
|
||||
for c in items(s):
|
||||
case c
|
||||
of '\0'..'\31', '\128'..'\255':
|
||||
add(result, "\\x")
|
||||
add(result, toHex(ord(c), 2))
|
||||
of '\\': add(result, "\\\\")
|
||||
of '\'': add(result, "\\'")
|
||||
of '\"': add(result, "\\\"")
|
||||
else: add(result, c)
|
||||
add(result, suffix)
|
||||
|
||||
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
|
||||
for i in 0..level-1: res.add " "
|
||||
if n.kind in NodeKinds:
|
||||
res.add("new" & ($n.kind).substr(3) & "Node(")
|
||||
elif n.kind in LitKinds:
|
||||
res.add("newLit(")
|
||||
elif n.kind == nnkNilLit:
|
||||
res.add("newNilLit()")
|
||||
else:
|
||||
res.add($n.kind)
|
||||
|
||||
case n.kind
|
||||
of nnkEmpty: discard
|
||||
of nnkNilLit: res.add("nil")
|
||||
of nnkEmpty, nnkNilLit: discard
|
||||
of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
|
||||
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
|
||||
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
|
||||
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
|
||||
of nnkIdent: res.add(($n.ident).escape())
|
||||
of nnkSym: res.add(($n.symbol).escape())
|
||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
|
||||
res.add(n.strVal.newLit.repr)
|
||||
of nnkNone: assert false
|
||||
else:
|
||||
res.add(".newTree(")
|
||||
|
|
@ -769,11 +845,10 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
|
|||
## See `dumpTree`.
|
||||
|
||||
macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
|
||||
## Deprecated.
|
||||
## Deprecated. Use `dumpTree` instead.
|
||||
|
||||
macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr
|
||||
## Deprecated.
|
||||
|
||||
## Deprecated. Use `dumpLisp` instead.
|
||||
|
||||
proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} =
|
||||
## Create a new empty node
|
||||
|
|
@ -1019,28 +1094,21 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
|
|||
|
||||
proc basename*(a: NimNode): NimNode {.compiletime, benign.}
|
||||
|
||||
|
||||
proc `$`*(node: NimNode): string {.compileTime.} =
|
||||
## Get the string of an identifier node
|
||||
case node.kind
|
||||
of nnkIdent:
|
||||
result = $node.ident
|
||||
of nnkPostfix:
|
||||
result = $node.basename.ident & "*"
|
||||
of nnkStrLit..nnkTripleStrLit:
|
||||
result = node.basename.strVal & "*"
|
||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
|
||||
result = node.strVal
|
||||
of nnkSym:
|
||||
result = $node.symbol
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||
result = $node[0]
|
||||
of nnkAccQuoted:
|
||||
result = $node[0]
|
||||
of nnkCommentStmt:
|
||||
result = node.strVal
|
||||
else:
|
||||
badNodeKind node.kind, "$"
|
||||
|
||||
proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
|
||||
proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
|
||||
## Create a new ident node from a string
|
||||
|
||||
iterator items*(n: NimNode): NimNode {.inline.} =
|
||||
|
|
@ -1131,40 +1199,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} =
|
|||
## An alias for copyNimTree().
|
||||
return node.copyNimTree()
|
||||
|
||||
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
|
||||
proc toLower(c: char): char {.inline.} =
|
||||
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
|
||||
else: result = c
|
||||
var i = 0
|
||||
var j = 0
|
||||
# first char is case sensitive
|
||||
if a[0] != b[0]: return 1
|
||||
while true:
|
||||
while a[i] == '_': inc(i)
|
||||
while b[j] == '_': inc(j) # BUGFIX: typo
|
||||
var aa = toLower(a[i])
|
||||
var bb = toLower(b[j])
|
||||
result = ord(aa) - ord(bb)
|
||||
if result != 0 or aa == '\0': break
|
||||
inc(i)
|
||||
inc(j)
|
||||
when defined(nimVmEqIdent):
|
||||
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
||||
## Style insensitive comparison.
|
||||
|
||||
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
|
||||
## Check if two idents are identical.
|
||||
proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
||||
## Style insensitive comparison.
|
||||
## ``a`` can be an identifier or a symbol.
|
||||
|
||||
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
|
||||
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
|
||||
## is the same as ``s``. Note that this is the preferred way to check! Most
|
||||
## other ways like ``node.ident`` are much more error-prone, unfortunately.
|
||||
case node.kind
|
||||
of nnkIdent:
|
||||
result = node.ident == toNimIdent s
|
||||
of nnkSym:
|
||||
result = eqIdent($node.symbol, s)
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||
result = eqIdent($node[0], s)
|
||||
else:
|
||||
result = false
|
||||
proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
||||
## Style insensitive comparison.
|
||||
## ``b`` can be an identifier or a symbol.
|
||||
|
||||
proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
||||
## Style insensitive comparison.
|
||||
## ``a`` and ``b`` can be an identifier or a symbol.
|
||||
|
||||
else:
|
||||
# this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
|
||||
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
|
||||
proc toLower(c: char): char {.inline.} =
|
||||
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
|
||||
else: result = c
|
||||
var i = 0
|
||||
var j = 0
|
||||
# first char is case sensitive
|
||||
if a[0] != b[0]: return 1
|
||||
while true:
|
||||
while a[i] == '_': inc(i)
|
||||
while b[j] == '_': inc(j) # BUGFIX: typo
|
||||
var aa = toLower(a[i])
|
||||
var bb = toLower(b[j])
|
||||
result = ord(aa) - ord(bb)
|
||||
if result != 0 or aa == '\0': break
|
||||
inc(i)
|
||||
inc(j)
|
||||
|
||||
|
||||
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
|
||||
## Check if two idents are identical.
|
||||
|
||||
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
|
||||
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
|
||||
## is the same as ``s``. Note that this is the preferred way to check! Most
|
||||
## other ways like ``node.ident`` are much more error-prone, unfortunately.
|
||||
case node.kind
|
||||
of nnkSym, nnkIdent:
|
||||
result = eqIdent(node.strVal, s)
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||
result = eqIdent($node[0], s)
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
|
||||
## Search nnkFormalParams for an argument.
|
||||
|
|
@ -1215,33 +1300,81 @@ macro expandMacros*(body: typed): untyped =
|
|||
echo result.toStrLit
|
||||
|
||||
proc customPragmaNode(n: NimNode): NimNode =
|
||||
expectKind(n, {nnkSym, nnkDotExpr})
|
||||
if n.kind == nnkSym:
|
||||
let sym = n.symbol.getImpl()
|
||||
sym.expectRoutine()
|
||||
result = sym.pragma
|
||||
elif n.kind == nnkDotExpr:
|
||||
let typDef = getImpl(getTypeInst(n[0]).symbol)
|
||||
typDef.expectKind(nnkTypeDef)
|
||||
typDef[2].expectKind(nnkObjectTy)
|
||||
let recList = typDef[2][2]
|
||||
for identDefs in recList:
|
||||
for i in 0 .. identDefs.len - 3:
|
||||
if identDefs[i].kind == nnkPragmaExpr and
|
||||
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]:
|
||||
return identDefs[i][1]
|
||||
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
|
||||
let
|
||||
typ = n.getTypeInst()
|
||||
|
||||
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
|
||||
return typ[1][1]
|
||||
elif typ.typeKind == ntyTypeDesc:
|
||||
let impl = typ[1].getImpl()
|
||||
if impl[0].kind == nnkPragmaExpr:
|
||||
return impl[0][1]
|
||||
else:
|
||||
return impl[0] # handle types which don't have macro at all
|
||||
|
||||
if n.kind == nnkSym: # either an variable or a proc
|
||||
let impl = n.getImpl()
|
||||
if impl.kind in RoutineNodes:
|
||||
return impl.pragma
|
||||
else:
|
||||
return typ.getImpl()[0][1]
|
||||
|
||||
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
|
||||
let name = (if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
|
||||
var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]))
|
||||
while typDef != nil:
|
||||
typDef.expectKind(nnkTypeDef)
|
||||
typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
|
||||
let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy}
|
||||
if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
|
||||
typDef = getImpl(typDef[2][0])
|
||||
else: # object definition, maybe an object directly defined as a ref type
|
||||
let
|
||||
obj = (if isRef: typDef[2][0] else: typDef[2])
|
||||
var identDefsStack = newSeq[NimNode](obj[2].len)
|
||||
for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
|
||||
while identDefsStack.len > 0:
|
||||
var identDefs = identDefsStack.pop()
|
||||
if identDefs.kind == nnkRecCase:
|
||||
identDefsStack.add(identDefs[0])
|
||||
for i in 1..<identDefs.len:
|
||||
# if it is and empty branch, skip
|
||||
if identDefs[i][0].kind == nnkNilLit: continue
|
||||
if identDefs[i][1].kind == nnkIdentDefs:
|
||||
identDefsStack.add(identDefs[i][1])
|
||||
else: # nnkRecList
|
||||
for j in 0..<identDefs[i][1].len:
|
||||
identDefsStack.add(identDefs[i][1][j])
|
||||
|
||||
else:
|
||||
for i in 0 .. identDefs.len - 3:
|
||||
if identDefs[i].kind == nnkPragmaExpr and
|
||||
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $name:
|
||||
return identDefs[i][1]
|
||||
|
||||
if obj[1].kind == nnkOfInherit: # explore the parent object
|
||||
typDef = getImpl(obj[1][0])
|
||||
else:
|
||||
typDef = nil
|
||||
|
||||
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
|
||||
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
|
||||
## has custom pragma `cp`.
|
||||
## (if checking a field), a proc or a type has custom pragma `cp`.
|
||||
##
|
||||
## See also `getCustomPragmaVal`.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## template myAttr() {.pragma.}
|
||||
## type
|
||||
## MyObj = object
|
||||
## myField {.myAttr.}: int
|
||||
##
|
||||
## proc myProc() {.myAttr.} = discard
|
||||
##
|
||||
## var o: MyObj
|
||||
## assert(o.myField.hasCustomPragma(myAttr) == 0)
|
||||
## assert(o.myField.hasCustomPragma(myAttr))
|
||||
## assert(myProc.hasCustomPragma(myAttr))
|
||||
let pragmaNode = customPragmaNode(n)
|
||||
for p in pragmaNode:
|
||||
if (p.kind == nnkSym and p == cp) or
|
||||
|
|
@ -1251,20 +1384,25 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
|
|||
|
||||
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
|
||||
## Expands to value of custom pragma `cp` of expression `n` which is expected
|
||||
## to be `nnkDotExpr`.
|
||||
## to be `nnkDotExpr`, a proc or a type.
|
||||
##
|
||||
## See also `hasCustomPragma`
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## template serializationKey(key: string) {.pragma.}
|
||||
## type
|
||||
## MyObj = object
|
||||
## 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")
|
||||
let pragmaNode = customPragmaNode(n)
|
||||
for p in pragmaNode:
|
||||
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
|
||||
return p[1]
|
||||
return newEmptyNode()
|
||||
|
||||
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
|
||||
|
||||
|
||||
when not defined(booting):
|
||||
|
|
@ -1285,3 +1423,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
|
|||
result = newCall(callee)
|
||||
for i in 0 ..< args.len:
|
||||
result.add args[i]
|
||||
|
||||
proc getProjectPath*(): string = discard
|
||||
## Returns the path to the currently compiling project
|
||||
|
||||
|
|
|
|||
|
|
@ -7,133 +7,163 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import allocators, typetraits
|
||||
|
||||
import typetraits
|
||||
# strs already imported allocators for us.
|
||||
|
||||
## Default seq implementation used by Nim's core.
|
||||
type
|
||||
seq*[T] = object
|
||||
len, cap: int
|
||||
data: ptr UncheckedArray[T]
|
||||
NimSeqPayload {.core.}[T] = object
|
||||
cap: int
|
||||
region: Allocator
|
||||
data: UncheckedArray[T]
|
||||
|
||||
template frees(s) = dealloc(s.data, s.cap * sizeof(T))
|
||||
NimSeqV2*[T] = object
|
||||
len: int
|
||||
p: ptr NimSeqPayload[T]
|
||||
|
||||
const nimSeqVersion {.core.} = 2
|
||||
|
||||
template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator)
|
||||
|
||||
# XXX make code memory safe for overflows in '*'
|
||||
proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} =
|
||||
seq[T](len: x.len, cap: x.len, data: x)
|
||||
|
||||
when defined(nimHasTrace):
|
||||
proc `=trace`[T](s: seq[T]; a: Allocator) =
|
||||
for i in 0 ..< s.len: `=trace`(s.data[i], a)
|
||||
when false:
|
||||
# this is currently not part of Nim's type bound operators and so it's
|
||||
# built into the tracing proc generation just like before.
|
||||
proc `=trace`[T](s: NimSeqV2[T]) =
|
||||
for i in 0 ..< s.len: `=trace`(s.data[i])
|
||||
|
||||
proc `=destroy`[T](x: var seq[T]) =
|
||||
if x.data != nil:
|
||||
proc `=destroy`[T](s: var seq[T]) =
|
||||
var x = cast[ptr NimSeqV2[T]](addr s)
|
||||
var p = x.p
|
||||
if p != nil:
|
||||
when not supportsCopyMem(T):
|
||||
for i in 0..<x.len: `=destroy`(x[i])
|
||||
frees(x)
|
||||
x.data = nil
|
||||
for i in 0..<x.len: `=destroy`(p.data[i])
|
||||
p.region.dealloc(p.region, p, payloadSize(p.cap))
|
||||
x.p = nil
|
||||
x.len = 0
|
||||
x.cap = 0
|
||||
|
||||
proc `=`[T](a: var seq[T]; b: seq[T]) =
|
||||
if a.data == b.data: return
|
||||
if a.data != nil:
|
||||
frees(a)
|
||||
a.data = nil
|
||||
proc `=`[T](x: var seq[T]; y: seq[T]) =
|
||||
var a = cast[ptr NimSeqV2[T]](addr x)
|
||||
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
|
||||
|
||||
if a.p == b.p: return
|
||||
`=destroy`(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
if b.data != nil:
|
||||
a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
|
||||
if b.p != nil:
|
||||
a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
|
||||
when supportsCopyMem(T):
|
||||
copyMem(a.data, b.data, a.cap * sizeof(T))
|
||||
if a.len > 0:
|
||||
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
|
||||
else:
|
||||
for i in 0..<a.len:
|
||||
a.data[i] = b.data[i]
|
||||
a.p.data[i] = b.p.data[i]
|
||||
|
||||
proc `=sink`[T](a: var seq[T]; b: seq[T]) =
|
||||
if a.data != nil and a.data != b.data:
|
||||
frees(a)
|
||||
proc `=sink`[T](x: var seq[T]; y: seq[T]) =
|
||||
var a = cast[ptr NimSeqV2[T]](addr x)
|
||||
var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
|
||||
if a.p != nil and a.p != b.p:
|
||||
`=destroy`(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
a.data = b.data
|
||||
a.p = b.p
|
||||
|
||||
proc resize[T](s: var seq[T]) =
|
||||
let old = s.cap
|
||||
if old == 0: s.cap = 8
|
||||
else: s.cap = (s.cap * 3) shr 1
|
||||
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
|
||||
when false:
|
||||
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
|
||||
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
||||
compilerRtl.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
|
||||
proc reserveSlot[T](x: var seq[T]): ptr T =
|
||||
if x.len >= x.cap: resize(x)
|
||||
result = addr(x.data[x.len])
|
||||
inc x.len
|
||||
|
||||
template add*[T](x: var seq[T]; y: T) =
|
||||
reserveSlot(x)[] = y
|
||||
type
|
||||
PayloadBase = object
|
||||
cap: int
|
||||
region: Allocator
|
||||
|
||||
proc shrink*[T](x: var seq[T]; newLen: int) =
|
||||
assert newLen <= x.len
|
||||
assert newLen >= 0
|
||||
proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
|
||||
# we have to use type erasure here as Nim does not support generic
|
||||
# compilerProcs. Oh well, this will all be inlined anyway.
|
||||
if cap <= 0:
|
||||
let region = getLocalAllocator()
|
||||
var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator)))
|
||||
p.region = region
|
||||
p.cap = cap
|
||||
result = p
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} =
|
||||
if len+addlen <= len:
|
||||
result = p
|
||||
elif p == nil:
|
||||
result = newSeqPayload(len+addlen, elemSize)
|
||||
else:
|
||||
# Note: this means we cannot support things that have internal pointers as
|
||||
# they get reallocated here. This needs to be documented clearly.
|
||||
var p = cast[ptr PayloadBase](p)
|
||||
let region = if p.region == nil: getLocalAllocator() else: p.region
|
||||
let cap = max(resize(p.cap), len+addlen)
|
||||
var q = cast[ptr PayloadBase](region.realloc(region, p,
|
||||
sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
|
||||
sizeof(int) + sizeof(Allocator) + elemSize * cap))
|
||||
q.region = region
|
||||
q.cap = cap
|
||||
result = q
|
||||
|
||||
proc shrink*[T](x: var seq[T]; newLen: Natural) =
|
||||
sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
|
||||
when not supportsCopyMem(T):
|
||||
for i in countdown(x.len - 1, newLen - 1):
|
||||
`=destroy`(x.data[i])
|
||||
x.len = newLen
|
||||
`=destroy`(x[i])
|
||||
|
||||
proc grow*[T](x: var seq[T]; newLen: int; value: T) =
|
||||
if newLen <= x.len: return
|
||||
assert newLen >= 0
|
||||
if x.cap == 0: x.cap = newLen
|
||||
else: x.cap = max(newLen, (x.cap * 3) shr 1)
|
||||
x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
|
||||
for i in x.len..<newLen:
|
||||
cast[ptr NimSeqV2[T]](addr x).len = newLen
|
||||
|
||||
proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
|
||||
let oldLen = x.len
|
||||
if newLen <= oldLen: return
|
||||
var xu = cast[ptr NimSeqV2[T]](addr x)
|
||||
|
||||
xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
|
||||
xu.len = newLen
|
||||
for i in oldLen .. newLen-1:
|
||||
x.data[i] = value
|
||||
x.len = newLen
|
||||
|
||||
template default[T](t: typedesc[T]): T =
|
||||
var v: T
|
||||
v
|
||||
|
||||
proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
|
||||
if newlen < x.len: shrink(x, newLen)
|
||||
else: grow(x, newLen, default(T))
|
||||
|
||||
template `[]`*[T](x: seq[T]; i: Natural): T =
|
||||
assert i < x.len
|
||||
x.data[i]
|
||||
|
||||
template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
|
||||
assert i < x.len
|
||||
x.data[i] = y
|
||||
|
||||
proc `@`*[T](elems: openArray[T]): seq[T] =
|
||||
result.cap = elems.len
|
||||
result.len = elems.len
|
||||
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
||||
when supportsCopyMem(T):
|
||||
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
|
||||
proc setLen[T](s: var seq[T], newlen: Natural) =
|
||||
if newlen < s.len:
|
||||
shrink(s, newLen)
|
||||
else:
|
||||
for i in 0..<result.len:
|
||||
result.data[i] = elems[i]
|
||||
var v: T # get the default value of 'v'
|
||||
grow(s, newLen, v)
|
||||
|
||||
proc len*[T](x: seq[T]): int {.inline.} = x.len
|
||||
when false:
|
||||
proc resize[T](s: var NimSeqV2[T]) =
|
||||
let old = s.cap
|
||||
if old == 0: s.cap = 8
|
||||
else: s.cap = (s.cap * 3) shr 1
|
||||
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
|
||||
|
||||
proc `$`*[T](x: seq[T]): string =
|
||||
result = "@["
|
||||
var firstElement = true
|
||||
for i in 0..<x.len:
|
||||
let
|
||||
value = x.data[i]
|
||||
if firstElement:
|
||||
firstElement = false
|
||||
proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
|
||||
if x.len >= x.cap: resize(x)
|
||||
result = addr(x.data[x.len])
|
||||
inc x.len
|
||||
|
||||
template add*[T](x: var NimSeqV2[T]; y: T) =
|
||||
reserveSlot(x)[] = y
|
||||
|
||||
template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
|
||||
assert i < x.len
|
||||
x.data[i]
|
||||
|
||||
template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
|
||||
assert i < x.len
|
||||
x.data[i] = y
|
||||
|
||||
proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
|
||||
result.cap = elems.len
|
||||
result.len = elems.len
|
||||
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
||||
when supportsCopyMem(T):
|
||||
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
|
||||
else:
|
||||
result.add(", ")
|
||||
|
||||
when compiles(value.isNil):
|
||||
# this branch should not be necessary
|
||||
if value.isNil:
|
||||
result.add "nil"
|
||||
else:
|
||||
result.addQuoted(value)
|
||||
else:
|
||||
result.addQuoted(value)
|
||||
|
||||
result.add("]")
|
||||
for i in 0..<result.len:
|
||||
result.data[i] = elems[i]
|
||||
|
|
|
|||
|
|
@ -7,105 +7,166 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Default string implementation used by Nim's core.
|
||||
## Default new string implementation used by Nim's core.
|
||||
|
||||
when false:
|
||||
# these are to be implemented or changed in the code generator.
|
||||
|
||||
#proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
|
||||
# seems to be unused.
|
||||
proc copyDeepString(src: NimString): NimString {.inline.}
|
||||
# ----------------- sequences ----------------------------------------------
|
||||
|
||||
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
|
||||
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
||||
compilerRtl.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
|
||||
import allocators
|
||||
|
||||
type
|
||||
string {.core.} = object
|
||||
len, cap: int
|
||||
data: ptr UncheckedArray[char]
|
||||
NimStrPayload {.core.} = object
|
||||
cap: int
|
||||
region: Allocator
|
||||
data: UncheckedArray[char]
|
||||
|
||||
proc nimStringLiteral(x: cstring; len: int): string {.core.} =
|
||||
string(len: len, cap: len, data: x)
|
||||
NimStringV2 {.core.} = object
|
||||
len: int
|
||||
p: ptr NimStrPayload ## can be nil if len == 0.
|
||||
|
||||
template frees(s) = dealloc(s.data, s.cap + 1)
|
||||
const nimStrVersion {.core.} = 2
|
||||
|
||||
template isLiteral(s): bool = s.p == nil or s.p.region == nil
|
||||
|
||||
template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
|
||||
|
||||
template frees(s) =
|
||||
if not isLiteral(s):
|
||||
s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
|
||||
|
||||
proc `=destroy`(s: var string) =
|
||||
if s.data != nil:
|
||||
frees(s)
|
||||
s.data = nil
|
||||
s.len = 0
|
||||
s.cap = 0
|
||||
var a = cast[ptr NimStringV2](addr s)
|
||||
frees(a)
|
||||
a.len = 0
|
||||
a.p = nil
|
||||
|
||||
proc `=sink`(a: var string, b: string) =
|
||||
template lose(a) =
|
||||
frees(a)
|
||||
|
||||
proc `=sink`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
# we hope this is optimized away for not yet alive objects:
|
||||
if a.data != nil and a.data != b.data:
|
||||
frees(a)
|
||||
if unlikely(a.p == b.p): return
|
||||
lose(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
a.data = b.data
|
||||
a.p = b.p
|
||||
|
||||
proc `=`(a: var string; b: string) =
|
||||
if a.data != nil and a.data != b.data:
|
||||
frees(a)
|
||||
a.data = nil
|
||||
proc `=`(x: var string, y: string) =
|
||||
var a = cast[ptr NimStringV2](addr x)
|
||||
var b = cast[ptr NimStringV2](unsafeAddr y)
|
||||
if unlikely(a.p == b.p): return
|
||||
lose(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
if b.data != nil:
|
||||
a.data = cast[type(a.data)](alloc(a.cap + 1))
|
||||
copyMem(a.data, b.data, a.cap+1)
|
||||
if isLiteral(b):
|
||||
# we can shallow copy literals:
|
||||
a.p = b.p
|
||||
else:
|
||||
let region = if a.p.region != nil: a.p.region else: getLocalAllocator()
|
||||
# we have to allocate the 'cap' here, consider
|
||||
# 'let y = newStringOfCap(); var x = y'
|
||||
# on the other hand... These get turned into moves now.
|
||||
a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
|
||||
a.p.region = region
|
||||
a.p.cap = b.len
|
||||
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
|
||||
|
||||
proc resize(s: var string) =
|
||||
let old = s.cap
|
||||
if old == 0: s.cap = 8
|
||||
else: s.cap = (s.cap * 3) shr 1
|
||||
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
|
||||
proc resize(old: int): int {.inline.} =
|
||||
if old <= 0: result = 4
|
||||
elif old < 65536: result = old * 2
|
||||
else: result = old * 3 div 2 # for large arrays * 3/2 is better
|
||||
|
||||
proc add*(s: var string; c: char) =
|
||||
if s.len >= s.cap: resize(s)
|
||||
s.data[s.len] = c
|
||||
s.data[s.len+1] = '\0'
|
||||
proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
|
||||
if isLiteral(s):
|
||||
let oldP = s.p
|
||||
# can't mutate a literal, so we need a fresh copy here:
|
||||
let region = getLocalAllocator()
|
||||
s.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(s.len + addlen)))
|
||||
s.p.region = region
|
||||
s.p.cap = s.len + addlen
|
||||
if s.len > 0:
|
||||
# we are about to append, so there is no need to copy the \0 terminator:
|
||||
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
|
||||
elif s.len + addlen > s.p.cap:
|
||||
let cap = max(s.len + addlen, resize(s.p.cap))
|
||||
s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p,
|
||||
oldSize = contentSize(s.p.cap),
|
||||
newSize = contentSize(cap)))
|
||||
s.p.cap = cap
|
||||
|
||||
proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
|
||||
prepareAdd(s, 1)
|
||||
s.p.data[s.len] = c
|
||||
s.p.data[s.len+1] = '\0'
|
||||
inc s.len
|
||||
|
||||
proc ensure(s: var string; newLen: int) =
|
||||
let old = s.cap
|
||||
if newLen >= old:
|
||||
s.cap = max((old * 3) shr 1, newLen)
|
||||
if s.cap > 0:
|
||||
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
|
||||
proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
|
||||
if len <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
else:
|
||||
let region = getLocalAllocator()
|
||||
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
|
||||
p.region = region
|
||||
p.cap = len
|
||||
if len > 0:
|
||||
# we are about to append, so there is no need to copy the \0 terminator:
|
||||
copyMem(unsafeAddr p.data[0], str, len)
|
||||
result = NimStringV2(len: 0, p: p)
|
||||
|
||||
proc add*(s: var string; y: string) =
|
||||
if y.len != 0:
|
||||
let newLen = s.len + y.len
|
||||
ensure(s, newLen)
|
||||
copyMem(addr s.data[len], y.data, y.data.len + 1)
|
||||
proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
|
||||
if str == nil: toNimStr(str, 0)
|
||||
else: toNimStr(str, str.len)
|
||||
|
||||
proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} =
|
||||
if s.len == 0: result = cstring""
|
||||
else: result = cstring(unsafeAddr s.p.data)
|
||||
|
||||
proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
|
||||
if src.len > 0:
|
||||
# also copy the \0 terminator:
|
||||
copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
|
||||
|
||||
proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
|
||||
dest.p.data[dest.len] = c
|
||||
dest.p.data[dest.len+1] = '\0'
|
||||
inc dest.len
|
||||
|
||||
proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
|
||||
# this is also 'system.newStringOfCap'.
|
||||
if space <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
else:
|
||||
let region = getLocalAllocator()
|
||||
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space)))
|
||||
p.region = region
|
||||
p.cap = space
|
||||
result = NimStringV2(len: 0, p: p)
|
||||
|
||||
proc mnewString(len: int): NimStringV2 {.compilerProc.} =
|
||||
if len <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
else:
|
||||
let region = getLocalAllocator()
|
||||
var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
|
||||
p.region = region
|
||||
p.cap = len
|
||||
result = NimStringV2(len: len, p: p)
|
||||
|
||||
proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
|
||||
if newLen > s.len:
|
||||
prepareAdd(s, newLen - s.len)
|
||||
else:
|
||||
s.len = newLen
|
||||
|
||||
proc len*(s: string): int {.inline.} = s.len
|
||||
|
||||
proc newString*(len: int): string =
|
||||
result.len = len
|
||||
result.cap = len
|
||||
if len > 0:
|
||||
result.data = alloc0(len+1)
|
||||
|
||||
converter toCString(x: string): cstring {.core.} =
|
||||
if x.len == 0: cstring"" else: cast[cstring](x.data)
|
||||
|
||||
proc newStringOfCap*(cap: int): string =
|
||||
result.len = 0
|
||||
result.cap = cap
|
||||
if cap > 0:
|
||||
result.data = alloc(cap+1)
|
||||
|
||||
proc `&`*(a, b: string): string =
|
||||
let sum = a.len + b.len
|
||||
result = newStringOfCap(sum)
|
||||
result.len = sum
|
||||
copyMem(addr result.data[0], a.data, a.len)
|
||||
copyMem(addr result.data[a.len], b.data, b.len)
|
||||
if sum > 0:
|
||||
result.data[sum] = '\0'
|
||||
|
||||
proc concat(x: openArray[string]): string {.core.} =
|
||||
## used be the code generator to optimize 'x & y & z ...'
|
||||
var sum = 0
|
||||
for i in 0 ..< x.len: inc(sum, x[i].len)
|
||||
result = newStringOfCap(sum)
|
||||
sum = 0
|
||||
for i in 0 ..< x.len:
|
||||
let L = x[i].len
|
||||
copyMem(addr result.data[sum], x[i].data, L)
|
||||
inc(sum, L)
|
||||
|
||||
# this also only works because the destructor
|
||||
# looks at s.p and not s.len
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue