experimental support for querying the type of expressions within macros
normalised the line endings of macros.nim (minor edits otherwise)
This commit is contained in:
parent
9c8bc3a244
commit
2e5265bef5
5 changed files with 369 additions and 344 deletions
|
|
@ -41,6 +41,7 @@ type
|
||||||
callsite: PNode # for 'callsite' magic
|
callsite: PNode # for 'callsite' magic
|
||||||
mode*: TEvalMode
|
mode*: TEvalMode
|
||||||
globals*: TIdNodeTable # state of global vars
|
globals*: TIdNodeTable # state of global vars
|
||||||
|
getType*: proc(n: PNode): PNode
|
||||||
|
|
||||||
PEvalContext* = ref TEvalContext
|
PEvalContext* = ref TEvalContext
|
||||||
|
|
||||||
|
|
@ -1091,7 +1092,10 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
result = evalAux(c, n.sons[1], {})
|
result = evalAux(c, n.sons[1], {})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident")
|
if result.kind != nkIdent: stackTrace(c, n, errFieldXNotFound, "ident")
|
||||||
of mNGetType: result = evalAux(c, n.sons[1], {})
|
of mNGetType:
|
||||||
|
var ast = evalAux(c, n.sons[1], {})
|
||||||
|
InternalAssert c.getType != nil
|
||||||
|
result = c.getType(ast)
|
||||||
of mNStrVal:
|
of mNStrVal:
|
||||||
result = evalAux(c, n.sons[1], {})
|
result = evalAux(c, n.sons[1], {})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
|
|
@ -1152,7 +1156,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
var a = result
|
var a = result
|
||||||
result = evalAux(c, n.sons[2], {efLValue})
|
result = evalAux(c, n.sons[2], {efLValue})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
a.typ = result.typ # XXX: exception handling?
|
InternalAssert result.kind == nkSym and result.sym.kind == skType
|
||||||
|
a.typ = result.sym.typ
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
of mNSetStrVal:
|
of mNSetStrVal:
|
||||||
result = evalAux(c, n.sons[1], {efLValue})
|
result = evalAux(c, n.sons[1], {efLValue})
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind)
|
||||||
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind)
|
proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind)
|
||||||
proc addResultNode(c: PContext, n: PNode)
|
proc addResultNode(c: PContext, n: PNode)
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
||||||
|
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||||
|
|
||||||
proc typeMismatch(n: PNode, formal, actual: PType) =
|
proc typeMismatch(n: PNode, formal, actual: PType) =
|
||||||
if formal.kind != tyError and actual.kind != tyError:
|
if formal.kind != tyError and actual.kind != tyError:
|
||||||
|
|
@ -156,8 +157,18 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
markUsed(n, sym)
|
markUsed(n, sym)
|
||||||
if sym == c.p.owner:
|
if sym == c.p.owner:
|
||||||
GlobalError(n.info, errRecursiveDependencyX, sym.name.s)
|
GlobalError(n.info, errRecursiveDependencyX, sym.name.s)
|
||||||
|
|
||||||
if c.evalContext == nil:
|
if c.evalContext == nil:
|
||||||
c.evalContext = newEvalContext(c.module, "", emStatic)
|
c.evalContext = newEvalContext(c.module, "", emStatic)
|
||||||
|
c.evalContext.getType = proc (n: PNode): PNode =
|
||||||
|
var e = tryExpr(c, n)
|
||||||
|
if e == nil:
|
||||||
|
result = symNodeFromType(c, errorType(c), n.info)
|
||||||
|
elif e.typ == nil:
|
||||||
|
result = newSymNode(getSysSym"void")
|
||||||
|
else:
|
||||||
|
result = symNodeFromType(c, e.typ, n.info)
|
||||||
|
|
||||||
result = evalMacroCall(c.evalContext, n, nOrig, sym)
|
result = evalMacroCall(c.evalContext, n, nOrig, sym)
|
||||||
if semCheck: result = semAfterMacroCall(c, result, sym)
|
if semCheck: result = semAfterMacroCall(c, result, sym)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1237,12 +1237,7 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# we replace this node by a 'true' or 'false' node:
|
|
||||||
if sonsLen(n) != 2: return semDirectOp(c, n, flags)
|
|
||||||
result = newIntNode(nkIntLit, 0)
|
|
||||||
result.info = n.info
|
|
||||||
result.typ = getSysType(tyBool)
|
|
||||||
# watch out, hacks ahead:
|
# watch out, hacks ahead:
|
||||||
let oldErrorCount = msgs.gErrorCounter
|
let oldErrorCount = msgs.gErrorCounter
|
||||||
let oldErrorMax = msgs.gErrorMax
|
let oldErrorMax = msgs.gErrorMax
|
||||||
|
|
@ -1264,8 +1259,8 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
let oldProcCon = c.p
|
let oldProcCon = c.p
|
||||||
c.generics = newGenericsCache()
|
c.generics = newGenericsCache()
|
||||||
try:
|
try:
|
||||||
discard semExpr(c, n.sons[1])
|
result = semExpr(c, n, flags)
|
||||||
result.intVal = ord(msgs.gErrorCounter == oldErrorCount)
|
if msgs.gErrorCounter != oldErrorCount: result = nil
|
||||||
except ERecoverableError:
|
except ERecoverableError:
|
||||||
nil
|
nil
|
||||||
# undo symbol table changes (as far as it's possible):
|
# undo symbol table changes (as far as it's possible):
|
||||||
|
|
@ -1282,6 +1277,14 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
msgs.gErrorCounter = oldErrorCount
|
msgs.gErrorCounter = oldErrorCount
|
||||||
msgs.gErrorMax = oldErrorMax
|
msgs.gErrorMax = oldErrorMax
|
||||||
|
|
||||||
|
proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
# we replace this node by a 'true' or 'false' node:
|
||||||
|
if sonsLen(n) != 2: return semDirectOp(c, n, flags)
|
||||||
|
|
||||||
|
result = newIntNode(nkIntLit, ord(tryExpr(c, n, flags) != nil))
|
||||||
|
result.info = n.info
|
||||||
|
result.typ = getSysType(tyBool)
|
||||||
|
|
||||||
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if sonsLen(n) == 3:
|
if sonsLen(n) == 3:
|
||||||
# XXX ugh this is really a hack: shallowCopy() can be overloaded only
|
# XXX ugh this is really a hack: shallowCopy() can be overloaded only
|
||||||
|
|
|
||||||
|
|
@ -1,194 +1,188 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
## This module contains the interface to the compiler's abstract syntax
|
## This module contains the interface to the compiler's abstract syntax
|
||||||
## tree (`AST`:idx:). Macros operate on this tree.
|
## tree (`AST`:idx:). Macros operate on this tree.
|
||||||
|
|
||||||
## .. include:: ../doc/astspec.txt
|
## .. include:: ../doc/astspec.txt
|
||||||
|
|
||||||
type
|
type
|
||||||
TNimrodNodeKind* = enum
|
TNimrodNodeKind* = enum
|
||||||
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
||||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
|
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
|
||||||
nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
|
nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
|
||||||
nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
|
nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
|
||||||
nnkTripleStrLit, nnkNilLit, nnkMetaNode, 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, nnkCurlyExpr,
|
nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr,
|
||||||
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
|
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
|
||||||
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
|
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
|
||||||
nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
|
nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
|
||||||
nnkTableConstr, nnkBind,
|
nnkTableConstr, nnkBind,
|
||||||
nnkClosedSymChoice,
|
nnkClosedSymChoice,
|
||||||
nnkOpenSymChoice,
|
nnkOpenSymChoice,
|
||||||
nnkHiddenStdConv,
|
nnkHiddenStdConv,
|
||||||
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr,
|
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr,
|
||||||
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
|
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
|
||||||
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
|
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
|
||||||
nnkStringToCString, nnkCStringToString, nnkAsgn,
|
nnkStringToCString, nnkCStringToString, nnkAsgn,
|
||||||
nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
|
nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
|
||||||
nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
|
nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
|
||||||
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
|
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
|
||||||
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
|
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
|
||||||
nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
|
nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
|
||||||
nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
|
nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
|
||||||
nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
|
nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
|
||||||
nnkConstDef, nnkTypeDef,
|
nnkConstDef, nnkTypeDef,
|
||||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
||||||
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
||||||
nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
|
nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
|
||||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
||||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||||
nnkRefTy, nnkPtrTy, nnkVarTy,
|
nnkRefTy, nnkPtrTy, nnkVarTy,
|
||||||
nnkConstTy, nnkMutableTy,
|
nnkConstTy, nnkMutableTy,
|
||||||
nnkDistinctTy,
|
nnkDistinctTy,
|
||||||
nnkProcTy, nnkEnumTy,
|
nnkProcTy, nnkEnumTy,
|
||||||
nnkEnumFieldDef,
|
nnkEnumFieldDef,
|
||||||
nnkArglist, nnkPattern
|
nnkArglist, nnkPattern
|
||||||
nnkReturnToken
|
nnkReturnToken
|
||||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||||
TNimrodTypeKind* = enum
|
TNimrodTypeKind* = enum
|
||||||
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
||||||
ntyArrayConstr, ntyNil, ntyExpr, ntyStmt,
|
ntyArrayConstr, ntyNil, ntyExpr, ntyStmt,
|
||||||
ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst,
|
ntyTypeDesc, ntyGenericInvokation, ntyGenericBody, ntyGenericInst,
|
||||||
ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal,
|
ntyGenericParam, ntyDistinct, ntyEnum, ntyOrdinal,
|
||||||
ntyArray, ntyObject, ntyTuple, ntySet,
|
ntyArray, ntyObject, ntyTuple, ntySet,
|
||||||
ntyRange, ntyPtr, ntyRef, ntyVar,
|
ntyRange, ntyPtr, ntyRef, ntyVar,
|
||||||
ntySequence, ntyProc, ntyPointer, ntyOpenArray,
|
ntySequence, ntyProc, ntyPointer, ntyOpenArray,
|
||||||
ntyString, ntyCString, ntyForward, ntyInt,
|
ntyString, ntyCString, ntyForward, ntyInt,
|
||||||
ntyInt8, ntyInt16, ntyInt32, ntyInt64,
|
ntyInt8, ntyInt16, ntyInt32, ntyInt64,
|
||||||
ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128
|
ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128
|
||||||
TNimTypeKinds* = set[TNimrodTypeKind]
|
TNimTypeKinds* = set[TNimrodTypeKind]
|
||||||
TNimrodSymKind* = enum
|
TNimrodSymKind* = enum
|
||||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||||
nskGenericParam, nskTemp, nskType, nskConst,
|
nskGenericParam, nskTemp, nskType, nskConst,
|
||||||
nskVar, nskProc, nskMethod, nskIterator,
|
nskVar, nskProc, nskMethod, nskIterator,
|
||||||
nskConverter, nskMacro, nskTemplate, nskField,
|
nskConverter, nskMacro, nskTemplate, nskField,
|
||||||
nskEnumField, nskForVar, nskModule, nskLabel,
|
nskEnumField, nskForVar, nskModule, nskLabel,
|
||||||
nskStub
|
nskStub
|
||||||
TNimSymKinds* = set[TNimrodSymKind]
|
TNimSymKinds* = set[TNimrodSymKind]
|
||||||
|
|
||||||
type
|
|
||||||
TNimrodIdent* = object of TObject
|
|
||||||
## represents a Nimrod identifier in the AST
|
|
||||||
|
|
||||||
TNimrodSymbol {.final.} = object # hidden
|
|
||||||
TNimrodType {.final.} = object # hidden
|
|
||||||
|
|
||||||
PNimrodType* {.compilerproc.} = ref TNimrodType
|
|
||||||
## represents a Nimrod type in the compiler; currently this is not very
|
|
||||||
## useful as there is no API to deal with Nimrod types.
|
|
||||||
|
|
||||||
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
|
|
||||||
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
|
|
||||||
## *ident*.
|
|
||||||
|
|
||||||
const
|
|
||||||
nnkLiterals* = {nnkCharLit..nnkNilLit}
|
|
||||||
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
|
||||||
nnkCallStrLit}
|
|
||||||
|
|
||||||
proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
|
|
||||||
## get `n`'s `i`'th child.
|
|
||||||
|
|
||||||
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
|
|
||||||
## set `n`'s `i`'th child to `child`.
|
|
||||||
|
|
||||||
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
|
||||||
## constructs an identifier from the string `s`
|
|
||||||
|
|
||||||
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
|
|
||||||
## converts a Nimrod identifier to a string
|
|
||||||
|
|
||||||
proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
|
|
||||||
## converts a Nimrod symbol to a string
|
|
||||||
|
|
||||||
proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
|
type
|
||||||
## compares two Nimrod identifiers
|
TNimrodIdent* = object of TObject
|
||||||
|
## represents a Nimrod identifier in the AST
|
||||||
proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
|
||||||
## compares two Nimrod nodes
|
TNimrodSymbol {.final.} = object # hidden
|
||||||
|
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
|
||||||
proc len*(n: PNimrodNode): int {.magic: "NLen".}
|
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
|
||||||
## returns the number of children of `n`.
|
## *ident*.
|
||||||
|
|
||||||
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
|
const
|
||||||
## adds the `child` to the `father` node
|
nnkLiterals* = {nnkCharLit..nnkNilLit}
|
||||||
|
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||||
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {.
|
nnkCallStrLit}
|
||||||
magic: "NAddMultiple".}
|
|
||||||
## adds each child of `children` to the `father` node
|
proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
|
||||||
|
## get `n`'s `i`'th child.
|
||||||
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
|
|
||||||
## deletes `n` children of `father` starting at index `idx`.
|
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
|
||||||
|
## set `n`'s `i`'th child to `child`.
|
||||||
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
|
|
||||||
## returns the `kind` of the node `n`.
|
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
||||||
|
## constructs an identifier from the string `s`
|
||||||
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
|
|
||||||
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
|
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
|
||||||
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
|
## converts a Nimrod identifier to a string
|
||||||
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
|
|
||||||
proc typ*(n: PNimrodNode): PNimrodType {.magic: "NGetType".}
|
proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
|
||||||
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".}
|
## converts a Nimrod symbol to a string
|
||||||
|
|
||||||
proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
|
proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
|
||||||
proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
|
## compares two Nimrod identifiers
|
||||||
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
|
|
||||||
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
|
proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||||
proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
|
## compares two Nimrod nodes
|
||||||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
|
||||||
|
proc len*(n: PNimrodNode): int {.magic: "NLen".}
|
||||||
proc newNimNode*(kind: TNimrodNodeKind,
|
## returns the number of children of `n`.
|
||||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
|
||||||
|
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
|
||||||
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
|
## adds the `child` to the `father` node
|
||||||
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
|
|
||||||
|
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {.
|
||||||
proc error*(msg: string) {.magic: "NError".}
|
magic: "NAddMultiple".}
|
||||||
## writes an error message at compile time
|
## adds each child of `children` to the `father` node
|
||||||
|
|
||||||
proc warning*(msg: string) {.magic: "NWarning".}
|
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
|
||||||
## writes a warning message at compile time
|
## deletes `n` children of `father` starting at index `idx`.
|
||||||
|
|
||||||
proc hint*(msg: string) {.magic: "NHint".}
|
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
|
||||||
## writes a hint message at compile time
|
## returns the `kind` of the node `n`.
|
||||||
|
|
||||||
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
|
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
|
||||||
## creates a string literal node from `s`
|
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
|
||||||
result = newNimNode(nnkStrLit)
|
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
|
||||||
result.strVal = s
|
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
|
||||||
|
proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType".}
|
||||||
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
|
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".}
|
||||||
## creates a int literal node from `i`
|
|
||||||
result = newNimNode(nnkIntLit)
|
proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
|
||||||
result.intVal = i
|
proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
|
||||||
|
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
|
||||||
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
|
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
|
||||||
## creates a float literal node from `f`
|
proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".}
|
||||||
result = newNimNode(nnkFloatLit)
|
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
||||||
result.floatVal = f
|
|
||||||
|
proc newNimNode*(kind: TNimrodNodeKind,
|
||||||
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
|
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
||||||
## creates an identifier node from `i`
|
|
||||||
result = newNimNode(nnkIdent)
|
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
|
||||||
result.ident = i
|
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
|
||||||
|
|
||||||
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
|
proc error*(msg: string) {.magic: "NError".}
|
||||||
## creates an identifier node from `i`
|
## writes an error message at compile time
|
||||||
result = newNimNode(nnkIdent)
|
|
||||||
result.ident = !i
|
proc warning*(msg: string) {.magic: "NWarning".}
|
||||||
|
## writes a warning message at compile time
|
||||||
|
|
||||||
|
proc hint*(msg: string) {.magic: "NHint".}
|
||||||
|
## writes a hint message at compile time
|
||||||
|
|
||||||
|
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
|
||||||
|
## creates a string literal node from `s`
|
||||||
|
result = newNimNode(nnkStrLit)
|
||||||
|
result.strVal = s
|
||||||
|
|
||||||
|
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
|
||||||
|
## creates a int literal node from `i`
|
||||||
|
result = newNimNode(nnkIntLit)
|
||||||
|
result.intVal = i
|
||||||
|
|
||||||
|
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
|
||||||
|
## creates a float literal node from `f`
|
||||||
|
result = newNimNode(nnkFloatLit)
|
||||||
|
result.floatVal = f
|
||||||
|
|
||||||
|
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
|
||||||
|
## creates an identifier node from `i`
|
||||||
|
result = newNimNode(nnkIdent)
|
||||||
|
result.ident = i
|
||||||
|
|
||||||
|
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
|
||||||
|
## creates an identifier node from `i`
|
||||||
|
result = newNimNode(nnkIdent)
|
||||||
|
result.ident = !i
|
||||||
|
|
||||||
type
|
type
|
||||||
TBindSymRule* = enum ## specifies how ``bindSym`` behaves
|
TBindSymRule* = enum ## specifies how ``bindSym`` behaves
|
||||||
|
|
@ -212,157 +206,157 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
|
||||||
## returned even if the symbol is not ambiguous.
|
## returned even if the symbol is not ambiguous.
|
||||||
|
|
||||||
proc callsite*(): PNimrodNode {.magic: "NCallSite".}
|
proc callsite*(): PNimrodNode {.magic: "NCallSite".}
|
||||||
## returns the AST if the invokation expression that invoked this macro.
|
## returns the AST if the invokation expression that invoked this macro.
|
||||||
|
|
||||||
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
## converts the AST `n` to the concrete Nimrod code and wraps that
|
## converts the AST `n` to the concrete Nimrod code and wraps that
|
||||||
## in a string literal node
|
## in a string literal node
|
||||||
return newStrLitNode(repr(n))
|
return newStrLitNode(repr(n))
|
||||||
|
|
||||||
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
|
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
|
||||||
## returns the position the node appears in the original source file
|
## returns the position the node appears in the original source file
|
||||||
## in the form filename(line, col)
|
## in the form filename(line, col)
|
||||||
|
|
||||||
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
|
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
|
||||||
## Compiles the passed string to its AST representation.
|
## Compiles the passed string to its AST representation.
|
||||||
## Expects a single expression.
|
## Expects a single expression.
|
||||||
|
|
||||||
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
|
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
|
||||||
## Compiles the passed string to its AST representation.
|
## Compiles the passed string to its AST representation.
|
||||||
## Expects one or more statements.
|
## Expects one or more statements.
|
||||||
|
|
||||||
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
|
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
|
||||||
## Obtains the AST nodes returned from a macro or template invocation.
|
## Obtains the AST nodes returned from a macro or template invocation.
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
##
|
##
|
||||||
## macro FooMacro() =
|
## macro FooMacro() =
|
||||||
## var ast = getAst(BarTemplate())
|
## var ast = getAst(BarTemplate())
|
||||||
|
|
||||||
template emit*(s: expr): stmt =
|
template emit*(s: expr): stmt =
|
||||||
## accepts a single string argument and treats it as nimrod code
|
## accepts a single string argument and treats it as nimrod code
|
||||||
## that should be inserted verbatim in the program
|
## that should be inserted verbatim in the program
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## emit("echo " & '"' & "hello world".toUpper & '"')
|
## emit("echo " & '"' & "hello world".toUpper & '"')
|
||||||
##
|
##
|
||||||
block:
|
block:
|
||||||
const evaluated = s
|
const evaluated = s
|
||||||
eval: result = evaluated.parseStmt
|
eval: result = evaluated.parseStmt
|
||||||
|
|
||||||
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
|
|
||||||
## macros that check the AST that is passed to them.
|
|
||||||
if n.kind != k: error("macro expects a node of kind: " & repr(k))
|
|
||||||
|
|
||||||
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
|
|
||||||
## checks that `n` has at least `min` children. If this is not the case,
|
|
||||||
## compilation aborts with an error message. This is useful for writing
|
|
||||||
## macros that check its number of arguments.
|
|
||||||
if n.len < min: error("macro expects a node with " & $min & " children")
|
|
||||||
|
|
||||||
proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
|
|
||||||
## checks that `n` has exactly `len` children. If this is not the case,
|
|
||||||
## compilation aborts with an error message. This is useful for writing
|
|
||||||
## macros that check its number of arguments.
|
|
||||||
if n.len != len: error("macro expects a node with " & $len & " children")
|
|
||||||
|
|
||||||
proc newCall*(theProc: PNimrodNode,
|
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
||||||
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
## checks that `n` is of kind `k`. If this is not the case,
|
||||||
## produces a new call node. `theProc` is the proc that is called with
|
## compilation aborts with an error message. This is useful for writing
|
||||||
## the arguments ``args[0..]``.
|
## macros that check the AST that is passed to them.
|
||||||
result = newNimNode(nnkCall)
|
if n.kind != k: error("macro expects a node of kind: " & repr(k))
|
||||||
result.add(theProc)
|
|
||||||
result.add(args)
|
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
|
||||||
|
## checks that `n` has at least `min` children. If this is not the case,
|
||||||
proc newCall*(theProc: TNimrodIdent,
|
## compilation aborts with an error message. This is useful for writing
|
||||||
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
## macros that check its number of arguments.
|
||||||
## produces a new call node. `theProc` is the proc that is called with
|
if n.len < min: error("macro expects a node with " & $min & " children")
|
||||||
## the arguments ``args[0..]``.
|
|
||||||
result = newNimNode(nnkCall)
|
proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
|
||||||
result.add(newIdentNode(theProc))
|
## checks that `n` has exactly `len` children. If this is not the case,
|
||||||
result.add(args)
|
## compilation aborts with an error message. This is useful for writing
|
||||||
|
## macros that check its number of arguments.
|
||||||
proc newCall*(theProc: string,
|
if n.len != len: error("macro expects a node with " & $len & " children")
|
||||||
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
|
||||||
## produces a new call node. `theProc` is the proc that is called with
|
proc newCall*(theProc: PNimrodNode,
|
||||||
## the arguments ``args[0..]``.
|
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
result = newNimNode(nnkCall)
|
## produces a new call node. `theProc` is the proc that is called with
|
||||||
result.add(newIdentNode(theProc))
|
## the arguments ``args[0..]``.
|
||||||
result.add(args)
|
result = newNimNode(nnkCall)
|
||||||
|
result.add(theProc)
|
||||||
proc nestList*(theProc: TNimrodIdent,
|
result.add(args)
|
||||||
x: PNimrodNode): PNimrodNode {.compileTime.} =
|
|
||||||
## nests the list `x` into a tree of call expressions:
|
proc newCall*(theProc: TNimrodIdent,
|
||||||
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
|
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
var L = x.len
|
## produces a new call node. `theProc` is the proc that is called with
|
||||||
result = newCall(theProc, x[L-2], x[L-1])
|
## the arguments ``args[0..]``.
|
||||||
var a = result
|
result = newNimNode(nnkCall)
|
||||||
for i in countdown(L-3, 0):
|
result.add(newIdentNode(theProc))
|
||||||
a = newCall(theProc, x[i], copyNimTree(a))
|
result.add(args)
|
||||||
|
|
||||||
proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
|
proc newCall*(theProc: string,
|
||||||
## Convert the AST `n` to a human-readable tree-like string.
|
args: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
##
|
## produces a new call node. `theProc` is the proc that is called with
|
||||||
## See also `repr` and `lispRepr`.
|
## the arguments ``args[0..]``.
|
||||||
proc traverse(res: var string, level: int, n: PNimrodNode) =
|
result = newNimNode(nnkCall)
|
||||||
for i in 0..level-1: res.add " "
|
result.add(newIdentNode(theProc))
|
||||||
res.add(($n.kind).substr(3))
|
result.add(args)
|
||||||
|
|
||||||
case n.kind
|
proc nestList*(theProc: TNimrodIdent,
|
||||||
of nnkEmpty: nil # same as nil node in this representation
|
x: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
of nnkNilLit: res.add(" nil")
|
## nests the list `x` into a tree of call expressions:
|
||||||
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
|
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
|
||||||
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
|
var L = x.len
|
||||||
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
|
result = newCall(theProc, x[L-2], x[L-1])
|
||||||
of nnkIdent: res.add(" !\"" & $n.ident & '"')
|
var a = result
|
||||||
|
for i in countdown(L-3, 0):
|
||||||
|
a = newCall(theProc, x[i], copyNimTree(a))
|
||||||
|
|
||||||
|
proc treeRepr*(n: PNimrodNode): string {.compileTime.} =
|
||||||
|
## Convert the AST `n` to a human-readable tree-like string.
|
||||||
|
##
|
||||||
|
## See also `repr` and `lispRepr`.
|
||||||
|
proc traverse(res: var string, level: int, n: PNimrodNode) =
|
||||||
|
for i in 0..level-1: res.add " "
|
||||||
|
res.add(($n.kind).substr(3))
|
||||||
|
|
||||||
|
case n.kind
|
||||||
|
of nnkEmpty: nil # same as nil node in this representation
|
||||||
|
of nnkNilLit: res.add(" nil")
|
||||||
|
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(" !\"" & $n.ident & '"')
|
||||||
of nnkSym: res.add(" \"" & $n.symbol & '"')
|
of nnkSym: res.add(" \"" & $n.symbol & '"')
|
||||||
of nnkNone: assert false
|
of nnkNone: assert false
|
||||||
else:
|
else:
|
||||||
for j in 0..n.len-1:
|
for j in 0..n.len-1:
|
||||||
res.add "\n"
|
res.add "\n"
|
||||||
traverse(res, level + 1, n[j])
|
traverse(res, level + 1, n[j])
|
||||||
|
|
||||||
result = ""
|
result = ""
|
||||||
traverse(result, 0, n)
|
traverse(result, 0, n)
|
||||||
|
|
||||||
proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
|
proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
|
||||||
## Convert the AST `n` to a human-readable lisp-like string,
|
## Convert the AST `n` to a human-readable lisp-like string,
|
||||||
##
|
##
|
||||||
## See also `repr` and `treeRepr`.
|
## See also `repr` and `treeRepr`.
|
||||||
|
|
||||||
result = ($n.kind).substr(3)
|
result = ($n.kind).substr(3)
|
||||||
add(result, "(")
|
add(result, "(")
|
||||||
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nnkEmpty: nil # same as nil node in this representation
|
of nnkEmpty: nil # same as nil node in this representation
|
||||||
of nnkNilLit: add(result, "nil")
|
of nnkNilLit: add(result, "nil")
|
||||||
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
||||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
||||||
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
||||||
of nnkIdent: add(result, "!\"" & $n.ident & '"')
|
of nnkIdent: add(result, "!\"" & $n.ident & '"')
|
||||||
of nnkSym, nnkNone: assert false
|
of nnkSym, nnkNone: assert false
|
||||||
else:
|
else:
|
||||||
add(result, lispRepr(n[0]))
|
add(result, lispRepr(n[0]))
|
||||||
for j in 1..n.len-1:
|
for j in 1..n.len-1:
|
||||||
add(result, ", ")
|
add(result, ", ")
|
||||||
add(result, lispRepr(n[j]))
|
add(result, lispRepr(n[j]))
|
||||||
|
|
||||||
add(result, ")")
|
add(result, ")")
|
||||||
|
|
||||||
macro dumpTree*(s: stmt): stmt = echo s.treeRepr
|
macro dumpTree*(s: stmt): stmt = echo s.treeRepr
|
||||||
## Accepts a block of nimrod code and prints the parsed abstract syntax
|
## Accepts a block of nimrod code and prints the parsed abstract syntax
|
||||||
## tree using the `toTree` function. Printing is done *at compile time*.
|
## tree using the `toTree` function. Printing is done *at compile time*.
|
||||||
##
|
##
|
||||||
## You can use this as a tool to explore the Nimrod's abstract syntax
|
## 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
|
## tree and to discover what kind of nodes must be created to represent
|
||||||
## a certain expression/statement.
|
## a certain expression/statement.
|
||||||
|
|
||||||
macro dumpLisp*(s: stmt): stmt = echo s.lispRepr
|
macro dumpLisp*(s: stmt): stmt = echo s.lispRepr
|
||||||
## Accepts a block of nimrod code and prints the parsed abstract syntax
|
## Accepts a block of nimrod code and prints the parsed abstract syntax
|
||||||
## tree using the `toLisp` function. Printing is done *at compile time*.
|
## tree using the `toLisp` function. Printing is done *at compile time*.
|
||||||
##
|
##
|
||||||
## See `dumpTree`.
|
## See `dumpTree`.
|
||||||
|
|
||||||
|
|
|
||||||
12
tests/compile/tmacrotypes.nim
Normal file
12
tests/compile/tmacrotypes.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import macros, typetraits
|
||||||
|
|
||||||
|
macro checkType(ex, expected: expr): stmt {.immediate.} =
|
||||||
|
var t = ex.typ
|
||||||
|
assert t.name == expected.strVal
|
||||||
|
|
||||||
|
proc voidProc = echo "hello"
|
||||||
|
proc intProc(a, b): int = 10
|
||||||
|
|
||||||
|
checkType(voidProc(), "void")
|
||||||
|
checkType(intProc(10, 20.0), "int")
|
||||||
|
checkType(noproc(10, 20.0), "Error Type")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue