diff options
-rwxr-xr-x | doc/manual.txt | 42 | ||||
-rwxr-xr-x | rod/ast.nim | 3 | ||||
-rwxr-xr-x | rod/c2nim/cparse.nim | 2 | ||||
-rw-r--r-- | rod/c2nim/tests/systest2.c | 17 | ||||
-rwxr-xr-x | rod/condsyms.nim | 25 | ||||
-rwxr-xr-x | rod/pragmas.nim | 275 | ||||
-rwxr-xr-x | rod/semdata.nim | 2 | ||||
-rwxr-xr-x | rod/semstmts.nim | 6 | ||||
-rwxr-xr-x | rod/wordrecg.nim | 8 | ||||
-rw-r--r-- | tests/accept/compile/tuserpragma.nim | 7 | ||||
-rwxr-xr-x | web/news.txt | 1 |
11 files changed, 237 insertions, 151 deletions
diff --git a/doc/manual.txt b/doc/manual.txt index 7fce8ac85..36be32538 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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. + diff --git a/rod/ast.nim b/rod/ast.nim index 15e74198c..5b5676495 100755 --- a/rod/ast.nim +++ b/rod/ast.nim @@ -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 diff --git a/rod/c2nim/cparse.nim b/rod/c2nim/cparse.nim index a96aece3c..5da9f3015 100755 --- a/rod/c2nim/cparse.nim +++ b/rod/c2nim/cparse.nim @@ -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 diff --git a/rod/c2nim/tests/systest2.c b/rod/c2nim/tests/systest2.c new file mode 100644 index 000000000..bf3027cfc --- /dev/null +++ b/rod/c2nim/tests/systest2.c @@ -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); + diff --git a/rod/condsyms.nim b/rod/condsyms.nim index 0325a2b77..72cca33ee 100755 --- a/rod/condsyms.nim +++ b/rod/condsyms.nim @@ -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) diff --git a/rod/pragmas.nim b/rod/pragmas.nim index c52e4a10b..24c5be923 100755 --- a/rod/pragmas.nim +++ b/rod/pragmas.nim @@ -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): diff --git a/rod/semdata.nim b/rod/semdata.nim index 5020f103d..ebad21ad8 100755 --- a/rod/semdata.nim +++ b/rod/semdata.nim @@ -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) diff --git a/rod/semstmts.nim b/rod/semstmts.nim index 0f96e5b94..fdf931815 100755 --- a/rod/semstmts.nim +++ b/rod/semstmts.nim @@ -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) diff --git a/rod/wordrecg.nim b/rod/wordrecg.nim index daadd57d0..ac7265f7d 100755 --- a/rod/wordrecg.nim +++ b/rod/wordrecg.nim @@ -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"] diff --git a/tests/accept/compile/tuserpragma.nim b/tests/accept/compile/tuserpragma.nim new file mode 100644 index 000000000..784baa176 --- /dev/null +++ b/tests/accept/compile/tuserpragma.nim @@ -0,0 +1,7 @@ + +{.pragma: rtl, cdecl, exportc.} + +proc myproc(x, y: int): int {.rtl} = + nil + + diff --git a/web/news.txt b/web/news.txt index dcb8cb144..dd7e330c0 100755 --- a/web/news.txt +++ b/web/news.txt @@ -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 |