Implement custom annotations (#6987)
This commit is contained in:
parent
aff787db69
commit
2c9e56a783
10 changed files with 254 additions and 42 deletions
|
|
@ -190,3 +190,6 @@ let
|
||||||
|
|
||||||
- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
|
- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
|
||||||
This allow to among other things to reinterpret signed integers as unsigned.
|
This allow to among other things to reinterpret signed integers as unsigned.
|
||||||
|
- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and ``{.exportc("myname").}``
|
||||||
|
- Custom pragmas are now supported using pragma ``pragma``, please see language manual for details
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,7 @@ const
|
||||||
sfEscapes* = sfProcvar # param escapes
|
sfEscapes* = sfProcvar # param escapes
|
||||||
sfBase* = sfDiscriminant
|
sfBase* = sfDiscriminant
|
||||||
sfIsSelf* = sfOverriden # param is 'self'
|
sfIsSelf* = sfOverriden # param is 'self'
|
||||||
|
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
||||||
|
|
||||||
const
|
const
|
||||||
# getting ready for the future expr/stmt merge
|
# getting ready for the future expr/stmt merge
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import
|
||||||
const
|
const
|
||||||
FirstCallConv* = wNimcall
|
FirstCallConv* = wNimcall
|
||||||
LastCallConv* = wNoconv
|
LastCallConv* = wNoconv
|
||||||
|
nkPragmaCallKinds = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||||
|
|
||||||
const
|
const
|
||||||
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||||
|
|
@ -29,7 +30,7 @@ const
|
||||||
converterPragmas* = procPragmas
|
converterPragmas* = procPragmas
|
||||||
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
|
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
|
||||||
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
|
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
|
||||||
wDelegator, wExportNims, wUsed}
|
wDelegator, wExportNims, wUsed, wPragma}
|
||||||
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
|
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
|
||||||
wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern,
|
wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern,
|
||||||
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
|
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
|
||||||
|
|
@ -74,7 +75,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||||
let p = procAst[pragmasPos]
|
let p = procAst[pragmasPos]
|
||||||
if p.kind == nkEmpty: return nil
|
if p.kind == nkEmpty: return nil
|
||||||
for it in p:
|
for it in p:
|
||||||
if it.kind == nkExprColonExpr and it[0].kind == nkIdent and
|
if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent and
|
||||||
it[0].ident.id == ord(name):
|
it[0].ident.id == ord(name):
|
||||||
return it[1]
|
return it[1]
|
||||||
|
|
||||||
|
|
@ -89,7 +90,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
|
||||||
if n != nil:
|
if n != nil:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent:
|
if it.kind in nkPragmaCallKinds and it.len == 2 and it.sons[0].kind == nkIdent:
|
||||||
case whichKeyword(it.sons[0].ident)
|
case whichKeyword(it.sons[0].ident)
|
||||||
of wSubsChar:
|
of wSubsChar:
|
||||||
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
|
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
|
||||||
|
|
@ -151,7 +152,7 @@ proc newEmptyStrNode(n: PNode): PNode {.noinline.} =
|
||||||
result.strVal = ""
|
result.strVal = ""
|
||||||
|
|
||||||
proc getStrLitNode(c: PContext, n: PNode): PNode =
|
proc getStrLitNode(c: PContext, n: PNode): PNode =
|
||||||
if n.kind != nkExprColonExpr:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
# error correction:
|
# error correction:
|
||||||
result = newEmptyStrNode(n)
|
result = newEmptyStrNode(n)
|
||||||
|
|
@ -168,7 +169,7 @@ proc expectStrLit(c: PContext, n: PNode): string =
|
||||||
result = getStrLitNode(c, n).strVal
|
result = getStrLitNode(c, n).strVal
|
||||||
|
|
||||||
proc expectIntLit(c: PContext, n: PNode): int =
|
proc expectIntLit(c: PContext, n: PNode): int =
|
||||||
if n.kind != nkExprColonExpr:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
localError(n.info, errIntLiteralExpected)
|
localError(n.info, errIntLiteralExpected)
|
||||||
else:
|
else:
|
||||||
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
||||||
|
|
@ -177,7 +178,7 @@ proc expectIntLit(c: PContext, n: PNode): int =
|
||||||
else: localError(n.info, errIntLiteralExpected)
|
else: localError(n.info, errIntLiteralExpected)
|
||||||
|
|
||||||
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
|
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
|
||||||
if n.kind == nkExprColonExpr: result = expectStrLit(c, n)
|
if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
|
||||||
else: result = defaultStr
|
else: result = defaultStr
|
||||||
|
|
||||||
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
||||||
|
|
@ -186,7 +187,7 @@ proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
||||||
proc processMagic(c: PContext, n: PNode, s: PSym) =
|
proc processMagic(c: PContext, n: PNode, s: PSym) =
|
||||||
#if sfSystemModule notin c.module.flags:
|
#if sfSystemModule notin c.module.flags:
|
||||||
# liMessage(n.info, errMagicOnlyInSystem)
|
# liMessage(n.info, errMagicOnlyInSystem)
|
||||||
if n.kind != nkExprColonExpr:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
return
|
return
|
||||||
var v: string
|
var v: string
|
||||||
|
|
@ -204,7 +205,7 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
|
||||||
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
|
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
|
||||||
|
|
||||||
proc isTurnedOn(c: PContext, n: PNode): bool =
|
proc isTurnedOn(c: PContext, n: PNode): bool =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
let x = c.semConstBoolExpr(c, n.sons[1])
|
let x = c.semConstBoolExpr(c, n.sons[1])
|
||||||
n.sons[1] = x
|
n.sons[1] = x
|
||||||
if x.kind == nkIntLit: return x.intVal != 0
|
if x.kind == nkIntLit: return x.intVal != 0
|
||||||
|
|
@ -223,7 +224,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
|
||||||
else: excl(c.module.flags, flag)
|
else: excl(c.module.flags, flag)
|
||||||
|
|
||||||
proc processCallConv(c: PContext, n: PNode) =
|
proc processCallConv(c: PContext, n: PNode) =
|
||||||
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
|
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
|
||||||
var sw = whichKeyword(n.sons[1].ident)
|
var sw = whichKeyword(n.sons[1].ident)
|
||||||
case sw
|
case sw
|
||||||
of FirstCallConv..LastCallConv:
|
of FirstCallConv..LastCallConv:
|
||||||
|
|
@ -244,7 +245,7 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
|
||||||
result.isOverriden = options.isDynlibOverride(path.strVal)
|
result.isOverriden = options.isDynlibOverride(path.strVal)
|
||||||
|
|
||||||
proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
||||||
if n.kind != nkExprColonExpr:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
# error correction:
|
# error correction:
|
||||||
result = newEmptyStrNode(n)
|
result = newEmptyStrNode(n)
|
||||||
|
|
@ -264,7 +265,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||||
if not lib.isOverriden:
|
if not lib.isOverriden:
|
||||||
c.optionStack[^1].dynlib = lib
|
c.optionStack[^1].dynlib = lib
|
||||||
else:
|
else:
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds:
|
||||||
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
||||||
if not lib.isOverriden:
|
if not lib.isOverriden:
|
||||||
addToLib(lib, sym)
|
addToLib(lib, sym)
|
||||||
|
|
@ -279,7 +280,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||||
sym.typ.callConv = ccCDecl
|
sym.typ.callConv = ccCDecl
|
||||||
|
|
||||||
proc processNote(c: PContext, n: PNode) =
|
proc processNote(c: PContext, n: PNode) =
|
||||||
if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
|
if (n.kind in nkPragmaCallKinds) and (sonsLen(n) == 2) and
|
||||||
(n.sons[0].kind == nkBracketExpr) and
|
(n.sons[0].kind == nkBracketExpr) and
|
||||||
(n.sons[0].sons.len == 2) and
|
(n.sons[0].sons.len == 2) and
|
||||||
(n.sons[0].sons[1].kind == nkIdent) and
|
(n.sons[0].sons[1].kind == nkIdent) and
|
||||||
|
|
@ -307,7 +308,7 @@ proc processNote(c: PContext, n: PNode) =
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
proc processOption(c: PContext, n: PNode): bool =
|
proc processOption(c: PContext, n: PNode): bool =
|
||||||
if n.kind != nkExprColonExpr: result = true
|
if n.kind notin nkPragmaCallKinds or n.len != 2: result = true
|
||||||
elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
|
elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
|
||||||
elif n.sons[0].kind != nkIdent: result = true
|
elif n.sons[0].kind != nkIdent: result = true
|
||||||
else:
|
else:
|
||||||
|
|
@ -355,8 +356,8 @@ proc processOption(c: PContext, n: PNode): bool =
|
||||||
else: result = true
|
else: result = true
|
||||||
|
|
||||||
proc processPush(c: PContext, n: PNode, start: int) =
|
proc processPush(c: PContext, n: PNode, start: int) =
|
||||||
if n.sons[start-1].kind == nkExprColonExpr:
|
if n.sons[start-1].kind in nkPragmaCallKinds:
|
||||||
localError(n.info, errGenerated, "':' after 'push' not supported")
|
localError(n.info, errGenerated, "'push' can't have arguments")
|
||||||
var x = newOptionEntry()
|
var x = newOptionEntry()
|
||||||
var y = c.optionStack[^1]
|
var y = c.optionStack[^1]
|
||||||
x.options = gOptions
|
x.options = gOptions
|
||||||
|
|
@ -381,14 +382,14 @@ proc processPop(c: PContext, n: PNode) =
|
||||||
c.optionStack.setLen(c.optionStack.len - 1)
|
c.optionStack.setLen(c.optionStack.len - 1)
|
||||||
|
|
||||||
proc processDefine(c: PContext, n: PNode) =
|
proc processDefine(c: PContext, n: PNode) =
|
||||||
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
|
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
|
||||||
defineSymbol(n.sons[1].ident.s)
|
defineSymbol(n.sons[1].ident.s)
|
||||||
message(n.info, warnDeprecated, "define")
|
message(n.info, warnDeprecated, "define")
|
||||||
else:
|
else:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
proc processUndef(c: PContext, n: PNode) =
|
proc processUndef(c: PContext, n: PNode) =
|
||||||
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
|
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
|
||||||
undefSymbol(n.sons[1].ident.s)
|
undefSymbol(n.sons[1].ident.s)
|
||||||
message(n.info, warnDeprecated, "undef")
|
message(n.info, warnDeprecated, "undef")
|
||||||
else:
|
else:
|
||||||
|
|
@ -420,7 +421,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
let it = if n.kind == nkExprColonExpr: n.sons[1] else: n
|
let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n
|
||||||
if it.kind == nkPar and it.len == 2:
|
if it.kind == nkPar and it.len == 2:
|
||||||
let s = getStrLit(c, it, 0)
|
let s = getStrLit(c, it, 0)
|
||||||
let dest = getStrLit(c, it, 1)
|
let dest = getStrLit(c, it, 1)
|
||||||
|
|
@ -453,7 +454,7 @@ proc pragmaBreakpoint(c: PContext, n: PNode) =
|
||||||
discard getOptionalStr(c, n, "")
|
discard getOptionalStr(c, n, "")
|
||||||
|
|
||||||
proc pragmaWatchpoint(c: PContext, n: PNode) =
|
proc pragmaWatchpoint(c: PContext, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
n.sons[1] = c.semExpr(c, n.sons[1])
|
n.sons[1] = c.semExpr(c, n.sons[1])
|
||||||
else:
|
else:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
@ -494,7 +495,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||||
result = newNode(nkAsmStmt, n.info)
|
result = newNode(nkAsmStmt, n.info)
|
||||||
|
|
||||||
proc pragmaEmit(c: PContext, n: PNode) =
|
proc pragmaEmit(c: PContext, n: PNode) =
|
||||||
if n.kind != nkExprColonExpr:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
else:
|
else:
|
||||||
let n1 = n[1]
|
let n1 = n[1]
|
||||||
|
|
@ -512,12 +513,12 @@ proc pragmaEmit(c: PContext, n: PNode) =
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
|
|
||||||
proc noVal(n: PNode) =
|
proc noVal(n: PNode) =
|
||||||
if n.kind == nkExprColonExpr: invalidPragma(n)
|
if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(n)
|
||||||
|
|
||||||
proc pragmaUnroll(c: PContext, n: PNode) =
|
proc pragmaUnroll(c: PContext, n: PNode) =
|
||||||
if c.p.nestedLoopCounter <= 0:
|
if c.p.nestedLoopCounter <= 0:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
elif n.kind == nkExprColonExpr:
|
elif n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
var unrollFactor = expectIntLit(c, n)
|
var unrollFactor = expectIntLit(c, n)
|
||||||
if unrollFactor <% 32:
|
if unrollFactor <% 32:
|
||||||
n.sons[1] = newIntNode(nkIntLit, unrollFactor)
|
n.sons[1] = newIntNode(nkIntLit, unrollFactor)
|
||||||
|
|
@ -525,10 +526,11 @@ proc pragmaUnroll(c: PContext, n: PNode) =
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
proc pragmaLine(c: PContext, n: PNode) =
|
proc pragmaLine(c: PContext, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
n.sons[1] = c.semConstExpr(c, n.sons[1])
|
||||||
let a = n.sons[1]
|
let a = n.sons[1]
|
||||||
if a.kind == nkPar:
|
if a.kind == nkPar:
|
||||||
|
# unpack the tuple
|
||||||
var x = a.sons[0]
|
var x = a.sons[0]
|
||||||
var y = a.sons[1]
|
var y = a.sons[1]
|
||||||
if x.kind == nkExprColonExpr: x = x.sons[1]
|
if x.kind == nkExprColonExpr: x = x.sons[1]
|
||||||
|
|
@ -549,7 +551,7 @@ proc pragmaLine(c: PContext, n: PNode) =
|
||||||
|
|
||||||
proc processPragma(c: PContext, n: PNode, i: int) =
|
proc processPragma(c: PContext, n: PNode, i: int) =
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
if it.kind != nkExprColonExpr: invalidPragma(n)
|
if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(n)
|
||||||
elif it.sons[0].kind != nkIdent: invalidPragma(n)
|
elif it.sons[0].kind != nkIdent: invalidPragma(n)
|
||||||
elif it.sons[1].kind != nkIdent: invalidPragma(n)
|
elif it.sons[1].kind != nkIdent: invalidPragma(n)
|
||||||
|
|
||||||
|
|
@ -566,7 +568,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||||
localError(x.info, errGenerated, "invalid type for raises/tags list")
|
localError(x.info, errGenerated, "invalid type for raises/tags list")
|
||||||
x.typ = t
|
x.typ = t
|
||||||
|
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
let it = n.sons[1]
|
let it = n.sons[1]
|
||||||
if it.kind notin {nkCurly, nkBracket}:
|
if it.kind notin {nkCurly, nkBracket}:
|
||||||
processExc(c, it)
|
processExc(c, it)
|
||||||
|
|
@ -576,7 +578,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
proc pragmaLockStmt(c: PContext; it: PNode) =
|
proc pragmaLockStmt(c: PContext; it: PNode) =
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind notin nkPragmaCallKinds or it.len != 2:
|
||||||
invalidPragma(it)
|
invalidPragma(it)
|
||||||
else:
|
else:
|
||||||
let n = it[1]
|
let n = it[1]
|
||||||
|
|
@ -587,7 +589,7 @@ proc pragmaLockStmt(c: PContext; it: PNode) =
|
||||||
n.sons[i] = c.semExpr(c, n.sons[i])
|
n.sons[i] = c.semExpr(c, n.sons[i])
|
||||||
|
|
||||||
proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
|
proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind notin nkPragmaCallKinds or it.len != 2:
|
||||||
invalidPragma(it)
|
invalidPragma(it)
|
||||||
else:
|
else:
|
||||||
case it[1].kind
|
case it[1].kind
|
||||||
|
|
@ -604,7 +606,7 @@ proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
|
||||||
result = TLockLevel(x)
|
result = TLockLevel(x)
|
||||||
|
|
||||||
proc typeBorrow(sym: PSym, n: PNode) =
|
proc typeBorrow(sym: PSym, n: PNode) =
|
||||||
if n.kind == nkExprColonExpr:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
let it = n.sons[1]
|
let it = n.sons[1]
|
||||||
if it.kind != nkAccQuoted:
|
if it.kind != nkAccQuoted:
|
||||||
localError(n.info, "a type can only borrow `.` for now")
|
localError(n.info, "a type can only borrow `.` for now")
|
||||||
|
|
@ -624,7 +626,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
|
||||||
if pragma.kind != nkBracket:
|
if pragma.kind != nkBracket:
|
||||||
localError(pragma.info, "list of key:value pairs expected"); return
|
localError(pragma.info, "list of key:value pairs expected"); return
|
||||||
for n in pragma:
|
for n in pragma:
|
||||||
if n.kind in {nkExprColonExpr, nkExprEqExpr}:
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||||
let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
|
let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
|
||||||
if dest == nil or dest.kind in routineKinds:
|
if dest == nil or dest.kind in routineKinds:
|
||||||
localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines")
|
localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines")
|
||||||
|
|
@ -638,7 +640,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
|
||||||
localError(n.info, "key:value pair expected")
|
localError(n.info, "key:value pair expected")
|
||||||
|
|
||||||
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind notin nkPragmaCallKinds or it.len != 2:
|
||||||
invalidPragma(it); return
|
invalidPragma(it); return
|
||||||
let n = it[1]
|
let n = it[1]
|
||||||
if n.kind == nkSym:
|
if n.kind == nkSym:
|
||||||
|
|
@ -655,13 +657,36 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||||
else:
|
else:
|
||||||
result = qualifiedLookUp(c, n, {checkUndeclared})
|
result = qualifiedLookUp(c, n, {checkUndeclared})
|
||||||
|
|
||||||
|
proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||||
|
assert(n.kind in nkPragmaCallKinds + {nkIdent})
|
||||||
|
|
||||||
|
if n.kind == nkIdent:
|
||||||
|
result = newTree(nkCall, n)
|
||||||
|
elif n.kind == nkExprColonExpr:
|
||||||
|
# pragma: arg -> pragma(arg)
|
||||||
|
result = newTree(nkCall, n[0], n[1])
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
|
result = c.semOverloadedCall(c, result, n, {skTemplate}, {})
|
||||||
|
if sfCustomPragma notin result[0].sym.flags:
|
||||||
|
invalidPragma(n)
|
||||||
|
|
||||||
|
if n.kind == nkIdent:
|
||||||
|
result = result[0]
|
||||||
|
elif n.kind == nkExprColonExpr:
|
||||||
|
result.kind = n.kind # pragma(arg) -> pragma: arg
|
||||||
|
|
||||||
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
validPragmas: TSpecialWords): bool =
|
validPragmas: TSpecialWords): bool =
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
|
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
|
||||||
if key.kind == nkBracketExpr:
|
if key.kind == nkBracketExpr:
|
||||||
processNote(c, it)
|
processNote(c, it)
|
||||||
return
|
return
|
||||||
|
elif key.kind notin nkIdentKinds:
|
||||||
|
n.sons[i] = semCustomPragma(c, it)
|
||||||
|
return
|
||||||
let ident = considerQuotedIdent(key)
|
let ident = considerQuotedIdent(key)
|
||||||
var userPragma = strTableGet(c.userPragmas, ident)
|
var userPragma = strTableGet(c.userPragmas, ident)
|
||||||
if userPragma != nil:
|
if userPragma != nil:
|
||||||
|
|
@ -785,7 +810,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
of wExplain:
|
of wExplain:
|
||||||
sym.flags.incl sfExplain
|
sym.flags.incl sfExplain
|
||||||
of wDeprecated:
|
of wDeprecated:
|
||||||
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
|
if it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
|
||||||
elif sym != nil: incl(sym.flags, sfDeprecated)
|
elif sym != nil: incl(sym.flags, sfDeprecated)
|
||||||
else: incl(c.module.flags, sfDeprecated)
|
else: incl(c.module.flags, sfDeprecated)
|
||||||
of wVarargs:
|
of wVarargs:
|
||||||
|
|
@ -864,8 +889,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
result = true
|
result = true
|
||||||
of wPop: processPop(c, it)
|
of wPop: processPop(c, it)
|
||||||
of wPragma:
|
of wPragma:
|
||||||
processPragma(c, n, i)
|
if not sym.isNil and sym.kind == skTemplate:
|
||||||
result = true
|
sym.flags.incl sfCustomPragma
|
||||||
|
else:
|
||||||
|
processPragma(c, n, i)
|
||||||
|
result = true
|
||||||
of wDiscardable:
|
of wDiscardable:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym != nil: incl(sym.flags, sfDiscardable)
|
if sym != nil: incl(sym.flags, sfDiscardable)
|
||||||
|
|
@ -939,7 +967,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
elif sym.typ == nil: invalidPragma(it)
|
elif sym.typ == nil: invalidPragma(it)
|
||||||
else: sym.typ.lockLevel = pragmaLocks(c, it)
|
else: sym.typ.lockLevel = pragmaLocks(c, it)
|
||||||
of wBitsize:
|
of wBitsize:
|
||||||
if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr:
|
if sym == nil or sym.kind != skField:
|
||||||
invalidPragma(it)
|
invalidPragma(it)
|
||||||
else:
|
else:
|
||||||
sym.bitsize = expectIntLit(c, it)
|
sym.bitsize = expectIntLit(c, it)
|
||||||
|
|
@ -957,7 +985,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
if sym == nil: invalidPragma(it)
|
if sym == nil: invalidPragma(it)
|
||||||
else: magicsys.registerNimScriptSymbol(sym)
|
else: magicsys.registerNimScriptSymbol(sym)
|
||||||
of wInjectStmt:
|
of wInjectStmt:
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind notin nkPragmaCallKinds or it.len != 2:
|
||||||
localError(it.info, errExprExpected)
|
localError(it.info, errExprExpected)
|
||||||
else:
|
else:
|
||||||
it.sons[1] = c.semExpr(c, it.sons[1])
|
it.sons[1] = c.semExpr(c, it.sons[1])
|
||||||
|
|
@ -968,10 +996,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
else:
|
else:
|
||||||
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
||||||
of wThis:
|
of wThis:
|
||||||
if it.kind == nkExprColonExpr:
|
if it.kind in nkPragmaCallKinds and it.len == 2:
|
||||||
c.selfName = considerQuotedIdent(it[1])
|
c.selfName = considerQuotedIdent(it[1])
|
||||||
else:
|
elif it.kind == nkIdent or it.len == 1:
|
||||||
c.selfName = getIdent("self")
|
c.selfName = getIdent("self")
|
||||||
|
else:
|
||||||
|
localError(it.info, "'this' pragma is allowed to have zero or one arguments")
|
||||||
of wNoRewrite:
|
of wNoRewrite:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
of wBase:
|
of wBase:
|
||||||
|
|
@ -987,7 +1017,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
else: sym.flags.incl sfUsed
|
else: sym.flags.incl sfUsed
|
||||||
of wLiftLocals: discard
|
of wLiftLocals: discard
|
||||||
else: invalidPragma(it)
|
else: invalidPragma(it)
|
||||||
else: invalidPragma(it)
|
else:
|
||||||
|
n.sons[i] = semCustomPragma(c, it)
|
||||||
|
|
||||||
|
|
||||||
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||||
validPragmas: TSpecialWords) =
|
validPragmas: TSpecialWords) =
|
||||||
|
|
@ -1015,7 +1047,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
|
||||||
return false
|
return false
|
||||||
|
|
||||||
for p in n.sons:
|
for p in n.sons:
|
||||||
var key = if p.kind == nkExprColonExpr: p[0] else: p
|
var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p
|
||||||
if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
|
if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -951,7 +951,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
result = semMacroExpr(c, n, n, s, flags)
|
result = semMacroExpr(c, n, n, s, flags)
|
||||||
of skTemplate:
|
of skTemplate:
|
||||||
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
|
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
|
||||||
|
sfCustomPragma in sym.flags:
|
||||||
markUsed(n.info, s, c.graph.usageSym)
|
markUsed(n.info, s, c.graph.usageSym)
|
||||||
styleCheckUse(n.info, s)
|
styleCheckUse(n.info, s)
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
|
|
|
||||||
|
|
@ -1153,6 +1153,9 @@ proc semProcAnnotation(c: PContext, prc: PNode;
|
||||||
else:
|
else:
|
||||||
localError(prc.info, errOnlyACallOpCanBeDelegator)
|
localError(prc.info, errOnlyACallOpCanBeDelegator)
|
||||||
continue
|
continue
|
||||||
|
elif sfCustomPragma in m.flags:
|
||||||
|
continue # semantic check for custom pragma happens later in semProcAux
|
||||||
|
|
||||||
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
|
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
|
||||||
# let the semantic checker deal with it:
|
# let the semantic checker deal with it:
|
||||||
var x = newNodeI(nkCall, n.info)
|
var x = newNodeI(nkCall, n.info)
|
||||||
|
|
|
||||||
|
|
@ -608,7 +608,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
s.ast = n
|
s.ast = n
|
||||||
result = n
|
result = n
|
||||||
if n.sons[bodyPos].kind == nkEmpty:
|
if sfCustomPragma in s.flags:
|
||||||
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
|
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
||||||
|
elif n.sons[bodyPos].kind == nkEmpty:
|
||||||
localError(n.info, errImplOfXexpected, s.name.s)
|
localError(n.info, errImplOfXexpected, s.name.s)
|
||||||
var proto = searchForProc(c, c.currentScope, s)
|
var proto = searchForProc(c, c.currentScope, s)
|
||||||
if proto == nil:
|
if proto == nil:
|
||||||
|
|
|
||||||
|
|
@ -1087,3 +1087,70 @@ In the above example, providing the -d flag causes the symbol
|
||||||
``FooBar`` to be overwritten at compile time, printing out 42. If the
|
``FooBar`` to be overwritten at compile time, printing out 42. If the
|
||||||
``-d:FooBar=42`` were to be omitted, the default value of 5 would be
|
``-d:FooBar=42`` were to be omitted, the default value of 5 would be
|
||||||
used.
|
used.
|
||||||
|
|
||||||
|
|
||||||
|
Custom annotations
|
||||||
|
------------------
|
||||||
|
It is possible to define custom typed pragmas. Custom pragmas do not effect
|
||||||
|
code generation directly, but their presence can be detected by macros.
|
||||||
|
Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
template dbTable(name: string, table_space: string = nil) {.pragma.}
|
||||||
|
template dbKey(name: string = nil, primary_key: bool = false) {.pragma.}
|
||||||
|
template dbForeignKey(t: typedesc) {.pragma.}
|
||||||
|
template dbIgnore {.pragma.}
|
||||||
|
|
||||||
|
|
||||||
|
Consider stylized example of possible Object Relation Mapping (ORM) implementation:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments
|
||||||
|
|
||||||
|
type
|
||||||
|
User {.dbTable("users", tblspace).} = object
|
||||||
|
id {.dbKey(primary_key = true).}: int
|
||||||
|
name {.dbKey"full_name".}: string
|
||||||
|
is_cached {.dbIgnore.}: bool
|
||||||
|
age: int
|
||||||
|
|
||||||
|
UserProfile {.dbTable("profiles", tblspace).} = object
|
||||||
|
id {.dbKey(primary_key = true).}: int
|
||||||
|
user_id {.dbForeignKey: User.}: int
|
||||||
|
read_access: bool
|
||||||
|
write_access: bool
|
||||||
|
admin_acess: bool
|
||||||
|
|
||||||
|
In this example custom pragmas are used to describe how Nim objects are
|
||||||
|
mapped to the schema of the relational database. Custom pragmas can have
|
||||||
|
zero or more arguments. In order to pass multiple arguments use one of
|
||||||
|
template call syntaxes. All arguments are typed and follow standard
|
||||||
|
overload resolution rules for templates. Therefore, it is possible to have
|
||||||
|
default values for arguments, pass by name, varargs, etc.
|
||||||
|
|
||||||
|
Custom pragmas can be used in all locations where ordinary pragmas can be
|
||||||
|
specified. It is possible to annotate procs, templates, type and variable
|
||||||
|
definitions, statements, etc.
|
||||||
|
|
||||||
|
Macros module includes helpers which can be used to simplify custom pragma
|
||||||
|
access `hasCustomPragma`, `getCustomPragmaVal`. Please consult macros module
|
||||||
|
documentation for details. These macros are no magic, they don't do anything
|
||||||
|
you cannot do yourself by walking AST object representation.
|
||||||
|
|
||||||
|
More examples with custom pragmas:
|
||||||
|
- Better serialization/deserialization control:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
type MyObj = object
|
||||||
|
a {.dontSerialize.}: int
|
||||||
|
b {.defaultDeserialize: 5.}: int
|
||||||
|
c {.serializationKey: "_c".}: string
|
||||||
|
|
||||||
|
- Adopting type for gui inspector in a game engine:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
type MyComponent = object
|
||||||
|
position {.editable, animatable.}: Vector3
|
||||||
|
alpha {.editRange: [0.0..1.0], animatable.}: float32
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ const
|
||||||
nnkLiterals* = {nnkCharLit..nnkNilLit}
|
nnkLiterals* = {nnkCharLit..nnkNilLit}
|
||||||
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||||
nnkCallStrLit}
|
nnkCallStrLit}
|
||||||
|
nnkPragmaCallKinds = {nnkExprColonExpr, nnkCall, nnkCallStrLit}
|
||||||
|
|
||||||
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
|
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
|
||||||
## constructs an identifier from the string `s`
|
## constructs an identifier from the string `s`
|
||||||
|
|
@ -1213,6 +1214,59 @@ macro expandMacros*(body: typed): untyped =
|
||||||
result = getAst(inner(body))
|
result = getAst(inner(body))
|
||||||
echo result.toStrLit
|
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]
|
||||||
|
|
||||||
|
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
|
||||||
|
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
|
||||||
|
## has custom pragma `cp`.
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## template myAttr() {.pragma.}
|
||||||
|
## type
|
||||||
|
## MyObj = object
|
||||||
|
## myField {.myAttr.}: int
|
||||||
|
## var o: MyObj
|
||||||
|
## assert(o.myField.hasCustomPragma(myAttr) == 0)
|
||||||
|
let pragmaNode = customPragmaNode(n)
|
||||||
|
for p in pragmaNode:
|
||||||
|
if (p.kind == nnkSym and p == cp) or
|
||||||
|
(p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp):
|
||||||
|
return newLit(true)
|
||||||
|
return newLit(false)
|
||||||
|
|
||||||
|
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
|
||||||
|
## Expands to value of custom pragma `cp` of expression `n` which is expected
|
||||||
|
## to be `nnkDotExpr`.
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## template serializationKey(key: string) {.pragma.}
|
||||||
|
## type
|
||||||
|
## MyObj = object
|
||||||
|
## myField {.serializationKey: "mf".}: int
|
||||||
|
## var o: MyObj
|
||||||
|
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
when not defined(booting):
|
when not defined(booting):
|
||||||
template emit*(e: static[string]): untyped {.deprecated.} =
|
template emit*(e: static[string]): untyped {.deprecated.} =
|
||||||
## accepts a single string argument and treats it as nim code
|
## accepts a single string argument and treats it as nim code
|
||||||
|
|
|
||||||
5
tests/pragmas/custom_pragma.nim
Normal file
5
tests/pragmas/custom_pragma.nim
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# imported by tcustom_pragmas to test scoping
|
||||||
|
|
||||||
|
template serializationKey*(s: string) {.pragma.}
|
||||||
|
template defaultValue*(V: typed) {.pragma.}
|
||||||
|
template alternativeKey*(s: string = nil, V: typed) {.pragma.}
|
||||||
43
tests/pragmas/tcustom_pragma.nim
Normal file
43
tests/pragmas/tcustom_pragma.nim
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import macros
|
||||||
|
|
||||||
|
block:
|
||||||
|
template myAttr() {.pragma.}
|
||||||
|
|
||||||
|
proc myProc():int {.myAttr.} = 2
|
||||||
|
const myAttrIdx = myProc.hasCustomPragma(myAttr)
|
||||||
|
static:
|
||||||
|
assert(myAttrIdx)
|
||||||
|
|
||||||
|
block:
|
||||||
|
template myAttr(a: string) {.pragma.}
|
||||||
|
|
||||||
|
type MyObj = object
|
||||||
|
myField1, myField2 {.myAttr: "hi".}: int
|
||||||
|
var o: MyObj
|
||||||
|
static:
|
||||||
|
assert o.myField2.hasCustomPragma(myAttr)
|
||||||
|
assert(not o.myField1.hasCustomPragma(myAttr))
|
||||||
|
|
||||||
|
import custom_pragma
|
||||||
|
block: # A bit more advanced case
|
||||||
|
type
|
||||||
|
Subfield = object
|
||||||
|
c {.serializationKey: "cc".}: float
|
||||||
|
|
||||||
|
MySerializable = object
|
||||||
|
a {.serializationKey"asdf", defaultValue: 5.} : int
|
||||||
|
b {.custom_pragma.defaultValue"hello".} : int
|
||||||
|
field: Subfield
|
||||||
|
d {.alternativeKey("df", 5).}: float
|
||||||
|
e {.alternativeKey(V = 5).}: seq[bool]
|
||||||
|
|
||||||
|
var s: MySerializable
|
||||||
|
|
||||||
|
const aDefVal = s.a.getCustomPragmaVal(defaultValue)
|
||||||
|
static: assert(aDefVal == 5)
|
||||||
|
|
||||||
|
const aSerKey = s.a.getCustomPragmaVal(serializationKey)
|
||||||
|
static: assert(aSerKey == "asdf")
|
||||||
|
|
||||||
|
const cSerKey = getCustomPragmaVal(s.field.c, serializationKey)
|
||||||
|
static: assert(cSerKey == "cc")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue