Merge pull request #54 from zah/pretty-print-ast

String interpolation implemented in terms of macros
This commit is contained in:
Araq 2011-09-20 12:13:24 -07:00
commit 57f184cf48
13 changed files with 492 additions and 53 deletions

View file

@ -57,10 +57,10 @@ type
nkStrLit, # a string literal "" nkStrLit, # a string literal ""
nkRStrLit, # a raw string literal r"" nkRStrLit, # a raw string literal r""
nkTripleStrLit, # a triple string literal """ nkTripleStrLit, # a triple string literal """
nkMetaNode, # difficult to explain; represents itself
# (used for macros)
nkNilLit, # the nil literal nkNilLit, # the nil literal
# end of atoms # end of atoms
nkMetaNode, # difficult to explain; represents itself
# (used for macros)
nkDotCall, # used to temporarily flag a nkCall node; nkDotCall, # used to temporarily flag a nkCall node;
# this is used # this is used
# for transforming ``s.len`` to ``len(s)`` # for transforming ``s.len`` to ``len(s)``
@ -313,6 +313,7 @@ type
TMagic* = enum # symbols that require compiler magic: TMagic* = enum # symbols that require compiler magic:
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf, mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
mEcho, mShallowCopy, mSlurp, mEcho, mShallowCopy, mSlurp,
mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
mUnaryLt, mSucc, mUnaryLt, mSucc,
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
@ -610,6 +611,23 @@ proc copyTree*(src: PNode): PNode
proc discardSons*(father: PNode) proc discardSons*(father: PNode)
proc len*(n: PNode): int {.inline.} =
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc safeLen*(n: PNode): int {.inline.} =
## works even for leaves.
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
else: result = len(n.sons)
proc add*(father, son: PNode) =
assert son != nil
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
proc `[]`*(n: PNode, i: int): PNode {.inline.} =
result = n.sons[i]
var emptyNode* = newNode(nkEmpty) var emptyNode* = newNode(nkEmpty)
# There is a single empty node that is shared! Do not overwrite it! # There is a single empty node that is shared! Do not overwrite it!
@ -754,6 +772,10 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result.info = info result.info = info
result.typ = typ result.typ = typ
proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
result = newNodeIT(nkMetaNode, info, typ)
result.add(tree)
proc NewType(kind: TTypeKind, owner: PSym): PType = proc NewType(kind: TTypeKind, owner: PSym): PType =
new(result) new(result)
result.kind = kind result.kind = kind
@ -866,23 +888,6 @@ proc sonsLen(n: PNode): int =
if isNil(n.sons): result = 0 if isNil(n.sons): result = 0
else: result = len(n.sons) else: result = len(n.sons)
proc len*(n: PNode): int {.inline.} =
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc safeLen*(n: PNode): int {.inline.} =
## works even for leaves.
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
else: result = len(n.sons)
proc add*(father, son: PNode) =
assert son != nil
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
proc `[]`*(n: PNode, i: int): PNode {.inline.} =
result = n.sons[i]
proc newSons(father: PNode, length: int) = proc newSons(father: PNode, length: int) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
setlen(father.sons, len(father.sons) + length) setlen(father.sons, len(father.sons) + length)

View file

@ -15,7 +15,8 @@
import import
strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets,
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf msgs, os, condsyms, idents, renderer, types, passes, semfold, transf,
parser, ropes
type type
PStackFrame* = ref TStackFrame PStackFrame* = ref TStackFrame
@ -751,6 +752,31 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
proc isEmpty(n: PNode): bool = proc isEmpty(n: PNode): bool =
result = (n != nil) and (n.kind == nkEmpty) result = (n != nil) and (n.kind == nkEmpty)
# The lexer marks multi-line strings as residing at the line where they are closed
# This function returns the line where the string begins
# Maybe the lexer should mark both the beginning and the end of expressions, then
# this function could be removed
proc stringStartingLine(s: PNode): int =
var totalLines = 0
for ln in splitLines(s.strVal): inc totalLines
result = s.info.line - totalLines
proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
var code = evalAux(c, n.sons[1], {})
var ast = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
if sonsLen(ast) != 1:
GlobalError(code.info, errExprExpected, "multiple statements")
result = ast.sons[0]
result.typ = newType(tyExpr, c.module)
proc evalParseStmt(c: PEvalContext, n: Pnode): Pnode =
var code = evalAux(c, n.sons[1], {})
result = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine)
result.typ = newType(tyStmt, c.module)
proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
var m = getMagic(n) var m = getMagic(n)
case m case m
@ -775,6 +801,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mAppendStrCh: result = evalAppendStrCh(c, n) of mAppendStrCh: result = evalAppendStrCh(c, n)
of mAppendStrStr: result = evalAppendStrStr(c, n) of mAppendStrStr: result = evalAppendStrStr(c, n)
of mAppendSeqElem: result = evalAppendSeqElem(c, n) of mAppendSeqElem: result = evalAppendSeqElem(c, n)
of mParseExprToAst: result = evalParseExpr(c, n)
of mParseStmtToAst: result = evalParseStmt(c, n)
of mNLen: of mNLen:
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return
@ -981,6 +1009,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
if (a == b) or if (a == b) or
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
result.intVal = 1 result.intVal = 1
of mAstToYaml:
var ast = evalAux(c, n.sons[1], {efLValue})
result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
of mNHint: of mNHint:
result = evalAux(c, n.sons[1], {}) result = evalAux(c, n.sons[1], {})
if isSpecial(result): return if isSpecial(result): return
@ -1034,6 +1065,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
dec(gNestedEvals) dec(gNestedEvals)
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations) if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
case n.kind # atoms: case n.kind # atoms:
of nkMetaNode:
result = copyTree(n.sons[0])
result.typ = n.typ
of nkEmpty: result = n of nkEmpty: result = n
of nkSym: result = evalSym(c, n, flags) of nkSym: result = evalSym(c, n, flags)
of nkType..nkNilLit: result = copyNode(n) # end of atoms of nkType..nkNilLit: result = copyNode(n) # end of atoms

View file

@ -31,6 +31,11 @@ proc parseTopLevelStmt*(p: var TParser): PNode
# implements an iterator. Returns the next top-level statement or # implements an iterator. Returns the next top-level statement or
# emtyNode if end of stream. # emtyNode if end of stream.
proc parseString*(s: string, filename: string = "", line: int = 0): PNode
# filename and line could be set optionally, when the string originates
# from a certain source file. This way, the compiler could generate
# correct error messages referring to the original source.
# helpers for the other parsers # helpers for the other parsers
proc getPrecedence*(tok: TToken): int proc getPrecedence*(tok: TToken): int
proc isOperator*(tok: TToken): bool proc isOperator*(tok: TToken): bool
@ -1369,3 +1374,14 @@ proc parseTopLevelStmt(p: var TParser): PNode =
result = complexOrSimpleStmt(p) result = complexOrSimpleStmt(p)
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
break break
proc parseString(s: string, filename: string = "", line: int = 0): PNode =
var stream = LLStreamOpen(s)
stream.lineOffset = line
var parser : TParser
OpenParser(parser, filename, stream)
result = parser.parseAll
CloseParser(parser)

View file

@ -8,6 +8,7 @@
# #
# this module does the semantic checking for expressions # this module does the semantic checking for expressions
const const
ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyArrayConstr, tyTuple, tySet} tyArrayConstr, tyTuple, tySet}
@ -384,6 +385,10 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
else: else:
nil nil
proc isCallExpr(n: PNode): bool =
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit}
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
if n.kind == nkHiddenDeref: if n.kind == nkHiddenDeref:
checkSonsLen(n, 1) checkSonsLen(n, 1)
@ -742,7 +747,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1]) n.sons[1] = semAndEvalConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in
{tyInt..tyInt64}: {tyInt..tyInt64}:
var idx = getOrdValue(n.sons[1]) var idx = getOrdValue(n.sons[1])
@ -883,11 +888,38 @@ proc setMs(n: PNode, s: PSym): PNode =
n.sons[0] = newSymNode(s) n.sons[0] = newSymNode(s)
n.sons[0].info = n.info n.sons[0].info = n.info
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
result = c.semAndEvalConstExpr(n.sons[i+1])
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(result.info, errStringLiteralExpected)
proc semExpandMacroToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 2:
if not isCallExpr(n.sons[1]):
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
var macroCall = n.sons[1]
var s = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
if s == nil:
GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].renderTree)
var expanded : Pnode
case s.kind
of skMacro: expanded = semMacroExpr(c, macroCall, s, false)
of skTemplate: expanded = semTemplateExpr(c, macroCall, s, false)
else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
var macroRetType = newTypeS(s.typ.sons[0].kind, c)
result = newMetaNodeIT(expanded, n.info, macroRetType)
else:
result = semDirectOp(c, n, flags)
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if sonsLen(n) == 2: if sonsLen(n) == 2:
var a = c.semConstExpr(c, n.sons[1]) var a = expectStringArg(c, n, 0)
if a.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
GlobalError(a.info, errStringLiteralExpected)
try: try:
var content = readFile(a.strVal) var content = readFile(a.strVal)
result = newStrNode(nkStrLit, content) result = newStrNode(nkStrLit, content)
@ -921,6 +953,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)
of mSlurp: result = semSlurp(c, n, flags) of mSlurp: result = semSlurp(c, n, flags)
of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
else: result = semDirectOp(c, n, flags) else: result = semDirectOp(c, n, flags)
proc semIfExpr(c: PContext, n: PNode): PNode = proc semIfExpr(c: PContext, n: PNode): PNode =
@ -1078,10 +1111,6 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
closeScope(c.tab) closeScope(c.tab)
Dec(c.p.nestedBlockCounter) Dec(c.p.nestedBlockCounter)
proc isCallExpr(n: PNode): bool =
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit}
proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode = proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode =
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
var a: PNode var a: PNode

View file

@ -208,6 +208,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mNewString, mNewStringOfCap, of mNewString, mNewStringOfCap,
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh, mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
mParseExprToAst, mParseStmtToAst,
mNLen..mNError, mEqRef: mNLen..mNError, mEqRef:
nil nil
else: InternalError(a.info, "evalOp(" & $m & ')') else: InternalError(a.info, "evalOp(" & $m & ')')

View file

@ -19,7 +19,7 @@ proc semWhen(c: PContext, n: PNode): PNode =
case it.kind case it.kind
of nkElifBranch: of nkElifBranch:
checkSonsLen(it, 2) checkSonsLen(it, 2)
var e = semConstBoolExpr(c, it.sons[0]) var e = semAndEvalConstExpr(c, it.sons[0])
if (e.kind != nkIntLit): InternalError(n.info, "semWhen") if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
if (e.intVal != 0) and (result == nil): if (e.intVal != 0) and (result == nil):
result = semStmt(c, it.sons[1]) # do not open a new scope! result = semStmt(c, it.sons[1]) # do not open a new scope!

View file

@ -19,7 +19,7 @@ type
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit, nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit, nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall, nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr, nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix, nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
@ -184,6 +184,53 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## in a string literal node ## in a string literal node
return newStrLitNode(repr(n)) return newStrLitNode(repr(n))
proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
## Convert the AST `n` to a human-readable string
##
## You can use this as a tool to explore the Nimrod's abstract syntax
## tree and to discover what kind of nodes must be created to represent
## a certain expression/statement
if n == nil: return "nil"
result = $n.kind
add(result, "(")
case n.kind
of nnkEmpty: nil # same as nil node in this representation
of nnkNilLit: add(result, "nil")
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, $n.ident)
of nnkSym, nnkNone: assert false
else:
add(result, prettyPrint(n[0]))
for j in 1..n.len-1:
add(result, ", ")
add(result, prettyPrint(n[j]))
add(result, ")")
proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}
## Converts the AST `n` to an YAML string
##
## Provides more detailed, potentially harder to digest information
## than `prettyPrint`
proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}
## Compiles the passed string to its AST representation
## Expects a single expression
proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
## Compiles the passed string to its AST representation
## Expects one or more statements
proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}
## Obtains the AST nodes returned from a macro or template invocation
## example:
## macro FooMacro() =
## var ast = getAst(BarTemplate())
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case, ## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing ## compilation aborts with an error message. This is useful for writing

View file

@ -76,6 +76,18 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
ident = substr(s, start, i-1) ident = substr(s, start, i-1)
result = i-start result = i-start
proc parseIdent*(s: string, start = 0): string =
## parses an identifier and stores it in ``ident``.
## Returns the parsed identifier or an empty string in case of an error.
result = ""
var i = start
if s[i] in IdentStartChars:
inc(i)
while s[i] in IdentChars: inc(i)
result = substr(s, start, i-1)
proc parseToken*(s: string, token: var string, validChars: set[char], proc parseToken*(s: string, token: var string, validChars: set[char],
start = 0): int {.inline, deprecated.} = start = 0): int {.inline, deprecated.} =
## parses a token and stores it in ``token``. Returns ## parses a token and stores it in ``token``. Returns
@ -254,4 +266,94 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
result = parseBiggestFloat(s, bf, start) result = parseBiggestFloat(s, bf, start)
number = bf number = bf
proc isEscaped*(s: string, pos: int) : bool =
assert pos >= 0 and pos < s.len
var
backslashes = 0
j = pos - 1
while j >= 0:
if s[j] == '\\':
inc backslashes
dec j
else:
break
return backslashes mod 2 != 0
type
TInterpolatedKind* = enum
ikString, ikExpr
TInterpStrFragment* = tuple[kind: TInterpolatedKind, value: string]
iterator interpolatedFragments*(s: string): TInterpStrFragment =
var
i = 0
tokenStart = 0
proc token(kind: TInterpolatedKind, value: string): TInterpStrFragment =
result.kind = kind
result.value = value
while i < s.len:
# The $ sign marks the start of an interpolation.
#
# It's followed either by a varialbe name or an opening bracket
# (so it should be before the end of the string)
# if the dollar sign is escaped, don't trigger interpolation
if s[i] == '$' and i < (s.len - 1) and not isEscaped(s, i):
# Interpolation starts here.
# Return any string that we've ran over so far.
if i != tokenStart:
yield token(ikString, s[tokenStart..i-1])
var next = s[i+1]
if next == '{':
# Complex expression: ${foo(bar) in {1..100}}
# Find closing braket, while respecting any nested brackets
inc i
tokenStart = i + 1
var
brackets = {'{', '}'}
nestingCount = 1
while i < s.len:
inc i, skipUntil(s, brackets, i+1) + 1
if not isEscaped(s, i):
if s[i] == '}':
dec nestingCount
if nestingCount == 0: break
else:
inc nestingCount
yield token(ikExpr, s[tokenStart..(i-1)])
tokenStart = i + 1
else:
tokenStart = i + 1
var identifier = parseIdent(s, i+1)
if identifier.len > 0:
inc i, identifier.len
yield token(ikExpr, s[tokenStart..i])
tokenStart = i + 1
else:
raise newException(EInvalidValue, "Unable to parse a varible name at " & s[i..s.len])
inc i
#end while
# We've reached the end of the string without finding a new interpolation.
# Return the last fragment at string.
if i != tokenStart:
yield token(ikString, s[tokenStart..i])
{.pop.} {.pop.}

30
lib/pure/uuid.nim Normal file
View file

@ -0,0 +1,30 @@
# This module implements the RFC 4122 specification for generating universally unique identifiers
# http://en.wikipedia.org/wiki/Universally_unique_identifier
# This module is a work-in-progress
# If you want to help with the implementation, take a loot at:
# http://dsource.org/projects/tango/docs/current/tango.util.uuid.Uuid.html
type TUuid* = array[0..15, char]
when defined(windows):
# This is actually available only on Windows 2000+
type PUuid* {.importc: "UUID __RPC_FAR *", header: "<Rpc.h>".} = ptr TUuid
proc uuid1Sys*(uuid: PUuid) {.importc: "UuidCreateSequential", header: "<Rpc.h>".}
else:
type PUuid {.importc: "uuid_t", header: "<uuid/uuid.h>".} = ptr TUuid
proc uuid1Sys*(uuid: PUuid) {.importc: "uuid_generate_time", header: "<uuid/uuid.h>".}
# v1 UUIDs include the MAC address of the machine generating the ID and a timestamp
# This scheme has the strongest guaranty of uniqueness, but discloses when the ID was generated
proc uuidMacTime* : TUuid = uuid1Sys(addr(result))
# v4 UUID are created entirely using a random number generator.
# Some bits have fixed value in order to indicate the UUID type
proc uuidRandom*[RandomGenerator](rand: RandomGenerator) : TUuid = nil
# v3 and v5 UUIDs are derived from given namespace and name using a secure hashing algorithm.
# v3 uses MD5, v5 uses SHA1.
proc uuidByName*[Hash](namespace: TUuid, name: string, hasher: Hash, v: int) : TUuid = nil

View file

@ -896,6 +896,11 @@ type # these work for most platforms:
PInt64* = ptr Int64 ## an alias for ``ptr int64`` PInt64* = ptr Int64 ## an alias for ``ptr int64``
PInt32* = ptr Int32 ## an alias for ``ptr int32`` PInt32* = ptr Int32 ## an alias for ``ptr int32``
type TOptional*[T] = object
case hasValue* : bool
of true: value*: T
of false: nil
proc toFloat*(i: int): float {. proc toFloat*(i: int): float {.
magic: "ToFloat", noSideEffect, importc: "toFloat".} magic: "ToFloat", noSideEffect, importc: "toFloat".}
## converts an integer `i` into a ``float``. If the conversion ## converts an integer `i` into a ``float``. If the conversion

View file

@ -2,28 +2,26 @@
import macros import macros
proc dumpit(n: PNimrodNode): string {.compileTime.} = template plus(a, b: expr): expr =
if n == nil: return "nil" a + b
result = $n.kind
add(result, "(") macro call(e: expr): expr =
case n.kind return newCall("foo", newStrLitNode("bar"))
of nnkEmpty: nil # same as nil node in this representation
of nnkNilLit: add(result, "nil")
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, $n.ident)
of nnkSym, nnkNone: assert false
else:
add(result, dumpit(n[0]))
for j in 1..n.len-1:
add(result, ", ")
add(result, dumpit(n[j]))
add(result, ")")
macro dumpAST(n: stmt): stmt = macro dumpAST(n: stmt): stmt =
# dump AST as a side-effect and return the inner node # dump AST as a side-effect and return the inner node
echo dumpit(n) echo n.prettyPrint
echo n.toYaml
var plusAst = getAst(plus(1, 2))
echo plusAst.prettyPrint
var callAst = getAst(call())
echo callAst.prettyPrint
var e = parseExpr("foo(bar + baz)")
echo e.prettyPrint
result = n[1] result = n[1]
dumpAST: dumpAST:
@ -32,4 +30,3 @@ dumpAST:
proc sub(x, y: int): int = return x - y proc sub(x, y: int): int = return x - y

View file

@ -0,0 +1,71 @@
discard """
file: "tstringinterp.nim"
output: "Hello Alice, 64 | Hello Bob, 10"
"""
import macros, parseutils, strutils
proc concat(strings: openarray[string]) : string =
result = newString(0)
for s in items(strings): result.add(s)
# This will run though the intee
template ProcessInterpolations(e: expr) =
var
s = e[1].strVal
for f in interpolatedFragments(s):
if f.kind == ikString:
addString(f.value)
else:
addExpr(f.value)
macro formatStyleInterpolation(e: expr): expr =
var
formatString = ""
arrayNode = newNimNode(nnkBracket)
idx = 1
proc addString(s: string) =
formatString.add(s)
proc addExpr(e: expr) =
arrayNode.add(e)
formatString.add("$" & $(idx))
inc idx
ProcessInterpolations(e)
result = parseExpr("\"x\" % [y]")
result[1].strVal = formatString
result[2] = arrayNode
macro concatStyleInterpolation(e: expr): expr =
var args : seq[PNimrodNode]
newSeq(args, 0)
proc addString(s: string) = args.add(newStrLitNode(s))
proc addExpr(e: expr) = args.add(e)
ProcessInterpolations(e)
result = newCall("concat", args)
###
proc sum(a, b, c: int): int =
return (a + b + c)
var
alice = "Alice"
bob = "Bob"
a = 10
b = 20
c = 34
var
s1 = concatStyleInterpolation"Hello ${alice}, ${sum (a, b, c)}}"
s2 = formatStyleInterpolation"Hello ${bob}, ${sum (alice.len, bob.len, 2)}"
write(stdout, s1 & " | " & s2)

View file

@ -0,0 +1,102 @@
discard """
file: "tusingstatement.nim"
output: "Using test.Closing test."
"""
import
macros
# This macro mimics the using statement from C#
#
# XXX:
# It doen't match the C# version exactly yet.
# In particular, it's not recursive, which prevents it from dealing
# with exceptions thrown from the variable initializers when multiple.
# variables are used.
#
# Also, since nimrod relies less on exceptions in general, a more
# idiomatic definition could be:
# var x = init()
# if opened(x):
# try:
# body
# finally:
# close(x)
#
# `opened` here could be an overloaded proc which any type can define.
# A common practice can be returing an Optional[Resource] obj for which
# `opened` is defined to `optional.hasValue`
macro using(e: expr) : stmt =
if e.len != 2:
error "Using statement: unexpected number of arguments. Got " &
$e.len & ", expected: 1 or more variable assignments and a block"
var args = e[0]
var body = e[1]
var
variables : seq[PNimrodNode]
closingCalls : seq[PNimrodNode]
newSeq(variables, 0)
newSeq(closingCalls, 0)
for i in countup(1, args.len-1):
if args[i].kind == nnkExprEqExpr:
var varName = args[i][0]
var varValue = args[i][1]
var varAssignment = newNimNode(nnkIdentDefs)
varAssignment.add(varName)
varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
varAssignment.add(varValue)
variables.add(varAssignment)
closingCalls.add(newCall(!"close", varName))
else:
error "Using statement: Unexpected expression. Got " &
$args[i].kind & " instead of assignment."
var varSection = newNimNode(nnkVarSection)
varSection.add(variables)
var finallyBlock = newNimNode(nnkStmtList)
finallyBlock.add(closingCalls)
# XXX: Use a template here once getAst is working properly
var targetAst = parseStmt"""block:
var
x = foo()
y = bar()
try:
body()
finally:
close x
close y
"""
targetAst[0][1][0] = varSection
targetAst[0][1][1][0] = body
targetAst[0][1][1][1][0] = finallyBlock
return targetAst
type
TResource* = object
field*: string
proc openResource(param: string): TResource =
result.field = param
proc close(r: var TResource) =
write(stdout, "Closing " & r.field & ".")
proc use(r: var TResource) =
write(stdout, "Using " & r.field & ".")
using(r = openResource("test")):
use r