Merge branch 'master' of https://github.com/Araq/Nimrod into vm2

Conflicts:
	web/news.txt
This commit is contained in:
Araq 2013-12-23 01:23:16 +01:00
commit b76729df1c
35 changed files with 1047 additions and 269 deletions

397
.gitignore vendored
View file

@ -63,7 +63,10 @@ examples/cross_calculator/android/tags
/examples/allany /examples/allany
/examples/cairoex /examples/cairoex
/examples/cgiex /examples/cgiex
/examples/cgi/cgi_stacktrace
/examples/cgi/example
/examples/curlex /examples/curlex
/examples/debugging
/examples/docstrings /examples/docstrings
/examples/filterex /examples/filterex
/examples/fizzbuzz /examples/fizzbuzz
@ -89,6 +92,12 @@ examples/cross_calculator/android/tags
/examples/pythonex /examples/pythonex
/examples/sdlex /examples/sdlex
/examples/statcsv /examples/statcsv
/examples/talk/dsl
/examples/talk/formatoptimizer
/examples/talk/hoisting
/examples/talk/lazyeval
/examples/talk/quasiquote
/examples/talk/tags
/examples/tclex /examples/tclex
/examples/transff /examples/transff
/examples/tunit /examples/tunit
@ -96,10 +105,13 @@ examples/cross_calculator/android/tags
/examples/x11ex /examples/x11ex
/lib/libnimrtl.dylib /lib/libnimrtl.dylib
/lib/libserver.dylib /lib/libserver.dylib
/lib/packages/docutils/highlite
/lib/pure/actors /lib/pure/actors
/lib/pure/algorithm /lib/pure/algorithm
/lib/pure/asyncio /lib/pure/asyncio
/lib/pure/base64 /lib/pure/base64
/lib/pure/basic2d
/lib/pure/basic3d
/lib/pure/browsers /lib/pure/browsers
/lib/pure/cgi /lib/pure/cgi
/lib/pure/collections/sequtils /lib/pure/collections/sequtils
@ -111,6 +123,7 @@ examples/cross_calculator/android/tags
/lib/pure/encodings /lib/pure/encodings
/lib/pure/endians /lib/pure/endians
/lib/pure/events /lib/pure/events
/lib/pure/fsmonitor
/lib/pure/ftpclient /lib/pure/ftpclient
/lib/pure/gentabs /lib/pure/gentabs
/lib/pure/hashes /lib/pure/hashes
@ -127,19 +140,23 @@ examples/cross_calculator/android/tags
/lib/pure/math /lib/pure/math
/lib/pure/md5 /lib/pure/md5
/lib/pure/memfiles /lib/pure/memfiles
/lib/pure/mersenne
/lib/pure/mimetypes /lib/pure/mimetypes
/lib/pure/nimprof /lib/pure/nimprof
/lib/pure/numeric
/lib/pure/oids /lib/pure/oids
/lib/pure/os /lib/pure/os
/lib/pure/osproc /lib/pure/osproc
/lib/pure/parsecfg /lib/pure/parsecfg
/lib/pure/parsecsv /lib/pure/parsecsv
/lib/pure/parseopt /lib/pure/parseopt
/lib/pure/parseopt2
/lib/pure/parsesql /lib/pure/parsesql
/lib/pure/parseurl /lib/pure/parseurl
/lib/pure/parseutils /lib/pure/parseutils
/lib/pure/parsexml /lib/pure/parsexml
/lib/pure/pegs /lib/pure/pegs
/lib/pure/poly
/lib/pure/redis /lib/pure/redis
/lib/pure/romans /lib/pure/romans
/lib/pure/ropes /lib/pure/ropes
@ -167,9 +184,9 @@ examples/cross_calculator/android/tags
/run.json /run.json
/testresults.html /testresults.html
/testresults.json /testresults.json
/tests/caas/SymbolProcRun.*/
/tests/caas/absurd_nesting /tests/caas/absurd_nesting
/tests/caas/completion_dot_syntax_main /tests/caas/completion_dot_syntax_main
/tests/caasdriver
/tests/caas/forward_declarations /tests/caas/forward_declarations
/tests/caas/idetools_api /tests/caas/idetools_api
/tests/caas/imported /tests/caas/imported
@ -178,6 +195,380 @@ examples/cross_calculator/android/tags
/tests/caas/issue_477_dynamic_dispatch /tests/caas/issue_477_dynamic_dispatch
/tests/caas/its_full_of_procs /tests/caas/its_full_of_procs
/tests/caas/main /tests/caas/main
/tests/caasdriver /tests/caas/SymbolProcRun.*/
/tools/nimgrep /tests/ccg/tmissingbracket
/tests/compile/talias
/tests/compile/tambsym2
/tests/compile/tarrindx
/tests/compile/tassign
/tests/compile/tbindoverload
/tests/compile/tcan_alias_generic
/tests/compile/tcan_alias_specialised_generic
/tests/compile/tcan_inherit_generic
/tests/compile/tcan_specialise_generic
/tests/compile/tccgen1
/tests/compile/tclosure4
/tests/compile/tclosurebug2
/tests/compile/tcmdline
/tests/compile/tcodegenbug1
/tests/compile/tcolonisproc
/tests/compile/tcolors
/tests/compile/tcommontype
/tests/compile/tcompiles
/tests/compile/tcomputedgoto
/tests/compile/tconsteval
/tests/compile/tconstraints
/tests/compile/tconvcolors
/tests/compile/tcputime
/tests/compile/tdefaultprocparam
/tests/compile/tdictdestruct
/tests/compile/tdiscardable
/tests/compile/tdllvar
/tests/compile/tdumpast
/tests/compile/tdumpast2
/tests/compile/techo
/tests/compile/teffects1
/tests/compile/temptyecho
/tests/compile/tendian
/tests/compile/tenum
/tests/compile/tenum2
/tests/compile/tenum3
/tests/compile/teval1
/tests/compile/texport
/tests/compile/tfib
/tests/compile/tforwardgeneric
/tests/compile/tforwty
/tests/compile/tforwty2
/tests/compile/tgeneric
/tests/compile/tgeneric2
/tests/compile/tgeneric3
/tests/compile/tgeneric4
/tests/compile/tgenericmatcher
/tests/compile/tgenericmatcher2
/tests/compile/tgenericprocvar
/tests/compile/tgenericprop
/tests/compile/tgenericrefs
/tests/compile/tgenerictmpl
/tests/compile/tgenericvariant
/tests/compile/tgensymgeneric
/tests/compile/tgetstartmilsecs
/tests/compile/tglobalforvar
/tests/compile/thallo
/tests/compile/theaproots
/tests/compile/thexrange
/tests/compile/thygienictempl
/tests/compile/tident
/tests/compile/timplicititems
/tests/compile/timplictderef
/tests/compile/tinheritref
/tests/compile/tio
/tests/compile/tircbot
/tests/compile/titer
/tests/compile/titer2
/tests/compile/titer_no_tuple_unpack
/tests/compile/titerovl
/tests/compile/tlastmod
/tests/compile/tlinearscanend
/tests/compile/tloops
/tests/compile/tmacro1
/tests/compile/tmacro2
/tests/compile/tmacroaspragma
/tests/compile/tmacrostmt
/tests/compile/tmandelbrot
/tests/compile/tmarshal
/tests/compile/tmath
/tests/compile/tmatrix1
/tests/compile/tmatrix2
/tests/compile/tmongo
/tests/compile/tnamedparamanonproc
/tests/compile/tnamedparams
/tests/compile/tnestedproc
/tests/compile/tnew
/tests/compile/tnewsets
/tests/compile/tnewuns
/tests/compile/tnoargopenarray
/tests/compile/tnoforward
/tests/compile/tobjconstr2
/tests/compile/tobjcov
/tests/compile/tobject2
/tests/compile/tobject3
/tests/compile/tobjects
/tests/compile/toop
/tests/compile/toptions
/tests/compile/tos
/tests/compile/toverprc
/tests/compile/tparedef
/tests/compile/tparscfg
/tests/compile/tparsefloat
/tests/compile/tparsopt
/tests/compile/tposix
/tests/compile/tprep
/tests/compile/tprocvars
/tests/compile/tpush
/tests/compile/tquicksort
/tests/compile/tquit
/tests/compile/tradix
/tests/compile/treadln
/tests/compile/treadx
/tests/compile/trecmod
/tests/compile/trecmod2
/tests/compile/trectuple
/tests/compile/trectuples
/tests/compile/tredef
/tests/compile/trepr
/tests/compile/tsecondarrayproperty
/tests/compile/tseq2
/tests/compile/tseqcon2
/tests/compile/tshadow_magic_type
/tests/compile/tsizeof
/tests/compile/tslurp
/tests/compile/tsockets
/tests/compile/tsortcall
/tests/compile/tspecialised_is_equivalent
/tests/compile/tstrace
/tests/compile/tstrdesc
/tests/compile/tstrdist
/tests/compile/tstreams
/tests/compile/tstrset
/tests/compile/tstrtabs
/tests/compile/ttableconstr
/tests/compile/ttempl
/tests/compile/ttempl3
/tests/compile/ttempl4
/tests/compile/ttempl5
/tests/compile/ttemplreturntype
/tests/compile/tthread_generic
/tests/compile/ttime
/tests/compile/ttuple1
/tests/compile/ttypeclasses
/tests/compile/ttypeconverter1
/tests/compile/tuserpragma
/tests/compile/tvoid
/tests/compile/twalker
/tests/compile/typalias
/tests/dll/client
/tests/gc/closureleak
/tests/gc/cycleleak
/tests/gc/gcbench
/tests/gc/gcleak
/tests/gc/gcleak2
/tests/gc/gcleak3
/tests/gc/gctest
/tests/gc/weakrefs
/tests/manyloc/argument_parser/ex_wget
/tests/manyloc/nake/nakefile
/tests/manyloc/packages/noconflicts
/tests/manyloc/standalone/barebone
/tests/patterns/targlist
/tests/patterns/tcse
/tests/patterns/thoist
/tests/patterns/tmatrix
/tests/patterns/tnoalias
/tests/patterns/tnoendlessrec
/tests/patterns/tor
/tests/patterns/tpartial
/tests/patterns/tstar
/tests/patterns/tstmtlist
/tests/reject/t99bott
/tests/reject/tcheckedfield1
/tests/reject/tdeprecated
/tests/reject/tdisallowif
/tests/reject/tuninit1
/tests/rodfiles/aconv
/tests/rodfiles/bconv
/tests/rodfiles/bmethods
/tests/rodfiles/bmethods2
/tests/rodfiles/deada
/tests/rodfiles/deada2
/tests/rodfiles/hallo
/tests/rodfiles/hallo2
/tests/rodfiles/tgeneric1
/tests/rodfiles/tgeneric2
/tests/run/tack
/tests/run/tactiontable
/tests/run/tambsym2
/tests/run/tambsys
/tests/run/tanontuples
/tests/run/tarray
/tests/run/tarray2
/tests/run/tarray3
/tests/run/tarraycons
/tests/run/tassert
/tests/run/tastoverload1
/tests/run/tasynciossl
/tests/run/tasyncudp
/tests/run/tbind1
/tests/run/tbind3
/tests/run/tbintre2
/tests/run/tbintree
/tests/run/tborrow
/tests/run/tbug499771
/tests/run/tbug511622
/tests/run/tcase_setconstr
/tests/run/tcasestm
/tests/run/tcgbug
/tests/run/tclosure2
/tests/run/tclosure3
/tests/run/tcnstseq
/tests/run/tcnstseq2
/tests/run/tcnstseq3
/tests/run/tconcat
/tests/run/tconstr2
/tests/run/tcontinue
/tests/run/tcontinuexc
/tests/run/tcopy
/tests/run/tcountup
/tests/run/tcritbits
/tests/run/tcurrncy
/tests/run/tdestructor
/tests/run/tdrdobbs_examples
/tests/run/temit
/tests/run/tenumhole
/tests/run/tenumitems
/tests/run/teventemitter
/tests/run/tevents
/tests/run/texceptions
/tests/run/texcpt1
/tests/run/texcsub
/tests/run/texplicitgeneric1
/tests/run/tfieldindex
/tests/run/tfielditerator
/tests/run/tfielditerator2
/tests/run/tfilter
/tests/run/tfinally
/tests/run/tfinally2
/tests/run/tfinally3
/tests/run/tfinalobj
/tests/run/tfloat1
/tests/run/tfloat2
/tests/run/tfloat3
/tests/run/tformat
/tests/run/tfriends
/tests/run/tgenericassign
/tests/run/tgenericassigntuples
/tests/run/tgenericconverter
/tests/run/tgenericprocvar
/tests/run/tgenerics1
/tests/run/tgensym
/tests/run/tglobal
/tests/run/thashes
/tests/run/thexlit
/tests/run/thintoff
/tests/run/tidgen
/tests/run/tindent1
/tests/run/tinit
/tests/run/tinterf
/tests/run/tints
/tests/run/tisopr
/tests/run/titer3
/tests/run/titer5
/tests/run/titer6
/tests/run/titer7
/tests/run/titer8
/tests/run/titer9
/tests/run/titerslice
/tests/run/titervaropenarray
/tests/run/tkoeniglookup
/tests/run/tlet
/tests/run/tlists
/tests/run/tlocals
/tests/run/tlowhigh
/tests/run/tmacro2
/tests/run/tmacro3
/tests/run/tmacro4
/tests/run/tmacros1
/tests/run/tmath
/tests/run/tmatrix
/tests/run/tmemoization
/tests/run/tmethods1
/tests/run/tmixin
/tests/run/tmoditer
/tests/run/tmultim1
/tests/run/tmultim2
/tests/run/tmultim3
/tests/run/tmultim4
/tests/run/tmultim6
/tests/run/tnamedenumfields
/tests/run/tnestif
/tests/run/tnestprc
/tests/run/tnewderef
/tests/run/tnodeadlocks
/tests/run/tobjasgn
/tests/run/tobjconstr
/tests/run/tobject
/tests/run/tofopr
/tests/run/tonraise
/tests/run/toop1
/tests/run/topenarrayrepr
/tests/run/topenlen
/tests/run/toprprec
/tests/run/toverflw
/tests/run/toverflw2
/tests/run/toverl2
/tests/run/toverl3
/tests/run/toverwr
/tests/run/tovfint
/tests/run/tpatterns
/tests/run/tpegs
/tests/run/tpos
/tests/run/tprecedence
/tests/run/tprintf
/tests/run/tprocvar
/tests/run/tquotewords
/tests/run/tregex
/tests/run/treguse
/tests/run/trepr
/tests/run/treraise
/tests/run/tromans
/tests/run/tseqcon
/tests/run/tseqtuple
/tests/run/tsequtils
/tests/run/tsets
/tests/run/tsets2
/tests/run/tsidee2
/tests/run/tsidee3
/tests/run/tsimmeth
/tests/run/tsimplesort
/tests/run/tslices
/tests/run/tsortdev
/tests/run/tsplit
/tests/run/tstempl
/tests/run/tstmtexprs
/tests/run/tstrange
/tests/run/tstringinterp
/tests/run/tstrlits
/tests/run/tstrutil
/tests/run/tsubrange
/tests/run/tsubrange2
/tests/run/ttables
/tests/run/ttables2
/tests/run/ttoseq
/tests/run/ttypedesc1
/tests/run/tunhandledexc
/tests/run/tunidecode
/tests/run/tunittests
/tests/run/tuserassert
/tests/run/tvarargs_vs_generic
/tests/run/tvardecl
/tests/run/tvariantasgn
/tests/run/tvariantstack
/tests/run/tvarious1
/tests/run/tvarnums
/tests/run/tvarres1
/tests/run/tvarres2
/tests/run/tvartup
/tests/run/tvtable
/tests/run/twrongexc
/tests/run/txmlgen
/tests/run/txmltree
/tests/run/tzeroarray
/tests/system/helpers/readall_echo
/tests/system/io
/tests/system/params /tests/system/params
/tests/tester
/tests/threads/tactors
/tests/threads/tactors2
/tests/threads/tthreadanalysis2
/tests/threads/tthreadanalysis3
/tests/threads/tthreadheapviolation1
/tools/nimgrep

View file

@ -290,6 +290,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
genNamedParamCall(p, e, d) genNamedParamCall(p, e, d)
else: else:
genPrefixCall(p, nil, e, d) genPrefixCall(p, nil, e, d)
postStmtActions(p)
when false: when false:
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
@ -303,6 +304,7 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
genNamedParamCall(p, ri, d) genNamedParamCall(p, ri, d)
else: else:
genPrefixCall(p, le, ri, d) genPrefixCall(p, le, ri, d)
postStmtActions(p)
when false: when false:
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d) if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)

View file

@ -921,7 +921,12 @@ proc genPragma(p: BProc, n: PNode) =
of wEmit: genEmit(p, it) of wEmit: genEmit(p, it)
of wBreakpoint: genBreakPoint(p, it) of wBreakpoint: genBreakPoint(p, it)
of wWatchpoint: genWatchpoint(p, it) of wWatchpoint: genWatchpoint(p, it)
else: nil of wInjectStmt:
var p = newProc(nil, p.module)
p.options = p.options - {optLineTrace, optStackTrace}
genStmts(p, it.sons[1])
p.module.injectStmt = p.s(cpsStmts)
else: discard
proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool =
if optFieldCheck in p.options: if optFieldCheck in p.options:

View file

@ -289,6 +289,9 @@ proc genLineDir(p: BProc, t: PNode) =
linefmt(p, cpsStmts, "nimln($1, $2);$n", linefmt(p, cpsStmts, "nimln($1, $2);$n",
line.toRope, t.info.quotedFilename) line.toRope, t.info.quotedFilename)
proc postStmtActions(p: BProc) {.inline.} =
app(p.s(cpsStmts), p.module.injectStmt)
proc accessThreadLocalVar(p: BProc, s: PSym) proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(): bool {.inline.} proc emulatedThreadVars(): bool {.inline.}
@ -1119,6 +1122,9 @@ proc newPostInitProc(m: BModule): BProc =
# little hack so that unique temporaries are generated: # little hack so that unique temporaries are generated:
result.labels = 200_000 result.labels = 200_000
proc initProcOptions(m: BModule): TOptions =
if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
proc rawNewModule(module: PSym, filename: string): BModule = proc rawNewModule(module: PSym, filename: string): BModule =
new(result) new(result)
InitLinkedList(result.headerFiles) InitLinkedList(result.headerFiles)
@ -1131,7 +1137,7 @@ proc rawNewModule(module: PSym, filename: string): BModule =
result.module = module result.module = module
result.typeInfoMarker = initIntSet() result.typeInfoMarker = initIntSet()
result.initProc = newProc(nil, result) result.initProc = newProc(nil, result)
result.initProc.options = gOptions result.initProc.options = initProcOptions(result)
result.preInitProc = newPreInitProc(result) result.preInitProc = newPreInitProc(result)
result.postInitProc = newPostInitProc(result) result.postInitProc = newPostInitProc(result)
initNodeTable(result.dataCache) initNodeTable(result.dataCache)
@ -1139,7 +1145,12 @@ proc rawNewModule(module: PSym, filename: string): BModule =
result.forwardedProcs = @[] result.forwardedProcs = @[]
result.typeNodesName = getTempName() result.typeNodesName = getTempName()
result.nimTypesName = getTempName() result.nimTypesName = getTempName()
result.PreventStackTrace = sfSystemModule in module.flags # no line tracing for the init sections of the system module so that we
# don't generate a TFrame which can confuse the stack botton initialization:
if sfSystemModule in module.flags:
result.PreventStackTrace = true
excl(result.preInitProc.options, optStackTrace)
excl(result.postInitProc.options, optStackTrace)
proc nullify[T](arr: var T) = proc nullify[T](arr: var T) =
for i in low(arr)..high(arr): for i in low(arr)..high(arr):
@ -1152,7 +1163,7 @@ proc resetModule*(m: var BModule) =
m.declaredProtos = initIntSet() m.declaredProtos = initIntSet()
initIdTable(m.forwTypeCache) initIdTable(m.forwTypeCache)
m.initProc = newProc(nil, m) m.initProc = newProc(nil, m)
m.initProc.options = gOptions m.initProc.options = initProcOptions(m)
m.preInitProc = newPreInitProc(m) m.preInitProc = newPreInitProc(m)
m.postInitProc = newPostInitProc(m) m.postInitProc = newPostInitProc(m)
initNodeTable(m.dataCache) initNodeTable(m.dataCache)
@ -1242,7 +1253,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
result = n result = n
if b == nil or passes.skipCodegen(n): return if b == nil or passes.skipCodegen(n): return
var m = BModule(b) var m = BModule(b)
m.initProc.options = gOptions m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n) genStmts(m.initProc, n)
proc finishModule(m: BModule) = proc finishModule(m: BModule) =
@ -1329,7 +1340,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
if b == nil or passes.skipCodegen(n): return if b == nil or passes.skipCodegen(n): return
var m = BModule(b) var m = BModule(b)
if n != nil: if n != nil:
m.initProc.options = gOptions m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n) genStmts(m.initProc, n)
# cached modules need to registered too: # cached modules need to registered too:
registerModuleToMain(m.module) registerModuleToMain(m.module)

View file

@ -111,6 +111,7 @@ type
labels*: natural # for generating unique module-scope names labels*: natural # for generating unique module-scope names
extensionLoaders*: array['0'..'9', PRope] # special procs for the extensionLoaders*: array['0'..'9', PRope] # special procs for the
# OpenGL wrapper # OpenGL wrapper
injectStmt*: PRope
var var
mainModProcs*, mainModInit*, mainDatInit*: PRope # parts of the main module mainModProcs*, mainModInit*, mainDatInit*: PRope # parts of the main module

View file

@ -11,10 +11,10 @@
# semantic checking is done for the code. Cross-references are generated # semantic checking is done for the code. Cross-references are generated
# by knowing how the anchors are going to be named. # by knowing how the anchors are going to be named.
import import
ast, strutils, strtabs, options, msgs, os, ropes, idents, ast, strutils, strtabs, options, msgs, os, ropes, idents,
wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
importer, sempass2 importer, sempass2, json
type type
TSections = array[TSymKind, PRope] TSections = array[TSymKind, PRope]
@ -25,7 +25,7 @@ type
indexValFilename: string indexValFilename: string
PDoc* = ref TDocumentor PDoc* = ref TDocumentor
proc compilerMsgHandler(filename: string, line, col: int, proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.TMsgKind, arg: string) {.procvar.} = msgKind: rst.TMsgKind, arg: string) {.procvar.} =
# translate msg kind: # translate msg kind:
@ -41,69 +41,69 @@ proc compilerMsgHandler(filename: string, line, col: int,
of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
GlobalError(newLineInfo(filename, line, col), k, arg) GlobalError(newLineInfo(filename, line, col), k, arg)
proc parseRst(text, filename: string, proc parseRst(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode = rstOptions: TRstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
options.FindFile, compilerMsgHandler) options.FindFile, compilerMsgHandler)
proc newDocumentor*(filename: string, config: PStringTable): PDoc = proc newDocumentor*(filename: string, config: PStringTable): PDoc =
new(result) new(result)
initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex), initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective}, options.gConfigVars, filename, {roSupportRawDirective},
options.FindFile, compilerMsgHandler) options.FindFile, compilerMsgHandler)
result.id = 100 result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) =
if gCmd != cmdRst2Tex: appf(dest, xml, args) if gCmd != cmdRst2Tex: appf(dest, xml, args)
else: appf(dest, tex, args) else: appf(dest, tex, args)
proc getVarIdx(varnames: openarray[string], id: string): int = proc getVarIdx(varnames: openarray[string], id: string): int =
for i in countup(0, high(varnames)): for i in countup(0, high(varnames)):
if cmpIgnoreStyle(varnames[i], id) == 0: if cmpIgnoreStyle(varnames[i], id) == 0:
return i return i
result = -1 result = -1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string],
varvalues: openarray[PRope]): PRope = varvalues: openarray[PRope]): PRope =
var i = 0 var i = 0
var L = len(frmt) var L = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i < L: while i < L:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '#': of '#':
app(result, varvalues[num]) app(result, varvalues[num])
inc(num) inc(num)
inc(i) inc(i)
of '$': of '$':
app(result, "$") app(result, "$")
inc(i) inc(i)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = (j * 10) + Ord(frmt[i]) - ord('0') j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i) inc(i)
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j num = j
app(result, varvalues[j - 1]) app(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = "" var id = ""
while true: while true:
add(id, frmt[i]) add(id, frmt[i])
inc(i) inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id) else: rawMessage(errUnkownSubstitionVar, id)
of '{': of '{':
var id = "" var id = ""
inc(i) inc(i)
while frmt[i] != '}': while frmt[i] != '}':
if frmt[i] == '\0': rawMessage(errTokenExpected, "}") if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
add(id, frmt[i]) add(id, frmt[i])
inc(i) inc(i)
@ -124,17 +124,17 @@ proc genComment(d: PDoc, n: PNode): string =
var dummyHasToc: bool var dummyHasToc: bool
if n.comment != nil and startsWith(n.comment, "##"): if n.comment != nil and startsWith(n.comment, "##"):
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
toLineNumber(n.info), toColumn(n.info), toLineNumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}), result) dummyHasToc, d.options + {roSkipPounds}), result)
proc genRecComment(d: PDoc, n: PNode): PRope = proc genRecComment(d: PDoc, n: PNode): PRope =
if n == nil: return nil if n == nil: return nil
result = genComment(d, n).toRope result = genComment(d, n).toRope
if result == nil: if result == nil:
if n.kind notin {nkEmpty..nkNilLit}: if n.kind notin {nkEmpty..nkNilLit}:
for i in countup(0, len(n)-1): for i in countup(0, len(n)-1):
result = genRecComment(d, n.sons[i]) result = genRecComment(d, n.sons[i])
if result != nil: return if result != nil: return
else: else:
n.comment = nil n.comment = nil
@ -158,10 +158,10 @@ proc extractDocComment*(s: PSym, d: PDoc = nil): string =
else: else:
result = n.comment.substr(2).replace("\n##", "\n").strip result = n.comment.substr(2).replace("\n##", "\n").strip
proc isVisible(n: PNode): bool = proc isVisible(n: PNode): bool =
result = false result = false
if n.kind == nkPostfix: if n.kind == nkPostfix:
if n.len == 2 and n.sons[0].kind == nkIdent: if n.len == 2 and n.sons[0].kind == nkIdent:
var v = n.sons[0].ident var v = n.sons[0].ident
result = v.id == ord(wStar) or v.id == ord(wMinus) result = v.id == ord(wStar) or v.id == ord(wMinus)
elif n.kind == nkSym: elif n.kind == nkSym:
@ -171,36 +171,36 @@ proc isVisible(n: PNode): bool =
result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported} result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
elif n.kind == nkPragmaExpr: elif n.kind == nkPragmaExpr:
result = isVisible(n.sons[0]) result = isVisible(n.sons[0])
proc getName(d: PDoc, n: PNode, splitAfter = -1): string = proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
case n.kind case n.kind
of nkPostfix: result = getName(d, n.sons[1], splitAfter) of nkPostfix: result = getName(d, n.sons[1], splitAfter)
of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter) of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter)
of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter) of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
of nkIdent: result = esc(d.target, n.ident.s, splitAfter) of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
of nkAccQuoted: of nkAccQuoted:
result = esc(d.target, "`") result = esc(d.target, "`")
for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter)) for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter))
result.add esc(d.target, "`") result.add esc(d.target, "`")
else: else:
internalError(n.info, "getName()") internalError(n.info, "getName()")
result = "" result = ""
proc getRstName(n: PNode): PRstNode = proc getRstName(n: PNode): PRstNode =
case n.kind case n.kind
of nkPostfix: result = getRstName(n.sons[1]) of nkPostfix: result = getRstName(n.sons[1])
of nkPragmaExpr: result = getRstName(n.sons[0]) of nkPragmaExpr: result = getRstName(n.sons[0])
of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName) of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName)
of nkIdent: result = newRstNode(rnLeaf, n.ident.s) of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
of nkAccQuoted: of nkAccQuoted:
result = getRstName(n.sons[0]) result = getRstName(n.sons[0])
for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text) for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text)
else: else:
internalError(n.info, "getRstName()") internalError(n.info, "getRstName()")
result = nil result = nil
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return if not isVisible(nameNode): return
var name = toRope(getName(d, nameNode)) var name = toRope(getName(d, nameNode))
var result: PRope = nil var result: PRope = nil
var literal = "" var literal = ""
@ -208,73 +208,89 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var comm = genRecComment(d, n) # call this here for the side-effect! var comm = genRecComment(d, n) # call this here for the side-effect!
var r: TSrcGen var r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
while true: while true:
getNextTok(r, kind, literal) getNextTok(r, kind, literal)
case kind case kind
of tkEof: of tkEof:
break break
of tkComment: of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh: of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)]) [toRope(literal)])
of tkOpr: of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit: of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>", dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) "\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
of tkCharLit: of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit: of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>", dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit: of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>", dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
of tkSymbol: of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>", dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
app(result, literal) app(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon, tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
inc(d.id) inc(d.id)
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID"], ["name", "header", "desc", "itemID"],
[name, result, comm, toRope(d.id)])) [name, result, comm, toRope(d.id)]))
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID"], [ ["name", "header", "desc", "itemID"], [
toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)])) toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
setIndexTerm(d[], $d.id, getName(d, nameNode)) setIndexTerm(d[], $d.id, getName(d, nameNode))
proc checkForFalse(n: PNode): bool = proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode =
if not isVisible(nameNode): return
var
name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr()
r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k) }
if comm != nil and comm != "":
result["description"] = %comm
if r.buf != nil:
result["code"] = %r.buf
proc checkForFalse(n: PNode): bool =
result = n.kind == nkIdent and IdentEq(n.ident, "false") result = n.kind == nkIdent and IdentEq(n.ident, "false")
proc traceDeps(d: PDoc, n: PNode) = proc traceDeps(d: PDoc, n: PNode) =
const k = skModule const k = skModule
if d.section[k] != nil: app(d.section[k], ", ") if d.section[k] != nil: app(d.section[k], ", ")
dispA(d.section[k], dispA(d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>", "<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [toRope(getModuleName(n))]) "$1", [toRope(getModuleName(n))])
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n)) of nkCommentStmt: app(d.modDesc, genComment(d, n))
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc) genItem(d, n, n.sons[namePos], skProc)
of nkMethodDef: of nkMethodDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skMethod) genItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef: of nkIteratorDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skIterator) genItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
@ -284,27 +300,69 @@ proc generateDoc*(d: PDoc, n: PNode) =
genItem(d, n, n.sons[namePos], skConverter) genItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt: if n.sons[i].kind != nkCommentStmt:
# order is always 'type var let const': # order is always 'type var let const':
genItem(d, n.sons[i], n.sons[i].sons[0], genItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection))) succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
of nkWhenStmt: of nkWhenStmt:
# generate documentation for the first branch only: # generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]): if not checkForFalse(n.sons[0].sons[0]):
generateDoc(d, lastSon(n.sons[0])) generateDoc(d, lastSon(n.sons[0]))
of nkImportStmt: of nkImportStmt:
for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
else: nil else: nil
proc genSection(d: PDoc, kind: TSymKind) = proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode =
case n.kind
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
result = %{ "comment": %stripped }
of nkProcDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skProc)
of nkMethodDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef:
result = genJSONItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef:
result = genJSONItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
# order is always 'type var let const':
result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList:
var elem = jArray
if elem == nil: elem = newJArray()
for i in countup(0, sonsLen(n) - 1):
var r = generateJson(d, n.sons[i], elem)
if r != nil:
elem.add(r)
if result == nil: result = elem
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
discard generateJson(d, lastSon(n.sons[0]), jArray)
else: nil
proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [ const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
"Iterators", "Converters", "Macros", "Templates" "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return
var title = sectionNames[kind].toRope var title = sectionNames[kind].toRope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
@ -313,7 +371,7 @@ proc genSection(d: PDoc, kind: TSymKind) =
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]]) ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]])
proc genOutFile(d: PDoc): PRope = proc genOutFile(d: PDoc): PRope =
var var
code, content: PRope code, content: PRope
title = "" title = ""
@ -321,7 +379,7 @@ proc genOutFile(d: PDoc): PRope =
var tmp = "" var tmp = ""
renderTocEntries(d[], j, 1, tmp) renderTocEntries(d[], j, 1, tmp)
var toc = tmp.toRope var toc = tmp.toRope
for i in countup(low(TSymKind), high(TSymKind)): for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i) genSection(d, i)
app(toc, d.toc[i]) app(toc, d.toc[i])
if toc != nil: if toc != nil:
@ -329,30 +387,30 @@ proc genOutFile(d: PDoc): PRope =
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] if d.meta[metaTitle].len != 0: title = d.meta[metaTitle]
else: title = "Module " & extractFilename(changeFileExt(d.filename, "")) else: title = "Module " & extractFilename(changeFileExt(d.filename, ""))
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], "tableofcontents", "moduledesc", "date", "time", "content"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), code]) toRope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!? # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope, toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
d.meta[metaVersion].toRope]) d.meta[metaVersion].toRope])
else: else:
code = content code = content
result = code result = code
proc generateIndex*(d: PDoc) = proc generateIndex*(d: PDoc) =
if optGenIndex in gGlobalOptions: if optGenIndex in gGlobalOptions:
writeIndexFile(d[], splitFile(options.outFile).dir / writeIndexFile(d[], splitFile(options.outFile).dir /
splitFile(d.filename).name & indexExt) splitFile(d.filename).name & indexExt)
proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
var content = genOutFile(d) var content = genOutFile(d)
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
writeRope(stdout, content) writeRope(stdout, content)
@ -361,7 +419,7 @@ proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
proc CommandDoc*() = proc CommandDoc*() =
var ast = parseFile(gProjectMainIdx) var ast = parseFile(gProjectMainIdx)
if ast == nil: return if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
generateDoc(d, ast) generateDoc(d, ast)
@ -388,12 +446,26 @@ proc CommandRst2TeX*() =
splitter = "\\-" splitter = "\\-"
CommandRstAux(gProjectFull, TexExt) CommandRstAux(gProjectFull, TexExt)
proc CommandJSON*() =
var ast = parseFile(gProjectMainIdx)
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
var json = generateJson(d, ast)
var content = newRope(pretty(json))
if optStdout in gGlobalOptions:
writeRope(stdout, content)
else:
echo getOutFile(gProjectFull, JsonExt)
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc CommandBuildIndex*() = proc CommandBuildIndex*() =
var content = mergeIndexes(gProjectFull).toRope var content = mergeIndexes(gProjectFull).toRope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version"],
["Index".toRope, nil, nil, toRope(getDateStr()), ["Index".toRope, nil, nil, toRope(getDateStr()),
toRope(getClockStr()), content, nil, nil]) toRope(getClockStr()), content, nil, nil])
writeRope(code, getOutFile("theindex", HtmlExt)) writeRope(code, getOutFile("theindex", HtmlExt))

View file

@ -463,9 +463,9 @@ proc getCompileOptions: string =
proc getLinkOptions: string = proc getLinkOptions: string =
result = linkOptions result = linkOptions
for linkedLib in items(cLinkedLibs): for linkedLib in items(cLinkedLibs):
result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteIfContainsWhite) result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteShell)
for libDir in items(cLibs): for libDir in items(cLibs):
result.add([cc[ccompiler].linkDirCmd, libDir.quoteIfContainsWhite]) result.add([cc[ccompiler].linkDirCmd, libDir.quoteShell])
proc needsExeExt(): bool {.inline.} = proc needsExeExt(): bool {.inline.} =
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
@ -485,10 +485,10 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
var includeCmd, compilePattern: string var includeCmd, compilePattern: string
if not noAbsolutePaths(): if not noAbsolutePaths():
# compute include paths: # compute include paths:
includeCmd = cc[c].includeCmd & quoteIfContainsWhite(libpath) includeCmd = cc[c].includeCmd & quoteShell(libpath)
for includeDir in items(cIncludes): for includeDir in items(cIncludes):
includeCmd.add([cc[c].includeCmd, includeDir.quoteIfContainsWhite]) includeCmd.add([cc[c].includeCmd, includeDir.quoteShell])
compilePattern = JoinPath(ccompilerpath, exe) compilePattern = JoinPath(ccompilerpath, exe)
else: else:
@ -501,17 +501,17 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
toObjFile(cfile) toObjFile(cfile)
else: else:
completeCFilePath(toObjFile(cfile)) completeCFilePath(toObjFile(cfile))
cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt)) cfile = quoteShell(AddFileExt(cfile, cExt))
objfile = quoteIfContainsWhite(objfile) objfile = quoteShell(objfile)
result = quoteIfContainsWhite(compilePattern % [ result = quoteShell(compilePattern % [
"file", cfile, "objfile", objfile, "options", options, "file", cfile, "objfile", objfile, "options", options,
"include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath]) "include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath])
add(result, ' ') add(result, ' ')
addf(result, cc[c].compileTmpl, [ addf(result, cc[c].compileTmpl, [
"file", cfile, "objfile", objfile, "file", cfile, "objfile", objfile,
"options", options, "include", includeCmd, "options", options, "include", includeCmd,
"nimrod", quoteIfContainsWhite(getPrefixDir()), "nimrod", quoteShell(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]) "lib", quoteShell(libpath)])
proc footprint(filename: string): TCrc32 = proc footprint(filename: string): TCrc32 =
result = crcFromFile(filename) >< result = crcFromFile(filename) ><
@ -590,7 +590,7 @@ proc CallCCompiler*(projectfile: string) =
while it != nil: while it != nil:
let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
add(objfiles, ' ') add(objfiles, ' ')
add(objfiles, quoteIfContainsWhite( add(objfiles, quoteShell(
addFileExt(objFile, cc[ccompiler].objExt))) addFileExt(objFile, cc[ccompiler].objExt)))
it = PStrEntry(it.next) it = PStrEntry(it.next)
@ -602,8 +602,8 @@ proc CallCCompiler*(projectfile: string) =
var linkerExe = getConfigVar(c, ".linkerexe") var linkerExe = getConfigVar(c, ".linkerexe")
if len(linkerExe) == 0: linkerExe = cc[c].linkerExe if len(linkerExe) == 0: linkerExe = cc[c].linkerExe
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(): linkCmd = quoteIfContainsWhite(linkerExe) if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe)) else: linkCmd = quoteShell(JoinPath(ccompilerpath, linkerExe))
if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui
else: buildGui = "" else: buildGui = ""
var exefile: string var exefile: string
@ -616,18 +616,19 @@ proc CallCCompiler*(projectfile: string) =
if options.outFile.len > 0: if options.outFile.len > 0:
exefile = options.outFile exefile = options.outFile
if not noAbsolutePaths(): if not noAbsolutePaths():
exefile = joinPath(splitFile(projectFile).dir, exefile) if not exeFile.isAbsolute():
exefile = quoteIfContainsWhite(exefile) exefile = joinPath(splitFile(projectFile).dir, exefile)
exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() let linkOptions = getLinkOptions()
linkCmd = quoteIfContainsWhite(linkCmd % ["builddll", builddll, linkCmd = quoteShell(linkCmd % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) "exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
linkCmd.add ' ' linkCmd.add ' '
addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll, addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile, "objfiles", objfiles, "exefile", exefile,
"nimrod", quoteIfContainsWhite(getPrefixDir()), "nimrod", quoteShell(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]) "lib", quoteShell(libpath)])
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
else: else:
linkCmd = "" linkCmd = ""

View file

