implemented user-defined pragmas

This commit is contained in:
Andreas Rumpf
2010-07-23 20:17:12 +02:00
parent 5a2163d71d
commit 804e2ac89d
11 changed files with 237 additions and 151 deletions

View File

@@ -2651,6 +2651,30 @@ Example:
.. code-block:: nimrod
{.deadCodeElim: on.}
Pragma pragma
-------------
The `pragma`:idx: pragma can be used to declare user defined pragmas. This is
useful because Nimrod's templates and macros do not affect pragmas. User
defined pragmas are in a different module-wide scope than all other symbols.
They cannot be imported from a module.
Example:
.. code-block:: nimrod
when appType == "lib":
{.pragma: rtl, exportc, dynlib, cdecl.}
else:
{.pragma: rtl, importc, dynlib: "client.dll", cdecl.}
proc p*(a, b: int): int {.rtl.} =
return a+b
In the example a new pragma named ``rtl`` is introduced that either imports
a symbol from a dynamic library or exports the symbol for dynamic library
generation.
Disabling certain messages
@@ -2715,8 +2739,8 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string
Dynlib pragma
-------------
Dynlib pragma for import
------------------------
With the `dynlib`:idx: pragma a procedure can be imported from
a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The
non-optional argument has to be the name of the dynamic library:
@@ -2762,3 +2786,17 @@ string expressions in general:
**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant
strings, because they are precompiled.
Dynlib pragma for export
------------------------
With the ``dynlib`` pragma a procedure can also be exported to
a dynamic library. The pragma then has no argument and has to be used in
conjunction with the ``exportc`` pragma:
.. code-block:: Nimrod
proc exportme(): int {.cdecl, export, dynlib.}
This is only useful if the program is compiled as a dynamic library via the
``--app:lib`` command line option.

View File

@@ -288,7 +288,8 @@ type
skIterator, # an iterator
skConverter, # a type converter
skMacro, # a macro
skTemplate, # a template
skTemplate, # a template; currently also misused for user-defined
# pragmas
skField, # a field in a record or object
skEnumField, # an identifier in an enum
skForVar, # a for loop variable

View File

@@ -11,6 +11,8 @@
## It translates a C source file into a Nimrod AST. Then the renderer can be
## used to convert the AST to its text representation.
## XXX cleanup of declaration handling. Standalone enums.
import
os, llstream, rnimsyn, clex, idents, strutils, pegs, ast, astalgo, msgs,
options, strtabs

View File

@@ -0,0 +1,17 @@
#ifdef C2NIM
# header "iup.h"
# cdecl
# mangle "'GTK_'{.*}" "TGtk$1"
# mangle "'PGTK_'{.*}" "PGtk$1"
#endif
typedef struct stupidTAG {
mytype a, b;
} GTK_MyStruct, *PGTK_MyStruct;
typedef struct {
mytype a, b;
} GTK_MyStruct, *PGTK_MyStruct;
int IupConvertXYToPos(PIhandle ih, int x, int y);

View File

@@ -24,11 +24,8 @@ proc countDefinedSymbols*(): int
# implementation
proc DefineSymbol(symbol: string) =
var
sym: PSym
i: PIdent
i = getIdent(symbol)
sym = StrTableGet(gSymbols, i)
var i = getIdent(symbol)
var sym = StrTableGet(gSymbols, i)
if sym == nil:
new(sym) # circumvent the ID mechanism
sym.kind = skConditional
@@ -37,20 +34,16 @@ proc DefineSymbol(symbol: string) =
sym.position = 1
proc UndefSymbol(symbol: string) =
var sym: PSym
sym = StrTableGet(gSymbols, getIdent(symbol))
var sym = StrTableGet(gSymbols, getIdent(symbol))
if sym != nil: sym.position = 0
proc isDefined(symbol: PIdent): bool =
var sym: PSym
sym = StrTableGet(gSymbols, symbol)
var sym = StrTableGet(gSymbols, symbol)
result = (sym != nil) and (sym.position == 1)
proc ListSymbols() =
var
it: TTabIter
s: PSym
s = InitTabIter(it, gSymbols)
var it: TTabIter
var s = InitTabIter(it, gSymbols)
MessageOut("-- List of currently defined symbols --")
while s != nil:
if s.position == 1: MessageOut(s.name.s)
@@ -58,10 +51,8 @@ proc ListSymbols() =
MessageOut("-- End of list --")
proc countDefinedSymbols(): int =
var
it: TTabIter
s: PSym
s = InitTabIter(it, gSymbols)
var it: TTabIter
var s = InitTabIter(it, gSymbols)
result = 0
while s != nil:
if s.position == 1: inc(result)

View File

@@ -33,7 +33,7 @@ const
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wBreakpoint,
wCheckpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated, wFloatChecks,
wInfChecks, wNanChecks}
wInfChecks, wNanChecks, wPragma}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wPure, wDeprecated}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
@@ -303,10 +303,9 @@ proc processCompile(c: PContext, n: PNode) =
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
var f, found: string
f = expectStrLit(c, n)
var f = expectStrLit(c, n)
if splitFile(f).ext == "": f = toObjFile(f)
found = findFile(f)
var found = findFile(f)
if found == "": found = f # use the default
case feature
of linkNormal: extccomp.addFileToLink(found)
@@ -325,137 +324,159 @@ proc PragmaCheckpoint(c: PContext, n: PNode) =
proc noVal(n: PNode) =
if n.kind == nkExprColonExpr: invalidPragma(n)
proc processPragma(c: PContext, n: PNode, i: int) =
var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n)
elif it.sons[0].kind != nkIdent: invalidPragma(n)
elif it.sons[1].kind != nkIdent: invalidPragma(n)
var userPragma = NewSym(skTemplate, it.sons[1].ident, nil)
userPragma.info = it.info
var body = newNodeI(nkPragma, n.info)
for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
userPragma.ast = body
StrTableAdd(c.userPragmas, userPragma)
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
if key.kind == nkIdent:
var k = whichKeyword(key.ident)
if k in validPragmas:
case k
of wExportc:
makeExternExport(sym, getOptionalStr(c, it, sym.name.s))
incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, sym.name.s))
of wAlign:
if sym.typ == nil: invalidPragma(it)
sym.typ.align = expectIntLit(c, it)
if not IsPowerOfTwo(sym.typ.align) and (sym.typ.align != 0):
liMessage(it.info, errPowerOfTwoExpected)
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not IsPowerOfTwo(size) or size <= 0 or size > 8:
liMessage(it.info, errPowerOfTwoExpected)
else:
sym.typ.size = size
of wNodecl:
noVal(it)
incl(sym.loc.Flags, lfNoDecl)
of wPure:
noVal(it)
if sym != nil: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(it)
incl(sym.flags, sfThreadVar)
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.Flags, lfNoDecl)
of wMerge:
noval(it)
incl(sym.flags, sfMerge)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.Flags, lfNoDecl) # implies nodecl, because
# otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
of wNosideeffect:
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideEffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoReturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynLib:
processDynLib(c, it, sym)
of wCompilerProc:
noVal(it) # compilerproc may not get a string!
makeExternExport(sym, sym.name.s)
incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym)
of wProcvar:
noVal(it)
incl(sym.flags, sfProcVar)
of wDeprecated:
noVal(it)
if sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfVarargs)
of wBorrow:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfFinal)
of wAcyclic:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfAcyclic)
of wTypeCheck:
noVal(it)
incl(sym.flags, sfTypeCheck)
of wHint: liMessage(it.info, hintUser, expectStrLit(c, it))
of wWarning: liMessage(it.info, warnUser, expectStrLit(c, it))
of wError: liMessage(it.info, errUser, expectStrLit(c, it))
of wFatal:
liMessage(it.info, errUser, expectStrLit(c, it))
quit(1)
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinkSys: processCommonLink(c, it, linkSys)
of wPassL: extccomp.addLinkOption(expectStrLit(c, it))
of wPassC: extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint: PragmaBreakpoint(c, it)
of wCheckpoint: PragmaCheckpoint(c, it)
of wPush:
processPush(c, n, i + 1)
break
of wPop: processPop(c, it)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef, wCallConv,
wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks:
processOption(c, it) # calling conventions (boring...):
of firstCallConv..lastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
sym.typ.callConv = wordToCallConv(k)
var userPragma = StrTableGet(c.userPragmas, key.ident)
if userPragma != nil:
pragma(c, sym, userPragma.ast, validPragmas)
# XXX BUG: possible infinite recursion!
else:
var k = whichKeyword(key.ident)
if k in validPragmas:
case k
of wExportc:
makeExternExport(sym, getOptionalStr(c, it, sym.name.s))
incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, sym.name.s))
of wAlign:
if sym.typ == nil: invalidPragma(it)
sym.typ.align = expectIntLit(c, it)
if not IsPowerOfTwo(sym.typ.align) and (sym.typ.align != 0):
liMessage(it.info, errPowerOfTwoExpected)
of wSize:
if sym.typ == nil: invalidPragma(it)
var size = expectIntLit(c, it)
if not IsPowerOfTwo(size) or size <= 0 or size > 8:
liMessage(it.info, errPowerOfTwoExpected)
else:
sym.typ.size = size
of wNodecl:
noVal(it)
incl(sym.loc.Flags, lfNoDecl)
of wPure:
noVal(it)
if sym != nil: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
of wRegister:
noVal(it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(it)
incl(sym.flags, sfThreadVar)
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
noVal(it)
incl(sym.flags, sfCompileTime)
incl(sym.loc.Flags, lfNoDecl)
of wMerge:
noval(it)
incl(sym.flags, sfMerge)
of wHeader:
var lib = getLib(c, libHeader, getStrLitNode(c, it))
addToLib(lib, sym)
incl(sym.flags, sfImportc)
incl(sym.loc.flags, lfHeader)
incl(sym.loc.Flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
of wNosideeffect:
noVal(it)
incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideEffect:
noVal(it)
incl(sym.flags, sfSideEffect)
of wNoReturn:
noVal(it)
incl(sym.flags, sfNoReturn)
of wDynLib:
processDynLib(c, it, sym)
of wCompilerProc:
noVal(it) # compilerproc may not get a string!
makeExternExport(sym, sym.name.s)
incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym)
of wProcvar:
noVal(it)
incl(sym.flags, sfProcVar)
of wDeprecated:
noVal(it)
if sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated)
of wVarargs:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfVarargs)
of wBorrow:
noVal(it)
incl(sym.flags, sfBorrow)
of wFinal:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfFinal)
of wAcyclic:
noVal(it)
if sym.typ == nil: invalidPragma(it)
incl(sym.typ.flags, tfAcyclic)
of wTypeCheck:
noVal(it)
incl(sym.flags, sfTypeCheck)
of wHint: liMessage(it.info, hintUser, expectStrLit(c, it))
of wWarning: liMessage(it.info, warnUser, expectStrLit(c, it))
of wError: liMessage(it.info, errUser, expectStrLit(c, it))
of wFatal:
liMessage(it.info, errUser, expectStrLit(c, it))
quit(1)
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinkSys: processCommonLink(c, it, linkSys)
of wPassL: extccomp.addLinkOption(expectStrLit(c, it))
of wPassC: extccomp.addCompileOption(expectStrLit(c, it))
of wBreakpoint: PragmaBreakpoint(c, it)
of wCheckpoint: PragmaCheckpoint(c, it)
of wPush:
processPush(c, n, i + 1)
break
of wPop: processPop(c, it)
of wPragma:
processPragma(c, n, i)
break
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef,
wCallConv,
wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks:
processOption(c, it) # calling conventions (boring...):
of firstCallConv..lastCallConv:
assert(sym != nil)
if sym.typ == nil: invalidPragma(it)
sym.typ.callConv = wordToCallConv(k)
else: invalidPragma(it)
else: invalidPragma(it)
else: invalidPragma(it)
else: processNote(c, it)
if (sym != nil) and (sym.kind != skModule):
if (lfExportLib in sym.loc.flags) and not (sfExportc in sym.flags):

View File

@@ -51,6 +51,7 @@ type
semExpr*: proc (c: PContext, n: PNode): PNode # for the pragmas
includedFiles*: TIntSet # used to detect recursive include files
filename*: string # the module's filename
userPragmas*: TStrTable
var gInstTypes*: TIdTable # map PType to PType
@@ -124,6 +125,7 @@ proc newContext(module: PSym, nimfile: string): PContext =
result.converters = @ []
result.filename = nimfile
IntSetInit(result.includedFiles)
initStrTable(result.userPragmas)
proc addConverter(c: PContext, conv: PSym) =
var L = len(c.converters)

View File

@@ -708,7 +708,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
pushOwner(s)
s.options = gOptions
if n.sons[codePos] != nil:
if {sfImportc, sfBorrow} * s.flags != {}:
# for DLL generation, it is annoying to check for sfImportc!
if sfBorrow in s.flags:
liMessage(n.sons[codePos].info, errImplOfXNotAllowed, s.name.s)
if (n.sons[genericParamsPos] == nil):
c.p = newProcCon(s)
@@ -720,6 +721,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if (s.typ.sons[0] != nil) and (kind != skIterator):
addDecl(c, newSym(skUnknown, getIdent("result"), nil))
n.sons[codePos] = semGenericStmtScope(c, n.sons[codePos])
if sfImportc in s.flags:
# so we just ignore the body after semantic checking for importc:
n.sons[codePos] = nil
else:
if proto != nil: liMessage(n.info, errImplOfXexpected, proto.name.s)
if {sfImportc, sfBorrow} * s.flags == {}: incl(s.flags, sfForward)

