mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-24 03:46:50 +00:00
Merge pull request #54 from zah/pretty-print-ast
String interpolation implemented in terms of macros
This commit is contained in:
@@ -57,10 +57,10 @@ type
|
||||
nkStrLit, # a string literal ""
|
||||
nkRStrLit, # a raw string literal r""
|
||||
nkTripleStrLit, # a triple string literal """
|
||||
nkMetaNode, # difficult to explain; represents itself
|
||||
# (used for macros)
|
||||
nkNilLit, # the nil literal
|
||||
# end of atoms
|
||||
nkMetaNode, # difficult to explain; represents itself
|
||||
# (used for macros)
|
||||
nkDotCall, # used to temporarily flag a nkCall node;
|
||||
# this is used
|
||||
# for transforming ``s.len`` to ``len(s)``
|
||||
@@ -313,6 +313,7 @@ type
|
||||
TMagic* = enum # symbols that require compiler magic:
|
||||
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
|
||||
mEcho, mShallowCopy, mSlurp,
|
||||
mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
|
||||
mUnaryLt, mSucc,
|
||||
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
||||
@@ -610,6 +611,23 @@ proc copyTree*(src: PNode): 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)
|
||||
# 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.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 =
|
||||
new(result)
|
||||
result.kind = kind
|
||||
@@ -866,23 +888,6 @@ proc sonsLen(n: PNode): int =
|
||||
if isNil(n.sons): result = 0
|
||||
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) =
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
setlen(father.sons, len(father.sons) + length)
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
|
||||
import
|
||||
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
|
||||
PStackFrame* = ref TStackFrame
|
||||
@@ -751,6 +752,31 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
|
||||
proc isEmpty(n: PNode): bool =
|
||||
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 =
|
||||
var m = getMagic(n)
|
||||
case m
|
||||
@@ -775,6 +801,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||
of mAppendStrCh: result = evalAppendStrCh(c, n)
|
||||
of mAppendStrStr: result = evalAppendStrStr(c, n)
|
||||
of mAppendSeqElem: result = evalAppendSeqElem(c, n)
|
||||
of mParseExprToAst: result = evalParseExpr(c, n)
|
||||
of mParseStmtToAst: result = evalParseStmt(c, n)
|
||||
of mNLen:
|
||||
result = evalAux(c, n.sons[1], {efLValue})
|
||||
if isSpecial(result): return
|
||||
@@ -981,6 +1009,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||
if (a == b) or
|
||||
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
|
||||
result.intVal = 1
|
||||
of mAstToYaml:
|
||||
var ast = evalAux(c, n.sons[1], {efLValue})
|
||||
result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
|
||||
of mNHint:
|
||||
result = evalAux(c, n.sons[1], {})
|
||||
if isSpecial(result): return
|
||||
@@ -1034,6 +1065,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||
dec(gNestedEvals)
|
||||
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
|
||||
case n.kind # atoms:
|
||||
of nkMetaNode:
|
||||
result = copyTree(n.sons[0])
|
||||
result.typ = n.typ
|
||||
of nkEmpty: result = n
|
||||
of nkSym: result = evalSym(c, n, flags)
|
||||
of nkType..nkNilLit: result = copyNode(n) # end of atoms
|
||||
|
||||
@@ -30,6 +30,11 @@ proc closeParser*(p: var TParser)
|
||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||
# implements an iterator. Returns the next top-level statement or
|
||||
# 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
|
||||
proc getPrecedence*(tok: TToken): int
|
||||
@@ -1369,3 +1374,14 @@ proc parseTopLevelStmt(p: var TParser): PNode =
|
||||
result = complexOrSimpleStmt(p)
|
||||
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||
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)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#
|
||||
|
||||
# this module does the semantic checking for expressions
|
||||
|
||||
const
|
||||
ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyArrayConstr, tyTuple, tySet}
|
||||
@@ -384,6 +385,10 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||
else:
|
||||
nil
|
||||
|
||||
proc isCallExpr(n: PNode): bool =
|
||||
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
||||
nkCallStrLit}
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||
if n.kind == nkHiddenDeref:
|
||||
checkSonsLen(n, 1)
|
||||
@@ -742,7 +747,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
checkSonsLen(n, 2)
|
||||
n.sons[0] = makeDeref(n.sons[0])
|
||||
# [] 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
|
||||
{tyInt..tyInt64}:
|
||||
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].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 =
|
||||
if sonsLen(n) == 2:
|
||||
var a = c.semConstExpr(c, n.sons[1])
|
||||
if a.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
||||
GlobalError(a.info, errStringLiteralExpected)
|
||||
var a = expectStringArg(c, n, 0)
|
||||
try:
|
||||
var content = readFile(a.strVal)
|
||||
result = newStrNode(nkStrLit, content)
|
||||
@@ -921,6 +953,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
else:
|
||||
result = semDirectOp(c, n, flags)
|
||||
of mSlurp: result = semSlurp(c, n, flags)
|
||||
of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
|
||||
else: result = semDirectOp(c, n, flags)
|
||||
|
||||
proc semIfExpr(c: PContext, n: PNode): PNode =
|
||||
@@ -1078,10 +1111,6 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
|
||||
closeScope(c.tab)
|
||||
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 =
|
||||
checkMinSonsLen(n, 2)
|
||||
var a: PNode
|
||||
|
||||
@@ -208,6 +208,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
of mNewString, mNewStringOfCap,
|
||||
mExit, mInc, ast.mDec, mEcho, mAssert, mSwap, mAppendStrCh,
|
||||
mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
|
||||
mParseExprToAst, mParseStmtToAst,
|
||||
mNLen..mNError, mEqRef:
|
||||
nil
|
||||
else: InternalError(a.info, "evalOp(" & $m & ')')
|
||||
|
||||
@@ -19,7 +19,7 @@ proc semWhen(c: PContext, n: PNode): PNode =
|
||||
case it.kind
|
||||
of nkElifBranch:
|
||||
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.intVal != 0) and (result == nil):
|
||||
result = semStmt(c, it.sons[1]) # do not open a new scope!
|
||||
|
||||
@@ -19,7 +19,7 @@ type
|
||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
||||
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
||||
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
|
||||
nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
|
||||
nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
|
||||
nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
|
||||
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
|
||||
@@ -184,6 +184,53 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||
## in a string literal node
|
||||
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.} =
|
||||
## checks that `n` is of kind `k`. If this is not the case,
|
||||
## compilation aborts with an error message. This is useful for writing
|
||||
|
||||
@@ -76,6 +76,18 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
|
||||
ident = substr(s, start, i-1)
|
||||
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],
|
||||
start = 0): int {.inline, deprecated.} =
|
||||
## 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)
|
||||
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.}
|
||||
|
||||
30
lib/pure/uuid.nim
Normal file
30
lib/pure/uuid.nim
Normal 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
|
||||
|
||||
@@ -710,7 +710,7 @@ proc `&` * (x, y: string): string {.
|
||||
proc `&` * (x: char, y: string): string {.
|
||||
magic: "ConStrStr", noSideEffect, merge.}
|
||||
## is the `concatenation operator`. It concatenates `x` and `y`.
|
||||
|
||||
|
||||
# implementation note: These must all have the same magic value "ConStrStr" so
|
||||
# that the merge optimization works properly.
|
||||
|
||||
@@ -895,7 +895,12 @@ type # these work for most platforms:
|
||||
PFloat64* = ptr Float64 ## an alias for ``ptr float64``
|
||||
PInt64* = ptr Int64 ## an alias for ``ptr int64``
|
||||
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 {.
|
||||
magic: "ToFloat", noSideEffect, importc: "toFloat".}
|
||||
## converts an integer `i` into a ``float``. If the conversion
|
||||
|
||||
@@ -2,28 +2,26 @@
|
||||
|
||||
import macros
|
||||
|
||||
proc dumpit(n: PNimrodNode): string {.compileTime.} =
|
||||
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, dumpit(n[0]))
|
||||
for j in 1..n.len-1:
|
||||
add(result, ", ")
|
||||
add(result, dumpit(n[j]))
|
||||
add(result, ")")
|
||||
template plus(a, b: expr): expr =
|
||||
a + b
|
||||
|
||||
macro call(e: expr): expr =
|
||||
return newCall("foo", newStrLitNode("bar"))
|
||||
|
||||
macro dumpAST(n: stmt): stmt =
|
||||
macro dumpAST(n: stmt): stmt =
|
||||
# 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]
|
||||
|
||||
dumpAST:
|
||||
@@ -32,4 +30,3 @@ dumpAST:
|
||||
|
||||
proc sub(x, y: int): int = return x - y
|
||||
|
||||
|
||||
|
||||
71
tests/accept/run/tstringinterp.nim
Normal file
71
tests/accept/run/tstringinterp.nim
Normal 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)
|
||||
|
||||
102
tests/accept/run/tusingstatement.nim
Normal file
102
tests/accept/run/tusingstatement.nim
Normal 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user