@ -9,8 +9,8 @@
# implements the command dispatcher and several commands # implements the command dispatcher and several commands
import import
llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
os, condsyms, rodread, rodwrite, times, os, condsyms, rodread, rodwrite, times,
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
@ -98,7 +98,7 @@ proc CommandCompileToC =
# rodread.rodcompilerProcs # rodread.rodcompilerProcs
# rodread.gTypeTable # rodread.gTypeTable
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed? # semthreads.computed?
# #
@ -166,7 +166,7 @@ proc commandEval(exp: string) =
proc CommandPrettyOld = proc CommandPrettyOld =
var projectFile = addFileExt(mainCommandArg(), NimExt) var projectFile = addFileExt(mainCommandArg(), NimExt)
var module = parseFile(projectFile.fileInfoIdx) var module = parseFile(projectFile.fileInfoIdx)
if module != nil: if module != nil:
renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt)) renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))
proc CommandPretty = proc CommandPretty =
@ -175,24 +175,24 @@ proc CommandPretty =
registerPass(prettyPass) registerPass(prettyPass)
compileProject() compileProject()
pretty.overwriteFiles() pretty.overwriteFiles()
proc CommandScan = proc CommandScan =
var f = addFileExt(mainCommandArg(), nimExt) var f = addFileExt(mainCommandArg(), nimExt)
var stream = LLStreamOpen(f, fmRead) var stream = LLStreamOpen(f, fmRead)
if stream != nil: if stream != nil:
var var
L: TLexer L: TLexer
tok: TToken tok: TToken
initToken(tok) initToken(tok)
openLexer(L, f, stream) openLexer(L, f, stream)
while true: while true:
rawGetTok(L, tok) rawGetTok(L, tok)
PrintTok(tok) PrintTok(tok)
if tok.tokType == tkEof: break if tok.tokType == tkEof: break
CloseLexer(L) CloseLexer(L)
else: else:
rawMessage(errCannotOpenFile, f) rawMessage(errCannotOpenFile, f)
proc CommandSuggest = proc CommandSuggest =
if isServing: if isServing:
# XXX: hacky work-around ahead # XXX: hacky work-around ahead
@ -246,7 +246,7 @@ proc resetMemory =
for i in low(buckets)..high(buckets): for i in low(buckets)..high(buckets):
buckets[i] = nil buckets[i] = nil
idAnon = nil idAnon = nil
# XXX: clean these global vars # XXX: clean these global vars
# ccgstmts.gBreakpoints # ccgstmts.gBreakpoints
# ccgthreadvars.nimtv # ccgthreadvars.nimtv
@ -262,7 +262,7 @@ proc resetMemory =
# rodread.rodcompilerProcs # rodread.rodcompilerProcs
# rodread.gTypeTable # rodread.gTypeTable
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed? # semthreads.computed?
# #
@ -289,7 +289,7 @@ const
proc MainCommand* = proc MainCommand* =
when SimiluateCaasMemReset: when SimiluateCaasMemReset:
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
# In "nimrod serve" scenario, each command must reset the registered passes # In "nimrod serve" scenario, each command must reset the registered passes
clearPasses() clearPasses()
gLastCmdTime = epochTime() gLastCmdTime = epochTime()
@ -301,7 +301,7 @@ proc MainCommand* =
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule
passes.gImportModule = importModule passes.gImportModule = importModule
case command.normalize case command.normalize
of "c", "cc", "compile", "compiletoc": of "c", "cc", "compile", "compiletoc":
# compile means compileToC currently # compile means compileToC currently
gCmd = cmdCompileToC gCmd = cmdCompileToC
wantMainModule() wantMainModule()
@ -325,13 +325,13 @@ proc MainCommand* =
when hasTinyCBackend: when hasTinyCBackend:
extccomp.setCC("tcc") extccomp.setCC("tcc")
CommandCompileToC() CommandCompileToC()
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
of "js", "compiletojs": of "js", "compiletojs":
gCmd = cmdCompileToJS gCmd = cmdCompileToJS
wantMainModule() wantMainModule()
CommandCompileToJS() CommandCompileToJS()
of "compiletollvm": of "compiletollvm":
gCmd = cmdCompileToLLVM gCmd = cmdCompileToLLVM
wantMainModule() wantMainModule()
when has_LLVM_Backend: when has_LLVM_Backend:
@ -353,21 +353,27 @@ proc MainCommand* =
wantMainModule() wantMainModule()
DefineSymbol("nimdoc") DefineSymbol("nimdoc")
CommandDoc2() CommandDoc2()
of "rst2html": of "rst2html":
gCmd = cmdRst2html gCmd = cmdRst2html
LoadConfigs(DocConfig) LoadConfigs(DocConfig)
wantMainModule() wantMainModule()
CommandRst2Html() CommandRst2Html()
of "rst2tex": of "rst2tex":
gCmd = cmdRst2tex gCmd = cmdRst2tex
LoadConfigs(DocTexConfig) LoadConfigs(DocTexConfig)
wantMainModule() wantMainModule()
CommandRst2TeX() CommandRst2TeX()
of "jsondoc":
gCmd = cmdDoc
LoadConfigs(DocConfig)
wantMainModule()
DefineSymbol("nimdoc")
CommandJSON()
of "buildindex": of "buildindex":
gCmd = cmdDoc gCmd = cmdDoc
LoadConfigs(DocConfig) LoadConfigs(DocConfig)
CommandBuildIndex() CommandBuildIndex()
of "gendepend": of "gendepend":
gCmd = cmdGenDepend gCmd = cmdGenDepend
wantMainModule() wantMainModule()
CommandGenDepend() CommandGenDepend()
@ -400,16 +406,16 @@ proc MainCommand* =
gCmd = cmdCheck gCmd = cmdCheck
wantMainModule() wantMainModule()
CommandCheck() CommandCheck()
of "parse": of "parse":
gCmd = cmdParse gCmd = cmdParse
wantMainModule() wantMainModule()
discard parseFile(gProjectMainIdx) discard parseFile(gProjectMainIdx)
of "scan": of "scan":
gCmd = cmdScan gCmd = cmdScan
wantMainModule() wantMainModule()
CommandScan() CommandScan()
MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!") MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
of "i": of "i":
gCmd = cmdInteractive gCmd = cmdInteractive
CommandInteractive() CommandInteractive()
of "e": of "e":
@ -427,11 +433,11 @@ proc MainCommand* =
of "serve": of "serve":
isServing = true isServing = true
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
msgs.gErrorMax = high(int) # do not stop after first error msgs.gErrorMax = high(int) # do not stop after first error
serve(MainCommand) serve(MainCommand)
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
if (msgs.gErrorCounter == 0 and if (msgs.gErrorCounter == 0 and
gCmd notin {cmdInterpret, cmdRun, cmdDump} and gCmd notin {cmdInterpret, cmdRun, cmdDump} and
gVerbosity > 0): gVerbosity > 0):

View file

@ -13,9 +13,9 @@ when defined(gcc) and defined(windows):
else: else:
{.link: "icons/nimrod_icon.o".} {.link: "icons/nimrod_icon.o".}
import import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, platform, main, parseopt, service extccomp, strutils, os, osproc, platform, main, parseopt, service
when hasTinyCBackend: when hasTinyCBackend:
import tccgen import tccgen
@ -23,7 +23,7 @@ when hasTinyCBackend:
when defined(profiler) or defined(memProfiler): when defined(profiler) or defined(memProfiler):
{.hint: "Profiling support is turned on!".} {.hint: "Profiling support is turned on!".}
import nimprof import nimprof
proc prependCurDir(f: string): string = proc prependCurDir(f: string): string =
when defined(unix): when defined(unix):
if os.isAbsolute(f): result = f if os.isAbsolute(f): result = f
@ -61,12 +61,18 @@ proc HandleCmdLine() =
tccgen.run() tccgen.run()
if optRun in gGlobalOptions: if optRun in gGlobalOptions:
if gCmd == cmdCompileToJS: if gCmd == cmdCompileToJS:
var ex = quoteIfContainsWhite( var ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram("node " & ex & ' ' & service.arguments) execExternalProgram("node " & ex & ' ' & service.arguments)
else: else:
var ex = quoteIfContainsWhite( var binPath: string
changeFileExt(gProjectFull, exeExt).prependCurDir) if options.outFile.len > 0:
# If the user specified an outFile path, use that directly.
binPath = options.outFile.prependCurDir
else:
# Figure out ourselves a valid binary name.
binPath = changeFileExt(gProjectFull, exeExt).prependCurDir
var ex = quoteShell(binPath)
execExternalProgram(ex & ' ' & service.arguments) execExternalProgram(ex & ' ' & service.arguments)
when defined(GC_setMaxPause): when defined(GC_setMaxPause):

View file

@ -132,6 +132,7 @@ const
NimExt* = "nim" NimExt* = "nim"
RodExt* = "rod" RodExt* = "rod"
HtmlExt* = "html" HtmlExt* = "html"
JsonExt* = "json"
TexExt* = "tex" TexExt* = "tex"
IniExt* = "ini" IniExt* = "ini"
DefaultConfig* = "nimrod.cfg" DefaultConfig* = "nimrod.cfg"

View file

@ -43,7 +43,8 @@ const
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop, wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated, wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto} wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto,
wInjectStmt}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame, wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
@ -722,6 +723,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wOperator: of wOperator:
if sym == nil: invalidPragma(it) if sym == nil: invalidPragma(it)
else: sym.position = expectIntLit(c, it) else: sym.position = expectIntLit(c, it)
of wInjectStmt:
if it.kind != nkExprColonExpr:
localError(it.info, errExprExpected)
else:
it.sons[1] = c.semExpr(c, it.sons[1])
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else: invalidPragma(it)
else: processNote(c, it) else: processNote(c, it)

View file

@ -380,7 +380,8 @@ proc analyseThreadProc*(prc: PSym) =
var formals = skipTypes(prc.typ, abstractInst).n var formals = skipTypes(prc.typ, abstractInst).n
for i in 1 .. formals.len-1: for i in 1 .. formals.len-1:
var formal = formals.sons[i].sym var formal = formals.sons[i].sym
c.mapping[formal.id] = toTheirs # thread receives foreign data! # the input is copied and belongs to the thread:
c.mapping[formal.id] = toMine
discard analyse(c, prc.getBody) discard analyse(c, prc.getBody)
proc needsGlobalAnalysis*: bool = proc needsGlobalAnalysis*: bool =

View file

@ -60,7 +60,7 @@ type
wPassc, wPassl, wBorrow, wDiscardable, wPassc, wPassl, wBorrow, wDiscardable,
wFieldChecks, wFieldChecks,
wWatchPoint, wSubsChar, wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame, wNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wImplicitStatic, wGlobal, wCodegenDecl,
@ -142,7 +142,8 @@ const
"compiletime", "noinit", "compiletime", "noinit",
"passc", "passl", "borrow", "discardable", "fieldchecks", "passc", "passl", "borrow", "discardable", "fieldchecks",
"watchpoint", "watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"nostackframe", "implicitstatic", "global", "codegendecl", "nostackframe", "implicitstatic", "global", "codegendecl",

View file

@ -8,6 +8,11 @@
.. contents:: .. contents::
.. raw:: html
<blockquote><p>
"yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
</p></blockquote>
Nimrod differs from many other compilers in that it is really fast, Nimrod differs from many other compilers in that it is really fast,
and being so fast makes it suited to provide external queries for and being so fast makes it suited to provide external queries for
text editors about the source code being written. Through the text editors about the source code being written. Through the
@ -522,21 +527,6 @@ At the moment idetools support is still in development so the test
suite is not integrated with the main test suite and you have to suite is not integrated with the main test suite and you have to
run it manually. First you have to compile the tester:: run it manually. First you have to compile the tester::
$ cd my/nimrod/checkout
$ nimrod c tests/tester.nim
Running the tester without parameters will display some options.
To run the caas test suite (and other special tests) you need to
use the `special` command. You need to run this command from the
root of the checkout or it won't be able to open the required files::
$ ./tests/tester special
However this is a roundabout way of running the test suite. You can
also compile and run ``tests/caasdriver.nim`` manually. In fact,
running it manually will allow you to specify special parameters
too. Example::
$ cd my/nimrod/checkout/tests $ cd my/nimrod/checkout/tests
$ nimrod c caasdriver.nim $ nimrod c caasdriver.nim

View file

@ -4708,7 +4708,11 @@ fatal pragma
------------ ------------
The `fatal`:idx: pragma is used to make the compiler output an error message The `fatal`:idx: pragma is used to make the compiler output an error message
with the given content. In contrast to the ``error`` pragma, compilation with the given content. In contrast to the ``error`` pragma, compilation
is guaranteed to be aborted by this pragma. is guaranteed to be aborted by this pragma. Example:
.. code-block:: nimrod
when not defined(objc):
{.fatal: "Compile this program with the objc command!".}
warning pragma warning pragma
-------------- --------------
@ -5053,6 +5057,18 @@ Note that this pragma is somewhat of a misnomer: Other backends will provide
the same feature under the same name. the same feature under the same name.
Extern pragma
-------------
Like ``exportc`` or ``importc`` the `extern`:idx: pragma affects name
mangling. The string literal passed to ``extern`` can be a format string:
.. code-block:: Nimrod
proc p(s: string) {.extern: "prefix$1".} =
echo s
In the example the external name of ``p`` is set to ``prefixp``.
Bycopy pragma Bycopy pragma
------------- -------------

View file

@ -468,6 +468,19 @@ proc is declared in the generated code:
proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} =
echo "realistic interrupt handler" echo "realistic interrupt handler"
InjectStmt pragma
-----------------
The `injectStmt`:idx: pragma can be used to inject a statement before every
other statement in the current module. It is only supposed to be used for
debugging:
.. code-block:: nimrod
{.injectStmt: gcInvariants().}
# ... complex code here that produces crashes ...
LineDir option LineDir option

View file

@ -0,0 +1,11 @@
#!/usr/bin/env python
import BaseHTTPServer
import CGIHTTPServer
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ('localhost', 8008)
handler.cgi_directories = ['/']
httpd = server(server_address, handler)
httpd.serve_forever()

View file

@ -0,0 +1,5 @@
import cgi
cgi.setStackTraceStdout()
var a: string = nil
a.add "foobar"

7
examples/cgi/example.nim Normal file
View file

@ -0,0 +1,7 @@
import cgi
write(stdout, "Content-type: text/html\n\n")
write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
write(stdout, "<html><head><title>Test</title></head><body>\n")
write(stdout, "Hello!")
writeln(stdout, "</body></html>")

View file

@ -274,6 +274,8 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## ##
## Example: ## Example:
## ##
## .. code-block:: nimrod
##
## macro check(ex: expr): stmt = ## macro check(ex: expr): stmt =
## # this is a simplified version of the check macro from the ## # this is a simplified version of the check macro from the
## # unittest module. ## # unittest module.
@ -296,6 +298,8 @@ template emit*(e: expr[string]): stmt =
## that should be inserted verbatim in the program ## that should be inserted verbatim in the program
## Example: ## Example:
## ##
## .. code-block:: nimrod
##
## emit("echo " & '"' & "hello world".toUpper & '"') ## emit("echo " & '"' & "hello world".toUpper & '"')
## ##
eval: result = e.parseStmt eval: result = e.parseStmt
@ -480,6 +484,34 @@ proc newDotExpr*(a, b: PNimrodNode): PNimrodNode {.compileTime.} =
proc newIdentDefs*(name, kind: PNimrodNode; proc newIdentDefs*(name, kind: PNimrodNode;
default = newEmptyNode()): PNimrodNode {.compileTime.} = default = newEmptyNode()): PNimrodNode {.compileTime.} =
## Creates a new ``nnkIdentDefs`` node of a specific kind and value.
##
## ``nnkIdentDefs`` need to have at least three children, but they can have
## more: first comes a list of identifiers followed by a type and value
## nodes. This helper proc creates a three node subtree, the first subnode
## being a single identifier name. Both the ``kind`` node and ``default``
## (value) nodes may be empty depending on where the ``nnkIdentDefs``
## appears: tuple or object definitions will have an empty ``default`` node,
## ``let`` or ``var`` blocks may have an empty ``kind`` node if the
## identifier is being assigned a value. Example:
##
## .. code-block:: nimrod
##
## var varSection = newNimNode(nnkVarSection).add(
## newIdentDefs(ident("a"), ident("string")),
## newIdentDefs(ident("b"), newEmptyNode(), newLit(3)))
## # --> var
## # a: string
## # b = 3
##
## If you need to create multiple identifiers you need to use the lower level
## ``newNimNode``:
##
## .. code-block:: nimrod
##
## result = newNimNode(nnkIdentDefs).add(
## ident("a"), ident("b"), ident("c"), ident("string"),
## newStrLitNode("Hello"))
newNimNode(nnkIdentDefs).add(name, kind, default) newNimNode(nnkIdentDefs).add(name, kind, default)
proc newNilLit*(): PNimrodNode {.compileTime.} = proc newNilLit*(): PNimrodNode {.compileTime.} =
@ -503,11 +535,11 @@ from strutils import cmpIgnoreStyle, format
proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} = proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind) assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind)
proc newProc*(name = newEmptyNode(); params: openarray[PNimrodNode] = []; proc newProc*(name = newEmptyNode(); params: openarray[PNimrodNode] = [newEmptyNode()];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} = body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
## shortcut for creating a new proc ## shortcut for creating a new proc
## ##
## The ``params`` array should start with the return type of the proc, ## The ``params`` array must start with the return type of the proc,
## followed by a list of IdentDefs which specify the params. ## followed by a list of IdentDefs which specify the params.
assert procType in RoutineNodes assert procType in RoutineNodes
result = newNimNode(procType).add( result = newNimNode(procType).add(

View file

@ -7,7 +7,18 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements a generator of HTML/Latex from `reStructuredText`:idx:. ## This module implements a generator of HTML/Latex from
## `reStructuredText`:idx: (see http://docutils.sourceforge.net/rst.html for
## information on this markup syntax). You can generate HTML output through the
## convenience proc ``rstToHtml``, which provided an input string with rst
## markup returns a string with the generated HTML. The final output is meant
## to be embedded inside a full document you provide yourself, so it won't
## contain the usual ``<header>`` or ``<body>`` parts.
##
## You can also create a ``TRstGenerator`` structure and populate it with the
## other lower level methods to finally build complete documents. This requires
## many options and tweaking, but you are not limited to snippets and can
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
import strutils, os, hashes, strtabs, rstast, rst, highlite import strutils, os, hashes, strtabs, rstast, rst, highlite
@ -40,13 +51,51 @@ type
filename*: string filename*: string
meta*: array[TMetaEnum, string] meta*: array[TMetaEnum, string]
PDoc = var TRstGenerator PDoc = var TRstGenerator ## Alias to type less.
proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget, proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
config: PStringTable, filename: string, config: PStringTable, filename: string,
options: TRstParseOptions, options: TRstParseOptions,
findFile: TFindFileHandler, findFile: TFindFileHandler,
msgHandler: TMsgHandler) = msgHandler: TMsgHandler) =
## Initializes a ``TRstGenerator``.
##
## You need to call this before using a ``TRstGenerator`` with any other
## procs in this module. Pass a non ``nil`` ``PStringTable`` value as
## ``config`` with parameters used by the HTML output generator. If you
## don't know what to use, pass the results of the ``defaultConfig()`` proc.
## The ``filename`` is symbolic and used only for error reporting, you can
## pass any non ``nil`` string here.
##
## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types
## are defined in the the `packages/docutils/rst module <rst.html>`_.
## ``options`` selects the behaviour of the rst parser.
##
## ``findFile`` is a proc used by the rst ``include`` directive among others.
## The purpose of this proc is to mangle or filter paths. It receives paths
## specified in the rst document and has to return a valid path to existing
## files or the empty string otherwise. If you pass ``nil``, a default proc
## will be used which given a path returns the input path only if the file
## exists. One use for this proc is to transform relative paths found in the
## document to absolute path, useful if the rst file and the resources it
## references are not in the same directory as the current working directory.
##
## The ``msgHandler`` is a proc used for user error reporting. It will be
## called with the filename, line, col, and type of any error found during
## parsing. If you pass ``nil``, a default message handler will be used which
## writes the messages to the standard output.
##
## Example:
##
## .. code-block:: nimrod
##
## import packages/docutils/rstgen
##
## var gen: TRstGenerator
##
## gen.initRstGenerator(outHtml, defaultConfig(),
## "filename", {}, nil, nil)
g.config = config g.config = config
g.target = target g.target = target
g.tocPart = @[] g.tocPart = @[]
@ -147,7 +196,19 @@ proc dispA(target: TOutputTarget, dest: var string,
if target != outLatex: addf(dest, xml, args) if target != outLatex: addf(dest, xml, args)
else: addf(dest, tex, args) else: addf(dest, tex, args)
proc renderRstToOut*(d: PDoc, n: PRstNode, result: var string) proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string)
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
##
## Before using this proc you need to initialise a ``TRstGenerator`` with
## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
## `packages/docutils/rst module <rst.html>`_. Example:
##
## .. code-block:: nimrod
##
## # ...configure gen and rst vars...
## var generatedHTML = ""
## renderRstToOut(gen, rst, generatedHTML)
## echo generatedHTML
proc renderAux(d: PDoc, n: PRstNode, result: var string) = proc renderAux(d: PDoc, n: PRstNode, result: var string) =
for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result) for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
@ -162,7 +223,7 @@ proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) =
# ---------------- index handling -------------------------------------------- # ---------------- index handling --------------------------------------------
proc setIndexTerm*(d: PDoc, id, term: string) = proc setIndexTerm*(d: var TRstGenerator, id, term: string) =
d.theIndex.add(term) d.theIndex.add(term)
d.theIndex.add('\t') d.theIndex.add('\t')
let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt) let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt)
@ -295,7 +356,7 @@ proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n", "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
proc renderTocEntries*(d: PDoc, j: var int, lvl: int, result: var string) = proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int, result: var string) =
var tmp = "" var tmp = ""
while j <= high(d.tocPart): while j <= high(d.tocPart):
var a = abs(d.tocPart[j].n.level) var a = abs(d.tocPart[j].n.level)
@ -678,8 +739,26 @@ $content
proc rstToHtml*(s: string, options: TRstParseOptions, proc rstToHtml*(s: string, options: TRstParseOptions,
config: PStringTable): string = config: PStringTable): string =
## exported for *nimforum*. ## Converts an input rst string into embeddable HTML.
##
## This convenience proc parses any input string using rst markup (it doesn't
## have to be a full document!) and returns an embeddable piece of HTML. The
## proc is meant to be used in *online* environments without access to a
## meaningful filesystem, and therefore rst ``include`` like directives won't
## work. For an explanation of the ``config`` parameter see the
## ``initRstGenerator`` proc. Example:
##
## .. code-block:: nimrod
## import packages/docutils/rstgen, strtabs
##
## echo rstToHtml("*Hello* **world**!", {},
## newStringTable(modeStyleInsensitive))
## # --> <em>Hello</em> <strong>world</strong>!
##
## If you need to allow the rst ``include`` directive or tweak the generated
## output you have to create your own ``TRstGenerator`` with
## ``initRstGenerator`` and related procs.
proc myFindFile(filename: string): string = proc myFindFile(filename: string): string =
# we don't find any files in online mode: # we don't find any files in online mode:
result = "" result = ""
@ -692,4 +771,8 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
result = "" result = ""
renderRstToOut(d, rst, result) renderRstToOut(d, rst, result)
when isMainModule:
echo rstToHtml("*Hello* **world**!", {},
newStringTable(modeStyleInsensitive))

View file

@ -33,10 +33,10 @@ proc openDefaultBrowser*(url: string) =
else: else:
discard ShellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL) discard ShellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
elif defined(macosx): elif defined(macosx):
discard execShellCmd("open " & quoteIfContainsWhite(url)) discard execShellCmd("open " & quoteShell(url))
else: else:
const attempts = ["gnome-open ", "kde-open ", "xdg-open "] const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
var u = quoteIfContainsWhite(url) var u = quoteShell(url)
for a in items(attempts): for a in items(attempts):
if execShellCmd(a & u) == 0: return if execShellCmd(a & u) == 0: return
for b in getEnv("BROWSER").string.split(PathSep): for b in getEnv("BROWSER").string.split(PathSep):

View file

@ -342,16 +342,35 @@ proc writeContentType*() =
## ##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
## write(stdout, "Content-type: text/html\n\n") ## write(stdout, "Content-type: text/html\n\n")
##
## It also modifies the debug stack traces so that they contain
## ``<br />`` and are easily readable in a browser.
write(stdout, "Content-type: text/html\n\n") write(stdout, "Content-type: text/html\n\n")
system.stackTraceNewLine = "<br />\n"
proc setStackTraceNewLine*() = proc resetForStacktrace() =
## Modifies the debug stack traces so that they contain stdout.write """<!--: spam
## ``<br />`` and are easily readable in a browser. Content-Type: text/html
system.stackTraceNewLine = "<br />\n"
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
</font> </font> </font> </script> </object> </blockquote> </pre>
</table> </table> </table> </table> </table> </font> </font> </font>
"""
proc writeErrorMessage*(data: string) =
## Tries to reset browser state and writes `data` to stdout in
## <plaintext> tag.
resetForStacktrace()
# We use <plaintext> here, instead of escaping, so stacktrace can
# be understood by human looking at source.
stdout.write("<plaintext>\n")
stdout.write(data)
proc setStackTraceStdout*() =
## Makes Nimrod output stacktraces to stdout, instead of server log.
errorMessageWriter = writeErrorMessage
proc setStackTraceNewLine*() {.deprecated.} =
## Makes Nimrod output stacktraces to stdout, instead of server log.
## Depracated alias for setStackTraceStdout.
setStackTraceStdout()
proc setCookie*(name, value: string) = proc setCookie*(name, value: string) =
## Sets a cookie. ## Sets a cookie.
@ -374,4 +393,3 @@ when isMainModule:
const test1 = "abc\L+def xyz" const test1 = "abc\L+def xyz"
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz" assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
assert UrlDecode(UrlEncode(test1)) == test1 assert UrlDecode(UrlEncode(test1)) == test1

View file

@ -956,17 +956,37 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
if crename(source, dest) != 0'i32: if crename(source, dest) != 0'i32:
raise newException(EOS, $strerror(errno)) raise newException(EOS, $strerror(errno))
when not defined(ENOENT): when not defined(ENOENT) and not defined(Windows):
var ENOENT {.importc, header: "<errno.h>".}: cint var ENOENT {.importc, header: "<errno.h>".}: cint
when defined(Windows):
when useWinUnicode:
template DeleteFile(file: expr): expr {.immediate.} = DeleteFileW(file)
template SetFileAttributes(file, attrs: expr): expr {.immediate.} =
SetFileAttributesW(file, attrs)
else:
template DeleteFile(file: expr): expr {.immediate.} = DeleteFileA(file)
template SetFileAttributes(file, attrs: expr): expr {.immediate.} =
SetFileAttributesA(file, attrs)
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Removes the `file`. If this fails, `EOS` is raised. This does not fail ## Removes the `file`. If this fails, `EOS` is raised. This does not fail
## if the file never existed in the first place. ## if the file never existed in the first place.
## On Windows, ignores the read-only attribute. ## On Windows, ignores the read-only attribute.
when defined(Windows): when defined(Windows):
setFilePermissions(file, {fpUserWrite}) when useWinUnicode:
if cremove(file) != 0'i32 and errno != ENOENT: let f = newWideCString(file)
raise newException(EOS, $strerror(errno)) else:
let f = file
if DeleteFile(f) == 0:
if GetLastError() == ERROR_ACCESS_DENIED:
if SetFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
OSError(OSLastError())
if DeleteFile(f) == 0:
OSError(OSLastError())
else:
if cremove(file) != 0'i32 and errno != ENOENT:
raise newException(EOS, $strerror(errno))
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [FExecIO].} = tags: [FExecIO].} =

View file

@ -41,6 +41,58 @@ type
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams poParentStreams ## use the parent's streams
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to Windows API.
## Based on Python's subprocess.list2cmdline
## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
let needQuote = {' ', '\t'} in s or s.len == 0
result = ""
var backslashBuff = ""
if needQuote:
result.add("\"")
for c in s:
if c == '\\':
backslashBuff.add(c)
elif c == '\"':
result.add(backslashBuff)
result.add(backslashBuff)
backslashBuff.setLen(0)
result.add("\\\"")
else:
if backslashBuff.len != 0:
result.add(backslashBuff)
backslashBuff.setLen(0)
result.add(c)
if needQuote:
result.add("\"")
proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to POSIX shell.
## Based on Python's pipes.quote
const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
'0'..'9', 'A'..'Z', 'a'..'z'}
if s.len == 0:
return "''"
let safe = s.allCharsInSet(safeUnixChars)
if safe:
return s
else:
return "'" & s.replace("'", "'\"'\"'") & "'"
proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to shell.
when defined(Windows):
return quoteShellWindows(s)
elif defined(posix):
return quoteShellPosix(s)
else:
{.error:"quoteShell is not supported on your system".}
proc execProcess*(command: string, proc execProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): TaintedString {. poUseShell}): TaintedString {.
@ -307,10 +359,10 @@ when defined(Windows) and not defined(useNimRtl):
result.writeDataImpl = hsWriteData result.writeDataImpl = hsWriteData
proc buildCommandLine(a: string, args: openarray[string]): cstring = proc buildCommandLine(a: string, args: openarray[string]): cstring =
var res = quoteIfContainsWhite(a) var res = quoteShell(a)
for i in 0..high(args): for i in 0..high(args):
res.add(' ') res.add(' ')
res.add(quoteIfContainsWhite(args[i])) res.add(quoteShell(args[i]))
result = cast[cstring](alloc0(res.len+1)) result = cast[cstring](alloc0(res.len+1))
copyMem(result, cstring(res), res.len) copyMem(result, cstring(res), res.len)
@ -510,10 +562,10 @@ elif not defined(useNimRtl):
writeIdx = 1 writeIdx = 1
proc addCmdArgs(command: string, args: openarray[string]): string = proc addCmdArgs(command: string, args: openarray[string]): string =
result = quoteIfContainsWhite(command) result = quoteShell(command)
for i in 0 .. high(args): for i in 0 .. high(args):
add(result, " ") add(result, " ")
add(result, quoteIfContainsWhite(args[i])) add(result, quoteShell(args[i]))
proc toCStringArray(b, a: openarray[string]): cstringArray = proc toCStringArray(b, a: openarray[string]): cstringArray =
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring))) result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
@ -792,5 +844,14 @@ proc execCmdEx*(command: string, options: set[TProcessOption] = {
close(p) close(p)
when isMainModule: when isMainModule:
var x = execProcess("gcc -v") assert quoteShellWindows("aaa") == "aaa"
echo "ECHO ", x assert quoteShellWindows("aaa\"") == "aaa\\\""
assert quoteShellWindows("") == "\"\""
assert quoteShellPosix("aaa") == "aaa"
assert quoteShellPosix("aaa a") == "'aaa a'"
assert quoteShellPosix("") == "''"
assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
when defined(posix):
assert quoteShell("") == "''"

View file

@ -709,9 +709,11 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
if result != -1: return if result != -1: return
return -1 return -1
proc quoteIfContainsWhite*(s: string): string = proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
## returns ``'"' & s & '"'`` if `s` contains a space and does not ## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s` ## start with a quote, else returns `s`
## DEPRECATED as it was confused for shell quoting function.
## For this application use osproc.quoteShell.
if find(s, {' ', '\t'}) >= 0 and s[0] != '"': if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
result = '"' & s & '"' result = '"' & s & '"'
else: else:

View file

@ -923,6 +923,10 @@ proc quit*(errorcode: int = QuitSuccess) {.
## The proc ``quit(QuitSuccess)`` is called implicitly when your nimrod ## The proc ``quit(QuitSuccess)`` is called implicitly when your nimrod
## program finishes without incident. A raised unhandled exception is ## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``. ## equivalent to calling ``quit(QuitFailure)``.
##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't
## have any compile time effect. If you need to stop the compiler inside a
## macro, use the ``error`` or ``fatal`` pragmas.
template sysAssert(cond: bool, msg: string) = template sysAssert(cond: bool, msg: string) =
when defined(useSysAssert): when defined(useSysAssert):
@ -1947,15 +1951,13 @@ when not defined(JS): #and not defined(NimrodVM):
## The standard output stream. ## The standard output stream.
stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile
## The standard error stream. ## The standard error stream.
##
## Note: In my opinion, this should not be used -- the concept of a when defined(useStdoutAsStdmsg):
## separate error stream is a design flaw of UNIX. A separate *message template stdmsg*: TFile = stdout
## stream* is a good idea, but since it is named ``stderr`` there are few else:
## programs out there that distinguish properly between ``stdout`` and template stdmsg*: TFile = stderr
## ``stderr``. So, that's what you get if you don't name your variables ## Template which expands to either stdout or stderr depending on
## appropriately. It also annoys people if redirection ## `useStdoutAsStdmsg` compile-time switch.
## via ``>output.txt`` does not work because the program writes
## to ``stderr``.
proc Open*(f: var TFile, filename: string, proc Open*(f: var TFile, filename: string,
mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].} mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}

View file

@ -760,7 +760,7 @@ proc getOccupiedMem(a: TMemRegion): int {.inline.} =
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
template InstantiateForRegion(allocator: expr) = template InstantiateForRegion(allocator: expr) =
when false: when defined(fulldebug):
proc interiorAllocatedPtr*(p: pointer): pointer = proc interiorAllocatedPtr*(p: pointer): pointer =
result = interiorAllocatedPtr(allocator, p) result = interiorAllocatedPtr(allocator, p)

View file

@ -11,14 +11,13 @@
# use the heap (and nor exceptions) do not include the GC or memory allocator. # use the heap (and nor exceptions) do not include the GC or memory allocator.
var var
stackTraceNewLine*: string ## undocumented feature; it is replaced by ``<br>`` errorMessageWriter*: (proc(msg: string): void {.tags: [FWriteIO].})
## for CGI applications ## Function that will be called
## instead of stdmsg.write when printing stacktrace.
template stackTraceNL: expr = ## Unstable API.
(if IsNil(stackTraceNewLine): "\n" else: stackTraceNewLine)
when not defined(windows) or not defined(guiapp): when not defined(windows) or not defined(guiapp):
proc writeToStdErr(msg: CString) = write(stdout, msg) proc writeToStdErr(msg: CString) = write(stdmsg, msg)
else: else:
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {. proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
@ -27,6 +26,12 @@ else:
proc writeToStdErr(msg: CString) = proc writeToStdErr(msg: CString) =
discard MessageBoxA(0, msg, nil, 0) discard MessageBoxA(0, msg, nil, 0)
proc showErrorMessage(data: cstring) =
if errorMessageWriter != nil:
errorMessageWriter($data)
else:
writeToStdErr(data)
proc chckIndx(i, a, b: int): int {.inline, compilerproc.} proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
proc chckRange(i, a, b: int): int {.inline, compilerproc.} proc chckRange(i, a, b: int): int {.inline, compilerproc.}
proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
@ -111,7 +116,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
add(s, tempDlInfo.dli_sname) add(s, tempDlInfo.dli_sname)
else: else:
add(s, '?') add(s, '?')
add(s, stackTraceNL) add(s, "\n")
else: else:
if dlresult != 0 and tempDlInfo.dli_sname != nil and if dlresult != 0 and tempDlInfo.dli_sname != nil and
c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32: c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
@ -172,21 +177,18 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
add(s, ')') add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, tempFrames[j].procname) add(s, tempFrames[j].procname)
add(s, stackTraceNL) add(s, "\n")
when hasSomeStackTrace: when hasSomeStackTrace:
proc rawWriteStackTrace(s: var string) = proc rawWriteStackTrace(s: var string) =
when nimrodStackTrace: when nimrodStackTrace:
if framePtr == nil: if framePtr == nil:
add(s, "No stack traceback available") add(s, "No stack traceback available\n")
add(s, stackTraceNL)
else: else:
add(s, "Traceback (most recent call last)") add(s, "Traceback (most recent call last)\n")
add(s, stackTraceNL)
auxWriteStackTrace(framePtr, s) auxWriteStackTrace(framePtr, s)
elif defined(nativeStackTrace) and nativeStackTraceSupported: elif defined(nativeStackTrace) and nativeStackTraceSupported:
add(s, "Traceback from system (most recent call last)") add(s, "Traceback from system (most recent call last)\n")
add(s, stackTraceNL)
auxWriteStackTraceWithBacktrace(s) auxWriteStackTraceWithBacktrace(s)
else: else:
add(s, "No stack traceback available\n") add(s, "No stack traceback available\n")
@ -207,7 +209,7 @@ proc raiseExceptionAux(e: ref E_Base) =
pushCurrentException(e) pushCurrentException(e)
c_longjmp(excHandler.context, 1) c_longjmp(excHandler.context, 1)
elif e[] of EOutOfMemory: elif e[] of EOutOfMemory:
writeToStdErr(e.name) showErrorMessage(e.name)
quitOrDebug() quitOrDebug()
else: else:
when hasSomeStackTrace: when hasSomeStackTrace:
@ -219,7 +221,7 @@ proc raiseExceptionAux(e: ref E_Base) =
add(buf, " [") add(buf, " [")
add(buf, $e.name) add(buf, $e.name)
add(buf, "]\n") add(buf, "]\n")
writeToStdErr(buf) showErrorMessage(buf)
else: else:
# ugly, but avoids heap allocations :-) # ugly, but avoids heap allocations :-)
template xadd(buf, s, slen: expr) = template xadd(buf, s, slen: expr) =
@ -235,7 +237,7 @@ proc raiseExceptionAux(e: ref E_Base) =
add(buf, " [") add(buf, " [")
xadd(buf, e.name, c_strlen(e.name)) xadd(buf, e.name, c_strlen(e.name))
add(buf, "]\n") add(buf, "]\n")
writeToStdErr(buf) showErrorMessage(buf)
quitOrDebug() quitOrDebug()
proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
@ -255,9 +257,9 @@ proc WriteStackTrace() =
when hasSomeStackTrace: when hasSomeStackTrace:
var s = "" var s = ""
rawWriteStackTrace(s) rawWriteStackTrace(s)
writeToStdErr(s) showErrorMessage(s)
else: else:
writeToStdErr("No stack traceback available\n") showErrorMessage("No stack traceback available\n")
proc getStackTrace(): string = proc getStackTrace(): string =
when hasSomeStackTrace: when hasSomeStackTrace:
@ -298,13 +300,13 @@ when not defined(noSignalHandler):
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)
rawWriteStackTrace(buf) rawWriteStackTrace(buf)
processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) processSignal(sig, buf.add) # nice hu? currying a la nimrod :-)
writeToStdErr(buf) showErrorMessage(buf)
GC_enable() GC_enable()
else: else:
var msg: cstring var msg: cstring
template asgn(y: expr) = msg = y template asgn(y: expr) = msg = y
processSignal(sig, asgn) processSignal(sig, asgn)
writeToStdErr(msg) showErrorMessage(msg)
when defined(endb): dbgAborting = True when defined(endb): dbgAborting = True
quit(1) # always quit when SIGABRT quit(1) # always quit when SIGABRT

View file

@ -345,8 +345,9 @@ proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: TWalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(cell.typ != nil, "forAllChildren: 2") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" gcAssert(cell.typ != nil, "forAllChildren: 3")
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
let marker = cell.typ.marker let marker = cell.typ.marker
if marker != nil: if marker != nil:
marker(cellToUsr(cell), op.int) marker(cellToUsr(cell), op.int)
@ -361,7 +362,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
for i in 0..s.len-1: for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +% forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: nil else: discard
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
@ -408,8 +409,10 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
add(gch.zct, res) add(gch.zct, res)
{.push stackTrace: off, profiler:off.} {.push stackTrace: off, profiler:off.}
proc gcInvariant*(msg: string) = proc gcInvariant*() =
sysAssert(allocInv(gch.region), msg) sysAssert(allocInv(gch.region), "injected")
when defined(markForDebug):
markForDebug(gch)
{.pop.} {.pop.}
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =

View file

@ -18,7 +18,8 @@ const
logGC = false logGC = false
traceGC = false # extensive debugging traceGC = false # extensive debugging
alwaysCycleGC = false alwaysCycleGC = false
alwaysGC = false # collect after every memory allocation (for debugging) alwaysGC = defined(fulldebug) # collect after every memory
# allocation (for debugging)
leakDetector = false leakDetector = false
overwriteFree = false overwriteFree = false
trackAllocationSource = leakDetector trackAllocationSource = leakDetector

View file

@ -416,17 +416,17 @@ var
SOMAXCONN* {.importc, header: "Winsock2.h".}: cint SOMAXCONN* {.importc, header: "Winsock2.h".}: cint
INVALID_SOCKET* {.importc, header: "Winsock2.h".}: TSocketHandle INVALID_SOCKET* {.importc, header: "Winsock2.h".}: TSocketHandle
SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint SOL_SOCKET* {.importc, header: "Winsock2.h".}: cint
SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording SO_DEBUG* {.importc, header: "Winsock2.h".}: cint ## turn on debugging info recording
SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen() SO_ACCEPTCONN* {.importc, header: "Winsock2.h".}: cint # socket has had listen()
SO_REUSEADDR* {.importc, header: "Winsock2.h".}: cint # allow local address reuse SO_REUSEADDR* {.importc, header: "Winsock2.h".}: cint # allow local address reuse
SO_KEEPALIVE* {.importc, header: "Winsock2.h".}: cint # keep connections alive SO_KEEPALIVE* {.importc, header: "Winsock2.h".}: cint # keep connections alive
SO_DONTROUTE* {.importc, header: "Winsock2.h".}: cint # just use interface addresses SO_DONTROUTE* {.importc, header: "Winsock2.h".}: cint # just use interface addresses
SO_BROADCAST* {.importc, header: "Winsock2.h".}: cint # permit sending of broadcast msgs SO_BROADCAST* {.importc, header: "Winsock2.h".}: cint # permit sending of broadcast msgs
SO_USELOOPBACK* {.importc, header: "Winsock2.h".}: cint # bypass hardware when possible SO_USELOOPBACK* {.importc, header: "Winsock2.h".}: cint # bypass hardware when possible
SO_LINGER* {.importc, header: "Winsock2.h".}: cint # linger on close if data present SO_LINGER* {.importc, header: "Winsock2.h".}: cint # linger on close if data present
SO_OOBINLINE* {.importc, header: "Winsock2.h".}: cint # leave received OOB data in line SO_OOBINLINE* {.importc, header: "Winsock2.h".}: cint # leave received OOB data in line
SO_DONTLINGER* {.importc, header: "Winsock2.h".}: cint SO_DONTLINGER* {.importc, header: "Winsock2.h".}: cint
SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse
proc `==`*(x, y: TSocketHandle): bool {.borrow.} proc `==`*(x, y: TSocketHandle): bool {.borrow.}
@ -553,18 +553,26 @@ const
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
# Error Constants
const
ERROR_ACCESS_DENIED* = 5
when useWinUnicode: when useWinUnicode:
proc CreateFileW*(lpFileName: widecstring, dwDesiredAccess, dwShareMode: DWORD, proc CreateFileW*(lpFileName: widecstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer, lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD, dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {. hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileW".} stdcall, dynlib: "kernel32", importc: "CreateFileW".}
proc DeleteFileW*(pathName: widecstring): int32 {.
importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
else: else:
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD, proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer, lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD, dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {. hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileA".} stdcall, dynlib: "kernel32", importc: "CreateFileA".}
proc DeleteFileA*(pathName: cstring): int32 {.
importc: "DeleteFileA", dynlib: "kernel32", stdcall.}
proc SetEndOfFile*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32", proc SetEndOfFile*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".} importc: "SetEndOfFile".}

View file

@ -14,7 +14,7 @@ when haveZipLib:
import zipfiles import zipfiles
import import
os, strutils, parseopt, parsecfg, strtabs, streams, debcreation os, osproc, strutils, parseopt, parsecfg, strtabs, streams, debcreation
const const
maxOS = 20 # max number of OSes maxOS = 20 # max number of OSes
@ -486,7 +486,7 @@ proc setupDist(c: var TConfigData) =
if c.innoSetup.path.len == 0: if c.innoSetup.path.len == 0:
c.innoSetup.path = "iscc.exe" c.innoSetup.path = "iscc.exe"
var outcmd = if c.outdir.len == 0: "build" else: c.outdir var outcmd = if c.outdir.len == 0: "build" else: c.outdir
var cmd = "$# $# /O$# $#" % [quoteIfContainsWhite(c.innoSetup.path), var cmd = "$# $# /O$# $#" % [quoteShell(c.innoSetup.path),
c.innoSetup.flags, outcmd, n] c.innoSetup.flags, outcmd, n]
echo(cmd) echo(cmd)
if execShellCmd(cmd) == 0: if execShellCmd(cmd) == 0:
@ -587,4 +587,3 @@ if actionZip in c.actions:
quit("libzip is not installed") quit("libzip is not installed")
if actionDeb in c.actions: if actionDeb in c.actions:
debDist(c) debDist(c)

View file

@ -28,7 +28,8 @@ Changes affecting backwards compatibility
require an error code to be passed to them. This error code can be retrieved require an error code to be passed to them. This error code can be retrieved
using the new ``OSLastError`` proc. using the new ``OSLastError`` proc.
- ``os.parentDir`` now returns "" if there is no parent dir. - ``os.parentDir`` now returns "" if there is no parent dir.
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout
is used.
Compiler Additions Compiler Additions
------------------ ------------------