View File

@@ -42,10 +42,11 @@ type
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks,
wFloatchecks, wNanChecks, wInfChecks,
wFloatchecks, wNanChecks, wInfChecks,
wAssertions, wWarnings, wW,
wHints, wOptimization, wSpeed, wSize, wNone, wPath, wP, wD, wU, wDebuginfo,
wCompileonly, wNolinking, wForcebuild, wF, wDeadCodeElim, wSafecode,
wPragma,
wCompileTime, wGc, wRefc, wBoehm, wA, wOpt, wO, wApp, wConsole, wGui,
wPassc, wT, wPassl, wL, wListcmd, wGendoc, wGenmapping, wOs, wCpu,
wGenerate, wG, wC, wCpp, wBorrow, wRun, wR, wVerbosity, wV, wHelp, wH,
@@ -53,7 +54,7 @@ type
wCc, wGenscript, wCheckPoint, wCheckPoints, wNoMain, wSubsChar,
wAcyclic, wIndex,
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM, wPretty,
wDoc, wPas, wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy,
wDoc, wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy,
wRst2html, wRst2tex, wI,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar
@@ -91,6 +92,7 @@ const
"assertions", "warnings", "w", "hints",
"optimization", "speed", "size", "none", "path", "p", "d", "u", "debuginfo",
"compileonly", "nolinking", "forcebuild", "f", "deadcodeelim", "safecode",
"pragma",
"compiletime", "gc", "refc", "boehm", "a", "opt", "o", "app", "console",
"gui", "passc", "t", "passl", "l", "listcmd", "gendoc", "genmapping", "os",
"cpu", "generate", "g", "c", "cpp", "borrow", "run", "r", "verbosity", "v",
@@ -98,7 +100,7 @@ const
"skipcfg", "skipprojcfg", "cc", "genscript", "checkpoint", "checkpoints",
"nomain", "subschar", "acyclic", "index",
"compiletoc", "compiletocpp", "compiletoecmascript", "compiletollvm",
"pretty", "doc", "pas", "gendepend", "listdef", "check", "parse", "scan",
"pretty", "doc", "gendepend", "listdef", "check", "parse", "scan",
"boot", "lazy", "rst2html", "rst2tex", "i",
"write", "putenv", "prependenv", "appendenv", "threadvar"]

View File

@@ -0,0 +1,7 @@
{.pragma: rtl, cdecl, exportc.}
proc myproc(x, y: int): int {.rtl} =
nil

View File

@@ -42,6 +42,7 @@ Additions
- Implemented implicit type arguments for generics.
- Implemented ``{.size: sizeof(cint).}`` pragma for enum types. This is useful
for interfacing with C.
- Implemented ``{.pragma.}`` pragma for user defined pragmas.
2010-03-14 Version 0.8.8 released